「SystemTap」の版間の差分

提供: Wikinote
移動: 案内検索
(組み込み関数)
(プローブポイント)
行29: 行29:
 
=== プローブポイント ===
 
=== プローブポイント ===
 
詳細は、'''man stapprobes''' を参照のこと。
 
詳細は、'''man stapprobes''' を参照のこと。
* begin/end/error — 名前の通り
+
SystemTap のバージョンにより使えるものがかなり違うので注意すること。
* timer.jiffies/s/ms/us/ns/hz(N) — インターバル指定
+
* syscall.*[.return] — システムコール関数
+
** argstr — 引き数の値のリスト
+
** name — 関数名
+
** retstr — 返り値 (.return のみ)
+
* kernel.function("PATTERN") — カーネル関数
+
* kernel.statement("PATTERN")
+
** kernel.statement("*@mm/swap.c:412") という風に使う?
+
* module("MPATTERN").function("PATTERN") — モジュール関数
+
* module("MPATTERN").statement("PATTERN")
+
** $var — ソース中の変数にアクセス
+
** $var->field
+
** $return — 返り値 (.return のみ)
+
** $var[N]
+
** $$vars — sprintf("parm1=%x ... var1=%x ...", parm1, ..., var1, ...) と等価 (変数の文字列展開)
+
** $$locals — ローカル変数を文字列展開
+
** $$perms — 引き数を文字列展開
+
** $$return — 返り値を文字列展開 (.return のみ)
+
  
PATTERN の部分は、以下のような指定も可能。
+
; begin/end/error
* kernel.function("*@kernel/timer.c:123")
+
: 名前の通り。
* kernel.statement("sys_adjtimex@kernel/time.c+3")
+
; timer.[ jiffies | s | ms | us | ns | hz ](N)
 +
: インターバル指定。例えば、timer.s(10) で 10 秒ごとに動作する。
 +
; syscall.*[.return]
 +
:システムコール関数。特別に以下の変数を利用可能。
 +
:* argstr — 引き数の値のリスト
 +
:* name — 関数名
 +
:* retstr — 返り値 (.return のみ)
 +
; kernel.function("PATTERN")[.return]
 +
: 任意のカーネル関数。ワイルドカード (*, ?, [ ]) による指定も可能。以下ように変数へのアクセスが可能。
 +
:* $var — ソース中の変数にアクセス
 +
:* $var->field
 +
:* $return — 返り値 (.return のみ)
 +
:* $var[N]
 +
:* $$vars — sprintf("parm1=%x ... var1=%x ...", parm1, ..., var1, ...) と等価 (変数の文字列展開)
 +
:* $$locals — ローカル変数を文字列展開
 +
:* $$perms — 引き数を文字列展開
 +
:* $$return — 返り値を文字列展開 (.return のみ)
 +
; kernel.statement("PATTERN")
 +
: カーネルの任意の位置。以下のような指定が可能。
 +
:* kernel.function("*@kernel/timer.c:123")
 +
:* kernel.statement("sys_adjtimex@kernel/time.c+3")
 +
; module("MPATTERN").function("PATTERN")
 +
; module("MPATTERN").statement("PATTERN")
 +
: モジュール内の関数などを指定する。例えば、module("ext3").function("ext3_invalidatepage") など。
  
 
=== 組み込み関数 ===
 
=== 組み込み関数 ===

2009年9月10日 (木) 00:22時点における版

ついに伝家の宝刀 (でも諸刃の剣) を抜く時が来たようだ…。

参考 URL

  • SystemTap 公式ページ。Documentation と Wiki を見ればとりあえず事足りる。

覚え書き

基本形

文法は、awk に似ている。begin/end はオプション。

#!/usr/bin/stap -v

function hoge() {
    ...
}

probe begin {
    ...
}

probe kernel.function("sys_*") {
    printf("%s[%d] called %s()\n", execname(), pid(), probefunc())
}

probe end {
    ...
}

プローブポイント

詳細は、man stapprobes を参照のこと。 SystemTap のバージョンにより使えるものがかなり違うので注意すること。

begin/end/error
名前の通り。
timer.[ jiffies | s | ms | us | ns | hz ](N)
インターバル指定。例えば、timer.s(10) で 10 秒ごとに動作する。
syscall.*[.return]
システムコール関数。特別に以下の変数を利用可能。
  • argstr — 引き数の値のリスト
  • name — 関数名
  • retstr — 返り値 (.return のみ)
kernel.function("PATTERN")[.return]
任意のカーネル関数。ワイルドカード (*, ?, [ ]) による指定も可能。以下ように変数へのアクセスが可能。
  • $var — ソース中の変数にアクセス
  • $var->field
  • $return — 返り値 (.return のみ)
  • $var[N]
  • $$vars — sprintf("parm1=%x ... var1=%x ...", parm1, ..., var1, ...) と等価 (変数の文字列展開)
  • $$locals — ローカル変数を文字列展開
  • $$perms — 引き数を文字列展開
  • $$return — 返り値を文字列展開 (.return のみ)
