SystemtapでディスクI/Oの監視
はてぶでhttp://d.hatena.ne.jp/sh2/20100120を見かけたので、ちょっと調べてみました。id:sh2はperlと組み合わせて処理していますが、定期的にサマリを表示させるのならstapだけでもいいですね。
ただ、カーネル内でfdからパス名に変換するのって、ちょっとスクリプト内で完結させるのは難しいかもしれません。(多分TOMOYOはこのあたりを処理しているはずなのでなんとかなると思うけど・・・proc以下をみるほうが簡単な気がしました*1)
シェルスクリプトと組み合わせてチートさせてみたのが以下の実装です。特に新しい機能とかつけていません。
- iotrace.sh
#!/bin/sh //usr/bin/env stap $0 $1 | while read pid fd data; do if [ -z "$pid" ]; then echo; continue; fi; file=`readlink /proc/$pid/fd/$fd`; echo $1\($pid\) $data from $file; done; exit $? # --- systemtap script --- global rdbs, wrbs; probe syscall.read, syscall.pread { if (execname() == @1) rdbs[pid(), fd] <<< count; } probe syscall.write, syscall.pwrite { if (execname() == @1) wrbs[pid(), fd] <<< count; } probe timer.s(5) { foreach ([pid+, fd] in rdbs) { printf("%d %d read %d B/s (avg %d bytes)\n", pid, fd, @sum(rdbs[pid, fd])/5, @avg(rdbs[pid, fd])); } foreach ([pid+, fd] in wrbs) { printf("%d %d write %d B/s (avg %d bytes)\n", pid, fd, @sum(wrbs[pid, fd])/5, @avg(wrbs[pid, fd])); } delete rdbs delete wrbs println(""); }
一行目がやたら長いのはご愛嬌:)。
$ sudo iotrace.sh firefox
とかすると、指定した名前のプロセスの出力がこんな風に出ます。
firefox(1974) read 435814 B/s (avg 4096 bytes) from socket:[17483] firefox(1974) read 18022 B/s (avg 2048 bytes) from socket:[17596] firefox(1974) read 33 B/s (avg 1 bytes) from pipe:[17500] firefox(1974) read 204 B/s (avg 1024 bytes) from pipe:[17493] firefox(1974) write 33 B/s (avg 1 bytes) from pipe:[17500] firefox(1974) write 8842 B/s (avg 44210 bytes) from firefox(1974) write 0 B/s (avg 1 bytes) from pipe:[17493] firefox(1974) read 611942 B/s (avg 4096 bytes) from socket:[17483] firefox(1974) read 9830 B/s (avg 2048 bytes) from socket:[17596] firefox(1974) read 29 B/s (avg 1 bytes) from pipe:[17500] firefox(1974) write 29 B/s (avg 1 bytes) from pipe:[17500]
簡単な説明
- @Nを使うと、コマンドの引数を、文字列として取り込めます。$Nだと、スクリプト内の数値として使えます。
- トレースデータはその場で出さず、statistics変数に溜め込み、平均値とか最大値を出せるようにしておくと、トレース出力も減らせて一石二鳥です。
- シェルスクリプトに融合させてしまう*2。http://d.hatena.ne.jp/mhiramat/20081219/1229658198でも触れたように、行の頭が//で始まっていれば、systemtap自体はその行を無視するので、シェルスクリプトと融合させるのはそんなに難しくないです。一行野郎はいやだ、という方には、行頭に//bin/true;とか書くのはどうでしょう(笑)。
systemtap公式のサンプルスクリプト
しかし本当に重要なのは、systemtapのサンプルスクリプトは、systemtap-testsuiteパッケージに含まれているという事実ではないでしょうか・・・。実のところ、/usr/share/systemtap/testsuite/systemtap.examples/以下に、上記のようなioのトレースサンプルがいくつも含まれていたりします。
$ ls /usr/share/systemtap/testsuite/systemtap.examples/io/ disktop.meta iostat-scsi.stp io_submit.stp mbrwatch.meta traceio.stp disktop.stp iostat-scsi.txt iotime.meta mbrwatch.stp ttyspy.meta ioblktime.meta iostats.meta iotime.stp traceio2.meta ttyspy.stp ioblktime.stp iostats.stp iotop.meta traceio2.stp iostat-scsi.meta io_submit.meta iotop.stp traceio.meta
他にも、ネットワークのサンプルとかが多数含まれているので、要チェックです。
perf probeとは何か。
perf-toolsでは、カーネル内のトレースポイントを「イベント」として扱っています。perf probeでは、後付けで追加の「イベント」を動的に定義できるようになります。perf-probeでは、カーネルのデバッグ情報を利用して、関数の行番号を指定して新しい「イベント」の場所を定義したり、関数のローカル変数を「イベント」の引数に指定できます。
デバッガで言うところの、ブレークポイントを指定するようなものです。
perf probe導入方法
2.6.33-rc1でperf probeがサポートされたので、導入方法などを書いておきます。
- libdwarf-dev(el)パッケージ(20081218以降)をインストールする。
- 注意:Ubuntuの人は、残念ながらlibdwarfが古い模様。(10.04だと大丈夫っぽい)
- 2.6.33-rc1か、tipツリーを持ってきてビルド→インストール→再起動
- CONFIG_KPROBES, CONFIG_KPROBE_EVENT, CONFIG_DEBUG_INFOを有効にしておくこと。
- tools/perf/以下で、makeを実行する。
- ./perf probe -hで使い方が表示されるはず。
perf probe簡単な使い方
ちなみに、イベントの追加は今のところrootユーザしか出来ません。
- XXX関数のYYY引数をとってくるイベントを定義したい。
./perf probe XXX YYY
- XXX関数の頭からNNN行目にイベントを定義したい。
./perf probe XXX:NNN
- FFF.cファイルのNNN行目にイベントを定義したい。(ファイル名か関数かは、'.'があるかどうかだけで判断しています(適当))
./perf probe FFF.c:NNN
- XXX関数が戻るときに、戻り値をみるイベントを定義したい。
./perf probe XXX%return '$retval'
- 上記のイベントをリストする
./perf probe -l
- 上記のイベントを使ってlsの情報を見る。(使い方はトレースポイントのイベントと同じ)
./perf record -e 'probe:イベント名:record' -a ls ./perf trace
多分こんな感じで出来たと思う。
ハマりポイント
- perf probe --add XXX YYY ってしたら'YYY'がないって怒られた。→--addオプションを使うときは、イベントの定義を''で囲むなどして、一つの引数にしてください。(この場合は perf probe --add 'XXX YYY'になります)
- XXX関数が1000行目にあるので、XXX:1020としたら見つからないって言われた。→'関数:行番号'の書き方では、行番号は関数の頭からの相対行番号になります。(この場合は XXX:20が正しい)
- XXX関数の30行目を指定したら、見つからないって言われた。→最適化などで消えたりする行は、デバッグ情報に含まれていないことがあります。前後の行を指定してみて試してください。
- XXX関数の10行目を指定したら、いっぱい見つかった。→最適化でループが展開されたりして、そうなることがあります。気にしないで全部トレースしましょう。
本日のtip/auto-latest
$ git shortlog -n -s -e v2.6.32..
189 Linus Torvalds
133 Arnaldo Carvalho de Melo
127 Masami Hiramatsu
。 。
/ / ポーン!
( Д )
100越えたらしいのは知ってたが、これほどとは。
perf probeが効いているに違いない。
出生届を出しにいく
生後一ヶ月と少したって、ようやく生活のリズムも分かってきたところです。
出生証明書も手に入れた(生まれたボストン市ではなく、地元の役所にコピーが届くのを待っていた)ので、総領事館に出生届を出してきました。
金曜日ということもあって道が混んでいたりしたので、総領事館についたのが午後3:53、窓口は4:00に閉まるらしいので、ぎりぎりでした(実際は融通が効くみたいでしたが・・・)。
窓口で聞いてみて、以下のことが分かりました。
- 米国の出生証明の登録をXX市で行い、そのコピーを地元でとったときは、出生届には地元の役所で発給されたと書く。
- City of XXはXX市、Town of YYはYY町である。(アメリカだと郊外はほとんど町になるような・・・)
- 出生証明書の要約は、間違っても訂正印は不要。
あとは2ヶ月ぐらい待って、戸籍に載っているかどうか日本に問い合わせてOKだったら、日本のパスポートが作れるようです。
これまでの流れのまとめ
LKMLを追っかけている人には分かるかもしれないですが。
- SystemtapがカーネルメンテナにDisられる。
- このままだとkprobesもDisられかねない?
- 対策としてkprobesをftraceから使えるようにする(カーネル内ユーザが必要ということで)と、意外に受ける。
- x86命令の区切りが分からないとkprobesのプローブポイント指定できないんじゃね?
- kprobe-tracerいいけど、ほかのトレーサと整合性がないよね?
- Dynamic tracepoint tracerという位置づけにする。tracepointライクなインタフェースを追加。
- アセンブリレベルなのは使いにくいよ。Debuginfo使えない?
- c2kpeコマンドはどうでしょうか。
- perf-tools作ってるんだけど、それこっちに入れられない?
- c2kpeからperf-probeへ移植
- Oops, x86命令デコーダがサポートしていない命令があるよ!
- AVX命令かよ!まだプロセッサも出てないよ・・・てかそれデータバイト列ですからorz < 今ここ
なんか泥縄式に作るものが増えているような・・・。まあゴールは目前のはず!
しばらくぶりに更新
ほったらかしにしていたらスパムコメントが生えていた・・・orz