DTrace には、pid プロバイダの機能を拡張するため、アプリケーションコード内にカスタムプローブを定義する機能があります。この機能は、ユーザーアプリケーション開発者向けです。これらのカスタムプローブは、静的プローブです。無効にすると、ほとんどオーバーヘッドはかかりません。有効にするときは、ほかの DTrace プローブと同じように動的に有効にします。こうした静的プローブを使用すれば、アプリケーションの実装に関する知識があるかどうかを問わず、DTrace ユーザーにアプリケーションのセマンティクスを示すことができます。この章では、ユーザーアプリケーション内に静的プローブを定義する方法と、DTrace を使ってこうしたプローブをユーザープロセス内で有効にする方法について説明します。
DTrace では、開発者は、アプリケーションコード内 (完成したアプリケーションと共有ライブラリを含む) に静的プローブポイントを埋め込むことができます。それが開発環境であろうが本稼働環境であろうが、そのアプリケーションやライブラリを実行しているときはいつでも、これらのプローブを有効にすることができます。プローブを定義するときは、DTrace ユーザーコミュニティにとって、その意味が理解しやすいかどうかを考慮する必要があります。たとえば、要求の送付元クライアントと通信する Web サーバーに query-receive プローブ、その要求に応答する Web サーバーには query-respond プローブを定義するとします。ほとんどの DTrace ユーザーは、これらのプローブの意味を難なく理解できます。これらのプローブは、アプリケーションの下位の実装詳細レベルではなく、最上位の抽象レベルに対応しています。DTrace ユーザーは、これらのプローブを使って、要求の時間的分布を調べることができます。query-receive プローブの引数が URL 要求文字列である場合は、このプローブと io プロバイダを組み合わせて、ディスク入出力の大部分を生成している要求を特定できます。
プローブ名とプローブの位置を選択するときは、抽象化の安定性についても考慮する必要があります。アプリケーションの将来のリリースで実装が変更されても、引き続き使用できるプローブかどうか。どのシステムアーキテクチャにも通用するプローブか。あるいは、特定の命令セットに固有のプローブか。この章では、こうした判断をもとに静的トレースの定義を行なっていく方法を詳しく説明します。
ライブラリや実行可能ファイルの DTrace プローブは、対応するアプリケーションバイナリの ELF セクションに定義されます。この節では、プローブを定義し、アプリケーションのソースコードに追加することにより、アプリケーションの構築プロセスに DTrace プローブ定義を追加する方法について説明します。
.d ソースファイルに DTrace プローブを定義します。このソースファイルは、あとでアプリケーションのコンパイルとリンク時に使用されます。まず、適切なユーザーアプリケーションプロバイダの名前を選択します。アプリケーションコードを実行している各プロセスのプロセス ID に、選択したプロバイダ名が追加されます。たとえば、プロセス ID 1203 で実行されている Web サーバー用としてプロバイダ名 myserv を選択した場合、このプロセスに対応する DTrace プロバイダ名は myserv1203 になります。.d ソースファイルに、次のようなプロバイダ定義を追加します。
provider myserv { ... };
次に、各プローブとその引数の定義を追加します。次の例では、「プローブポイントの選択」で説明した 2 つのプローブを定義しています。最初のプローブは、string 型の引数を 2 つとります。2 番目のプローブは引数をとりません。D コンパイラは、プローブ名に含まれる 2 つの連続した下線 (--) を単一のハイフン (-) に変換します。
provider myserv { probe query__receive(string, string); probe query__respond(); };
プロバイダ定義には安定性属性を追加して、プローブの使用者がアプリケーションの将来のバージョンでの変更可能性について判断できるようにしてください。DTrace の安定性属性の詳細については、第 39 章安定性を参照してください。以下は、安定性属性の定義例です。
#pragma D attributes Evolving/Evolving/Common provider myserv provider #pragma D attributes Private/Private/Unknown provider myserv module #pragma D attributes Private/Private/Unknown provider myserv function #pragma D attributes Evolving/Evolving/Common provider myserv name #pragma D attributes Evolving/Evolving/Common provider myserv args provider myserv { probe query__receive(string, string); probe query__respond(); };
ユーザーが追加したプローブの整数でない引数を D スクリプト内で使用する場合、copyin() および copyinstr() 関数を使ってそれらの引数を取得する必要があります。詳細については、第 33 章ユーザープロセスのトレースを参照してください。
.d ファイルを使ってプローブを定義できたら、ソースコードに、プローブがトリガーされる位置の情報を追加する必要があります。次のような C アプリケーションソースコードがあるとします。
void main_look(void) { ... query = wait_for_new_query(); process_query(query) ... }
プローブの位置の情報を追加するには、<sys/sdt.h>に定義されている DTRACE_PROBE() マクロの参照を追加します。次の例を参照してください。
#include <sys/sdt.h> ... void main_look(void) { ... query = wait_for_new_query(); DTRACE_PROBE2(myserv, query__receive, query->clientname, query->msg); process_query(query) ... }
マクロ名 DTRACE_PROBE2 の接尾辞 2 は、プローブに渡される引数の数を表します。プローブマクロの最初の 2 つの引数は、プロバイダ名とプローブ名です。これらはそれぞれ、D プロバイダ定義とプローブ定義に一致していなければなりません。残りのマクロ引数は、プローブの起動時に DTrace arg0..9 変数に割り当てられる引数です。アプリケーションソースコード内で同じプロバイダ名やプローブ名を繰り返し参照できます。ソースコード内で同じプローブが何回も参照されている場合、そのいずれかがプローブを起動することになります。
アプリケーションの構築プロセスに、DTrace プロバイダとプローブの定義を追加する必要があります。通常の構築プロセスでは、各ソースファイルがコンパイルされ、対応するオブジェクトファイルが生成されます。次に、このコンパイルされたオブジェクトファイルが互いにリンクされることにより、最終的なアプリケーションバイナリが完成します。次の例を参照してください。
cc -c src1.c cc -c src2.c ... cc -o myserv src1.o src2.o ... |
アプリケーションに DTrace プローブ定義を追加するには、構築プロセスに適切な Makefile 規則を追加して、dtrace コマンドを実行します。次の例を参照してください。
cc -c src1.c cc -c src2.c ... dtrace -G -32 -s myserv.d src1.o src2.o ... cc -o myserv myserv.o src1.o src2.o ... |
この dtrace コマンドは、コンパイラコマンドによって生成されたオブジェクトファイルに後処理を施すことにより、myserv.d とその他のオブジェクトファイルから myserv.o という名前のオブジェクトファイルを生成します。プロバイダ定義とプローブ定義をユーザーアプリケーションにリンクするには、dtrace -G オプションを使用します。32 ビットアプリケーションバイナリを構築するには、-32 オプションを使用します。64 ビットアプリケーションバイナリを構築するには、-64 オプションを使用します。