こちらの続きです。第2回は拙作「BPFQL」を紹介したく。RbBCCの実例として2つのプロトタイプを作ったうちの1つです。
まず、汎用トレースツール bpftrace の話
前回RbBCCを紹介しましたが、一般にRbBCCやBCCは「サーバの特定の状況をトレースするために」使われます。たとえばdisk I/Oのレイテンシが知りたい、ページフォールトの状況が知りたい、TCPパケットの受信状況は、などなど...。
「どういう状況の時に」BCC製の「どのツールが」使われるかは上記のvargoさんの記事にも一部紹介されています。
一方で、私たちの運用しているサーバはどういう状況に遭遇するか簡単には予測できない面もあります。そういう場合に、初動などで様々な値を汎用的に取れるようにしているツールとしてbpftraceが存在し、tracepointやkprobe等の知識があればこのコマンドだけで非常にたくさんの情報が取得できます。
独自の外部DSL、bpftrace言語
で、bpftraceは以下のような、DTrace言語に似た外部DSLを利用するわけですが。
BEGIN { printf("Tracing block device I/O... Hit Ctrl-C to end.\n"); } kprobe:blk_account_io_start { @start[arg0] = nsecs; } kprobe:blk_account_io_done /@start[arg0]/ { @usecs = hist((nsecs - @start[arg0]) / 1000); delete(@start[arg0]); } END { clear(@start); }
以下より。
何回か素振りで書いていると結構、
- 冗長な時がある(正直pid、comm、タイムスタンプは毎回表示したい)
hist()
?count()
? 集計関数に癖がある...- 結構定型的な表現が多い(上記のようなレイテンシの計測など)
ということでもっと宣言的にシュッとかけないかなあ? という思いから作ってみたのがBPFQLでした。
YAML/Ruby のDSLで宣言的に欲しい値をトレースする BPFQL
論より証拠というか、以下がBPFQLのスクリプトです。
BPFQL do select "ts", "comm", "pid", "nr_sector" from "tracepoint:block:block_rq_complete" end
markdownでも ruby でシンタクスハイライトできる、完全なRubyの内部DSLです。 block:block_rq_complete
はブロックデバイスへのI/O要求の完了に関するtracepointです。
これをあるターミナルでbpfqlコマンドに渡すと、フィールドが出てきてブロックします。
bpfql blockreq.rb TS COMM PID NR_SECTOR
しばらく待つとマシン内でのブロックI/Oが実行されたことにより影響を受けた「セクタ数」が表示されます。
# bpfql blockreq.rb TS COMM PID NR_SECTOR 0.000000000 swapper/0 0 0 0.001375301 swapper/1 0 0 0.001369696 swapper/2 0 64 0.001528832 bpfql 2405 8 0.001593498 gmain 686 0 0.002032037 swapper/1 0 0 1.004273509 swapper/0 0 0 1.005479007 swapper/1 0 0 1.005726634 swapper/0 0 0 1.006048318 swapper/1 0 0 1.034544968 swapper/0 0 24 ...
今度はスクリプトを以下のように変更しましょう。今回は、しばらく待っても表示されるレコードが増えないと思います。
BPFQL do select "ts", "comm", "pid", "nr_sector" from "tracepoint:block:block_rq_complete" where "comm", is: "dd" end
別のターミナルで以下のコマンドを動かすと、
sudo dd if=/dev/sda1 of=/dev/null bs=1M
dd による読み込みセクタ数がすごい勢いで出てきます。このように「 where
句」での絞り込みは実装しているため、例えば特定のコマンドのみ追いかけることが可能です。ddコマンドの bs の値をいろいろに変えて変化を見ると面白いかもしれません。
0.518328203 dd 2523 1024 0.518666974 dd 2523 1024 0.524834622 dd 2523 1024 0.526855612 dd 2523 1024 0.531083256 dd 2523 1024 0.532084090 dd 2523 1024 0.535336335 dd 2523 1024 0.536024702 dd 2523 1024 0.551834078 dd 2523 1024 0.555861535 dd 2523 1024 0.556636709 dd 2523 1024 0.560012807 dd 2523 1024 0.561375370 dd 2523 1024 0.565537604 dd 2523 1024 0.566284519 dd 2523 1024 0.572071917 dd 2523 1024 0.576701969 dd 2523 2048 0.581984174 dd 2523 1024 0.582651214 dd 2523 1024 0.586457406 dd 2523 1024 ...
Caveats
さて、RubyのDSLという「いかにも直感的な見た目」を生かしたツールで、なんとなく書けそうな感じがする、これはすごい! みたいな印象を持たれたかもしれませんが...。
BPFQL はまだ絶賛開発中の段階で、たとえば:
- tracepoint しかトレースできません
- comm 以外の
char[]
のフィールドを表示できません - hist(), count() ほか集計関数は使えません
- レイテンシがまだ見られません
- エラーハンドリングなどもイマイチ...
と言った制約があり、PoCの段階を出ていません... -_-b
一方で、これらの未実装の機能を一通り実装すれば、それなりに使いでがあるツールに化ける気もしています。Contributionを歓迎します...。
ということで、せっかくRubyでBCCを使えるということでそれっぽいツールを一つ紹介しました。
BPFQL も前回の rbbcc と同じく、
gem install bpfql
でインストールできます。先述の通りバグも多いのですが... お試しください。