kernel.statement("PATTERN")
カーネルの任意の位置。以下のような指定が可能。
  • kernel.function("*@kernel/timer.c:123")
  • kernel.statement("sys_adjtimex@kernel/time.c+3")
module("MPATTERN").function("PATTERN")
module("MPATTERN").statement("PATTERN")
モジュール内の関数などを指定する。例えば、module("ext3").function("ext3_invalidatepage") など。

組み込み関数

使えそうなものを挙げておく。詳細は man stapfuncs を参照のこと。

  • cpu() — 現在の CPU 番号
  • execname() — 現在のプロセス名
  • exit() — SystemTap セッションを終了
  • pid() — 現在のプロセスの PID
  • gettimeofday_[ s | ms | us | ns ]() — UNIX epoch からの経過時間
  • ctime() — ctime(gettimeofday_s()) で現在の時刻を文字列に変換
  • pp() — プローブポイントの情報
  • probefunc() — プローブした関数名
  • probemod() — プローブしたモジュール名
  • print_backtrace() — コールスタックを表示する
  • print_regs() — レジスタ情報を表示する

出力例

print_regs() と print_backtrace() のサンプル

ソースコード <toggledisplay>

#!/usr/bin/stap -v
global count = 0
probe syscall.select {
    printf("%s[%d] called %s\n", execname(), pid(), probefunc())
    printf("print_regs():\n")
    print_regs()
    printf("print_backtrace():\n")
    print_backtrace()
    count++

    if (count == 10) {
        exit()
    }
}

</toggledisplay> この例では 10 回分出力されるが、1 つ抜き出すとこんな感じ。

mysqld[4840] called sys_select
print_regs():
EIP: c048256d
ESP: c0404f17
EAX: 0000008e EBX: 00000000 ECX: f11f1f88 EDX: 2ea34421
ESI: 00000000 EDI: b3366360 EBP: f11f1000 DS: 007b ES: 007b
CR0: 80050033 CR2: 00ec1280 CR3: 03510880 CR4: 000006f0
print_backtrace():
 0xc048256d : sys_select+0x1/0x180 [kernel]
 0xc0404f17 : sys_sigreturn+0x1f8/0xf2d [kernel] (inexact)
 0xdab33663 : packet_exit+0x1a40c91d/0x0 [kernel] (inexact)
 0xffdab336 : packet_exit+0x3f6845f0/0x0 [kernel] (inexact)
 0xffffdab3 : packet_exit+0x3f8d6d6d/0x0 [kernel] (inexact)
 0xffffffda : packet_exit+0x3f8d9294/0x0 [kernel] (inexact)

Tips

プローブポイントを確認

-l オプションでプローブポイントの一覧を出力できる。

[root@lab ~]# stap -v -l 'kernel.function("add_to_*")'
Pass 1: parsed user script and 45 library script(s) in 300usr/10sys/318real ms.
kernel.function("add_to_page_cache@mm/filemap.c:439")
kernel.function("add_to_page_cache_lru@mm/filemap.c:462")
kernel.function("add_to_swap@mm/swap_state.c:147")
kernel.function("add_to_swap_cache@mm/swap_state.c:99")
Pass 2: analyzed script: 4 probe(s), 0 function(s), 0 embed(s), 0 global(s) in 290usr/300sys/591real ms.

RHEL 4 の stap コマンドには -l オプションがない。 この場合は、-p2 オプションでプローブポイント確認後に動作を止めればよい。

[root@centos47 stap]# stap -e 'probe kernel.function("add_to_*"){}' -p2
# probes
kernel.function("add_to_page_cache@mm/filemap.c:343") /* pc=0x59caf */ /* <- kernel.function("add_to_*") */
kernel.function("add_to_page_cache_lru@mm/filemap.c:367") /* pc=0x59d61 */ /* <- kernel.function("add_to_*") */
kernel.function("add_to_swap@mm/swap_state.c:142") /* pc=0x712b2 */ /* <- kernel.function("add_to_*") */
kernel.function("add_to_swap_cache@mm/swap_state.c:95") /* pc=0x71635 */ /* <- kernel.function("add_to_*") */

参考:http://d.hatena.ne.jp/mhiramat/20081004

注意事項

  • probe kernel.function("*") (すべてのカーネル関数をフック) をやってはいけない。この中で何か出力しようものなら、あっという間にシステムがストールしてしまう (実証済み…)。何も出力しなければ or screen 上で実行しなければ問題ないのかもしれないが、もう怖くてできない。。