C/C++プログラマのためのDTrace入門

第4回 前提条件の記述

この記事を読むのに必要な時間:およそ 4 分

Dスクリプト文法詳細

先の実行例では,まずは動かしてみることを優先しましたので,ここでは文法的な面から掘り下げてみたいと思います。

述語(前提条件)の記述

第2回における説明の際に割愛したDスクリプトの文法要素を補うと,Dスクリプトの記述形式はリスト4のようになります。

リスト4 Dスクリプト文法

<Probe-Description> [, ....]
[/<Predicate>/]
{
    [<Action> ....]
}

<Predicate>は一般に「述語」と呼ばれ,⁠前提条件」のことを意味します。

<Predicate> 部分には,条件判定のための式が記述されます。式の値が非ゼロであれば,C/C++と同様に条件成立とみなされます。

<Predicate>部分を記述する場合,スラッシュ("/")で囲む必要があります。<Predicate>が省略された場合は,常に<Action>が実施されます。

Dスクリプトでは,この一連のまとまりのことを(clause)と呼びます。

selfを使用したデータ保持

先ほどのDスクリプトには,"self->traced"という記述がありましたが,これは初めて目にするものです。

この記述は,スレッド固有変数と呼ばれる記憶領域の作成/参照を行うためのものです。

Dスクリプトにおける変数をそれぞれの通用範囲の点から見た場合,以下に示す3種類に分類されます。

- 大域変数(global varible)
"VarName" 形式
- 節固有変数(clause local variable)
"this->VarName" 形式
- スレッド固有変数(thread local variable)
"self->VarName" 形式

「大域変数」はここで初めて出てきましたが,C/C++プログラムで言うところの大域変数と同じと思って構いません。

大域変数および節固有変数は,同一Dスクリプト内の任意の時点で参照可能です(※1⁠⁠。

但し,大域変数は暗黙の初期値として0が設定されるため,値が未設定の状態でも参照可能ですが,節固有変数を値が未設定の状態で参照した場合は,Dスクリプトの実行時エラーとなります。

スレッド固有変数に設定した値は,同じスレッド上から参照した場合のみ,設定した値を得ることができます。別のスレッドから参照した場合,事前に当該スレッドで値の参照がされていればその値が,そうでなければ 0(NULL)値が得られます。

大域変数や節固有変数は,複数スレッド間で共有されてしまいますので,対象プロセスがマルチスレッド稼動している場合,これらを一時退避等の用途で使用すると,想定外の挙動となる可能性(※2)がありますので注意してください。

※1)
第2回での節固有変数に対する「局所変数のようなもの」という説明は,あの時点での説明としてはそこそこ妥当ではあるものの,厳密に通用範囲の点から考えた場合,実はあまり適切ではありません。
※2)
実のところ第2回で説明したメモリ内容表示の例も,マルチスレッド稼動の際には節固有変数ではなくスレッド固有変数でallocaの戻り値を格納すべきです。

複数条件の組み合わせ

<Predicate> 部分はC/C++ 等での条件記述と同様に,"&&"や"||"を用いて複数の式を列挙することが可能です。

複数条件記述が使えることで,たとえば以下の様な採取範囲の絞り込みを行うことができます。

  • 関数f4()呼び出し以後のフローを採取
  • ただし,関数f2呼び出し以後のフローは不要

このような絞り込みを行うDスクリプトは,リスト5のようになります。

リスト5 抑止付きの絞り込み

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で復帰する際に,呼び出し時点での第1引数arg0を表示するには,リスト6のDスクリプトを使用します。

リスト6 戻り値を用いた述語記述

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() の戻り値を参照することになります。

著者プロフィール

藤原克則(ふじわらかつのり)

Mercurial三昧の日々が嵩じて, いつの間にやら『入門Mercurial Linux/Windows対応』を上梓。凝り性なのが災いして,年がら年中アップアップな一介の実装屋。最近は仕事の縁が元で,OpenSolarisに入れ込む毎日。