USDTプロバイダ
USDTプロバイダ(静的に定義されたユーザー領域トレース用)を使用して、アプリケーションにとって意味のあるプローブを使用してユーザー領域コードを計測します。
たとえば、アプリケーションにputおよびget操作がある場合、各操作が複数のコード・パスに実装されている場合でも、ソース・コードにputおよびgetインストゥルメンテーション・ポイントを挿入できます。 DTraceユーザーは、これらの操作がソース・コードでどのように実装されているかを知らなくても、そのようなプローブを有効にしてアクティビティをトレースできます。 通常、プローブが有効になっていない場合、DTraceプローブのパフォーマンスへの影響はごくわずかです。 USDTプローブは、共有ライブラリにも表示されます。
USDTはユーザー・スペース・プロセス用です。 カーネル・モジュールの場合、静的に定義されたトレースは、関連するSDTメカニズムによって処理されます。
USDTプロバイダおよびプローブの定義
ソース・コードに追加する.dファイルでUSDTプロバイダおよびプローブを定義します。 たとえば、ファイルmyproviders.dには次のものが含まれます:
provider myprov
{
probe my__put(int, int);
probe my__get();
};
この例では、プロバイダ名はmyprovですが、pidプロバイダの場合と同様に、DTraceユーザーは、対象となるプロセスのプロセスID (pid)を追加する必要があります。 pidプロバイダとは対照的に、USDTプロバイダの説明では、IDにワイルドカードを使用できます。 たとえば、myprov1234を指定すると、このプロバイダのプローブはプロセスID 1234に対してのみトレースされます。 対照的に、myprov*は、適切にインストゥルメントされたすべてのプロセス(まだ開始されていないプロセスも含む)について、このプロバイダのプローブをトレースします。 または、pidプロバイダと同様に、-cオプションで開始されたプロセスにmyprov$targetなどのシンボリックPIDを使用できます。
プロバイダ定義には、プローブとそのプローブ引数がリストされます。 Dコンパイラでは、プローブ名の2つの連続する下線(__)がダッシュ(-)に変換されます。
次の例では、コマンド./a.outを実行し、その1つのプロセスでUSDTプローブmy-putをトレースし、そのプローブの2つの引数を表示します。
sudo dtrace -c ./a.out -n 'myprov$target:::my-put { printf("put %d %d\n", arg0, arg1); }'
次の例では、まだ開始していない場合でも、myprovプローブを使用してすべてのプロセスをトレースします。 この例では、dtraceの起動時にゼロ・プロセスが一致する場合に、dtraceの-Zオプションを使用します。
sudo dtrace -Z -n 'myprov*:::my-put { printf("put %d %d\n", arg0, arg1); }'
アプリケーション・コードへのUSDTプローブの追加
次のCコードfunc.cについて考えてみます:
#include "myproviders.h"
void foo(void)
{
...
if (MYPROV_MY_PUT_ENABLED()) {
int arg0, arg1;
arg0 = bar(1111);
arg1 = bar(2222);
MYPROV_MY_PUT(arg0, arg1);
}
...
MYPROV_MY_GET();
...
}
この例では、自動的に生成されるヘッダー・ファイルが含まれています。 このヘッダー・ファイルの名前は、プローブにアクセスするためのマクロを定義するファイル名から導出されます。 USDTプローブへのアクセスを提供するマクロを定義します。
コードにプローブを配置し、プローブを参照するには、プロバイダとプローブ名を連結したマクロを使用し、大文字に変換します。 この例では、マクロはMYPROV_MY_PUT()およびMYPROV_MY_GET()です。
オプションの最適化では、プローブが有効かどうかをテストします。 無効化されたDTraceプローブの計算オーバーヘッドは、多くの場合、動作しない命令と似ていますが、プローブ引数の設定にはコストがかかる場合があります。 この例では、bar(1111)およびbar(2222)がコストのかかる関数コールである可能性があります。 したがって、プローブごとに、DTraceは_ENABLEDを追加して命名されたis-enabledマクロも提供します。 この例では、my-putプローブのMYPROV_MY_PUT_ENABLED()を使用して、無効化されたプローブに関連する作業のコストを最小限に抑えます。
USDTプローブを使用したアプリケーションの構築
dtraceコマンドはビルド・プロシージャの一部となり、次の4つの部分で考えられます:
-
プローブにアクセスするためのマクロを定義するヘッダー・ファイルを生成します。 次に例を示します:
dtrace -h -s myproviders.d前述のコマンドは、
myproviders.hヘッダー・ファイルを生成します。dtraceにはランタイム・トレースのroot権限が必要ですが、ヘッダー・ファイルの生成にはこの要件はありません。 -
ソース・コードをコンパイルします。これには、プロバイダおよびプローブの定義に基づいて、
dtraceで生成されたヘッダー・ファイルが含まれます。 たとえば、いくつかのソース・ファイルの場合:gcc -c func1.c gcc -c func2.c gcc -c func3.c -
dtraceを使用して各オブジェクト・ファイルをポスト処理します。 次に例を示します:dtrace -G -s myproviders.d func1.o func2.o func3.oこの場合も、
dtraceは、このステップのroot権限を必要としません。 このステップでは、myproviders.dおよび他のオブジェクト・ファイルからオブジェクト・ファイルmyproviders.oを生成し、プロバイダおよびプローブ定義をユーザー・アプリケーションにリンクします。 -
最後の実行可能ファイルをリンクします。
gccへの-Wl,--export-dynamicリンク・オプションは、D関数ustack()を使用する場合など、実行時にストリップされた実行可能ファイルのシンボル検索に必要です。 次に例を示します:gcc -Wl,--export-dynamic,--strip-all myproviders.o func1.o func2.o func3.o
USDTの例
-
次のものを含むファイル
myproviders.dを作成します:provider myprov { probe my__put(int, int); probe my__get(); }; -
次を含むCプログラム
func.cを作成します:#include <stdio.h> #include <unistd.h> #include "myproviders.h" int bar(int in) { printf("bar evaluates %d\n", in); return 3 * in; } void foo(void) { if (MYPROV_MY_PUT_ENABLED()) { int arg0, arg1; arg0 = bar(1111); arg1 = bar(2222); MYPROV_MY_PUT(arg0, arg1); } MYPROV_MY_GET(); } int main(int c, char **v) { while (1) { usleep(1000 * 1000); foo(); } return 0; } -
次を使用してアプリケーションを構築します:
dtrace -h -s myproviders.d gcc -c func.c dtrace -G -s myproviders.d func.o gcc -Wl,--export-dynamic,--strip-all myproviders.o func.o -
このプログラムは、いくつかの方法で実行できます。 次に例を示します:
-
次の方法でトレース・プログラムを実行できます:
sudo dtrace -c ./a.out -q -n ' myprov$target:::my-put { printf("put %d %d\n", arg0, arg1); } myprov$target:::my-get { printf("get\n"); } tick-5sec {exit(0)}'最初の例では、
-cオプションを指定してa.outコマンドを実行します。-qquietオプションは、無関係な出力を抑制します。 Dスクリプトはコマンドラインにあり、putプローブの両方の引数を出力し、getプローブを報告します。 この例では、$targetを象徴的に参照しています。ターゲット・コマンドのpidは、-cで指定されています。5秒後、
dtraceジョブが終了し、ターゲット・コマンドが終了します。 -
または、次のコマンドを使用してこの例を実行できます:
./a.out & pid=$! sudo dtrace -q -n ' myprov'$pid':::my-put { printf("put %d %d\n", arg0, arg1); } myprov'$pid':::my-get { printf("get\n"); } tick-5sec {exit(0)}' kill $pidこの例では、コマンドがすでにプロセスID
$pidで実行されています。 興味のある特定の数値PIDを指します。dtraceコマンドでは、対象となるプロセスは終了しません。 これは別々に扱います。 -
もう1つの可能性は、次のコマンドを使用してプログラムを実行することです:
sudo dtrace -Z -q -n ' myprov*:::my-put { printf("put %d %d\n", arg0, arg1); } myprov*:::my-get { printf("get\n"); } tick-10sec {exit(0)}' & ./a.out &この例では、
-Zオプションを使用すると、最初にゼロのプローブ一致が許可されます。 プローブは、USDTプロセスが開始されたあとで一致します。 短い遅延の後、USDTプロセスが開始されます。 ある時点で、USDTプロセスは終了しています。
これらの各ケースでは、出力は1秒後に出力され、次のようになります:
... bar evaluates 1111 bar evaluates 2222 put 3333 6666 get ...Dスクリプトでは、次を使用して
putプローブを省略できます:sudo dtrace -c ./a.out -q -n ' myprov$target:::my-get { printf("get\n"); } tick-5sec {exit(0)}'この場合、
putプローブが起動されるだけでなく、is-enabled条件付きMYPROV_MY_PUT_ENABLED()もfalseになります。 したがって、bar()関数はコールされません。 1秒ごとに表示される出力は、次のとおりです:get -