3.2.2. Systemtap ハンドラー/ボディー
以下のサンプルスクリプトを見てみましょう。
例3.4 helloworld.stp
probe begin { printf ("hello world\n") exit () }
例3.4「helloworld.stp」 では、
begin
イベント (セッションの開始) が { }
で囲まれているハンドラーを始動させます。これは単に hello world
を出力して改行し、終了するものです。
注記
printf ( ) ステートメント
printf()
ステートメントは、データを出力するための最も単純な関数の一つです。printf()
は、次の形式で多くの SystemTap 関数を使用してデータを表示するためにも使用できます。
printf ("format string\n", arguments)
format string では、arguments の出力方法を指定します。例3.4「helloworld.stp」 の書式文字列は、SystemTap に
hello world
の出力を指示するだけで、書式指定子は含みません。
引数によっては、
%s
(文字列用) や %d
(数字用) といった書式指定子を書式文字列に使用することもできます。書式文字列には複数の書式指定子を使用することが可能で、それぞれを対応する引数に一致させます。複数の引数はコンマ (,
) で区切ります。
注記
以下のプローブの例を見てみましょう。
例3.5 variables-in-printf-statements.stp
probe syscall.open { printf ("%s(%d) open\n", execname(), pid()) }
例3.5「variables-in-printf-statements.stp」 では、SystemTap がシステムコール
open
への全エントリーをプローブするように指示しています。各イベントでは、現行の execname()
(実行可能ファイル名の付いた文字列) と pid()
(現行のプロセス ID 番号) に続けて open
という単語を出力します。このプローブ出力の抜粋は以下のようになります。
vmware-guestd(2206) open hald(2360) open hald(2360) open hald(2360) open df(3433) open df(3433) open df(3433) open hald(2360) open
SystemTap 関数
SystemTap は、printf()
引数として使用できる多くの機能をサポートしています。例3.5「variables-in-printf-statements.stp」 は、SystemTap 関数 execname()
(カーネル関数を呼び出した/システムコールを実行したプロセスの名前) および pid()
(現在のプロセス ID) を使用します。
一般的に使用される SystemTap 関数を以下に挙げます。
- tid()
- 現行スレッドの ID。
- uid()
- 現行ユーザーの ID。
- cpu()
- 現行の CPU 番号。
- gettimeofday_s()
- Unix epoch (1970 年 1 月 1 日) からの秒数。
- ctime()
- UNIX epoch からの秒数を日にちに換算。
- pp()
- 現在処理されているプローブポイントを記述する文字列。
- thread_indent()
- この関数は出力結果をうまく整理するので、便利なものです。この関数はインデント差分の引数を取ります。これは、スレッドのインデントカウンターに追加する、またはそこから取り除くスペースの数を示すものです。その後、適切なインデントスペースの数と一般的な追跡データの文字列を返します。ここで返された文字列の一般的なデータに含まれるのは、タイムスタンプ (スレッドの
thread_indent()
への最初の呼び出しからのマイクロ秒)、プロセス名、およびスレッド ID です。これによりどの関数が呼び出されたか、誰が呼び出したか、各関数呼び出しの長さが特定できます。それぞれの呼び出しが終わり次第、次の呼び出しが始まれば、エントリーと終了の一致は容易です。ただし、ほとんどの場合は、最初の関数呼び出しエントリーがなされた後、これが終了する前に他の複数の呼び出しがエントリーおよび終了することがあります。インデントカウンターがあることで、最初の呼び出しが終了していない場合、次の関数呼び出しをインデントして、エントリーとそれに対応する終了が一致しやすくなります。以下でthread_indent()
の使用例を見てみましょう。例3.6 thread_indent.stp
probe kernel.function("*@net/socket.c") { printf ("%s -> %s\n", thread_indent(1), probefunc()) } probe kernel.function("*@net/socket.c").return { printf ("%s <- %s\n", thread_indent(-1), probefunc()) }
0 ftp(7223): -> sys_socketcall 1159 ftp(7223): -> sys_socket 2173 ftp(7223): -> __sock_create 2286 ftp(7223): -> sock_alloc_inode 2737 ftp(7223): <- sock_alloc_inode 3349 ftp(7223): -> sock_alloc 3389 ftp(7223): <- sock_alloc 3417 ftp(7223): <- __sock_create 4117 ftp(7223): -> sock_create 4160 ftp(7223): <- sock_create 4301 ftp(7223): -> sock_map_fd 4644 ftp(7223): -> sock_map_file 4699 ftp(7223): <- sock_map_file 4715 ftp(7223): <- sock_map_fd 4732 ftp(7223): <- sys_socket 4775 ftp(7223): <- sys_socketcall
この出力サンプルには、以下の情報が含まれています。- スレッドの最初の
thread_indent()
呼び出しからの時間 (マイクロ秒単位)。 - 関数呼び出しを行ったプロセス名 (およびそれに対応する ID)。
- 呼び出しがエントリー (
<-
) か終了 (->
) かを示す矢印。インデントがあることで、特定の関数呼び出しのエントリーとそれに対応する終了が一致しやすくなります。 - プロセスが呼び出した関数名。
- name
- 特定のシステムコールの名前を識別します。この変数は、イベント syscall.system_call を使用するプローブでのみ使用できます。
- target()
- 次の 2 つのコマンドのいずれかと組み合わせて使用します。
stap script -x process ID stap script -c command
プロセス ID またはコマンドの引数を取るスクリプトを指定したい場合、スクリプト内で参照先となる変数としてtarget()
を使用します。以下に例を示します。例3.7 targetexample.stp
probe syscall.* { if (pid() == target()) printf("%s/n", name) }
例3.7「targetexample.stp」 を引数-x process ID
で実行すると、(syscall.*
イベントで指定された) すべてのシステムコールを監視し、指定されたプロセスで実行された全システムコールの名前を出力します。これは、特定のプロセスをターゲットとしたい場合に毎回if (pid() == process ID)
と指定することと同様の効果があります。ただし、target()
を使用すると、スクリプトの再利用が容易になり、スクリプトを実行するたびに引数としてプロセス ID を渡すだけで済みます。以下に例を示します。stap targetexample.stp -x process ID
サポートされる SystemTap 関数の詳細情報は、stapfuncs(3) を参照してください。