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
        ...(以下省)...
        
  • 便利な timer

    • 特定のプローブポイントでの駆動ではなく、インターバルタイマによる駆動も可能
    • 例) 10ミリ秒毎にカレント・プロセスの名前を表示
      # stap -e 'probe timer.ms(10) { task = task_execname(task_current()) if ("swapper/0" != task) println(task) }'
      • "swapper/0" は頻度が高いため除外している
      • ms のほか、susnshzjiffies がある
  • 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
      
  • その他メモ

参考になったWebページ