SystemTap メモ
今日、初めて SystemTap を触ってみて感動したためメモを残す。
SystemTap の概要は下記のページで掴んだ
手元の Ubuntu 14.04 環境での下準備
% codename=$(lsb_release -c | awk '{print [}') % sudo tee /etc/apt/sources.list.d/ddebs.list << EOF deb http://ddebs.ubuntu.com/ ${codename} main restricted universe multiverse deb http://ddebs.ubuntu.com/ ${codename}-security main restricted universe multiverse deb http://ddebs.ubuntu.com/ ${codename}-updates main restricted universe multiverse deb http://ddebs.ubuntu.com/ ${codename}-proposed main restricted universe multiverse EOF # apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ECDCAD72428D7C01 # apt-get update # apt-get install linux-image-$(uname -r)-dbgsym # apt-get install systemtap
Hello World で確認
# stap -e 'probe begin { println("Hello, World!") exit() }' Hello, World!
組込み関数について少しメモ
- stapfuncs(5) を参照 (http://linux.die.net/man/5/stapfuncs)
- 例1)
task_current
でカレント・プロセス(current)の task_struct のアドレスを取得し、task_execname
でプロセス名を表示# sudo stap -e 'probe begin { println(task_execname(task_current())) exit() }' stapio
- 何気に便利)
errno_str
は引数の値をerrno文字列に変換する# stap -e 'probe begin { println(errno_str(1)) exit() }' EPERM
対象カーネルのプローブポイントを確認
# stap -l 'kernel.function("*")' | grep input_event *: kernel.function("input_event@/build/buildd/linux-3.13.0/drivers/input/input.c:421") kernel.function("input_event_from_user@/build/buildd/linux-3.13.0/drivers/input/input-compat.c:17") kernel.function("input_event_size@/build/buildd/linux-3.13.0/drivers/input/input-compat.h:68") kernel.function("input_event_size@/build/buildd/linux-3.13.0/drivers/input/misc/../input-compat.h:68") kernel.function("input_event_to_user@/build/buildd/linux-3.13.0/drivers/input/input-compat.c:41") kernel.function("uinput_events_to_user@/build/buildd/linux-3.13.0/drivers/input/misc/uinput.c:499") # sudo stap -l 'kernel.function("input_event")' kernel.function("input_event@/build/buildd/linux-3.13.0/drivers/input/input.c:421")
- コードブロックなしで
kernel.function
を呼ぶとプローブポイントを確認できる - 引数のプローブポイントにワイルドカードを指定することによって全てのプローブポイントを出力する
- プローブポイントの書式は
関数名[@ファイルパス[:行数]]
- コードブロックなしで
プローブポイントにデバッグログを仕込んでみる
- プローブポイントは input_event 関数(inputサブシステムのI/F)とし、以下のコードを
input_event.stp
等で保存するprobe begin { println("begin...") } probe kernel.function("input_event") { printf("%s: 0x%x 0x%x 0x%x\n", kernel_string(@cast($dev, "input_dev")->name), $type, $code, $value) }
- カーネルの変数には$変数名で参照可能
- 構造体メンバへのアクセスには、@cast という関数で一キャストする
- 第1引数には変数、第2引数には構造体の名称を指定する
- 以下で実行
# stap input_event.stp ...begin
- マウスやキーボードを適当に操作すると、ログが出力されることを確認(環境は VirtualBox)
VirtualBox mouse integration: 0x3 0x0 0xaeda VirtualBox mouse integration: 0x3 0x1 0x7b51 VirtualBox mouse integration: 0x0 0x0 0x0 ...(省略)... AT Translated Set 2 keyboard: 0x4 0x3 0x39 AT Translated Set 2 keyboard: 0x4 0x4 0x39 AT Translated Set 2 keyboard: 0x1 0x39 0x1 ...(以下省)...
- マウスやキーボードを適当に操作すると、ログが出力されることを確認(環境は VirtualBox)
- プローブポイントは input_event 関数(inputサブシステムのI/F)とし、以下のコードを
便利な timer
- 特定のプローブポイントでの駆動ではなく、インターバルタイマによる駆動も可能
- 例) 10ミリ秒毎にカレント・プロセスの名前を表示
# stap -e 'probe timer.ms(10) { task = task_execname(task_current()) if ("swapper/0" != task) println(task) }'
- "swapper/0" は頻度が高いため除外している
ms
のほか、s
、us
、ns
、hz
、jiffies
がある
Call graph サンプル・スクリプト
- 本家のサンプル・スクリプトを使用した関数呼び出しフロー
% wget https://sourceware.org/systemtap/examples/general/para-callgraph.stp # stap para-callgraph.stp 'kernel.function("*@fs/*.c")' 'kernel.function("sys_mkdir")' -c "mkdir /tmp/test-dir" -o /tmp/mkdir-call-graph.log
- -c で指定したコマンド終了時にスクリプトが終了するようにした
- -o でファイルに保存した標準出力は以下のようになった
0 mkdir(2346):->user_path_create dfd=0xffffffffffffff9c pathname=0x7fff26f41874 path=0xffff880000083f40 lookup_flags=0x2 57 mkdir(2346): ->getname_flags filename=0x7fff26f41874 flags=0x0 empty=0x0 60 mkdir(2346): <-getname_flags return=0xffff88003c976000 77 mkdir(2346): ->kern_path_create dfd=0xffffffffffffff9c pathname=0xffff88003c976020 path=0xffff880000083f40 lookup_flags=0x0 81 mkdir(2346): ->filename_lookup dfd=0xffffffffffffff9c name=0xffff880000083e20 flags=0x10 nd=0xffff880000083e40 86 mkdir(2346): ->path_lookupat dfd=0xffffffffffffff9c name=0xffff88003c976020 flags=0x50 nd=0xffff880000083e40 ...(省略)... 27 mkdir(2346): ->mntput mnt=0xffff88003dbbaca0 30 mkdir(2346): ->mntput_no_expire mnt=0xffff88003dbbac80 32 mkdir(2346): <-mntput_no_expire 33 mkdir(2346): <-mntput 35 mkdir(2346):<-done_path_create
- 本家のサンプル・スクリプトを使用した関数呼び出しフロー
その他メモ