Ruby: eBPFでメモリアロケーションをプロファイリングする(翻訳)

概要 原著者の許可および指示により「非公式の翻訳として」翻訳・公開いたします。 英語記事: Spying on a Ruby process’s memory allocations with eBPF - Julia Evans 原文公開日: 2018/01/31 著者: Julia Evans なお、eBPFはBPF(Berkeley Packet Filter)の拡張版(extended BPF)です。 参考: LinuxのBPF : (1) パケットフィルタ - 睡分不足 参考: LinuxのBPF : (3) eBPFの基礎 - 睡分不足 Ruby: eBPFでメモリアロケーションをプロファイリングする(翻訳) 今回は、CPUプロファイラを使う代わりに、まったく新しいアイデアに基づいた実験を披露いたします。 私がその日の朝に思いついたのは、Rubyの任意のプロセスのPID(既に実行されているプロセスのPID)を取得してメモリアロケーションを追えるのではないかというものでした。 ネタバレ: 一応動きました!その様子をasciinemaのデモでご覧いただけます。ここでは、rubocopのメモリアロケーションをクラスごとにカウントしたものを15秒間に渡って刻々と累積的に表示しています。rubocopが数千個ものArrayやStringやRange、そして若干のEnumeratorなどをアロケーションしている様子がわかります。 このデモは、rubocopのコードをまったく変更せずに実行しています。起動も普通のbundle exec rubocopで行いました。デモのコードはすべてhttps://github.com/jvns/ruby-mem-watcher-demoにあります(非常に実験的なコードなのでもしかすると今のところ私のPCでしか動かないかもしれませんが)。 しくみ(その1): eBPF + uprobes 基本的な動作の仕組みは比較的シンプルです。Linux 4.4以降ではuprobesと呼ばれる機能を利用可能です。これは自分の書いたコードをユーザー空間上の任意の関数にアタッチするもので、プロセスの外部から行えます。プログラムの実行中に対象となる関数の変更をカーネルに依頼し、関数が呼び出されるたびに自分のコードを実行します。 ただしカーネルに任意のコードの実行を依頼することは(少なくともeBPFがなければ)普通はできません。カーネルに依頼するのは「eBPFバイトコード」の実行であり、これは基本的にアクセス可能なメモリ領域を制限されているCコードです。かつ、ループは使えません。 私のアイデアは、rubocop内でRubyの新しいオブジェクトが作成されるたびにごく小さなコード片を実行し、そのコードでクラスごとのメモリアロケーションを数えるというものです。 … Continue reading Ruby: eBPFでメモリアロケーションをプロファイリングする(翻訳)