静的定義トレース (Statically Defined Tracing、SDT) プロバイダは、ソフトウェアプログラマが正式に指定した位置でプローブを作成します。プログラマは、SDT 機構を利用して、DTrace ユーザーにとって重要な箇所をプローブの設定位置として意識的に選択できます。さらに、このプローブに、その位置についてよくわかるようなプローブ名を付けることができます。Solaris カーネルにも少数の SDT プローブが定義されており、将来はさらに追加される予定です。DTrace はまた、ユーザーアプリケーション開発者を対象に、静的プローブを定義する機構を提供しています。これについては、第 34 章ユーザーアプリケーション向けの静的に定義されたトレースで説明します。
表 22–1 は、Solaris カーネルに定義されている SDT プローブです。これらのプローブの名前の安定性とデータの安定性は、どちらも「非公開」です。これは、ここでの記述がカーネルの実装を反映しているため、確約されたインタフェースとして推測されるべきでないからです。DTrace の安定性機構の詳細については、「安定性」を参照してください。
表 22–1 SDT プローブ|
プローブ名 |
説明 |
arg0 |
|---|---|---|
|
callout-start |
コールアウトの実行直前に起動するプローブ (<sys/callo.h> を参照)。コールアウトは、timeout(9F) の実装を表すものであり、システムクロックによって定期的に実行されます。 |
実行されるコールアウトを表す callout_t のポインタ (<sys/callo.h> を参照)。 |
|
callout-end |
コールアウトの実行直後に起動するプローブ (<sys/callo.h> を参照)。 |
直前に実行されたコールアウトを表す callout_t のポインタ (<sys/callo.h> を参照)。 |
|
interrupt-start |
デバイスの割り込みハンドラを呼び出す直前に起動するプローブ。 |
割り込みデバイスを表す dev_info 構造体のポインタ (<sys/ddi_impldefs.h> を参照)。 |
|
interrupt-complete |
デバイスの割り込みハンドラから復帰した直後に起動するプローブ。 |
割り込みデバイスを表す dev_info 構造体のポインタ (<sys/ddi_impldefs.h> を参照)。 |
以下は、1 秒に 1 回のペースでコールアウトの動作を調べるスクリプトです。
#pragma D option quiet
sdt:::callout-start
{
@callouts[((callout_t *)arg0)->c_func] = count();
}
tick-1sec
{
printa("%40a %10@d\n", @callouts);
clear(@callouts);
}
この例を実行すると、そのシステム内で timeout(9F) をよく使用するユーザーがわかります。次の出力例を参照してください。
# dtrace -s ./callout.d
FUNC COUNT
TS`ts_update 1
uhci`uhci_cmd_timeout_hdlr 3
genunix`setrun 5
genunix`schedpaging 5
ata`ghd_timeout 10
uhci`uhci_handle_root_hub_status_change 309
FUNC COUNT
ip`tcp_time_wait_collector 1
TS`ts_update 1
uhci`uhci_cmd_timeout_hdlr 3
genunix`schedpaging 4
genunix`setrun 8
ata`ghd_timeout 10
uhci`uhci_handle_root_hub_status_change 300
FUNC COUNT
ip`tcp_time_wait_collector 0
iprb`mii_portmon 1
TS`ts_update 1
uhci`uhci_cmd_timeout_hdlr 3
genunix`schedpaging 4
genunix`setrun 7
ata`ghd_timeout 10
uhci`uhci_handle_root_hub_status_change 300
|
timeout(9F) インタフェースは、単一のタイマーの有効期限を出力するだけです。timeout() のインターバルタイマー機能を利用する場合、通常 timeout() ハンドラから timeout を再インストールします。以下に例を示します。
#pragma D option quiet
sdt:::callout-start
{
self->callout = ((callout_t *)arg0)->c_func;
}
fbt::timeout:entry
/self->callout && arg2 <= 100/
{
/*
* In this case, we are most interested in interval timeout(9F)s that
* are short. We therefore do a linear quantization from 0 ticks to
* 100 ticks. The system clock's frequency — set by the variable
* "hz" — defaults to 100, so 100 system clock ticks is one second.
*/
@callout[self->callout] = lquantize(arg2, 0, 100);
}
sdt:::callout-end
{
self->callout = NULL;
}
END
{
printa("%a\n%@d\n\n", @callout);
}
このスクリプトを実行し、しばらく待ってから Control-C キーを押すと、次のような出力が得られます。
# dtrace -s ./interval.d
^C
genunix`schedpaging
value ------------- Distribution ------------- count
24 | 0
25 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 20
26 | 0
ata`ghd_timeout
value ------------- Distribution ------------- count
9 | 0
10 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 51
11 | 0
uhci`uhci_handle_root_hub_status_change
value ------------- Distribution ------------- count
0 | 0
1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1515
2 | 0
|
この出力からわかるように、uhci(7D) ドライバ内の uhci_handle_root_hub_status_change() は、このシステム上のもっとも短い間隔で呼び出されるインターバルタイマー (システムクロック刻みに合わせて呼び出される) です。
割り込みアクティビティに関する情報は、interrupt-start プローブから得ることができます。次の例では、ドライバ名を指定して、割り込みハンドラの実行にかかった時間を調べています。
interrupt-start
{
self->ts = vtimestamp;
}
interrupt-complete
/self->ts/
{
this->devi = (struct dev_info *)arg0;
@[stringof(`devnamesp[this->devi->devi_major].dn_name),
this->devi->devi_instance] = quantize(vtimestamp - self->ts);
}
このスクリプトを実行すると、次のような出力が得られます。
# dtrace -s ./intr.d
dtrace: script './intr.d' matched 2 probes
^C
isp 0
value ------------- Distribution ------------- count
8192 | 0
16384 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1
32768 | 0
pcf8584 0
value ------------- Distribution ------------- count
64 | 0
128 | 2
256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 157
512 |@@@@@@ 31
1024 | 3
2048 | 0
pcf8584 1
value ------------- Distribution ------------- count
2048 | 0
4096 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 154
8192 |@@@@@@@ 37
16384 | 2
32768 | 0
qlc 0
value ------------- Distribution ------------- count
16384 | 0
32768 |@@ 9
65536 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 126
131072 |@ 5
262144 | 2
524288 | 0
hme 0
value ------------- Distribution ------------- count
1024 | 0
2048 | 6
4096 | 2
8192 |@@@@ 89
16384 |@@@@@@@@@@@@@ 262
32768 |@ 37
65536 |@@@@@@@ 139
131072 |@@@@@@@@ 161
262144 |@@@ 73
524288 | 4
1048576 | 0
2097152 | 1
4194304 | 0
ohci 0
value ------------- Distribution ------------- count
8192 | 0
16384 | 3
32768 | 1
65536 |@@@ 143
131072 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1368
262144 | 0
|
デバイスドライバの開発者は、Solaris ドライバ内に独自の SDT プローブを作成したいと思うことがあるかもしれません。SDT プローブが無効にされている状態は、無操作マシン命令がいくつかある状態と実質的に同じです。したがって、必要に応じて SDT プローブをデバイスドライバに追加してかまいません。これらのプローブがパフォーマンスに悪影響を及ぼさないかぎり、出荷コード内に残しておいてもかまいません。
SDT プローブを宣言するときは、<sys/sdt.h> のマクロ DTRACE_PROBE 、DTRACE_PROBE1、DTRACE_PROBE2、 DTRACE_PROBE3、DTRACE_PROBE4 を使用します。SDT ベースのプローブのモジュール名は、カーネルモジュール名を反映しています。また、関数名は、そのプローブの関数を表しています。プローブ名は、DTRACE_PROBEn マクロで指定された名前によって決まります。この名前に 2 つの連続する下線 (__) が含まれていない場合、マクロに指定されたとおりのプローブ名になります。この名前に 2 つの連続する下線が含まれている場合、プローブ名では、この下線部分がダッシュ 1 個 (-) に変換されます。たとえば、DTRACE_PROBE マクロに transaction__start と指定されている場合、SDT プローブ名は transaction-start になります。このような置き換えが行われるので、C コード内のマクロ名が有効な C 識別子でなくても、文字列を指定しないで使用できます。
カーネルモジュール名と関数名は、DTrace によって、プローブを識別する組に含められます。したがって、名前空間の衝突を避けるためにプローブ名にこの情報を指定する必要性は特にありません。インストール済みのプローブと、DTrace ユーザーが確認できるフルネームを一覧するには、ドライバ上で dtrace -l -P sdt -m module (module はドライバ) を実行します。
各 SDT プローブの引数は、対応する DTRACE_PROBEn マクロ参照に指定された引数になります。引数の数は、どのマクロを使ってプローブを作成したかによって異なります。 たとえば、DTRACE_PROBE1 は引数を 1 つ、DTRACE_PROBE2 は 2 つ (以下同様) 指定します。SDT プローブを宣言するときは、ポインタを間接参照せず、プローブ引数内の大域変数からロードしないようにすれば、無効時のプローブの影響を最小限に抑えることができます。ポインタの間接参照も、大域変数のロードも、D のプローブ有効化アクション内で安全に実行できます。こうしたアクションが必要なときには要求してかまいません。
以下の表に、SDT プロバイダの安定性を DTrace の安定性機構に従って示します。安定性機構の詳細については、第 39 章安定性を参照してください。
|
要素 |
名前の安定性 |
データの安定性 |
依存クラス |
|---|---|---|---|
|
プロバイダ |
発展中 |
発展中 |
ISA |
|
モジュール |
非公開 |
非公開 |
不明 |
|
機能 |
非公開 |
非公開 |
不明 |
|
名前 |
非公開 |
非公開 |
ISA |
|
引数 |
非公開 |
非公開 |
ISA |