3.2.2. Systemtap ハンドラー/ボディー
以下のサンプルスクリプトを見てみましょう。
例3.4 helloworld.stp
例3.4「helloworld.stp」 では、
begin イベント (セッションの開始) が { } で囲まれているハンドラーを始動させます。これは単に hello world をプリントして改行し、終了するものです。
printf ( ) ステートメント
printf () ステートメントは、データをプリントする最も簡単な関数の 1 つです。printf () を以下の書式で使用すると、多くの SystemTap 関数を使用するデータを表示できます。
printf ("format string\n", arguments)
printf ("format string\n", arguments)
format string では、arguments のプリント方法を指定します。例3.4「helloworld.stp」 の書式文字列は、SystemTap に
hello world のプリントを指示するだけで、書式指定子は含みません。
引数によっては、
%s (文字列用) や %d (数字用) といった書式指定子を書式文字列に使用することもできます。書式文字列には複数の書式指定子を使用することが可能で、それぞれを対応する引数に一致させます。複数の引数はコンマ (,) で区切ります。
注記
セマンティックの面では、SystemTap
printf 関数は、C 言語の関数に非常によく似ています。上記の SystemTap の printf 関数における構文と書式は、C 言語の printf と同一のものです。
以下のプローブの例を見てみましょう。
例3.5 variables-in-printf-statements.stp
probe syscall.open
{
printf ("%s(%d) open\n", execname(), pid())
}
probe syscall.open
{
printf ("%s(%d) open\n", execname(), pid())
}
例3.5「variables-in-printf-statements.stp」 では、SystemTap がシステムコール
open への全エントリーをプローブするように指示しています。各イベントでは、現行の execname() (実行可能ファイル名の付いた文字列) と pid() (現行のプロセス ID 番号) に続けて 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
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 例3.6「thread_indent.stp」 では、各イベントでのthread_indent()とプローブの関数を以下の書式でプリントします。Copy to Clipboard Copied! Toggle word wrap Toggle overflow このサンプル出力には、以下の情報が含まれています。- スレッド (
thread_indent()から文字列に追加) の最初のthread_indent()呼び出しからの時間 (マイクロ秒単位)。 - 関数呼び出し (
thread_indent()から文字列に追加) を実施したプロセス名 (およびその対応 ID)。 - 呼び出しがエントリー (
<-) か終了 (->) かを示す矢印。インデントがあることで、特定の関数呼び出しのエントリーとそれに対応する終了が一致しやすくなります。 - プロセスが呼び出した関数名。
- name
- 特定のシステムコールの名前を識別します。この変数は、イベント
syscall.system_callを使用するプローブでのみ使用可能です。 - target()
stap script -x process IDまたはstap script -c commandと併用されます。プロセス ID またはコマンドの引数を取るスクリプトを指定したい場合、スクリプト内で参照先となる変数としてtarget()を使用します。以下に例を示します。例3.7 targetexample.stp
probe syscall.* { if (pid() == target()) printf("%s/n", name) }probe syscall.* { if (pid() == target()) printf("%s/n", name) }Copy to Clipboard Copied! Toggle word wrap Toggle overflow 例3.7「targetexample.stp」 を引数-x process IDで実行すると、(syscall.*イベントで指定された) すべてのシステムコールを監視し、指定されたプロセスで実行された全システムコールの名前をプリントします。これは、特定のプロセスをターゲットとしたい場合に毎回if (pid() == process ID)と指定することと同様の効果があります。ただし、target()を使用すると、スクリプトの再利用が容易になり、スクリプトを実行するたびに引数としてプロセス ID を渡すだけで済みます (例:stap targetexample.stp -x process ID)。
サポートされる SystemTap 関数の詳細情報は、
man stapfuncs を参照してください。