システムコールをトレースすることにより、ほとんどのユーザープロセスの動作を効果的に監視できます。管理者または開発者として Solaris の truss(1) ユーティリティーを使用したことがあるユーザーなら、このユーティリティーが問題の解決に役立つ手軽で便利なツールであることをすでにご存知でしょう。truss をまだ使ったことがないユーザーも、シェルに次のコマンドを入力して、今すぐその効果を試してください。
$ truss date |
date(1) によって実行されるすべてのシステムコールのトレース結果が書式付きで表示され、最後の行に date(1) そのものの出力が表示されます。次の例は、上記の rw.d プログラムに truss(1) とよく似た出力書式を設定し、出力を読みやすくしたものです。以下のプログラムを入力し、trussrw.d という名前のファイルに保存してください。
syscall::read:entry, syscall::write:entry /pid == $1/ { printf("%s(%d, 0x%x, %4d)", probefunc, arg0, arg1, arg2); } syscall::read:return, syscall::write:return /pid == $1/ { printf("\t\t = %d\n", arg1); }
この例では、すべての述語の定数 12345 の部分が、ラベル $1 で置き換えられています。このラベルを使用することで、監視対象のプロセスをスクリプトの引数として指定できます。 $1 には、スクリプトのコンパイル時に、最初の引数の値が代入されます。trussrw.d を実行するには、dtrace の -q オプションと -s オプションを指定したあと、最後の引数としてシェルのプロセス ID を指定します。-q オプションを指定すると、dtrace が非出力モードになり、これまでの例で出力されていた見出し行と CPU 列および ID 列が出力されなくなります。つまり、出力されるのは、明示的にトレースしたデータだけになります。指定のシェルウィンドウで次のコマンドを入力し (その際、12345 の部分はシェルプロセスのプロセス ID で置き換える)、指定したシェルで、Return キーを数回押します。
# dtrace -q -s trussrw.d 12345 = 1 write(2, 0x8089e48, 1) = 1 read(63, 0x8090a38, 1024) = 0 read(63, 0x8090a38, 1024) = 0 write(2, 0x8089e48, 52) = 52 read(0, 0x8089878, 1) = 1 write(2, 0x8089e48, 1) = 1 read(63, 0x8090a38, 1024) = 0 read(63, 0x8090a38, 1024) = 0 write(2, 0x8089e48, 52) = 52 read(0, 0x8089878, 1) = 1 write(2, 0x8089e48, 1) = 1 read(63, 0x8090a38, 1024) = 0 read(63, 0x8090a38, 1024) = 0 write(2, 0x8089e48, 52) = 52 read(0, 0x8089878, 1)^C # |
ここからは、D プログラムとその出力についてさらに詳しく見ていきます。まず、上記のプログラムとよく似た節により、シェルで呼び出されている read(2) と write(2) が計測されます。ただし、この例では、データのトレースと結果の書式付き出力に、新しい関数 printf() が使用されています。
syscall::read:entry, syscall::write:entry /pid == $1/ { printf("%s(%d, 0x%x, %4d)", probefunc, arg0, arg1, arg2); }
printf() 関数は、以前に使用した trace() 関数のようにデータをトレースする機能と、データとその他のテキストを指定された書式で出力する機能を兼ね備えています。DTrace は、printf() 関数からの指示を受けて、2 番目以降の各引数に関連付けられたデータをトレースし、最初の printf() 引数 (「書式設定文字列」) で指定された規則に従って結果を書式設定します。
書式設定文字列は、% で始まる任意の数の書式変換を含む標準文字列で、対応する引数の書式を指定します。この例の書式設定文字列の最初の変換は 2 番目の printf() 引数、2 番目の変換は 3 番目の引数 (以降同様) に対応しています。変換と変換の間のすべてのテキストは、変更されずそのまま出力されます。% 変換文字に続く文字は、対応する引数の書式を表します。以下に、trussrw.d で使用されている 3 つの書式変換の意味を示します。
%d |
対応する値を 10 進整数として出力 |
%s |
対応する値を文字列として出力 |
%x |
対応する値を 16 進整数として出力 |
DTrace の printf() の機能は、C のprintf(3C) ライブラリルーチンやシェルの printf(1) ユーティリティーと同じです。以前に printf() を使ったことがない場合は、第 12 章出力書式にその書式とオプションの詳細が記載されているので確認してください。この章は、すでに別の言語の printf() を使用したことがあるユーザーも、注意深く読む必要があります。D の printf() は組み込み関数であり、DTrace 専用の新しい書式変換を利用できます。
D コンパイラには、1 つ 1 つの printf() 書式設定文字列をその引数リストと照合することにより、プログラミングミスを防ぐ機能があります。上記の例の節にある probefunc を整数 123 に変更してみてください。変更後のプログラムを実行すると、文字列書式変換 %s では整数引数を扱うことができないことを示すエラーメッセージが表示されます。
# dtrace -q -s trussrw.d dtrace: failed to compile script trussrw.d: line 4: printf( ) argument #2 is incompatible with conversion #1 prototype: conversion: %s prototype: char [] or string (or use stringof) argument: int # |
システムコール read または write の名前とその引数を出力するには、次のような printf() 文を使用します。
printf("%s(%d, 0x%x, %4d)", probefunc, arg0, arg1, arg2);
この文は、現在のプローブ関数の名前とシステムコールの最初の 3 つの整数引数 (DTrace 変数 arg0、arg1、および arg2) をトレースします。プローブ引数の詳細は、第 3 章変数を参照してください。read(2) と write(2) の最初の引数はファイル記述子を表しており、10 進数として出力されます。2 番目の引数はバッファーアドレスを表しており、16 進数値として書式設定されています。最後の引数はバッファーサイズを表しており、10 進数値として書式設定されています。3 番目の引数で使用されている書式指定子 %4d は、%d 書式変換を使用し、最小フィールド幅を 4 文字に制限して値を出力するように指定しています。4 文字未満の整数であれば、printf() によって適切な数の空白文字が挿入され、出力の配置が調整されます。
システムコールの結果を出力し、各出力行を完成させるには、次の節を使用します。
syscall::read:return, syscall::write:return /pid == $1/ { printf("\t\t = %d\n", arg1); }
また、syscall プロバイダは、システムコールごとに、entry プローブと return プローブの両方を発行します。syscall プロバイダの return プローブの DTrace 変数 arg1 には、システムコールの戻り値が入ります。この戻り値は、10 進整数として書式設定されます。書式設定文字列内のバックスラッシュで始まる文字シーケンスは、タブ (\t) と改行 (\n) を表しています。これらの「エスケープシーケンス」は、入力するのが難しい文字を出力または記録する場合に使用します。D では、C、C++、および Java プログラミング言語と共通のエスケープシーケンスがサポートされています。すべてのエスケープシーケンスについては、第 2 章型、演算子、および式を参照してください。