これまで説明してきたDスクリプトでは、
今回は、
特定関数の呼び出し契機での絞り込み
本連載でこれまで例示してきたDスクリプトは、
しかし、
- 採取結果の中から調査対象を特定するのが難しくなる
- 採取結果の記録に必要なディスク領域が増加する
このようなデメリットを無くすためには、
たとえば、show_)
show_nesting )void f1(){ }
void f2(){ f1(); }
void f3(){ f2(); }
void f4(){ f3(); }
int
main(int argc,
const char* argv[])
{
f4();
return 0;
}
これまで使用してきた関数フロー採取用のDスクリプトを使用した場合、
CPU FUNCTION
0 -> _start
0 -> __fsr
0 <- __fsr
0 -> main
0 -> f4
0 -> f3
0 -> f2
0 -> f1
0 <- f1
0 <- f2
0 <- f3
0 <- f4
0 <- main
ここで、f3()呼び出しから先の処理である、
この前提における関数フローの採取は、f3()が呼ばれた時点から開始し、f3()から復帰した時点で停止する、
このような採取範囲の絞り込みを行うには、
watch_focused.d )pid$target:show_nesting:f3:entry
{
self->traced = 1;
}
pid$target:show_nesting::entry,
pid$target:show_nesting::return
/self->traced/
{
}
pid$target:show_nesting:f3:return
{
self->traced = 0;
}
それでは関数フローを採取してみましょう。
$ dtrace -F \
-s watch_focused.d \
-c ./show_nesting
dtrace: script 'watch_focused.d' matched 15 probes
CPU FUNCTION
0 -> f3
0 -> f2
0 -> f1
0 <- f1
0 <- f2
0 <- f3
dtrace: pid 12820 has exited
$
リスト2と比較してみればおわかりのように、f3()呼び出し以後の関数フローのみが、
Dスクリプト文法詳細
先の実行例では、
述語(前提条件)の記述
第2回における説明の際に割愛したDスクリプトの文法要素を補うと、
<Probe-Description> [, ....]
[/<Predicate>/]
{
[<Action> ....]
}
<Predicate>は一般に
<Predicate> 部分には、
<Predicate>部分を記述する場合、/")<Predicate>が省略された場合は、
Dスクリプトでは、
selfを使用したデータ保持
先ほどのDスクリプトには、self->traced"という記述がありましたが、
この記述は、
Dスクリプトにおける変数をそれぞれの通用範囲の点から見た場合、
- - 大域変数
(global varible) - "
VarName" 形式 - - 節固有変数(clause local variable)
- "
this->VarName" 形式 - - スレッド固有変数(thread local variable)
- "
self->VarName" 形式
「大域変数」
大域変数および節固有変数は、
但し、
スレッド固有変数に設定した値は、
大域変数や節固有変数は、
複数条件の組み合わせ
<Predicate> 部分はC/&&"や"||"を用いて複数の式を列挙することが可能です。
複数条件記述が使えることで、
- 関数
f4()呼び出し以後のフローを採取 - ただし、
関数 f2呼び出し以後のフローは不要
このような絞り込みを行うDスクリプトは、
pid$target:show_nesting:f4:entry
{
self->traced = 1;
}
pid$target:show_nesting:f2:return
/self->traced/
{
self->suppressed = 0;
}
pid$target:show_nesting::entry,
pid$target:show_nesting::return
/self->traced && !self->suppressed/
{
}
pid$target:show_nesting:f2:entry
/self->traced/
{
self->suppressed = 1;
}
pid$target:show_nesting:f4:return
{
self->traced = 0;
}
self->tracedが採取の許可を、self->suppressedが採取の抑止を制御しています。
引数・戻り値を参照する述語の記述
述語には、entryプローブ使用時)returnプローブ使用時)
たとえば、XXXXの関数xxxxx()が非0で復帰する際に、arg0)
pid$target:XXXX:xxxx:entry
{
self->arg0 = arg0;
}
pid$target:XXXX:xxxx:return
/arg1 != 0/
{
printf("arg0=%p", self->arg0);
}
述語"/arg1 == 0/"に記述されたarg1は、pidプロバイダのreturnプローブにおけるarg1を意味しますので、xxxx() の戻り値を参照することになります。
述語記述における注意点
述語
記述順序に関する注意
dtraceコマンドは、
たとえば、
/* 節 (1) */
pid$target:show_nesting:f3:entry
{
self->traced = 1;
}
/* 節 (2) */
pid$target:show_nesting:f3:return
{
self->traced = 0;
}
/* 節 (3) */
pid$target:show_nesting::entry,
pid$target:show_nesting::return
/self->traced/
{
}
このスクリプトの各節は、f3()の開始の際に以下のように振る舞います。
- 関数
f3()の開始なので、self->tracedを1に設定 - 関数
f3()の開始なので、無視 self->tracedが1なので、実施=関数フロー表示
結果として、f3()の開始」
その一方で、f3()の終了の際に、
- 関数
f3()の終了なので、無視 - 関数
f3()の終了なので、self->tracedを0に設定 self->tracedが0なので、無視
開始と異なり、f3()の終了」
もうひとつの例として、
/* 節 (1) */
pid$target:show_nesting:f4:entry
{
self->traced = 1;
}
/* 節 (2) */
pid$target:show_nesting:f2:return
/self->traced/
{
self->suppressed = 0;
}
/* 節 (3) */
pid$target:show_nesting::entry,
pid$target:show_nesting::return
/self->traced && !self->suppressed/
{
}
/* 節 (4) */
pid$target:show_nesting:f2:entry
/self->traced/
{
self->suppressed = 1;
}
/* 節 (5) */
pid$target:show_nesting:f4:return
{
self->traced = 0;
}
関数f4()に対する節(1)/(5)ではentry/returnの順序で記述されているプローブが、f2()に対する節(2)/(4)では逆順で書かれていることがわかります。
これは、entry/returnの順で記述した場合、entryプローブによるself->suppressed = 1が実施されるため、f2()自身がトレース採取対象から除外されてしまうのを防ぐためです。
指定の関数を採取対象に含めるか否かに応じて、
再帰呼び出しに関する注意
範囲を絞り込んだ採取の際に使用したDスクリプト
pid$target:show_nesting:f3:entry
{
self->traced = 1;
}
pid$target:show_nesting::entry,
pid$target:show_nesting::return
/self->traced/
{
}
pid$target:show_nesting:f3:return
{
self->traced = 0;
}
先述した実行例では期待通りの結果を得られましたが、
もしも、f3()が、self->traced = 1実施後に再度呼ばれる、f3()呼び出しがあっても、f3()終了でself->traced = 0が実施されてしまうため、
この問題を解決するためには、f3()呼び出しの入れ子状況を正しく把握する必要があります。
そこで、
pid$target:show_nesting:f3:entry
{
self->traced += 1;
}
pid$target:show_nesting::entry,
pid$target:show_nesting::return
/self->traced/
{
}
pid$target:show_nesting:f3:return
{
self->traced -= 1;
}
上記スクリプトでは、self->traced値を、f3()呼び出しの入れ子の深さを表す値として扱っています。
これにより、f3()の最初の呼び出しから復帰するまでは、self->traced値が0になることがありませんので、
なお、
次回予告
次回は、