静的定義トレース (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 |