SystemtapでディスクI/Oの監視

はてぶでhttp://d.hatena.ne.jp/sh2/20100120を見かけたので、ちょっと調べてみました。id:sh2perlと組み合わせて処理していますが、定期的にサマリを表示させるのなら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変数に溜め込み、平均値とか最大値を出せるようにしておくと、トレース出力も減らせて一石二鳥です。
  • シェルスクリプトに融合させてしまう*2http://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

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

*1:ごめん、本当は調べるのが面倒でした。

*2:これは向き不向きがあると思うけど

perf probeとは何か。

perf-toolsでは、カーネル内のトレースポイントを「イベント」として扱っています。perf probeでは、後付けで追加の「イベント」を動的に定義できるようになります。perf-probeでは、カーネルデバッグ情報を利用して、関数の行番号を指定して新しい「イベント」の場所を定義したり、関数のローカル変数を「イベント」の引数に指定できます。
デバッガで言うところの、ブレークポイントを指定するようなものです。

perf probe導入方法

2.6.33-rc1でperf probeがサポートされたので、導入方法などを書いておきます。

  1. libdwarf-dev(el)パッケージ(20081218以降)をインストールする。
  2. 2.6.33-rc1か、tipツリーを持ってきてビルド→インストール→再起動
    • CONFIG_KPROBES, CONFIG_KPROBE_EVENT, CONFIG_DEBUG_INFOを有効にしておくこと。
  3. tools/perf/以下で、makeを実行する。
  4. ./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行目を指定したら、いっぱい見つかった。→最適化でループが展開されたりして、そうなることがあります。気にしないで全部トレースしましょう。

出生届を出しにいく

生後一ヶ月と少したって、ようやく生活のリズムも分かってきたところです。
出生証明書も手に入れた(生まれたボストン市ではなく、地元の役所にコピーが届くのを待っていた)ので、総領事館に出生届を出してきました。
金曜日ということもあって道が混んでいたりしたので、総領事館についたのが午後3:53、窓口は4:00に閉まるらしいので、ぎりぎりでした(実際は融通が効くみたいでしたが・・・)。
窓口で聞いてみて、以下のことが分かりました。

  • 米国の出生証明の登録をXX市で行い、そのコピーを地元でとったときは、出生届には地元の役所で発給されたと書く。
  • City of XXはXX市、Town of YYはYY町である。(アメリカだと郊外はほとんど町になるような・・・)
  • 出生証明書の要約は、間違っても訂正印は不要。

あとは2ヶ月ぐらい待って、戸籍に載っているかどうか日本に問い合わせてOKだったら、日本のパスポートが作れるようです。

これまでの流れのまとめ

LKMLを追っかけている人には分かるかもしれないですが。

  1. SystemtapカーネルメンテナにDisられる。
  2. このままだとkprobesもDisられかねない?
    • 対策としてkprobesをftraceから使えるようにする(カーネル内ユーザが必要ということで)と、意外に受ける。
  3. x86命令の区切りが分からないとkprobesのプローブポイント指定できないんじゃね?
    • Djprobeにも必要だし、x86命令デコーダを作る。
    • ついでにDjprobeの1st提案。
  4. kprobe-tracerいいけど、ほかのトレーサと整合性がないよね?
    • Dynamic tracepoint tracerという位置づけにする。tracepointライクなインタフェースを追加。
  5. アセンブリレベルなのは使いにくいよ。Debuginfo使えない?
    • c2kpeコマンドはどうでしょうか。
  6. perf-tools作ってるんだけど、それこっちに入れられない?
    • c2kpeからperf-probeへ移植
  7. Oops, x86命令デコーダがサポートしていない命令があるよ!
    • AVX命令かよ!まだプロセッサも出てないよ・・・てかそれデータバイト列ですからorz < 今ここ

なんか泥縄式に作るものが増えているような・・・。まあゴールは目前のはず!