SystemTapのお勉強

今まで全く使い方知らなかったけど便利そうなので勉強してみた。
やっぱりDTraceと似た感じに思えるけど、DTrace on FreeBSDより出来ることが多いみたい。DTrace on Solarisは知らん。

コールグラフ

Call graph tracing
これコピってくれば取り敢えずでる。

どうせ同じところしか見ないんだから、引数で引っ張ってくる必要ないよね、って事で持ってきたコードを書き換えて、probeのポイントをスクリプト内に書いてみた:

function trace(entry_p) {
        if(tid() in trace)
                printf("%s%s%s\n",thread_indent(entry_p),
                        (entry_p>0?"->":"<-"), probefunc())
}

global trace
probe kernel.function("netif_receive_skb").call {
        if (execname() == "stapio") next # skip our own helper process
        trace[tid()] = 1
        trace(1)
}
probe kernel.function("netif_receive_skb").return {
        trace(-1)
        delete trace[tid()]
}

probe kernel.function("inet_recvmsg").call {
        if (execname() == "stapio") next # skip our own helper process
        trace[tid()] = 1
        trace(1)
}
probe kernel.function("inet_recvmsg").return {
        trace(-1)
        delete trace[tid()]
}

probe kernel.function("*@net/*.c").call { trace(1) }
probe kernel.function("*@net/*.c").return { trace(-1) }
$ sudo stap net-callgraph.stp

コンパイル&実行される。
当然/lib/modules/$(uname -r)/buildが存在してないとビルド失敗する。

用意されているprobeを使ってみる

Chapter 11. Networking Tapsetとかを見ると、色々probeが予め用意されてるっぽいので、さっきのスクリプトに書き加えて使ってみる:

probe tcp.recvmsg {
        printf("%stcp.recvmsg src:%16s:%6d dst:%16s:%6d\n",
                thread_indent(1), saddr, sport, daddr, dport)
}

これで、コールグラフの中にIP:portが表示されるようになった。

ピンポイントでdebug printしてみる

DTrace on FreeBSDではprobe埋め込まないと出来なかったのだが、SystemTapではこういうのもアリみたい:

probe kernel.statement("netif_receive_skb@net/core/dev.c:3327") {
        printf("%srxhash:%x sk:%p\n", thread_indent(1), $skb->rxhash, $sk)
}

すごい。けど、カーネル書き換えると行数変わっちゃうから反映させないといけないっていう問題もあるか。
できるだけkernel.functionでひっかけておく方が楽そう。

変数は、引数だけじゃなくてローカル変数でも参照可能みたい。
名前も、構造体アクセスも可能。

どうしてこうなった

この辺の、SystemTapスクリプト内でのキャストの記述方法がすごいことになっててビビった。
5.6.11 Pointer typecasting

@cast(tv, "timeval", "<sys/time.h>")->tv_sec
@cast(task, "task_struct", "kernel<linux/sched.h>")->tgid