Tipsなど

Systemtapのコマンド自体あまり知られていないですが、Systemtapを使う上でのTipsを紹介します。

プローブポイントからアクセスできるローカル変数一覧を知る

裏技的ですが、エラーメッセージから、どういう変数があるかが分かります。
例えば、以下のようにありえない名前のローカル変数にアクセスするコードを渡します。

$ stap -e 'probe kernel.function("sys_select") {log($sssss)}'
semantic error: unable to find local 'sssss' near pc 0xc019f8b0 (alternatives: n inp outp exp tvp timeout tv ret): identifier '$sssss' at :1:42
Pass 2: analysis failed.  Try again with more '-v' (verbose) options.

一つ注意することは、単に$ssssと書くのではなく、log()関数など、実際に実行される処理に渡すようにしなければならないということです。外部出力の参考にならない変数は、全て最適化されて消去されてしまいます。

基本的にはソースコードを当たるのがいいですが、変数の名前だけド忘れしてしまった場合などに有効です。

プローブポイントの一覧を知る

プローブポイントでワイルドカード指定をしたときに、実際にどのような関数がプローブの対象になるか、気になる場合があります。これを知るためには、-p2オプションを使えばいいです。
例えば、sys_で始まる関数は、どのように展開されるか知りたい場合、以下のようにします。

$ stap -e 'probe kernel.function("sys_*"){}' -p2
# probes
kernel.function("sys_set_thread_area@/build/buildd/linux-2.6.24/arch/x86/kernel/process_32.c:883") /* pc=_stext+0x1be0 */ /* <- kernel.function("sys_*") */
kernel.function("sys_fork@/build/buildd/linux-2.6.24/arch/x86/kernel/process_32.c:776") /* pc=_stext+0x1270 */ /* <- kernel.function("sys_*") */
kernel.function("sys_get_thread_area@/build/buildd/linux-2.6.24/arch/x86/kernel/process_32.c:950") /* pc=_stext+0x1da0 */ /* <- kernel.function("sys_*") */
kernel.function("sys_vfork@/build/buildd/linux-2.6.24/arch/x86/kernel/process_32.c:806") /* pc=_stext+0x1200 */ /* <- kernel.function("sys_*") */
kernel.function("sys_clone@/build/buildd/linux-2.6.24/arch/x86/kernel/process_32.c:781") /* pc=_stext+0x1230 */ /* <- kernel.function("sys_*") */
kernel.function("sys_execve@/build/buildd/linux-2.6.24/arch/x86/kernel/process_32.c:814") /* pc=_stext+0x1ac0 */ /* <- kernel.function("sys_*") */
...

SystemtapのVersion 0.7以降であれば、-lオプションを使って同様の処理が出来るようになりました。

$ stap -l 'kernel.function("sys_*")'
kernel.function("sys_set_thread_area@/build/buildd/linux-2.6.24/arch/x86/kernel/process_32.c:883")
kernel.function("sys_fork@/build/buildd/linux-2.6.24/arch/x86/kernel/process_32.c:776")
kernel.function("sys_get_thread_area@/build/buildd/linux-2.6.24/arch/x86/kernel/process_32.c:950")
kernel.function("sys_vfork@/build/buildd/linux-2.6.24/arch/x86/kernel/process_32.c:806")
kernel.function("sys_clone@/build/buildd/linux-2.6.24/arch/x86/kernel/process_32.c:781")
kernel.function("sys_execve@/build/buildd/linux-2.6.24/arch/x86/kernel/process_32.c:814")
...
  • lオプションを使うと、エイリアスの一覧も取得できます。
$ stap -l 'syscall.*'
syscall.accept
syscall.access
syscall.acct
syscall.add_key
syscall.adjtimex
syscall.alarm
...

エイリアスで定義されている変数の一覧を見る

0.7.1以降では、-lオプションよりさらに強力な-Lオプションも用意されています。このオプションをエイリアスに使うと、エイリアス内部で定義されている変数の一覧も取得できます。

$ stap -L 'syscall.*'
syscall.accept name:string sockfd:long addr_uaddr:long addrlen_uaddr:long argstr:string
syscall.access name:string pathname:string mode:long mode_str:string argstr:string
syscall.acct name:string filename:string argstr:string
syscall.add_key name:string type_uaddr:long description_auddr:long payload_uaddr:long plen:long ringid: long argstr:string
...

コマンドライン引数を使う

他のスクリプト言語と同様、Systemtapスクリプトも実行時のコマンドライン引数をスクリプト内部で参照することができます。引数の参照には$番号あるいは@番号を使います。$を使うと、引数がそのままスクリプトの一部として使われます(数値などの指定に使えます)が、@を使うと、引数が文字列として使われます。

$ stap -e 'probe begin {printf("%s %d\n", @1, $2)}' "hello" 1 
hello 1

気をつけたいのは、文字列を渡すつもりで$を使ってしまうと、期待通りの動きをしないことがあることです。

$ stap -e 'probe begin {printf("%s %d\n", $1, $2)}' "hello" 1 
WARNING: read-only local variable 'hello' : identifier 'hello' at < input >:1:32
 1

Systemtapも警告を出していますが、上記の例では第一引数の"hello"は、スクリプト内に展開される時に"(ダブルクオート)が外れてしまい、単なるhelloという中身がない変数と解釈されてしまいます。