PIDプロバイダ
pidプロバイダは、ユーザー・プロセス(関数entryとreturnの両方)および任意の命令をトレースします。
PIDプローブ
プローブは、そのプロバイダ、モジュール、関数、および名前に名前を付けることによって完全に指定されます。
pidプロバイダ名はpid$pidで、$pidは目的のプロセスのプロセスID (pid)です。 プロセスIDを指定する必要があります(ワイルドカードは指定しません)。ただし、シンボリック値を使用できます。 次に例を示します:
sudo dtrace -lP pid
プロセスIDが指定されていないため、前のコマンドでプローブが見つかりません。 かわりに、次のようなコマンドを使用します:
sudo dtrace -ln 'pid1234567:libc::entry'
sudo dtrace -p 1234567 -ln 'pid$target:libc::entry'
sudo dtrace -c ./a.out -n 'pid$target:libc::entry { trace("hi"); }'
sudo dtrace -n 'pid$1:libc::entry { trace("hi"); }'
この例では、1234567は架空のpid値で、システムに適したpid値で置き換える必要があります。$targetは、-pまたは-cで指定されたターゲット・コマンドのpidに展開されるマクロで、$1はdtraceのコマンドライン引数です。 この例では、次のように表示されます:
1234567
モジュール名は、実行可能ファイル内のモジュールです。 特殊なケースには、実行可能ファイルのロード・オブジェクト(a.outと呼ばれる場合があります)と、次の方法で参照できる共有ライブラリが含まれます:
-
フル・パス名(
/usr/lib/libc.so.1など)。 -
ベース名(
libc.so.1など)。 -
最初のベース名は、
.サフィクス(libc.soやlibcなど)まで一致します。
関数名は、通常、プローブが配置されている関数の名前です。 関数がコンパイラによってインライン化されている場合は、pidトレースには使用できません。
特別な関数-があります。 この場合、モジュール名は空白にするか、a.outモジュールを参照する必要があります。 さらに、プローブ名は、a.outモジュール内の命令に対する絶対16進オフセットである必要があります。
プローブ名は次のいずれかです:
-
entry: 関連する関数のエントリを参照します。 -
return: 関連する関数からの戻り値を示します。 DTraceの従来の実装では、プローブはなんらかの戻り命令で起動されました。 現在の実装では、returnはuretprobeを使用して実装され、呼出し側関数で起動されます。 -
命令オフセット。 先頭の
0xのない16進オフセットは、指定された関数に対する相対ですが、関数名が-の場合、絶対オフセットです。
pidプローブの引数
entryプローブの場合、プローブ引数はプローブされた関数の引数と同じです。
returnプローブの場合、arg1はプローブされた関数の戻り値です。
オフセット・プローブの場合、プローブ引数はありません。
pidの例
関数foo()をコールするmain.cという名前の次のプログラムについて考えてみます:
int foo(int i, int j) {
return (i + j) - 6666;
}
int main(int c, char **v) {
return foo(1234, 8765) != 3333;
}
foo()の引数は1234および8765で、戻り値は3333です。
プログラムをコンパイルします:
gcc main.c
D1.dという名前のDスクリプトを作成します:
pid$target:a.out:foo:entry,
pid$target:a.out:foo:return
{
printf("%x %s:%s\n", uregs[R_PC], probefunc, probename);
}
pid$target:a.out:foo:entry
{
printf("entry args: %d %d\n", arg0, arg1);
}
pid$target:a.out:foo:return
{
printf("return arg: %d\n", arg1);
}
Dスクリプトを実行します:
sudo dtrace -c ./a.out -qs D1.d
出力は次のようになります:
401106 foo:entry
entry args: 1234 8765
40113d foo:return
return arg: 3333
foo() entryおよびreturnでは、プローブの起動時に、ターゲット・スレッドの保存されたユーザー・モード・レジスタ値から、プローブ関数および名前とともにPCを出力します。 さらに、エントリ・プローブの2つの引数とリターン・プローブのarg1を出力します。 foo()エントリの引数が1234および8765で、戻り値が3333であることが予想されます。
PCを理解するには、objdumpを実行します:
objdump -d a.out
出力は次のようになります:
0000000000401106 <foo>:
401106: 55 push %rbp
401107: 48 89 e5 mov %rsp,%rbp
40110a: 89 7d fc mov %edi,-0x4(%rbp)
[...]
000000000040111f <main>:
40111f: 55 push %rbp
[...]
401138: e8 c9 ff ff ff callq 401106 <foo>
40113d: 83 f0 01 xor $0x1,%eax
[...]
出力の多くは抑制されていますが、Dスクリプトで報告されているように、foo() entry PCは0x401106です。 リターンPCは0x40113dで、これはfoo()コールの直後のPCです。
ノート:
DTraceの他のバージョンでは、戻りPCはコールされた関数の戻り命令用です。 Linuxでは、リターンuprobe (uretprobe)を使用して、コール元で指示を返します。
最後に、特定の命令を調べる方法を説明します。 foo()、PC 0x40110aで3番目の命令を選択します。 これは、foo()の先頭から4バイトの相対オフセットにあります。 このDスクリプトの名前はD2.dです:
pid$target:a.out:foo:4,
pid$target:a.out:-:40110a
{
printf("%x %s:%s\n", uregs[R_PC], probefunc, probename);
}
Dスクリプトを実行します:
sudo dtrace -c ./a.out -qs D2.d
出力は次のようになります:
40110a foo:4
40110a -:40110a
相対オフセットfoo:4と絶対オフセット-:40110aの両方を使用して、選択した命令を調べます。 両方のプローブが起動し、どちらも同じPC 0x40110aを報告します。