Solaris 動的トレースガイド

配列

D では、整数型の変数のほか、文字列型や複合型を表す型として、「構造体」と「共用体」を定義できます。D では、C で使用できるすべての型を使用できます。C プログラミングの経験がないユーザーは、第 2 章型、演算子、および式で、さまざまなデータ型について確認してください。D では、「連想配列」と呼ばれる特殊な変数もサポートされています。連想配列は、キーの集合を値の集合に関連付けるという点では通常の配列と同じですが、キーが固定範囲の整数値に制限されないという特徴を備えています。

D の連想配列には、1 個以上の任意の型の値からなるリストでインデックスを付けることができます。個々のキー値が集まって「」を形成し、この組が、配列へのインデックス付けと、個々のキーに対応する値の参照または変更に使用されます。連想配列で使用される組はすべて、同じ型署名を使用している必要があります。これは、すべての組キーの長さ、キー型、およびキーの並び順が一致していなければならないということです。連想配列の各要素に関連付けられる値も、配列全体の固定型になります。たとえば、次の D 文は、組署名が [ string, int ]、値の型が int の新しい連想配列 a を定義し、この配列に整数値 456 を格納します。

a["hello", 123] = 456;

配列の定義後は、その他の D 変数と同じ方法で、配列要素にアクセスできます。たとえば、次の D 文は、値を 456 から 457 に増分することにより、以前に a に格納された配列要素を変更します。

a["hello", 123]++;

配列要素の値がまだ割り当てられていない場合、値はゼロに設定されます。では、実際に D プログラム内で連想配列を使用してみましょう。以下のプログラムを入力し、rwtime.d という名前のファイルに保存してください。


例 1–3 rwtime.d: read(2) および write(2) 呼び出しの時間

syscall::read:entry,
syscall::write:entry
/pid == $1/
{
	ts[probefunc] = timestamp;
}

syscall::read:return,
syscall::write:return
/pid == $1 && ts[probefunc] != 0/
{
	printf("%d nsecs", timestamp - ts[probefunc]);
}

rwtime.d の実行時には、trussrw.d のときと同じように、シェルプロセスの ID を指定します。シェルコマンドをいくつか入力すると、各システムコールの経過時間が表示されます。次のコマンドを入力し、別のシェルで Return キーを何回か押します。


# dtrace -s rwtime.d `pgrep -n ksh`
dtrace: script 'rwtime.d' matched 4 probes
CPU     ID                    FUNCTION:NAME
  0     33                      read:return 22644 nsecs
  0     33                      read:return 3382 nsecs
  0     35                     write:return 25952 nsecs
  0     33                      read:return 916875239 nsecs
  0     35                     write:return 27320 nsecs
  0     33                      read:return 9022 nsecs
  0     33                      read:return 3776 nsecs
  0     35                     write:return 17164 nsecs
...
^C
#

各システムコールの経過時間をトレースするには、read(2)write(2) の entry (開始時) および return (終了時) を両方とも計測し、各ポイントで時間を確認する必要があります。次に、システムコールの終了時に、最初のタイムスタンプと 2 番目のタイムスタンプの差分を計算します。システムコールごとに別々の変数を使用することもできますが、そうすると新たにシステムコールを追加してプログラムを拡張するのが難しくなります。そのため、プローブ関数名でインデックスが付けられた連想配列を使用します。以下は、最初のプローブ節です。

syscall::read:entry,
syscall::write:entry
/pid == $1/
{
	ts[probefunc] = timestamp;
}

この節では、ts という名前の配列が定義され、適切なメンバーに DTrace 変数 timestamp の値が割り当てられます。この変数は、Solaris ライブラリルーチン gethrtime(3C) とよく似た機能を持っており、常に増加するナノ秒カウンタの値を返します。entry のタイムスタンプが保存されたあと、対応する return プローブにより、再度 timestamp が調べられ、現在の時間と保存された値の差分が報告されます。

syscall::read:return,
syscall::write:return
/pid == $1 && ts[probefunc] != 0/
{
	printf("%d nsecs", timestamp - ts[probefunc]);
}

return プローブの述語は、DTrace がトレースしているプロセスが適切であることと、対応する entry プローブがすでに起動していて、ts[probefunc] にゼロ以外の値が割り当てられていることを要求します。この方法で、DTrace がはじめて起動したときの無効な出力を排除できます。dtrace の実行時に、すでにシェルが read(2) システムコール内で入力を待っている場合、最初の read(2)read:entry を飛ばして、read:return プローブが起動します。このとき、まだ割り当てられていない ts[probefunc] の値はゼロになります。