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
他にも、ネットワークのサンプルとかが多数含まれているので、要チェックです。