3.2. SystemTap スクリプト
注記
probe event {statements}
,
) で区切ります。1 つのプローブで複数のイベントが指定された場合、SystemTap は指定されたイベントが発生するとそのハンドラーを実行します。
{ }
) で囲まれており、イベントごとに実行されるステートメントが含まれています。SystemTap はこれらのステートメントを順番に実行し、通常は複数のステートメントを分ける特別なセパレーターやターミネーターは必要ありません。
注記
function function_name(arguments){statements} probe event {function_name(arguments)}
重要
3.2.1. イベント
- syscall.system_call
- システムコール system_call へのエントリー。システムコールの終了を希望する場合は、
.return
をイベントに追加すると、システムコールの終了を監視するようになります。たとえば、close
システムコールのエントリーと終了を指定するには、それぞれsyscall.close
とsyscall.close.return
を使用します。 - vfs.file_operation
- 仮想ファイルシステム (VFS) の file_operation イベントへのエントリー。
syscall
イベントと同様に、イベントに.return
を追加すると、file_operation
動作の終了を監視します。 - kernel.function("function")
function
カーネル関数へのエントリー。たとえばkernel.function("sys_open")
は、sys_open
カーネル関数がシステム内のスレッドに呼び出される際に発生する イベント を指します。sys_open
カーネル関数の return を指定するには、kernel.function("sys_open").return
のようにreturn
文字列をイベントステートメントに追加します。プローブイベントを定義する際には、アスタリスク (*
) をワイルドカードに使用できます。また、カーネルソースファイル内の関数のエントリーと終了も追跡可能です。以下の例を見てみましょう。例3.1 wildcards.stp
probe kernel.function("*@net/socket.c") { } probe kernel.function("*@net/socket.c").return { }
この例では、最初のプローブのイベントはnet/socket.c
カーネルソースファイル内の全関数のエントリーを指定しています。2 つ目のプローブでは、これら全関数の終了を指定しています。この例では、ハンドラーにステートメントがないことに注意してください。このため、情報が収集されず、表示されることもありません。- kernel.trace("tracepoint")
- tracepoint の静的プローブ。最近のカーネル (2.6.30 およびそれ以降) には、カーネル内の特定イベント用のインストルメンテーションが含まれています。これらのイベントは、トレースポイントで静的にマークが付けられています。SystemTap で利用可能なトレースポイントの例としては、
kernel.trace("kfree_skb")
があります。これは、カーネル内でネットワークバッファーが解放されると合図します。 - module("module").function("function")
- モジュール内の関数のプローブを可能にします。例を示します。
例3.2 moduleprobe.stp
probe module("ext3").function("*") { } probe module("ext3").function("*").return { }
例3.2「moduleprobe.stp」 の最初のプローブは、ext3
モジュールの全関数のエントリーを指しています。2 つ目のプローブは、同じモジュールの全関数の終了を指しています。.return
接尾辞の使用は、kernel.function()
の場合と同様です。例3.2「moduleprobe.stp」 のプローブハンドラーにはステートメントがないことに注意してください。このため、有用なデータは表示されません (例3.1「wildcards.stp」 の場合と同様)。システムのカーネルモジュールは通常/lib/modules/kernel_version
にあります。ここでの kernel_version は、現在読み込まれているカーネルのバージョンを指します。モジュールは、ファイル名拡張子.ko
を使用します。
- begin
- SystemTap セッションの開始です。つまり、SystemTap スクリプトの実行と同時です。
- end
- SystemTap セッションの終了。
- timer イベント
- ハンドラーの定期実行を指定するイベント。例を示します。
例3.3 timer-s.stp
probe timer.s(4) { printf("hello world\n") }
例3.3「timer-s.stp」 では、プローブが 4 秒ごとにhello world
をプリントします。以下のような timer イベントも使用できます。timer.ms(ミリ秒)
timer.us(マイクロ秒)
timer.ns(ナノ秒)
timer.hz(ヘルツ)
timer.jiffies(jiffies)
timer イベントを情報を収集する他のプローブと併せて使用すると、定期的な更新が表示でき、その情報の変遷が分かります。
重要
3.2.2. Systemtap ハンドラー/ボディ
例3.4 helloworld.stp
probe begin { printf ("hello world\n") exit () }
begin
イベント (セッションの開始) が { }
で囲まれているハンドラーを始動させます。これは単に hello world
をプリントして改行し、終了するものです。
printf()
ステートメントは、データをプリントする最も簡単な関数の 1 つです。printf()
を以下の書式で使用すると、多くの SystemTap 関数を使用するデータを表示できます。
printf ("format string\n", arguments)
hello world
のプリントを指示するだけで、書式は指定していません。
%s
(文字列用) や %d
(数字用) といった書式指定子を format string に使用することもできます。format string には複数の書式指定子を使用することが可能で、それぞれを対応する引数に一致させます。複数の引数はコンマ (,
) で区切ります。
注記
例3.5 variables-in-printf-statements.stp
probe syscall.open { printf ("%s(%d) open\n", execname(), pid()) }
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 は、printf()
引数として使用可能な多くの関数をサポートしています。例3.5「variables-in-printf-statements.stp」では、SystemTap 関数 execname()
(カーネル関数を呼び出した、またはシステムコールを実行したプロセス名) および pid()
(現行プロセス ID) を使用しています。
- 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
このページには機械翻訳が使用されている場合があります (詳細はこちら)。