前回は、
今回は、
なお、pidプロバイダを使用する場合にも利用可能です。
可変長領域ダンプの採取
まずは、
採取対象の準備
まずはリスト1に示す独自プロバイダuserioを定義するものと仮定します。
userio.d )provider userio
{
probe readin(void* buf, size_t len);
probe writeout(void* buf, size_t len);
};
userioプロバイダが提供するプローブは、readinプローブなら実際のI/writeoutプローブなら実際のI/
プロバイダを定義したなら、
- ヘッダフィルの生成
- 採取対象ソースファイル
( read_)n_write. c の作成 - 採取対象ソースファイルのコンパイル
( read_の生成)n_write. o dtraceコマンドによるリンク前処理( userio.の生成)o - 採取対象実行可能バイナリのリンク
( read_の生成)n_write
作業手順例を図1に示します。
$ dtrace -s userio.d -h (1) ※read_n_write.cの作成(2) $ cc -c read_n_write.c (3) $ dtrace -s userio.d -G read_n_write.o (4) $ cc -o read_n_write read_n_write.o userio.o (5) $
tracemem()の問題点
第2回でデータ領域内容の採取に使用したDスクリプトをリスト2に再掲します。
pid$target:show_args:checksum:entry
{
this->iobuf = alloca(32);
copyinto(arg0, 32, this->iobuf);
tracemem(this->iobuf, 32);
}
tracemem()は、
また、tracemem()を繰り返す、
しかし、
数キロバイトまでの対応で割り切ることができるのであれば、tracemem()による固定長のダンプを、
可変長領域ダンプの実現
さて、
ここでは、
- スクリプト中の各節は、
記述順序に実行される - 述語
(=前提条件) 記述には、 改変を伴う式を記述することができる
繰り返し処理の大枠の構造を、
inline uintptr_t width = 128;
/* 節 (1) */
userio$target:$1::
{ self->offset = - width; }
/* 節 (2) */
userio$target:$1::
/(self->offset += width) < arg1/
{ ※ self->offset は 0 }
/* 節 (3) */
userio$target:$1::
/(self->offset += width) < arg1/
{ ※ self->offset は 128 }
/* 節 (4) */
userio$target:$1::
/(self->offset += width) < arg1/
{ ※ self->offset は 256 }
:
(以下、同じ記述の繰り返し)
:
冒頭の"inline int width = 128;"は、
第1節で"- width"、self->offset" 変数は、self->offset += width"記述により、
つまり、
この方法による擬似的な繰り返しが、self->offset"変数の値が有効値(この例ではarg1)を越えた後も、
なお、self->offset" の値をわざわざ負の値に初期化しているのは、self->offset"が0の場合の処理を特別扱いしないことで、
実際に可変長領域をダンプするDスクリプトをリスト4に示します。
inline uintptr_t width = 128;
userio$target:$1::
{
printf("addr=0x%p", arg1);
self->offset = - width;
}
userio$target:$1::
/(self->offset += width) < arg1/
{
self->buf = alloca(width);
copyinto(arg0 + self->offset, width, self->buf);
printf("+0x%p -", self->offset);
tracemem(self->buf, width);
}
:
(以下、同じ記述の繰り返し)
:
このスクリプトによる実行例を図 2 に示します。
$ dtrace -s watch_io.d \
-c './read_n_write ofile 400 300' \
read_n_write \
< ./read_n_write
dtrace: script 'watch_io.d' matched 10 probes
CPU ID FUNCTION:NAME
0 60346 main:readin addr=0x8062a10
0 60346 main:readin +0x0 -
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
0: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 .ELF............
10: 02 00 03 00 01 00 00 00 cc 10 05 08 34 00 00 00 ............4...
20: 48 38 00 00 00 00 00 00 34 00 20 00 06 00 28 00 H8......4. ...(.
30: 26 00 25 00 06 00 00 00 34 00 00 00 34 00 05 08 &.%.....4...4...
40: 00 00 00 00 c0 00 00 00 c0 00 00 00 05 00 00 00 ................
50: 00 00 00 00 03 00 00 00 f4 00 00 00 00 00 00 00 ................
60: 00 00 00 00 11 00 00 00 00 00 00 00 04 00 00 00 ................
70: 00 00 00 00 fd ff ff 6f 08 01 00 00 08 01 05 08 .......o........
0 60346 main:readin +0x80 -
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
0: 00 00 00 00 10 00 00 00 00 00 00 00 04 00 00 00 ................
10: 00 00 00 00 01 00 00 00 00 00 00 00 00 00 05 08 ................
20: 00 00 00 00 a9 23 00 00 a9 23 00 00 05 00 00 00 .....#...#......
30: 00 00 01 00 01 00 00 00 ac 23 00 00 ac 23 06 08 .........#...#..
40: 00 00 00 00 6c 02 00 00 58 06 00 00 07 00 00 00 ....l...X.......
50: 00 00 01 00 02 00 00 00 40 24 00 00 40 24 06 08 ........@$..@$..
60: 00 00 00 00 58 01 00 00 00 00 00 00 07 00 00 00 ....X...........
70: 00 00 00 00 2f 75 73 72 2f 6c 69 62 2f 6c 64 2e ..../usr/lib/ld.
....(以下略)
この例では、readinプローブを契機に、
なお、copyinto()サブルーチンに指定するサイズ情報は、
copyinto(arg0 + self->offset,
(arg1 - self->offset < width
? arg1 - self->offset
: width),
self->buf);
この記述により、widthよりも小さい場合には、copyinto()アクションによるカーネル空間への取り込み対象となります。
copyinto()アクションによるユーザ空間からカーネル空間へのコピーの際に、
サイズ指定をリスト5のように記述することで、
なお、a ? b : c"形式の"条件式"
間接参照先の取得
間接参照先の内容を知りたいケースの最も典型的な例は、main()関数のargv引数から文字列を取得したい、
DTrace動作原理に由来する制限
第2回で触れたように、
そのため、copyinstr()サブルーチンを使用する必要がありました。
間接参照先の情報を取得する場合も、
例として使用するmain()関数のargv引数の場合、char*" の配列」
つまり argvの値を直接使用した参照ができないのは勿論、argvの値を元にchar*" の配列」copyinto しても、char*"」、
間接参照先の取得
main() 関数のargv[0]が参照する文字列の取得を例に、
リスト 6 argv[0] 文字列取得スクリプト
(watch_)
self uintptr_t* buf;
pid$target:$1:main:entry
{
self->buf = alloca(sizeof(uintptr_t));
copyinto(arg1, sizeof(uintptr_t), self->buf);
printf("argv[0]='%s'", copyinstr(*(self->buf)));
}
copyinto()サブルーチンでカーネル空間に取り込んだアドレス情報self->bufに格納)copyinstr()サブルーチンを実施することで、argv[0] に相当)
Dスクリプト冒頭における"self uintptr_" 記述は、self->bufの型を宣言しておくことで、
DTraceは概ね自動的に型を判定してくれますが、
それでは実際に動かしてみましょう。
argv[0] 文字列の採取$ dtrace -s watch_argv0.d \
-32 \
-c '/usr/bin/true' \
true
dtrace: script 'watch_argv0.d' matched 1 probe
dtrace: pid 911 has exited
CPU ID FUNCTION:NAME
0 60346 main:entry argv[0]='/usr/bin/true'
$
採取対象に
先ほどの実行例 実はこのオプションは、 間接参照先の取得を行う場合、 Dスクリプトにおけるポインタ値のビット幅は、 そして、 そこで、 なお、 次回は、/usr/コマンドを用いているのは、ポインタ値ビット幅の問題
dtraceコマンド実行の際に、-32"オプションを使用しました。sizeof(intptr_" といった式を元に算出されますが、sizeof(intptr_" 値と、sizeof(intptr_" 値が異なります。-32"オプションによって指定するわけです。isainfo -b"などによりカーネルの動作モードを確認することをお勧めします。可変長間接参照への応用
main() 関数のargv[] 引数から任意の要素文字列を取得するような、argv[] 引数の任意個表示self uintptr_t* argv;
pid$target:$1:main:entry
{
self->index = -1;
}
pid$target:$1:main:entry
/(self->index += 1) < arg0/
{
self->argv = alloca(sizeof(uintptr_t));
copyinto(arg1 + (sizeof(uintptr_t) * self->index),
sizeof(uintptr_t),
self->argv);
printf("argv[%d]='%s'",
self->index, copyinstr(*(self->argv)));
}
:
(以下、同じ記述の繰り返し)
:
argv引数(=arg1)に対してsizeof(uintptr_をself->index倍した値を加算することで、argv[self->index]に相当するアドレス値を算出しているため、copyinto()サブルーチンを使用する箇所が、次回予告