7 投機トレース
この章では、DTraceの投機トレース機能の使用方法について説明します。これには、暫定的にデータをトレースし、後でそのデータをトレース・バッファにコミットするか破棄するかを決定できる機能が含まれます。
投機トレースについて
DTraceの場合、不要なイベントを除外するための主要なメカニズムは述語メカニズムです。詳細は、「Dプログラム構造」を参照してください。述語は、その起動時にプローブ・イベントが重要であるかどうかがわかっている場合に便利です。たとえば、特定のプロセスや特定のファイル記述子に関連するアクティビティのみが重要な場合は、プローブの起動時に、それが対象のプロセスまたはファイル記述子に関係するプローブかどうか判断できます。プローブの起動後しばらく経過しないと、そのプローブ・イベントが重要かどうかわからない場合もあります。
たとえば、EIO
やEINVAL
などの一般的なエラー・コードで失敗することがあるシステム・コールについて考えてみます。この場合、エラー状態につながるコード・パスを調べると役立つことがあります。このコード・パスを取得するために、すべてのプローブを有効にすることもできます。ただしそれが可能なのは、失敗したシステム・コールを切り分けて、意味のある述語を作成できる場合に限られます。エラーが散発的であったり特定困難な場合には、重要な可能性があるイベントをすべてトレースし、データを後処理して問題のコード・パスと無関係なイベントを除外する必要があります。この場合、重要なイベントの数がわずかであってもトレースが必要なイベントの数は膨大になり、後処理が難しくなります。
このような状況では、投機トレース機能を使用して、1つ以上のプローブの場所にあるデータを暫定的にトレースできます。その後、別のプローブの場所にある主バッファにデータをコミットできます。結果として、トレース・データには重要な出力のみが含まれ、後処理は必要とされず、DTraceのオーバーヘッドが最小化されます。
投機インタフェース
次の表では、DTraceの投機関数について説明します。
表7-1 DTraceの投機関数
関数 | 引数 | 説明 |
---|---|---|
|
なし |
新しい投機バッファの識別子を返します |
|
ID |
同じ節の残りの部分を、指定されたIDの投機バッファにトレースすることを示します |
|
ID |
指定されたIDの投機バッファをコミットします。 |
|
ID |
指定されたIDの投機バッファを破棄します。 |
投機の作成
speculation
関数は、投機バッファを割り当て、投機識別子を返します。投機識別子は、後でspeculate
関数をコールするときに使用する必要があります。投機バッファは有限のリソースです。speculation
をコールしたときに使用可能な投機バッファがない場合、IDとしてゼロが返され、対応するDTraceエラー・カウンタが増分されます。ゼロのIDは常に無効ですが、speculate
、commit
およびdiscard
の各関数に渡すことができます。speculation
のコールに失敗した場合は、次のようなメッセージがdtraceによって生成されます。
dtrace: 2 failed speculations (no speculative buffer space available)
投機バッファの数はデフォルトでは1個ですが、値オプションで増やすこともできます。「投機のオプションおよびチューニング」を参照してください。
投機の使用
投機を使用するには、speculation
から返された識別子を、データ記録アクションの前に節でspeculate
関数に渡す必要があります。speculate
を含む節の後続のデータ記録アクションはすべて、投機的にトレースされます。1つのDプローブ節でデータ記録アクションの後にspeculate
がコールされた場合は、Dコンパイラでコンパイル時エラーが生成されます。したがって、節には投機トレース・リクエストのみ、またはそれ非投機トレース・リクエストのみを含めることができ、両方を含めることはできません。
集積アクション、破壊アクションおよびexit
アクションは投機的になりません。speculate
を含む節でこれらのアクションのいずれかを実行しようとすると、コンパイル時エラーが発生します。また、speculate
がspeculate
の後に続くことはありません。1つの節に許可される投機は1つのみです。speculate
しか含まない節は、有効なプローブIDのみをトレースするように定義されているデフォルトのアクションを投機的にトレースします。デフォルト・アクションの詳細は、「アクションおよびサブルーチン」を参照してください。
次の例に示すように、通常は、speculation
の結果をスレッド固有変数に割り当て、その変数を他のプローブに対する述語として、またspeculate
に対する引数として使用します。
syscall::openat:entry { self->spec = speculation(); } syscall::: /self->spec/ { speculate(self->spec); printf("this is speculative"); }
投機のコミット
投機をコミットするには、commit
関数を使用します。投機バッファをコミットすると、そのデータが主バッファにコピーされます。指定した投機バッファに、主バッファで使用可能な領域より多くのデータが含まれている場合、データはコピーされず、バッファの欠落カウントの値が増分されます。バッファを複数のCPU上で投機的にトレースした場合、コミットされたCPU上の投機データはすぐにコピーされますが、その他のCPU上の投機データは、commit
の実行後しばらくしてからコピーされます。したがって、あるCPU上でcommit
が開始されてから、投機バッファのデータがすべてのCPU上の主バッファにコピーされるまでには、少し時間がかかります。この時間が、クリーニング・レートで指定された時間より長くなることはありません。「投機のオプションおよびチューニング」を参照してください。
コミットする投機バッファは、各CPUの投機バッファが対応するCPUごとの主バッファに完全にコピーされるまで、後続のspeculation
コールで使用することはできません。同様に、コミットするバッファに対する後続のspeculate
コールは暗黙的に破棄され、後続のcommit
コールやdiscard
コールは暗黙的に失敗します。最後に、commit
を含む節にデータ記録アクションを含めることはできません。ただし、1つの節に複数のcommit
コールを含めて、ばらばらのバッファをコミットできます。
投機の破棄
投機を破棄するには、discard
関数を使用します。投機バッファを破棄すると、その内容も破棄されます。投機がdiscard
をコールしたCPU上でのみアクティブになっていた場合は、バッファをその後のspeculation
コールですぐに使用できます。投機が複数のCPU上でアクティブになっていた場合は、discard
をコールした後で、破棄された投機バッファを次のspeculation
コールで使用できるようになるまで、少し時間がかかります。あるCPU上でdiscard
が呼び出された後で、バッファをその後の投機で使用できるようになるまでの時間が、クリーニング・レートで指定された時間より長くなることはありません。speculation
をコールした時点で、すべての投機バッファが破棄中またはコミット中のために使用可能なバッファがない場合は、次のようにdtraceメッセージが表示されます。
dtrace: 905 failed speculations (available buffer(s) still busy)
投機バッファ数やクリーニング・レートをチューニングすれば、すべてのバッファが同時に使用不可になるのを防ぐことができます。「投機のオプションおよびチューニング」を参照してください。
投機の例
投機を使用できる1つの場合として、特定のコード・パスをハイライトする例があります。次の例は、open()
システム・コールでコールに失敗したときのコード・パスをすべて示しています。次のソース・コードを入力し、specopen.d
という名前のファイルに保存します。
#!/usr/sbin/dtrace -Fs syscall::open:entry { /* * The call to speculation() creates a new speculation. If this fails, * dtrace will generate an error message indicating the reason for * the failed speculation(), but subsequent speculative tracing will be * silently discarded. */ self->spec = speculation(); speculate(self->spec); /* * Because this printf() follows the speculate(), it is being * speculatively traced; it will only appear in the data buffer if the * speculation is subsequently committed. */ printf("%s", copyinstr(arg0)); } syscall::open:return /self->spec/ { /* * To balance the output with the -F option, we want to be sure that * every entry has a matching return. Because we speculated the * open entry above, we want to also speculate the open return. * This is also a convenient time to trace the errno value. */ speculate(self->spec); trace(errno); } syscall::open:return /self->spec && errno != 0/ { /* * If errno is non-zero, we want to commit the speculation. */ commit(self->spec); self->spec = 0; } syscall::open:return /self->spec && errno == 0/ { /* * If errno is not set, we discard the speculation. */ discard(self->spec); self->spec = 0; }
前述のスクリプトを実行すると、次のような出力が生成されます。
# ./specopen.d dtrace: script ’./specopen.d’ matched 4 probes CPU FUNCTION 1 => open /var/ld/ld.config 1 <= open 2 1 => open /images/UnorderedList16.gif 1 <= open 4 ...
投機のオプションおよびチューニング
投機トレース・アクションを試行したときに投機バッファがいっぱいになっていると、バッファには何のデータも格納されず、欠落カウントが増分されます。この状況では、次のようなメッセージがdtraceによって生成されます。
dtrace: 38 speculative drops
投機が欠落しても、投機バッファのコミット時に投機バッファの全内容が主バッファにコピーされる機能は阻害されません。同様に、最終的に破棄された投機バッファで欠落が発生した場合でも、投機の欠落が発生することもあります。投機の欠落を少なくするには投機バッファのサイズを大きくしますが、これはspecsize
オプションを使用してチューニングします。specsize
オプションには、任意のサイズ接尾辞を指定できます。このバッファのサイズ変更ポリシーはbufresize
オプションで指定します。
speculation
のコール時に、投機バッファが使用できないこともあります。まだコミットも破棄もされていないバッファが存在する場合には、次のようなメッセージがdtraceで生成されます。
dtrace: 1 failed speculation (no speculative buffer available)
このような投機が失敗する可能性を小さくするには、nspec
オプションを指定して、投機バッファの数を増やします。nspec
のデフォルト値は1
です。
また、すべての投機バッファがビジー状態になっている場合も、speculation
は失敗します。この場合、次のようなエラー・メッセージが表示されます。
dtrace: 1 failed speculation (available buffer(s) still busy)
このエラー・メッセージは、1つの投機バッファに対してcommit
がコールされてから、そのバッファが実際にすべてのCPUでコミットされるまでの間にspeculation
がコールされたことを示しています。このような投機が失敗する可能性を小さくするには、cleanrate
オプションを使用して、CPUがクリーニングされるレートを大きくします。cleanrate
のデフォルト値は101
です。