簡単な使い方
Systemtapは、端的に言えばカーネルを対象とした(最新のものだとユーザプロセスも対象にできますが)プログラマブルなデバガです。基本はブレークポイントを埋め込んで、その箇所の情報(ローカル変数など)を取ってきてあれこれするのに使います。
例えば、以下のコマンドをrootユーザで実行すると、プロセスがどんなファイルをオープンしようとしているかが分かります。
# stap -e 'probe kernel.function("sys_open") {printf("%s open %s\n", execname(), user_string($filename))}' ... hal-ipw-killswi open /usr/lib/libpcre.so.3 hal-ipw-killswi open /lib/libselinux.so.1 hal-ipw-killswi open /lib/tls/i686/cmov/libdl.so.2 hal-ipw-killswi openhal-ipw-killswi open /proc/mounts dbus-daemon open /etc/passwd dbus-daemon open /etc/group ...
(Ctrl-cで終了します)
この例では、stapコマンドに直接コマンドラインからスクリプトを与えています。もちろん、ファイル経由でも渡せますし、パイプ経由でも渡せます。
# cat > test.stp probe kernel.function("sys_open") { printf("%s open %s\n", execname(), user_string($filename)) } # stap test.stp (もしくは) # cat test.stp | stap -
プログラムを見ていて分かったかもしれませんが、SystemtapのスクリプトはC言語やawkに似ています。命令の終わりには";"がいりません(あってもいいのですが)。では、例から最低限の使い方を説明します。
- probe
- プローブの定義命令です。Systemtapはカーネルの中にプローブポイント(デバガでいうブレークポイント)を設定し、そのプローブポイントが実行されるごとにプローブハンドラが呼ばれます。probeはこのプローブポイントとプローブハンドラの定義をするための命令です。Systemtapのスクリプトは、必ず一つ以上のプローブを定義しておかねばなりません。基本は以下の文法になります。
probe プローブポイント {プローブハンドラの処理}
- kernel.function("sys_open")
- プローブポイントの定義です。Systemtapはデバッグ情報を使うため、最小では行番号単位でプローブポイントを指示することができます。kernel.function("関数名")はもっともよく使うであろう関数の出入り口単位での指定です。この例では、sys_openという名前の関数を指定しています。詳しくは"man stapprobes"で解説が読めます。
- {...}
- プローブハンドラの本体の定義です。プローブハンドラは必ず{}でかこっておく必要があります。
- printf("%s open $s\n", ...)
- Cのprintfと同じフォーマットで引数を表示することができます。
- execname()
- Systemtapで用意している関数の一つです。プローブポイントを実行したプロセスの名前が表示されます。
- $filename
- $変数名 と書くと、指定した名前のローカル変数にアクセスできます。ただし、この場合変数自体は単なるポインタに過ぎないので、ここから文字列を引き出すには次の関数を使う必要があります。
- user_string(...)
- ユーザ空間から文字列を取り出します。sys_open関数のfilename引数は、以下のようにユーザ空間のデータとして定義されているので、この関数を使って取り出す必要があります。
asmlinkage long sys_open(const char __user *filename, int flags, int mode)
(fs/open.cより引用)
とりあえず、こんなもんでしょうか。唐突に始めすぎたのでよくわかりません><
一行スクリプトは普段から結構使います。長いスクリプトを書いているときに、これでいいんだっけ、と確認したいときなどに-eオプションで一行スクリプトで調べられると便利です。