これまで説明してきた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になることがありませんので、
なお、
次回予告
次回は、