11 DTraceプロバイダ
警告:
Oracle Linux 7は現在延長サポート中です。詳細は、Oracle Linux拡張サポートおよびOracleオープン・ソース・サポート・ポリシーを参照してください。
できるだけ早くアプリケーションとデータをOracle Linux 8またはOracle Linux 9に移行してください。
DTraceの詳細は、Oracle Linux: DTraceリリース・ノートおよびOracle Linux: システム・トレーシングのためのDTraceの使用を参照してください。
この章では、既存のDTraceプロバイダの一部について説明します。この章で説明するプロバイダのリストではすべてが網羅されているわけではありません。システムで使用可能なプロバイダを表示するには、dtrace -lコマンドを使用します。重要なデータ構造体用のトランスレータの詳細は、/usr/lib64/dtrace/version/*.dのファイルを参照してください。
dtraceプロバイダ
dtraceプロバイダには、DTrace自体に関連するプローブが含まれています。これらのプローブを使用して、トレースの開始前に状態を初期化し、トレースの完了後にその状態を処理して、他のプローブでの予期しない実行エラーを処理できます。
BEGINプローブ
BEGINプローブは、他のどのプローブより先に起動します。すべてのBEGIN節が完了するまで、他のプローブは起動しません。このプローブを使用すると、他のプローブで必要なすべての状態を初期化できます。次の例は、BEGINプローブを使用して、mmapの保護ビットとテキスト表現の間をマップする連想配列の初期化を示しています。
BEGIN
{
prot[0] = "---";
prot[1] = "r--";
prot[2] = "-w-";
prot[3] = "rw-";
prot[4] = "--x";
prot[5] = "r-x";
prot[6] = "-wx";
prot[7] = "rwx";
}
syscall::mmap:entry
{
printf("mmap with prot = %s", prot[arg2 & 0x7]);
}
BEGINプローブは、未指定のコンテキストで起動します。つまり、stackやustackの出力と、コンテキスト固有の変数(execnameなど)の値はすべて任意です。これらの値に依存したり、これらの値を有意な情報として解釈しないようにしてください。BEGINプローブに、引数は定義されていません。
ENDプローブ
ENDプローブは、他のどのプローブより後に起動します。このプローブは、他のプローブ節がすべて完了するまで起動しません。このプローブを使用すると、収集された状態の処理や、出力のフォーマットが可能です。そのため、ENDプローブではprintaアクションが多用されます。次のように、BEGINプローブとENDプローブを併用すると、トレースにかかった合計時間を測定できます。
BEGIN
{
start = timestamp;
}
/*
* ... other tracing actions...
*/
END
{
printf("total time: %d secs", (timestamp - start) / 1000000000);
}
ENDプローブのその他の使用方法は、「データの正規化」および「printaアクション」を参照してください。
BEGINプローブと同じく、ENDプローブにも引数は定義されていません。ENDプローブが起動するコンテキストは任意であるため、これに依存するプログラムは作成しないでください。
bufpolicyオプションをfillに設定してトレースを行うと、ENDプローブでトレースされるレコードに対応できるように適切な空間が予約されます。詳細は、「fillポリシーおよびENDプローブ」を参照してください。
ノート:
exitアクションは、トレースを停止してENDプローブを起動させます。ただし、exitアクションが呼び出されてからENDプローブが起動するまでには多少の遅延が発生します。この遅延の間は、どのプローブも起動しません。プローブがexitアクションを呼び出した後は、exitがコールされたことをDTraceコンシューマが判断してトレースを停止するまで、ENDプローブは起動しません。exitのステータスをチェックする間隔は、statusrateオプションで設定できます。詳細は、「オプションおよびチューニング可能パラメータ」を参照してください。
ERRORプローブ
ERRORプローブは、DTraceプローブの節を実行中にランタイム・エラーが発生した場合に起動します。次の例のように、NULLポインタを間接参照する節がある場合、ERRORプローブが起動します。error.dという名前のファイルに保存します。
BEGIN
{
*(char *)NULL;
}
ERROR
{
printf("Hit an error!");
}このプログラムを実行すると、次のような出力が表示されます。
# dtrace -s error.d dtrace: script 'error.d' matched 2 probes CPU ID FUNCTION:NAME 1 3 :ERROR Hit an error! dtrace: error on enabled probe ID 1 (ID 1: dtrace:::BEGIN): invalid address (0x0) in action #1 at DIF offset 16 ^C
前述の出力では、ERRORプローブが起動したことと、dtraceがエラーをレポートしたことが示されます。dtraceは独自の方法でERRORプローブを有効にし、エラーのレポートを可能にします。ERRORプローブを使用すると、独自のエラー処理を作成できます。
次の表では、ERRORプローブの引数について説明します。
| 引数 | 説明 |
|---|---|
|
|
エラーの原因となったプローブで有効化されたプローブID (EPID) |
|
|
フォルトの原因となったアクションのインデックス |
|
|
そのアクションへのDIFオフセット(該当しない場合は-1) |
|
|
フォルト・タイプ。 |
|
|
フォルト・タイプに固有の値。 |
次の表に、arg4で指定できる様々なフォルト・タイプと、各フォルト・タイプに対してarg5がとる値を示します。
| arg4の値 | 説明 | arg5の意味 |
|---|---|---|
|
|
不明なフォルト・タイプ |
なし |
|
|
マップされていないアドレスまたは無効なアドレスへのアクセス |
アクセスされるアドレス |
|
|
境界違反のメモリー・アクセス |
アクセスされるアドレス |
|
|
不正または無効な操作 |
なし |
|
|
整数のゼロ除算 |
なし |
|
|
スクラッチ割当てに対応できるスクラッチ・メモリーの不足 |
なし |
|
|
カーネル・アドレスまたはプロパティのアクセスが試行されたが、十分な権限がない |
アクセスされるアドレス(該当しない場合は0) |
|
|
ユーザー・アドレスまたはプロパティのアクセスが試行されたが、十分な権限がない |
アクセスされるアドレス(該当しない場合は0) |
|
|
DTrace内部パラメータのスタック・オーバーフロー |
なし |
|
|
ユーザー・プロセス・スタックが無効 |
無効なスタック・ポインタのアドレス |
ERRORプローブで実行されたアクションがエラーの原因になっている場合、そのエラーはメッセージなしに削除されます。ERRORプローブは、再帰的には呼び出されません。
dtraceの安定性
dtraceプロバイダは、DTraceの安定性メカニズムを使用してその安定性を記述します。これらの値を、次の表に示します。
| 要素 | 名前の安定性 | データの安定性 | 依存クラス |
|---|---|---|---|
|
プロバイダ |
安定 |
安定 |
共通 |
|
モジュール |
非公開 |
非公開 |
不明 |
|
関数 |
非公開 |
非公開 |
不明 |
|
名前 |
安定 |
安定 |
共通 |
|
引数 |
安定 |
安定 |
共通 |
安定性メカニズムの詳細は、「DTraceの安定性機能」を参照してください。
profileプロバイダ
profileプロバイダでは、指定の時間間隔で定期的に起動する割込みに関連するプローブを使用できます。このようなプローブは、特定の実行ポイントではなく非同期割込みイベントに関連付けられます。これらのプローブを使用して、特定の観点からシステム状態をサンプリングし、そのサンプルを使用してシステム動作を推測できます。サンプリング・レートが高い、またはサンプリング時間が長いと、正確な推測が可能になります。DTraceアクションを使用すると、profileプロバイダでシステムのあらゆる側面をサンプリングできます。たとえば、現在のスレッドの状態、CPUの状態、現在のマシン命令などの標本をサンプリングできます。
profile-nプローブ
profile-nプローブは、すべてのアクティブなCPUで、割込みレベルが高く、固定間隔で起動します。nのデフォルトの単位は秒当たりの起動レートとして表される周波数ですが、表11-1に示すように、時間間隔または周波数のいずれかを指定するオプションの接尾辞を値に付けることもできます。次の表では、tick- nプローブで有効な時間の接尾辞について説明します。
表11-1 有効な時間接尾辞
| 接尾辞 | 時間の単位 |
|---|---|
|
|
ナノ秒 |
|
|
マイクロ秒 |
|
|
ミリ秒 |
|
|
秒 |
|
|
分 |
|
|
時間 |
|
|
日 |
|
|
ヘルツ(秒当たりのレートとして表される周波数) |
tick-nプローブ
tick-nプローブは一定の間隔で、高い割込みレベルで間隔ごとに1つのCPUのみで起動します。すべてのCPUで起動するprofile-nプローブとは異なり、tick-nプローブは間隔ごとに1つのCPUのみで起動し、起動するCPUは時間の経過とともに変化する可能性があります。nのデフォルトの単位は秒当たりの起動レートとして表される周波数ですが、表11-1に示すように、時間間隔または周波数のいずれかを指定するオプションの時間接尾辞を値に付けることもできます。
tick-nプローブには、定期的な出力や定期的なアクションの実行など、いくつかの用途があります。
ノート:
デフォルトでは、サポートされている最も高い周波数は5000 Hzです(tick-5000)。
profileプローブの引数
次の表では、profileプローブの引数について説明します。
表11-2 profileプローブの引数
| プローブ | arg0 | arg1 | arg2 |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
— |
引数は次のとおりです。
-
pc: カーネル・プログラム・カウンタ -
upc: ユーザー空間プログラム・カウンタ -
nsecs: ナノ秒の経過時間
profileプローブの作成
他のプロバイダとは異なり、profileプロバイダは、必要に応じて動的にプローブを作成します。そのため、(たとえばdtrace -l -P profileコマンドの使用時に)すべてのプローブをリストしたとき、必要なプローブが表示されない場合がありますが、このプローブは明示的に有効にすると作成されます。
時間間隔が短すぎると、マシンは時間ベースの割込みを継続的に処理することになり、マシン上のサービスが拒否されます。profileプロバイダは、間隔が200ミリ秒未満になるプローブの作成をメッセージなしに拒否します。
profの安定性
profileプロバイダは、DTraceの安定性メカニズムを使用してその安定性を記述します。これらの安定性の値を、次の表に示します。
| 要素 | 名前の安定性 | データの安定性 | 依存クラス |
|---|---|---|---|
|
プロバイダ |
発展中 |
発展中 |
共通 |
|
モジュール |
変更の可能性あり |
変更の可能性あり |
不明 |
|
関数 |
非公開 |
非公開 |
不明 |
|
名前 |
発展中 |
発展中 |
共通 |
|
引数 |
発展中 |
発展中 |
共通 |
詳細は、「DTraceの安定性機能」を参照してください。
fbtプロバイダ
fbt (関数境界トレース)プロバイダには、Oracle Linuxカーネル内のほとんどの関数の開始および終了に関連するプローブが含まれています。そのため、数万のfbtプローブが存在する可能性が高いです。
fbtプロバイダがプロセッサのアーキテクチャで使用可能であることを確認するには、fbtインストゥルメンテーションを提供するモジュールをロードし、いくつかのプローブを正常にリストできる必要があります。このようなプローブの数が多いため、このプロセスには数秒かかることがあります。たとえば、rootとして実行される次のコマンドについて考えてみます。
# dtrace -l -P fbt | wc -l dtrace: failed to match fbt:::: No probe matches description 1 # modprobe fbt # dtrace -l -P fbt | wc -l 88958
前述の例では、最初のdtraceコマンドは、/etc/dtrace-modulesにリストされているモジュールを自動的にロードしますが、fbtがその中にはなかったことも確認します。fbtを手動でロードすると、多くのfbtプローブが表示されます。詳細は、「モジュールのロードとfbt」を参照してください。
他のDTraceプロバイダと同様に、関数境界トレース(FBT)は、明示的に有効にしていないと、プローブ効果はありません。有効にすると、FBTはプローブされた関数でのみプローブ効果をもたらします。FBT実装は命令セット・アーキテクチャにきわめて特異的なものですが、FBTはx86と64ビットの両方のARMプラットフォームに実装されています。各命令セットには、他の関数をコールせず、コンパイラによって高度に最適化される少数のleaf関数があり、これはFBTではインストゥルメントできません。これらの関数のプローブは、DTraceに存在しません。
FBTプローブを効果的に使用するには、オペレーティング・システム実装に関する知識が必要です。したがって、FBTは、カーネル・ソフトウェアの開発時、またはその他のプロバイダが不十分な場合にのみ使用することをお薦めします。syscall、sched、proc、ioなどの他のDTraceプロバイダを使用すると、オペレーティング・システムの実装知識を必要とせずに、ほとんどのシステム分析の問題の回答を導き出すことができます。
fbtプローブ
FBTは、それぞれentryおよびreturnという名前の、カーネル内のほとんどの関数の開始時と終了時のプローブを提供します。すべてのFBTプローブには、関数名とモジュール名があります。
fbtプローブの引数
entryプローブの引数は、対応するオペレーティング・システムのカーネル関数の引数と同じです。これらの引数には、arg0、arg1、arg2...の変数を使用して、int64_t値としてアクセスできます。
関数に戻り値がある場合、戻り値はreturnプローブのarg1に格納されます。関数に戻り値がない場合、arg1は定義されません。
特定の関数の開始ポイントは1つのみですが、コール元に戻るポイントは多数ある場合があります。FBTは、関数の複数のリターン・サイトを単一のreturnプローブに収集します。正確なリターン・パスを知る必要がある場合は、関数テキスト内の戻り命令のバイト単位のオフセットを示す、returnプローブのarg0値を調べることができます。
fbtの例
カーネルの実装を調べるために、簡単にfbtプロバイダを使用できます。次のスクリプト例では、clockプロセスからの最初のgettimeofdayコールを記録し、カーネルを介した後続のコード・パスに従います。次のDソース・コードを入力し、xgettimeofday.dという名前のファイルに保存します。
/*
* To make the output more readable, indent every function entry
* and unindent every function return. This is done by setting the
* "flowindent" option.
*/
#pragma D option flowindent
syscall::gettimeofday:entry
/execname == "clock" && guard++ == 0/
{
self->traceme = 1;
printf("start");
}
fbt:::
/self->traceme/
{}
syscall::gettimeofday:return
/self->traceme/
{
self->traceme = 0;
exit(0);
}このスクリプトを実行すると、次のような出力が得られます。
# dtrace -s ./xgettimeofday.d dtrace: script './xgettimeofday.d' matched 92115 probes CPU FUNCTION 0 => gettimeofday start 0 -> SyS_gettimeofday 0 -> getnstimeofday64 0 -> __getnstimeofday64 0 <- __getnstimeofday64 0 <- getnstimeofday64 0 -> _copy_to_user 0 <- _copy_to_user 0 <- SyS_gettimeofday 0 <= gettimeofday
前述の出力は、gettimeofdayシステム・コールの実行時にコールされる内部カーネル関数を示しています。
モジュールのロードとfbt
Oracle Linuxカーネルはカーネル・モジュールを動的にロードおよびアンロードできますが、fbtプローブの場合は、インストゥルメンテーションをサポートするためにfbtカーネル・モジュールをロードする必要があります。カーネル・モジュールのロードの詳細は、「DTraceスタート・ガイド」のノートを参照してください。fbtが/etc/dtrace-modulesにリストされていない場合、またはdtrace -lコマンドでfbtプローブがリストされない場合は、次のコマンドを使用します。
# modprobe fbt
反対に、次のコマンドを使用してfbtインストゥルメンテーションをアンロードできます。
# modprobe -r fbt
fbtモジュールがロードされると、FBTは、動的にロードされる新しいモジュールを含め、ロードされた他のすべてのモジュールをインストゥルメントするプローブを自動的に提供します。ロードされるモジュールに有効なFBTプローブがない場合、モジュールがアンロードされる場合があり、モジュールがアンロードされると、対応するプローブが破棄されます。ロードされるモジュールに有効なFBTプローブがある場合、モジュールはビジーとみなされ、アンロードできません。
fbtの安定性
fbtプロバイダは、DTraceの安定性メカニズムを使用してその安定性を記述します。これらの安定性の値を、次の表に示します。
| 要素 | 名前の安定性 | データの安定性 | 依存クラス |
|---|---|---|---|
|
プロバイダ |
発展中 |
発展中 |
共通 |
|
モジュール |
非公開 |
非公開 |
不明 |
|
関数 |
非公開 |
非公開 |
ISA |
|
名前 |
発展中 |
発展中 |
共通 |
|
引数 |
非公開 |
非公開 |
ISA |
詳細は、「DTraceの安定性機能」を参照してください。
syscallプロバイダ
syscallプロバイダは、システムのすべてのシステム・コールに対して開始時と終了時のプローブを使用可能にします。システム・コールは、ユーザー・レベルのアプリケーションとオペレーティング・システム・カーネルの間の主要なインタフェースのため、syscallプロバイダからは、システムに関連するアプリケーション動作について多くの洞察を得ることができます。
syscallプローブ
syscallプロバイダは、システム・コールごとに1組のプローブを提供します。1つはシステム・コールに入る前に起動するentryプローブ、もう1つはシステム・コールが完了してから制御がユーザー・レベルに戻るまでの間に起動するreturnプローブです。どのsyscallプローブでも、関数名はインストゥルメントされるシステム・コールの名前として設定されます。
多くの場合、syscallが提供するシステム・コールの名前は、マニュアル・ページの項2の名前と対応しています。ただし、syscallプロバイダのプローブの中には、文書化されたシステム・コールに直接対応していないものもあります。この矛盾のいくつかの主な理由について、次の項で説明します。
サブコード化されたシステム・コール
システム・コールの中には、他のシステム・コールの下位の操作として実装されるものもあります。たとえば、socketcall(),はソケット・システム・コールの共通カーネル・エントリ・ポイントです。
新しいシステム・コール
Oracle Linuxでは、たとえば次のようにat接頭辞の付いたシステム・インタフェースが個別のシステム・コールとして実装されています。
-
faccessat() -
fchmodat() -
fchownat() -
fstatat64() -
futimensat() -
linkat() -
mkdirat() -
mknodat() -
name_to_handle_at() -
newfstatat() -
open_by_handle_at() -
openat() -
readlinkat() -
renameat() -
symlinkat() -
unlinkat() -
utimensat()
これらのシステム・コールは、at接尾辞の付いていない古い形の機能のスーパーセットです。これらは、追加の第1引数としてオープン・ディレクトリのファイル記述子をとります。この場合、相対パス名に対する操作が特定のディレクトリと相対的に実行されます。または、予約値であるAT_FDCWDをとり、操作が現在の作業ディレクトリと相対的に実行されます。
置き換えられたシステム・コール
Oracle Linuxでは、次の古いシステム・コールが置き換えられ、新しいglibcインタフェースではコールされなくなります。旧バージョンのインタフェースは残りますが、本来のシステム・コール自体としてではなく、新しいシステム・コールに対するコールとして再実装されています。次の表に、旧バージョンのコールと、それに相当する新しいコールをリストします。
| 旧バージョンのシステム・コール | 新しいシステム・コール |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
大規模ファイルのシステム・コール
2GBを超える大規模ファイルをサポートする32ビット・プログラムは、64ビット・ファイルのオフセットを処理できる必要があります。大規模ファイルは大規模なオフセットを必要とするため、大規模ファイルの操作には複数のシステム・インタフェースを並行して利用します。次の表に、大規模ファイルのシステム・コール・インタフェースに対応するsyscallプローブの一部をリストします。
表11-3 syscall大規模ファイル・プローブ
| 大規模ファイルのsyscallプローブ | システム・コール |
|---|---|
|
|
|
|
|
|
|
|
|
syscallプローブの引数
entryプローブの場合、引数arg0 ... argnはシステム・コールの引数です。returnプローブの場合、arg0とarg1の両方に戻り値が格納されます。D変数errnoの値がゼロ以外の場合は、システム・コールが失敗であることを示します。
syscallの安定性
syscallプロバイダは、DTraceの安定性メカニズムを使用してその安定性を記述します。これらの安定性の値を、次の表に示します。
| 要素 | 名前の安定性 | データの安定性 | 依存クラス |
|---|---|---|---|
|
プロバイダ |
発展中 |
発展中 |
共通 |
|
モジュール |
非公開 |
非公開 |
不明 |
|
関数 |
非公開 |
非公開 |
命令セット・アーキテクチャ(ISA) |
|
名前 |
発展中 |
発展中 |
共通 |
|
引数 |
非公開 |
非公開 |
ISA |
安定性メカニズムの詳細は、「DTraceの安定性機能」を参照してください。
sdtプロバイダ
静的定義トレース(SDT)プロバイダ(sdt)は、ソフトウェア・プログラマが正式に指定した位置にプローブを作成します。プログラマは、SDTメカニズムを使用して、DTraceユーザーにとって重要な位置を意識的に選択し、それぞれの位置のセマンティクス情報をプローブ名によって伝えることができます。
重要なこととして、SDTはメタプロバイダとしても機能し、プローブを登録することにより、独自の専用モジュールを持たないio、procおよびschedなどの他のプロバイダからのプローブのように表示することもできます。したがって、SDTプロバイダは主に、新しいプロバイダの開発者のにとってのみ関心の対象になります。ほとんどのユーザーは、他のプロバイダを使用して間接的にのみSDTにアクセスします。
ノート:
Oracle Linuxカーネルのsdtプローブの定義は、今後変更される可能性があるため、ここにはリストされていません。プローブの名前の安定性とデータの安定性はどちらも非公開ですが、これはカーネルの実装を反映しており、これらのインタフェースが維持されることを確約するという意味ではありません。詳細は、「DTraceの安定性機能」を参照してください。
sdtプローブの作成
デバイス・ドライバの開発者であれば、使用しているOracle Linuxドライバ用に独自のsdtプローブを作成することに関心がある場合があります。SDTの無効化されたプローブの影響は、基本的にいくつかのno-opマシン命令のコストになります。したがって、必要に応じてsdtプローブをデバイス・ドライバに追加するようにしてください。これらのプローブは、パフォーマンスにマイナスの影響を及ぼすことがないかぎり、出荷コードに残しておくことができます。「カーネル・モジュールの静的定義トレース」を参照してください。
DTraceには、アプリケーション開発者がユーザー空間の静的なプローブを定義するためのメカニズムも用意されています。「ユーザー・アプリケーションの静的定義トレース」を参照してください。
プローブの宣言
sdtプローブは、<linux/sdt.h>のDTRACE_PROBEマクロを使用して宣言されます。
SDTベースのプローブのモジュール名および関数名はそれぞれ、プローブのカーネル・モジュールおよび関数に対応しています。DTraceでは、カーネル・モジュール名および関数名がプローブを識別するタプルの一部として含まれているため、名前空間の衝突を防ぐためにこの情報をプローブ名に含める必要はありません。ドライバ・モジュールに、インストール済のプローブとDTraceユーザーに表示されるフル・ネームをリストするには、dtrace -l -m moduleコマンドを使用します。
プローブの名前は、DTRACE_PROBEマクロに指定された名前に依存します。その名前に連続する2つのアンダースコア(__)が含まれていない場合、プローブの名前はマクロに記述されたとおりです。名前に連続する2つのアンダースコアが含まれている場合、プローブ名では連続するアンダースコアが単一のダッシュ(-)に変換されます。たとえば、DTRACE_PROBEマクロでtransaction__startが指定されている場合、SDTプローブはtransaction-startという名前になります。この置換により、有効なC識別子ではないマクロ名を文字列を指定せずにCコードで指定できます。
また、SDTはメタプロバイダとしても機能し、プローブを登録することにより、独自の専用モジュールを持たないio、procおよびschedなどの他のプロバイダからのプローブのように表示することもできます。たとえば、kernel/exit.cには、<linux/sdt.h>に次のように定義されている、DTRACE_PROCマクロのコールが含まれています。
# define DTRACE_PROC(name) \
DTRACE_PROBE(__proc_##name);
このようなマクロを使用するプローブは、sdt以外のプロバイダからのプローブのように表示されます。name引数に含まれる先頭の2つのアンダースコア、プロバイダ名および末尾のアンダースコアは、プロバイダを一致させるために使用され、プローブ名には含まれません。DTraceにハードコーディングされているプロバイダ以外のプロバイダのプローブを作成する機能は、現在使用できません。
sdtの安定性
sdtプロバイダは、DTraceの安定性メカニズムを使用してその安定性を記述します。これらの値を、次の表に示します。
| 要素 | 名前の安定性 | データの安定性 | 依存クラス |
|---|---|---|---|
|
プロバイダ |
発展中 |
発展中 |
ISA |
|
モジュール |
非公開 |
非公開 |
不明 |
|
関数 |
非公開 |
非公開 |
不明 |
|
名前 |
非公開 |
非公開 |
ISA |
|
引数 |
非公開 |
非公開 |
ISA |
安定性メカニズムの詳細は、「DTraceの安定性機能」を参照してください。
pidプロバイダ
pidプロバイダは、そのpidで指定されたように、任意のユーザー・プロセスのトレースを有効にします。
pidプロバイダは、ユーザー・プログラムでトレース関数の開始と終了を有効にします(fbtプロバイダがカーネルに対してその機能を提供するのと同様です)。このガイドでは、fbtプロバイダを使用してカーネル関数のコールをトレースするほとんどの例は、ユーザー・プロセスに適用するために若干変更できます。
pidプロバイダは、絶対アドレスまたは関数オフセットで指定されたように、任意の命令のトレースも有効にします。
プローブが有効になっていない場合、pidプロバイダにはプローブ効果がありません。プローブが有効な場合、プローブはトレースされるプロセスでのみプローブ効果をもたらします。
ノート:
コンパイラが関数をインライン化する場合、pidプロバイダのプローブは起動しません。特定のC関数をインライン化しないようにコンパイルするには、次のいずれかの方法を使用します。
-
Sun Studio:
#pragma no_inline (funcname[, funcname]) -
gcc:funcname __attribute__ ((noinline))
更新については、コンパイラのドキュメントを参照してください。
pidプローブの命名
pidプロバイダは、実際にはプロバイダのクラスを定義します。各プロセスは、独自の関連付けられたpidプロバイダを持つ可能性があります。たとえば、ID 123のプロセスは、pid123プロバイダを使用してトレースされます。
プローブ記述のモジュール部分は、対応するプロセスのアドレス空間にロードされるオブジェクトを参照します。my_exec用にロードされるオブジェクト、またはプロセスID 123用にロードされるオブジェクトを確認するには、次のコマンドを使用します。
# ldd my_exec ... # pldd 123 123: /tmp/my_exec linux-vdso.so.1 /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2p
プローブ記述では、フルパス名ではなく、ファイルの名前でオブジェクトに名前を付けます。接尾辞.6またはso.6を省略することもできます。次の例では、すべて同じプローブに名前を付けます。
pid123:libc.so.6:strcpy:entry pid123:libc.so:strcpy:entry pid123:libc:strcpy:entry
最初の例は、プローブの実際の名前です。その他の例は、内部でフル・ロード・オブジェクト名に置き換えられる便利な別名です。
実行可能ファイルのロード・オブジェクトについては、a.outの別名を使用できます。次の2つのプローブ記述は、同じプローブに名前を付けます。
pid123:my_exec:main:return pid123:a.out:main:return
プローブ記述の関数フィールドは、モジュール内の関数に名前を付けます。ユーザー・アプリケーション・バイナリには、同じ関数に対する複数の名前がある場合があります。たとえば、__gnu_get_libc_versionは、libc.so.6の関数gnu_get_libc_versionの代替名になります。DTraceは、このような関数に対して1つの正規名を選択し、その名前を内部的に使用します。
次の例は、DTraceがモジュール名と関数名を正規の形式に内部的に再マップする仕組みを示しています。
# dtrace -q -n 'pid123:libc:__gnu_get_libc_version:
{ printf("%s\n%s\n", probemod, probefunc)}'
libc.so.6
gnu_get_libc_version
pidプロバイダを効果的に使用する方法の例は、「ユーザー・プロセスのトレース」を参照してください。
pidプローブの引数
entryプローブは、トレースされた関数が呼び出されたときに起動します。entryプローブの引数は、トレースされた関数の引数の値です。
returnプローブは、トレースされた関数が戻るか、別の関数への末尾コールを行うと起動します。arg1プローブの引数は、関数の戻り値を保持します。
オフセット・プローブは、関数内の指定されたオフセットの命令に実行が到達したときに起動します。たとえば、アドレス4バイトの命令を関数mainにトレースするには、pid123:a.out:main:4を使用できます。オフセット・プローブの引数は定義されていません。uregs[]配列は、これらのプローブ・サイトでプロセスの状態を調べるときに役立ちます。「uregs[]配列」を参照してください。
pidの安定性
pidプロバイダは、DTraceの安定性メカニズムを使用してその安定性を記述します。これらの値を、次の表に示します。
| 要素 | 名前の安定性 | データの安定性 | 依存クラス |
|---|---|---|---|
|
プロバイダ |
発展中 |
発展中 |
ISA |
|
モジュール |
非公開 |
非公開 |
不明 |
|
関数 |
非公開 |
非公開 |
不明 |
|
名前 |
発展中 |
発展中 |
ISA |
|
引数 |
非公開 |
非公開 |
不明 |
安定性メカニズムの詳細は、「DTraceの安定性機能」を参照してください。
procプロバイダ
procプロバイダは、プロセスの作成と終了、LWPの作成と終了、新しいプログラム・イメージの実行、およびシグナルの送信と処理に関連するプローブを使用可能にします。
procプローブ
次の表に、procプロバイダのプローブを示します。
表11-4 procプローブ
| プローブ | 説明 |
|---|---|
|
|
|
|
|
プロセスが |
|
|
|
|
|
|
|
|
現在のプロセスが終了しようとするときに起動します。終了の理由が、いずれかの |
|
|
プロセス・スレッドの作成時に起動します。後者は通常、 |
|
|
シグナル、または |
|
|
新しく作成されたプロセスまたはプロセス・スレッドのコンテキストで起動します。 |
|
|
ターゲット・スレッドが |
|
|
シングルスレッド・プロセスにシグナルが送信され、シグナルがプロセスによってブロック解除されて無視されたときに起動します。この条件では、シグナルは作成後すぐに破棄されます。ターゲット・プロセスおよびスレッドの |
|
|
スレッドがシグナルを処理する直前に起動します。 |
|
|
プロセス、またはプロセスによって作成されたスレッドにシグナルが送信されたときに起動します。 |
|
|
新しく作成されたプロセスのコンテキストで起動します。 |
ノート:
Linuxでは、プロセスと、プロセスによって作成されるスレッドとの間の根本的な違いはありません。プロセスのスレッドはリソースを共有できるように設定されますが、各スレッドはプロセス表に独自のエントリを持ち、プロセスIDも独自です。
procプローブの引数
次の表は、procプローブの引数の型を示しています。引数の詳細は、表11-4を参照してください。
表11-5 procプローブの引数
| プローブ | args[0] | args[1] | args[2] |
|---|---|---|---|
|
|
|
— |
— |
|
|
|
— |
— |
|
|
|
— |
— |
|
|
— |
— |
— |
|
|
|
— |
— |
|
|
|
|
— |
|
|
— |
— |
— |
|
|
— |
— |
— |
|
|
|
— |
— |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
— |
— |
— |
lwpsinfo_t
一部のprocプローブは、lwpsinfo_t型の引数をとります。このデータ構造体の詳細は、/usr/lib64/dtrace/version/procfs.dを参照してください。DTraceコンシューマで使用可能なlwpsinfo_t構造体の定義は、次のとおりです。
typedef struct lwpsinfo {
int pr_flag; /* flags */
id_t pr_lwpid; /* thread id */
uintptr_t pr_addr; /* internal address of thread */
uintptr_t pr_wchan; /* wait addr for sleeping lwp (NULL on Linux) */
char pr_stype; /* sync event type (0 on Linux) */
char pr_state; /* numeric thread state */
char pr_sname; /* printable character for pr_state */
int pr_pri; /* priority, high value = high priority */
char pr_name[PRCLSZ]; /* scheduling class name */
processorid_t pr_onpro; /* processor which last ran this thread */
} lwpsinfo_t;ノート:
Linuxには、軽量プロセスが存在しません。かわりに、Oracle Linuxのタスク・リストでは、プロセスとスレッドはstruct task_struct型のプロセス記述子によって表されます。DTraceは、lwpsinfo_tのメンバーを、Oracle Linuxプロセスのtask_structから変換します。
スレッドが停止する場合、pr_flagは1に設定されます。そうでない場合、0に設定されます。
Oracle Linuxでは、pr_stypeフィールドはサポートされていないため、常に0です。
次の表では、pr_stateがとれる値と、pr_snameの対応する文字値について説明します。
表11-6 pr_stateの値
| pr_stateの値 | pr_sname Value
|
説明 |
|---|---|---|
|
|
R
|
スレッドは実行可能ですが、現在CPUで実行されていません。
Oracle Linuxで同等のタスク状態は、 |
|
|
S
|
スレッドはスリープ中です。
Oracle Linuxで同等のタスク状態は、 |
|
|
T
|
スレッドは、明示的な
Oracle Linuxで同等のタスク状態は、 |
|
|
W
|
スレッドは待機キューで待機中です。
Oracle Linuxで同等のタスク状態は、 |
|
|
Z
|
スレッドはゾンビです。
Oracle Linuxで同等のタスク状態は、 |
psinfo_t
一部のprocプローブは、psinfo_t型の引数をとります。このデータ構造体の詳細は、/usr/lib64/dtrace/version/procfs.dを参照してください。DTraceコンシューマで使用可能なpsinfo_t構造体の定義は、次のとおりです。
typedef struct psinfo {
int pr_nlwp; /* not supported */
pid_t pr_pid; /* unique process id */
pid_t pr_ppid; /* process id of parent */
pid_t pr_pgid; /* pid of process group leader */
pid_t pr_sid; /* session id */
uid_t pr_uid; /* real user id */
uid_t pr_euid; /* effective user id */
uid_t pr_gid; /* real group id */
uid_t pr_egid; /* effective group id */
uintptr_t pr_addr; /* address of process */
size_t pr_size; /* not supported */
size_t pr_rssize; /* not supported */
struct tty_struct *pr_ttydev; /* controlling tty (or -1) */
ushort_t pr_pctcpu; /* not supported */
ushort_t pr_pctmem; /* not supported */
timestruc_t pr_start; /* not supported */
timestruc_t pr_time; /* not supported */
timestruc_t pr_ctime; /* not supported */
char pr_fname[16]; /* name of exec'ed file */
char pr_psargs[80]; /* initial chars of arg list */
int pr_wstat; /* not supported */
int pr_argc; /* initial argument count */
uintptr_t pr_argv; /* address of initial arg vector */
uintptr_t pr_envp; /* address of initial env vector */
char pr_dmodel; /* data model */
taskid_t pr_taskid; /* not supported */
projid_t pr_projid; /* not supported */
int pr_nzomb; /* not supported */
poolid_t pr_poolid; /* not supported */
zoneid_t pr_zoneid; /* not supported */
id_t pr_contract; /* not supported */
lwpsinfo_t pr_lwp; /* not supported */
} psinfo_t;ノート:
Linuxには、軽量プロセスが存在しません。Oracle Linuxのタスク・リストでは、プロセスとスレッドはstruct task_struct型のプロセス記述子によって表されます。DTraceは、psinfo_tのメンバーを、Oracle Linuxプロセスのtask_structから変換します。
pr_dmodelは、32ビット・プロセスを表すPR_MODEL_ILP32か、64ビット・プロセスを表すPR_MODEL_LP64に設定されます。
procの例
次の例は、procプロバイダによって公開されるプローブの使用を示しています。
exec
次の例は、execプローブを使用して、どのプログラムが誰によって実行されているかを容易に判別する方法を示しています。次のDソース・コードを入力し、whoexec.dという名前のファイルに保存します。
#pragma D option quiet
proc:::exec
{
self->parent = execname;
}
proc:::exec-success
/self->parent != NULL/
{
@[self->parent, execname] = count();
self->parent = NULL;
}
proc:::exec-failure
/self->parent != NULL/
{
self->parent = NULL;
}
END
{
printf("%-20s %-20s %s\n", "WHO", "WHAT", "COUNT");
printa("%-20s %-20s %@d\n", @);
}
短時間この例のスクリプトを実行すると、次のような出力が得られます。
# dtrace -s ./whoexec.d ^C WHO WHAT COUNT abrtd abrt-handle-eve 1 firefox basename 1 firefox mkdir 1 firefox mozilla-plugin- 1 firefox mozilla-xremote 1 firefox run-mozilla.sh 1 firefox uname 1 gnome-panel firefox 1 kworker/u:1 modprobe 1 modprobe modprobe.ksplic 1 mozilla-plugin- plugin-config 1 mozilla-plugin- uname 1 nice sosreport 1 run-mozilla.sh basename 1 run-mozilla.sh dirname 1 run-mozilla.sh firefox 1 run-mozilla.sh uname 1 sh abrt-action-sav 1 sh blkid 1 sh brctl 1 sh cut 1 ...
startプローブとexitプローブ
プログラムが作成されてから終了するまでの実行時間を調べる場合は、次の例で示すようにstartプローブとexitプローブを有効にします。progtime.dという名前のファイルに保存します。
proc:::start
{
self->start = timestamp;
}
proc:::exit
/self->start/
{
@[execname] = quantize(timestamp - self->start);
self->start = 0;
}ビルド・サーバー上で数秒間この例のスクリプトを実行すると、次のような出力が得られます。
# dtrace -s ./progtime.d
dtrace: script ’./progtime.d’ matched 2 probes
^C
...
cc
value ------------- Distribution ------------- count
33554432 | 0
67108864 |@@@ 3
134217728 |@ 1
268435456 | 0
536870912 |@@@@ 4
1073741824 |@@@@@@@@@@@@@@ 13
2147483648 |@@@@@@@@@@@@ 11
4294967296 |@@@ 3
8589934592 | 0
sh
value ------------- Distribution ------------- count
262144 | 0
524288 |@ 5
1048576 |@@@@@@@ 29
2097152 | 0
4194304 | 0
8388608 |@@@ 12
16777216 |@@ 9
33554432 |@@ 9
67108864 |@@ 8
134217728 |@ 7
268435456 |@@@@@ 20
536870912 |@@@@@@ 26
1073741824 |@@@ 14
2147483648 |@@ 11
4294967296 | 3
8589934592 | 1
17179869184 | 0
...signal-send
次の例は、signal-sendプローブを使用して、シグナルに関連付けられたプロセスの送受信を確認する方法を示しています。次のDソース・コードを入力し、sig.dという名前のファイルに保存します。
#pragma D option quiet
proc:::signal-send
{
@[execname, stringof(args[1]->pr_fname), args[2]] = count();
}
END
{
printf("%20s %20s %12s %s\n",
"SENDER", "RECIPIENT", "SIG", "COUNT");
printa("%20s %20s %12d %@d\n", @);
}このスクリプトを実行すると、次のような出力が得られます。
# dtrace -s sig.d
^C
SENDER RECIPIENT SIG COUNT
gnome-panel Xorg 29 1
kworker/0:2 dtrace 2 1
Xorg Xorg 29 3
java Xorg 29 6
firefox Xorg 29 14
kworker/0:0 Xorg 29 1135procの安定性
procプロバイダは、DTraceの安定性メカニズムを使用してその安定性を記述します。これらの値を、次の表に示します。
| 要素 | 名前の安定性 | データの安定性 | 依存クラス |
|---|---|---|---|
|
プロバイダ |
発展中 |
発展中 |
ISA |
|
モジュール |
非公開 |
非公開 |
不明 |
|
関数 |
非公開 |
非公開 |
不明 |
|
名前 |
発展中 |
発展中 |
ISA |
|
引数 |
発展中 |
発展中 |
ISA |
安定性メカニズムの詳細は、「DTraceの安定性機能」を参照してください。
schedプロバイダ
schedプロバイダは、CPUスケジューリングに関連するプローブを使用可能にします。CPUはすべてのスレッドが必ず使用する単一のリソースであるため、schedプロバイダは、体系的な動作を把握するために非常に役立ちます。たとえば、schedプロバイダを使用すると、スレッドのスリープ、実行、優先順位の変更、別のスレッドの呼出しなどが実行されたタイミングと理由を調べることができます。
schedプローブ
次の表では、schedプロバイダのプローブについて説明します。
表11-7 schedプローブ
| プローブ | 説明 |
|---|---|
|
|
スレッドの優先順位が変更される直前に起動します。スレッドを表す |
|
|
実行可能スレッドが実行キューからデキューされる直前に起動します。デキューされるスレッドを表す |
|
|
実行可能スレッドが実行キューにエンキューされる直前に起動します。エンキューされるスレッドを表す |
|
|
現在のCPUがスレッドの実行を終了する直前に起動します。 |
|
|
CPUがスレッドの実行を開始した直後に起動します。 |
|
|
現在のスレッドが専有される直前に起動します。このプローブの起動後、現在のスレッドが実行するスレッドを選択し、現在のスレッドに対して |
|
|
スケジューリングに関する決定がなされても、ディスパッチャが現在のスレッドの実行を続行しようとしたときに起動します。 |
|
|
現在のスレッドが同期オブジェクトでスリープする直前に起動します。同期オブジェクトの型は、 |
|
|
CPUが、別のCPUから、スケジューリングに関する決定を下すよう命令されたときに起動します。この命令は通常、より優先順位の高いスレッドが実行可能になったときに発行されます。現在のスレッドを表す |
|
|
クロック刻みベースのアカウンティングの一環として起動します。クロック刻みベースのアカウンティングでは、固定間隔の割込みが発生したとき、どのスレッドとプロセスが実行されていたかを調べることによって、CPUのアカウンティングが実行されます。CPU時間を割り当てられているスレッドに対応する |
|
|
現在のスレッドが同期オブジェクトでスリープしているスレッドを呼び出す直前に起動します。スリープ中のスレッドを表す |
schedプローブの引数
次の表では、schedプローブの引数の型について説明します。引数の詳細は、表11-7を参照してください。
表11-8 schedプローブの引数
| プローブ | args[0] | args[1] | args[2] | args[3] |
|---|---|---|---|---|
|
|
|
|
|
— |
|
|
|
|
|
— |
|
|
|
|
|
|
|
|
|
|
— |
— |
|
|
— |
— |
— |
— |
|
|
— |
— |
— |
— |
|
|
— |
— |
— |
— |
|
|
— |
— |
— |
— |
|
|
|
|
— |
— |
|
|
|
|
— |
— |
|
|
|
|
— |
— |
cpuinfo_t
cpuinfo_t構造体は、CPUを定義します。表11-8に示すように、enqueueプローブとdequeueプローブの両方の引数には、cpuinfo_tへのポインタが含まれています。また、現在のCPUに対応するcpuinfo_tは、curcpu変数によってポイントされます。
cpuinfo_t構造体の定義は、次のとおりです。
typedef struct cpuinfo {
processorid_t cpu_id; /* CPU identifier */
psetid_t cpu_pset; /* not supported */
chipid_t cpu_chip; /* chip identifier */
lgrp_id_t cpu_lgrp; /* not supported */
cpuinfo_arch_t *cpu_info; /* CPU information */
} cpuinfo_t;cpu_id: プロセッサ識別子です。
cpu_chip: 物理チップの識別子です。物理チップには、複数のCPUコアが含まれる場合があります。
cpu_info: CPUに関連付けられたcpuinfo_arch_t構造体へのポインタです。
schedの例
次の例は、schedプローブの使用方法を示しています。
on-cpuプローブとoff-cpuプローブ
どのCPUが、どのくらいの時間スレッドを実行しているかを調べる必要がある場合がたびたびあります。次の例は、on-cpuプローブとoff-cpuプローブを使用して、システム全体でこの問題の回答を簡単に導き出すことができる方法を示しています。次のDソース・コードを入力し、where.dという名前のファイルに保存します。
sched:::on-cpu
{
self->ts = timestamp;
}
sched:::off-cpu
/self->ts/
{
@[cpu] = quantize(timestamp - self->ts);
self->ts = 0;
}前述のスクリプトを実行すると、次のような出力が得られます。
# dtrace -s ./where.d
dtrace: script ’./where.d’ matched 2 probes
^C
0
value ------------- Distribution ------------- count
2048 | 0
4096 |@@ 37
8192 |@@@@@@@@@@@@@ 212
16384 |@ 30
32768 | 10
65536 |@ 17
131072 | 12
262144 | 9
524288 | 6
1048576 | 5
2097152 | 1
4194304 | 3
8388608 |@@@@ 75
16777216 |@@@@@@@@@@@@ 201
33554432 | 6
67108864 | 0
1
value ------------- Distribution ------------- count
2048 | 0
4096 |@ 6
8192 |@@@@ 23
16384 |@@@ 18
32768 |@@@@ 22
65536 |@@@@ 22
131072 |@ 7
262144 | 5
524288 | 2
1048576 | 3
2097152 |@ 9
4194304 | 4
8388608 |@@@ 18
16777216 |@@@ 19
33554432 |@@@ 16
67108864 |@@@@ 21
134217728 |@@ 14
268435456 | 0前述の出力は、CPU 1上のスレッドが連続的に131072ナノ秒未満(100マイクロ秒ほど)実行されるか、8388608から134217728ナノ秒(約10から100ミリ秒)実行される傾向にあることを示しています。ヒストグラムから、2つのデータ・クラスタ間に顕著な差異があることがわかります。また、どのCPUが特定のプロセスを実行しているかを知りたい場合もあります。
この問題に答える場合にも、on-cpuプローブとoff-cpuプローブを使用します。次のスクリプトでは、指定されたアプリケーションを10秒間以上実行するCPUが表示されます。whererun.d.という名前のファイルに保存します。
#pragma D option quiet
dtrace:::BEGIN
{
start = timestamp;
}
sched:::on-cpu
/execname == $$1/
{
self->ts = timestamp;
}
sched:::off-cpu
/self->ts/
{
@[cpu] = sum(timestamp - self->ts);
self->ts = 0;
}
profile:::tick-1sec
/++x >= 10/
{
exit(0);
}
dtrace:::END
{
printf("CPU distribution over %d seconds:\n\n",
(timestamp - start) / 1000000000);
printf("CPU microseconds\n--- ------------\n");
normalize(@, 1000);
printa("%3d %@d\n", @);
}前述のスクリプトを大規模なメール・サーバー上で実行し、IMAPデーモンを指定すると、次のような出力が得られます。
# dtrace -s ./whererun.d imapd CPU distribution of imapd over 10 seconds: CPU microseconds --- ------------ 15 10102 12 16377 21 25317 19 25504 17 35653 13 41539 14 46669 20 57753 22 70088 16 115860 23 127775 18 160517
Oracle Linuxでは、スリープ時間が短かったスレッドは、移行しにくい傾向にあるため、スレッドを実行するCPUを選択する際に、スレッドがスリープしていた時間が考慮されます。off-cpuプローブとon-cpuプローブを使用して、この動作を監視します。次のソース・コードを入力し、howlong.dという名前のファイルに保存します。
sched:::off-cpu
/curlwpsinfo->pr_state == SSLEEP/
{
self->cpu = cpu;
self->ts = timestamp;
}
sched:::on-cpu
/self->ts/
{
@[self->cpu == cpu ?
"sleep time, no CPU migration" : "sleep time, CPU migration"] =
lquantize((timestamp - self->ts) / 1000000, 0, 500, 25);
self->ts = 0;
self->cpu = 0;
}前述のスクリプトを約30秒間実行すると、次のような出力が得られます。
# dtrace -s ./howlong.d
dtrace: script ’./howlong.d’ matched 2 probes
^C
sleep time, CPU migration
value ------------- Distribution ------------- count
< 0 | 0
0 |@@@@@@@ 6838
25 |@@@@@ 4714
50 |@@@ 3108
75 |@ 1304
100 |@ 1557
125 |@ 1425
150 | 894
175 |@ 1526
200 |@@ 2010
225 |@@ 1933
250 |@@ 1982
275 |@@ 2051
300 |@@ 2021
325 |@ 1708
350 |@ 1113
375 | 502
400 | 220
425 | 106
450 | 54
475 | 40
>= 500 |@ 1716
sleep time, no CPU migration
value ------------- Distribution ------------- count
< 0 | 0
0 |@@@@@@@@@@@@ 58413
25 |@@@ 14793
50 |@@ 10050
75 | 3858
100 |@ 6242
125 |@ 6555
150 | 3980
175 |@ 5987
200 |@ 9024
225 |@ 9070
250 |@@ 10745
275 |@@ 11898
300 |@@ 11704
325 |@@ 10846
350 |@ 6962
375 | 3292
400 | 1713
425 | 585
450 | 201
475 | 96
>= 500 | 3946前述の出力から、移行よりも、移行が行われていない回数の方が多いことがわかります。また、スリープ時間が長くなるほど、移行が発生しやすくなっています。100ミリ秒以下のときは分布状態に顕著な差異が見られますが、スリープ時間が長くなるほど、この差異はほとんど見られなくなります。この結果から、一定のしきい値を超えると、スケジューリングに関する決定の際にスリープ時間は考慮されなくなるものと推測できます。
enqueueプローブとdequeueプローブ
どのCPUプロセスとスレッドが実行を待機しているかについて知る必要がある場合があります。enqueueプローブをdequeueプローブとともに使用すると、この問題の回答を導き出すことができます。次のソース・コードを入力し、qtime.dという名前のファイルに保存します。
sched:::enqueue
{
a[args[0]->pr_lwpid, args[1]->pr_pid, args[2]->cpu_id] =
timestamp;
}
sched:::dequeue
/a[args[0]->pr_lwpid, args[1]->pr_pid, args[2]->cpu_id]/
{
@[args[2]->cpu_id] = quantize(timestamp -
a[args[0]->pr_lwpid, args[1]->pr_pid, args[2]->cpu_id]);
a[args[0]->pr_lwpid, args[1]->pr_pid, args[2]->cpu_id] = 0;
}前述のスクリプトを数秒間実行すると、次のような出力が得られます。
# dtrace -s qtime.d
dtrace: script 'qtime.d' matched 16 probes
^C
1
value ------------- Distribution ------------- count
8192 | 0
16384 | 1
32768 |@ 47
65536 |@@@@@@@ 365
131072 |@@@@@@@@@@@@ 572
262144 |@@@@@@@@@@@@ 570
524288 |@@@@@@@ 354
1048576 |@ 57
2097152 | 7
4194304 | 1
8388608 | 1
16777216 | 0
0
value ------------- Distribution ------------- count
8192 | 0
16384 | 6
32768 |@ 49
65536 |@@@@@ 261
131072 |@@@@@@@@@@@@@ 753
262144 |@@@@@@@@@@@@ 704
524288 |@@@@@@@@ 455
1048576 |@ 74
2097152 | 9
4194304 | 2
8388608 | 0
待機時間を検索するのではなく、一定時間における実行キューの長さを調べる必要がある場合もあります。enqueueプローブとdequeueプローブを使用すると、キューの長さを追跡する連想配列を設定できます。次のソース・コードを入力し、qlen.dという名前のファイルに保存します。
sched:::enqueue
{
this->len = qlen[args[2]->cpu_id]++;
@[args[2]->cpu_id] = lquantize(this->len, 0, 100);
}
sched:::dequeue
/qlen[args[2]->cpu_id]/
{
qlen[args[2]->cpu_id]--;
}ほとんどの場合にアイドル状態である単一プロセッサ・システム上で、前述のスクリプトを約30秒間実行すると、次のような出力が得られます。
# dtrace -s qlen.d
dtrace: script 'qlen.d' matched 16 probes
^C
1
value ------------- Distribution ------------- count
< 0 | 0
0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 8124
1 |@@@@@@ 1558
2 |@ 160
3 | 51
4 | 24
5 | 13
6 | 11
7 | 9
8 | 6
9 | 0
0
value ------------- Distribution ------------- count
< 0 | 0
0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 8569
1 |@@@@@@@@@ 2429
2 |@ 292
3 | 25
4 | 8
5 | 5
6 | 4
7 | 4
8 | 1
9 | 0アイドル状態のシステムでは、ほぼ予想どおりの出力が得られます。実行可能スレッドがエンキューされる時間の大部分では、実行キューはきわめて短くなります(長さは3スレッド以下)。ただし、このシステムがほとんどアイドル状態にあることを考えると、表の下部に例外データ・ポイントが出力されることは予想外になります。たとえば、この実行キューが実行可能スレッド8個分だったのはなぜなのでしょうか。この問題をさらに詳しく調べるには、実行キューが長いときに実行キューの内容を表示するDスクリプトを作成します。この問題が複雑なのは、Dの有効化をデータ構造に対して繰り返し実行できず、したがって実行キュー全体に対しても繰り返し実行できないためです。たとえDの有効化でこれが可能になるとしても、カーネルの内部データ構造に依存することは避ける必要があります。
この種のスクリプトを実行する場合は、enqueueプローブとdequeueプローブを有効にし、投機と連想配列の両方を使用します。スレッドがエンキューされると、スクリプトによってキューの長さが増分され、スレッドによりキー付けされた連想配列でタイムスタンプが記録されます。この場合、スレッドは別のスレッドによってエンキューされた可能性があるため、スレッド・ローカルの変数は使用できません。次に、スクリプトによってキューの長さが最大値を超えているかどうかが確認され、それを超えていた場合は新しい投機が開始され、タイムスタンプと新しい最大長が記録されます。次に、スレッドがデキューされると、enqueueのタイムスタンプと最大長のタイムスタンプが比較され、スレッドが最大長のタイムスタンプより前にエンキューされた場合、このスレッドは最大長のタイムスタンプが記録されとき、キューにあったことになります。この場合、このスレッドの情報が投機的にトレースされます。最大長のタイムスタンプの時点でエンキューされていた最後のスレッドがカーネルによってデキューされると、スクリプトによって投機データがコミットされます。次のソース・コードを入力し、whoqueue.dという名前のファイルに保存します。
#pragma D option quiet
#pragma D option nspec=4
#pragma D option specsize=100k
int maxlen;
int spec[int];
sched:::enqueue
{
this->len = ++qlen[this->cpu = args[2]->cpu_id];
in[args[0]->pr_addr] = timestamp;
}
sched:::enqueue
/this->len > maxlen && spec[this->cpu]/
{
/*
* There is already a speculation for this CPU. We just set a new
* record, so we’ll discard the old one.
*/
discard(spec[this->cpu]);
}
sched:::enqueue
/this->len > maxlen/
{
/*
* We have a winner. Set the new maximum length and set the timestamp
* of the longest length.
*/
maxlen = this->len;
longtime[this->cpu] = timestamp;
/*
* Now start a new speculation, and speculatively trace the length.
*/
this->spec = spec[this->cpu] = speculation();
speculate(this->spec);
printf("Run queue of length %d:\n", this->len);
}
sched:::dequeue
/(this->in = in[args[0]->pr_addr]) &&
this->in <= longtime[this->cpu = args[2]->cpu_id]/
{
speculate(spec[this->cpu]);
printf(" %d/%d (%s)\n",
args[1]->pr_pid, args[0]->pr_lwpid,
stringof(args[1]->pr_fname));
}
sched:::dequeue
/qlen[args[2]->cpu_id]/
{
in[args[0]->pr_addr] = 0;
this->len = --qlen[args[2]->cpu_id];
}
sched:::dequeue
/this->len == 0 && spec[this->cpu]/
{
/*
* We just processed the last thread that was enqueued at the time
* of longest length; commit the speculation, which by now contains
* each thread that was enqueued when the queue was longest.
*/
commit(spec[this->cpu]);
spec[this->cpu] = 0;
}前述のスクリプトを同じシステムに対して実行すると、次のような出力が得られます。
# dtrace -s whoqueue.d Run queue of length 1: 2850/2850 (java) Run queue of length 2: 4034/4034 (kworker/0:1) 16/16 (sync_supers) Run queue of length 3: 10/10 (ksoftirqd/1) 1710/1710 (hald-addon-inpu) 25350/25350 (dtrace) Run queue of length 4: 2852/2852 (java) 2850/2850 (java) 1710/1710 (hald-addon-inpu) 2099/2099 (Xorg) Run queue of length 5: 3149/3149 (notification-da) 2417/2417 (gnome-settings-) 2437/2437 (gnome-panel) 2461/2461 (wnck-applet) 2432/2432 (metacity) Run queue of length 9: 3685/3685 (firefox) 3149/3149 (notification-da) 2417/2417 (gnome-settings-) 2437/2437 (gnome-panel) 2852/2852 (java) 2452/2452 (nautilus) 2461/2461 (wnck-applet) 2432/2432 (metacity) 2749/2749 (gnome-terminal) ^C
sleepプローブとwakeupプローブ
次の例は、wakeupプローブを使用して、特定のプロセスが何によって呼び出されているか、またその期間はいつかを特定する方法を示しています。次のソース・コードを入力し、gterm.dという名前のファイルに保存します。
#pragma D option quiet
dtrace:::BEGIN
{
start = timestamp;
}
sched:::wakeup
/stringof(args[1]->pr_fname) == "gnome-terminal"/
{
@[execname] = lquantize((timestamp - start) / 1000000000, 0, 10);
}
profile:::tick-1sec
/++x == 10/
{
exit(0);
}このスクリプトを実行した出力は、次のとおりです。
# dtrace -s gterm.d
Xorg
value ------------- Distribution ------------- count
< 0 | 0
0 |@@@@@@@@@@@@@@@ 69
1 |@@@@@@@@ 35
2 |@@@@@@@@@ 42
3 | 2
4 | 0
5 | 0
6 | 0
7 |@@@@ 16
8 | 0
9 |@@@ 15
>= 10 | 0
この出力では、システムの対話中に、Xサーバーがgnome-terminalプロセスを起動していることがわかります。
また、sleepプローブとwakeupプローブを併用すると、どのアプリケーションが他のアプリケーションをどのくらいの時間ブロックしているかを判定できます。次のソース・コードを入力し、whofor.dという名前のファイルに保存します。
#pragma D option quiet
sched:::sleep
{
bedtime[curlwpsinfo->pr_addr] = timestamp;
}
sched:::wakeup
/bedtime[args[0]->pr_addr]/
{
@[stringof(args[1]->pr_fname), execname] =
quantize(timestamp - bedtime[args[0]->pr_addr]);
bedtime[args[0]->pr_addr] = 0;
}
END
{
printa("%s sleeping on %s:\n%@d\n", @);
}デスクトップ・システムで前述の例のスクリプトを数秒間実行すると、出力の末尾は次のようになります。
# dtrace -s whofor.d
^C
...
Xorg sleeping on metacity:
value ------------- Distribution ------------- count
65536 | 0
131072 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2
262144 | 0
gnome-power-man sleeping on Xorg:
value ------------- Distribution ------------- count
131072 | 0
262144 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1
524288 | 0
...preemptプローブとremain-cpuプローブ
Oracle Linuxはプリエンプティブ・システムであるため、優先度の高いスレッドがそれより優先度の低いスレッドに割り込みます。優先使用により、優先度の低いスレッドで大きな待機時間のバブルが発生することがあります。そのため、他のスレッドよりも優先度が低いスレッドを知る必要がある場合があります。
次の例では、preemptプローブとremain-cpuプローブを使用してこの情報を表示します。次のソース・コードを入力し、whopreempt.dという名前のファイルに保存します。
#pragma D option quiet
sched:::preempt
{
self->preempt = 1;
}
sched:::remain-cpu
/self->preempt/
{
self->preempt = 0;
}
sched:::off-cpu
/self->preempt/
{
/*
* If we were told to preempt ourselves, see who we ended up giving
* the CPU to.
*/
@[stringof(args[1]->pr_fname), args[0]->pr_pri, execname,
curlwpsinfo->pr_pri] = count();
self->preempt = 0;
}
END
{
printf("%30s %3s %30s %3s %5s\n", "PREEMPTOR", "PRI",
"PREEMPTED", "PRI", "#");
printa("%30s %3d %30s %3d %5@d\n", @);
}デスクトップ・システムで前述のスクリプトを数秒間実行すると、次のような出力が得られます。
# dtrace -s whopreempt.d
^C
PREEMPTOR PRI PREEMPTED PRI #
firefox 120 kworker/0:0 120 1
gnome-panel 120 swapper 120 1
gnome-panel 120 wnck-applet 120 1
jbd2/dm-0-8 120 swapper 120 1
khugepaged 139 kworker/0:0 120 1
ksoftirqd/1 120 kworker/0:0 120 1
kworker/0:0 120 gnome-terminal 120 1
kworker/0:2 120 Xorg 120 1
kworker/0:2 120 java 120 1
kworker/1:0 120 Xorg 120 1
nautilus 120 Xorg 120 1
rtkit-daemon 0 rtkit-daemon 120 1
rtkit-daemon 120 swapper 120 1
watchdog/0 0 swapper 120 1
watchdog/1 0 kworker/0:0 120 1
wnck-applet 120 Xorg 120 1
wnck-applet 120 swapper 120 1
automount 120 kworker/0:0 120 2
gnome-power-man 120 kworker/0:0 120 2
kworker/0:0 120 swapper 120 2
kworker/1:0 120 dtrace 120 2
metacity 120 kworker/0:0 120 2
notification-da 120 swapper 120 2
udisks-daemon 120 kworker/0:0 120 2
automount 120 swapper 120 3
gnome-panel 120 Xorg 120 3
gnome-settings- 120 Xorg 120 3
gnome-settings- 120 swapper 120 3
gnome-terminal 120 swapper 120 3
java 120 kworker/0:0 120 3
ksoftirqd/0 120 swapper 120 3
kworker/0:2 120 swapper 120 3
metacity 120 Xorg 120 3
nautilus 120 kworker/0:0 120 3
qpidd 120 swapper 120 3
metacity 120 swapper 120 4
gvfs-afc-volume 120 swapper 120 5
java 120 Xorg 120 5
notification-da 120 Xorg 120 5
notification-da 120 kworker/0:0 120 5
Xorg 120 kworker/0:0 120 6
wnck-applet 120 kworker/0:0 120 10
VBoxService 120 swapper 120 13
dtrace 120 swapper 120 14
kworker/1:0 120 kworker/0:0 120 16
dtrace 120 kworker/0:0 120 20
Xorg 120 swapper 120 90
hald-addon-inpu 120 swapper 120 100
java 120 swapper 120 108
gnome-terminal 120 kworker/0:0 120 110tick
NOHZをoffに設定すると、Oracle Linuxはクロック刻みベースのアカウンティングを使用します。この場合、システム・クロック割込みが固定間隔で起動し、CPU使用率はクロック刻みの時点で実行されていたプロセスによって決まります。次の例では、tickプローブを使用してこの属性を監視します。
# dtrace -n sched:::tick'{ @[stringof(args[1]->pr_fname)] = count() }'
dtrace: description 'sched:::tick' matched 1 probe
^C
VBoxService 1
gpk-update-icon 1
hald-addon-inpu 1
jbd2/dm-0-8 1
automount 2
gnome-session 2
hald 2
gnome-power-man 3
ksoftirqd/0 3
kworker/0:2 3
notification-da 4
devkit-power-da 6
nautilus 9
dbus-daemon 11
gnome-panel 11
gnome-settings- 11
dtrace 19
khugepaged 22
metacity 27
kworker/0:0 41
swapper 56
firefox 58
wnck-applet 61
gnome-terminal 67
java 84
Xorg 227クロック刻みのアカウンティングには、アカウンティングを実行するシステム・クロックが原因で、時間に関係するスケジューリング・アクティビティがディスパッチされることがあるという短所があります。その結果、クロック刻みと同間隔で(つまり10ミリ秒ごとに)スレッドがなんらかの作業を実行する場合、時間に関係するスケジューリング・アクティビティをディスパッチした前後どちらでアカウンティングが行われるかに応じて、アカウンティングの結果が実際よりも上下します。アカウンティングが時間関係のディスパッチより前に実行される場合、定期的な間隔で実行されているスレッドは低くアカウンティングされます。このようなスレッドが、クロック間隔より短く実行された場合、クロック間隔の裏に隠すことができます。
次の例では、システムにこのようなスレッドがあるかどうかを確認します。次のソース・コードを入力し、tick.dという名前のファイルに保存します。
sched:::tick,
sched:::enqueue
{
@[probename] = lquantize((timestamp / 1000000) % 10, 0, 10);
}
この例のスクリプトを実行した出力結果を見ると、10ミリ秒の間隔でミリ秒のオフセットが2つ存在することがわかります。1つはtickプローブのオフセット、もう1つはenqueueのオフセットです。
# dtrace -s tick.d
dtrace: script 'tick.d' matched 9 probes
^C
tick
value ------------- Distribution ------------- count
< 0 | 0
0 |@@@@@ 29
1 |@@@@@@@@@@@@@@@@@@@ 106
2 |@@@@@ 27
3 |@ 7
4 |@@ 10
5 |@@ 12
6 |@ 4
7 |@ 8
8 |@@ 9
9 |@@@ 17
>= 10 | 0
enqueue
value ------------- Distribution ------------- count
< 0 | 0
0 |@@@@ 82
1 |@@@@ 86
2 |@@@@ 76
3 |@@@ 65
4 |@@@@@ 101
5 |@@@@ 79
6 |@@@@ 75
7 |@@@@ 76
8 |@@@@ 89
9 |@@@@ 75
>= 10 | 0
tickという出力ヒストグラムからは、クロック刻みが1ミリ秒のオフセットで起動していることがわかります。この例では、enqueueの出力が10ミリ秒間隔で均等に配置されており、1ミリ秒間隔のスパイクは見つからないため、スレッドは時間ベースでスケジュールされていないと判断されます。
schedの安定性
schedプロバイダは、DTraceの安定性メカニズムを使用してその安定性を記述します。これらの値を、次の表に示します。
| 要素 | 名前の安定性 | データの安定性 | 依存クラス |
|---|---|---|---|
|
プロバイダ |
発展中 |
発展中 |
ISA |
|
モジュール |
非公開 |
非公開 |
不明 |
|
関数 |
非公開 |
非公開 |
不明 |
|
名前 |
発展中 |
発展中 |
ISA |
|
引数 |
発展中 |
発展中 |
ISA |
安定性メカニズムの詳細は、「DTraceの安定性機能」を参照してください。
ioプロバイダ
ioプロバイダは、ディスク入出力に関連したプローブを使用可能にします。ioプロバイダでは、iostatなどのI/O監視ツールを通じて監視された動作を迅速に調査できます。たとえばioプロバイダを使用して、デバイス、I/Oの種類、I/Oサイズ、プロセス、アプリケーション名を基準にI/Oを確認できます。
ioプローブ
次の表では、ioプロバイダのプローブについて説明します。
表11-9 ioプローブ
| プローブ | 説明 |
|---|---|
|
|
周辺機器またはNFSサーバーに対してI/Oリクエストが発行される直前に起動します。 |
|
|
I/Oリクエストが満たされた後に起動します。doneプローブは、I/Oが完了してから完了処理がバッファ上で実行されるまでの間に起動します。したがって、doneプローブが起動した時点では、 |
|
|
特定のI/Oリクエストの完了を待機してスレッドが保留状態に入る直前に起動します。 |
|
|
スレッドがI/Oリクエストの完了待機状態を終了したときに起動します。 |
ioプローブは、周辺機器に対するI/Oリクエストが発行されたときと、NFSサーバーに対するファイルの読取り/書込みリクエストが発行されたときに起動します。たとえば、NFSサーバーに対してメタデータのリクエストが発行されても、readdir()リクエストのためioプローブがトリガーされることはありません。
ioプローブの引数
次の表では、ioプローブの引数について説明します。
表11-10 ioプローブの引数
| 引数 | タイプ | 説明 |
|---|---|---|
|
|
|
対応するI/Oリクエストの |
|
|
|
対応するI/Oリクエストのデバイスの |
|
|
|
対応するI/Oリクエストのファイルの |
ノート:
DTraceは、現在、ioプローブでfileinfo_tの使用をサポートしていません。Oracle Linuxでは、I/Oリクエストが発行されたファイルについて起動されたioプローブのレベルですぐにアクセスできる情報はありません。
bufinfo_t
bufinfo_t構造体は、I/Oリクエストについて説明する抽象化オブジェクトです。I/Oリクエストに対応するバッファは、start、done、wait-start、wait-doneの各プローブのargs[0]によってポイントされます。このデータ構造体の詳細は、/usr/lib64/dtrace/version/io.dを参照してください。bufinfo_tの定義は、次のとおりです。
typedef struct bufinfo {
int b_flags; /* flags */
size_t b_bcount; /* number of bytes */
caddr_t b_addr; /* buffer address */
uint64_t b_blkno; /* expanded block # on device */
uint64_t b_lblkno; /* logical block # on device */
size_t b_resid; /* not supported */
size_t b_bufsize; /* size of allocated buffer */
caddr_t b_iodone; /* I/O completion routine */
int b_error; /* not supported */
dev_t b_edev; /* extended device */
} bufinfo_t;ノート:
DTraceは、カーネル・バージョンに応じて、bufinfo_tのメンバーを、Oracle Linux I/Oリクエスト構造体のbuffer_headまたはbioから変換します。
b_flagsはI/Oバッファの状態を表し、ビット単位または各種の状態値で構成されます。次の表では、サポートされる状態の値について説明します。
表11-11 b_flagsの値
| b_flags | 値 | 説明 |
|---|---|---|
|
|
|
I/Oリクエストが非同期であり、待機していないことを示します。 ノート:
非同期となるよう指示された一部のI/Oでは、 |
|
|
|
ページ化されたI/Oリクエストでバッファが使用されていることを示します。 |
|
|
|
ユーザー・データ領域に対する物理(直接) I/Oでバッファが使用されていることを示します。 |
|
|
|
データが周辺機器からメイン・メモリーへ読み込まれることを示します。 |
|
|
|
データがメイン・メモリーから周辺機器へ転送されることを示します。 |
b_bcount: I/Oリクエストの一部として転送されるバイト数です。
b_addr: 既知の場合、I/Oリクエストの仮想アドレスです。
b_blkno: デバイス上のどのブロックにアクセスするかを示します。
b_lblkno: デバイス上のどの論理ブロックにアクセスするかを示します。論理ブロックから物理ブロック(シリンダ、トラックなど)へのマッピングは、デバイスごとに定義されています。
b_bufsize: 割当て済バッファのサイズが格納されます。
b_iodone: I/Oの完了時にコールされるカーネル内の特定のルーチンを表します。
b_edev: アクセスされるデバイスのメジャー・デバイス番号とマイナー・デバイス番号が入ります。Dのサブルーチンgetmajorとgetminorを使用すると、b_edevフィールドからメジャーおよびマイナーのデバイス番号を抽出できます。
devinfo_t
devinfo_t構造体は、デバイスに関する情報を提供します。I/Oの対象であるデバイスに対応するdevinfo_t構造体は、start、done、wait-start、wait-doneの各プローブのargs[1]によってポイントされます。このデータ構造体の詳細は、/usr/lib64/dtrace/version/io.dを参照してください。devinfo_tの定義は、次のとおりです。
typedef struct devinfo {
int dev_major; /* major number */
int dev_minor; /* minor number */
int dev_instance; /* not supported */
string dev_name; /* name of device */
string dev_statname; /* name of device + instance/minor */
string dev_pathname; /* pathname of device */
} devinfo_t;ノート:
DTraceは、devinfo_tのメンバーを、Oracle Linux I/Oリクエスト構造体のbuffer_headから変換します。
dev_major: メジャー・デバイス番号です。
dev_minor: マイナー・デバイス番号です。
dev_name: デバイスを管理するデバイス・ドライバの名前です。
dev_statname: iostatによってレポートされるデバイスの名前です。このフィールドは、異常なiostatの出力を実際のI/Oアクティビティに迅速に対応付けるために提供されています。
dev_pathname: デバイスのフルパスです。dev_pathnameで指定されるパスには、デバイス・ノード、インスタンス番号およびマイナー・ノードを表す構成要素が含まれます。ただし、統計名にこれら3つの要素がすべて含まれているとはかぎりません。デバイスによっては、統計名がデバイス名とインスタンス番号で構成されている場合もあります。あるいは、デバイス名とマイナー・ノード番号で構成されている場合もあります。そのため、同じdev_statnameを持つ2つのデバイスでdev_pathnameが異なることもあります。
fileinfo_t
ノート:
DTraceは、現在、ioプローブのargs[2]引数でfileinfo_tの使用をサポートしていません。fileinfo_t構造体は、プロセスのオープン・ファイルに関する情報をfds[]配列を使用して取得するために使用できます。「組込み変数」を参照してください。
fileinfo_t構造体は、ファイルに関する情報を提供します。start、done、wait-start、wait-doneの各プローブのargs[2]は、I/Oリクエストが対応しているファイルをポイントします。ファイル情報が存在するかどうかは、I/Oリクエストをディスパッチするときにこの情報を提供するファイル・システムによって決まります。一部のファイル・システム、特にサードパーティのファイル・システムには、この情報を提供しないものもあります。また、I/Oリクエストの発行元であるファイル・システムにファイル情報が存在しない場合もあります。たとえば、ファイル・システムのメタデータに対するI/Oにはファイルが関連付けられていません。さらに、高度に最適化されたファイル・システムは、複数の無関係なファイルからのI/Oを集積して、単一のI/Oリクエストを生成することがあります。この場合、I/Oの大部分を表すファイルまたはI/Oの一部を表すファイルについてファイル情報が提供されることがあります。あるいは、ファイル・システムからファイル情報がまったく提供されない可能性もあります。
このデータ構造体の詳細は、/usr/lib64/dtrace/version/io.dを参照してください。fileinfo_tの定義は、次のとおりです。
typedef struct fileinfo {
string fi_name; /* name (basename of fi_pathname) */
string fi_dirname; /* directory (dirname of fi_pathname) */
string fi_pathname; /* full pathname */
loff_t fi_offset; /* offset within file */
string fi_fs; /* file system */
string fi_mount; /* not supported */
int fi_oflags; /* open() flags for file descriptor */
} fileinfo_t;
fi_nameフィールドには、ファイルの名前が含まれていますがディレクトリ・コンポーネントは含まれていません。ファイル情報がI/Oに関連付けられていない場合、fi_nameフィールドは文字列<none>に設定されます。まれに、ファイルに関連付けられたパス名が不明な場合があります。この場合、fi_nameフィールドは文字列<unknown>に設定されます。
fi_dirnameフィールドには、ファイル名のディレクトリ・コンポーネントのみが含まれています。fi_nameの場合と同じように、この文字列は、ファイル情報が存在しない場合は<none>、またはファイルに関連付けられたパス名が不明な場合は<unknown>に設定できます。
fi_pathnameフィールドには、ファイルに対するフル・パス名が含まれています。fi_nameの場合と同じように、この文字列は、ファイル情報が存在しない場合は<none>、またはファイルに関連付けられたパス名が不明な場合は<unknown>に設定できます。
fi_offsetフィールドには、ファイル内のオフセット、またはファイル情報が存在しないかオフセットがファイル・システムによって未指定の場合には-1が含まれています。
fi_fsフィールドには、ファイル・システムのタイプ、または情報がない場合は<none>,が含まれています。
fi_oflagsフィールドには、ファイルを開くときに指定されたフラグが含まれています。
ioの例
'次の例のスクリプトは、I/Oが発行されるたびにその情報を表示します。次のソース・コードを入力し、iosnoop.dという名前のファイルに保存します。
#pragma D option quiet
BEGIN
{
printf("%10s %2s\n", "DEVICE", "RW");
}
io:::start
{
printf("%10s %2s\n", args[1]->dev_statname,
args[0]->b_flags & B_READ ? "R" : "W");
}このスクリプトの出力結果は、次のようになります。
# dtrace -s ./iosnoop.d
DEVICE RW
dm-00 R
dm-00 R
dm-00 R
dm-00 R
dm-00 R
dm-00 R
...連想配列を使用して各I/Oの所要時間(ミリ秒)を追跡すると、次の例に示すように、この例のスクリプトがさらに複雑になります。
#pragma D option quiet
BEGIN
{
printf("%10s %2s %7s\n", "DEVICE", "RW", "MS");
}
io:::start
{
start[args[0]->b_edev, args[0]->b_blkno] = timestamp;
}
io:::done
/start[args[0]->b_edev, args[0]->b_blkno]/
{
this->elapsed = timestamp - start[args[0]->b_edev, args[0]->b_blkno];
printf("%10s %2s %3d.%03d\n", args[1]->dev_statname,
args[0]->b_flags & B_READ ? "R" : "W",
this->elapsed / 10000000, (this->elapsed / 1000) % 1000);
start[args[0]->b_edev, args[0]->b_blkno] = 0;
}変更したスクリプトでは、出力に「MS (ミリ秒)」列が追加されています。
次の例のように、デバイス、アプリケーション、プロセスID、転送されたバイトについて集積し、whoio.dという名前のファイルに保存できます。
#pragma D option quiet
io:::start
{
@[args[1]->dev_statname, execname, pid] = sum(args[0]->b_bcount);
}
END
{
printf("%10s %20s %10s %15s\n", "DEVICE", "APP", "PID", "BYTES");
printa("%10s %20s %10d %15@d\n", @);
}このスクリプトを数秒間実行すると、次のような出力が得られます。
# dtrace -s whoio.d
^C
DEVICE APP PID BYTES
dm-00 evince 14759 16384
dm-00 flush-252:0 1367 45056
dm-00 bash 14758 131072
dm-00 gvfsd-metadata 2787 135168
dm-00 evince 14758 139264
dm-00 evince 14338 151552
dm-00 jbd2/dm-0-8 390 356352
デバイス間でデータをコピーする場合、いずれかのデバイスがコピーのリミッタとして機能するかどうかの確認が必要な場合があります。この問題に答えるには、各デバイスが転送する秒当たりのバイト数ではなく、各デバイスの有効なスループットを把握する必要があります。たとえば、次のスクリプトを使用して、copy.dという名前のファイルに保存して、スループットを確認できます。
#pragma D option quiet
io:::start
{
start[args[0]->b_edev, args[0]->b_blkno] = timestamp;
}
io:::done
/start[args[0]->b_edev, args[0]->b_blkno]/
{
/*
* We want to get an idea of our throughput to this device in KB/sec.
* What we have, however, is nanoseconds and bytes. That is we want
* to calculate:
*
* bytes / 1024
* ------------------------
* nanoseconds / 1000000000
*
* But we cannot calculate this using integer arithmetic without losing
* precision (the denominator, for one, is between 0 and 1 for nearly
* all I/Os). So we restate the fraction, and cancel:
*
* bytes 1000000000 bytes 976562
* --------- * ------------- = --------- * -------------
* 1024 nanoseconds 1 nanoseconds
*
* This is easy to calculate using integer arithmetic.
*/
this->elapsed = timestamp - start[args[0]->b_edev, args[0]->b_blkno];
@[args[1]->dev_statname, args[1]->dev_pathname] =
quantize((args[0]->b_bcount * 976562) / this->elapsed);
start[args[0]->b_edev, args[0]->b_blkno] = 0;
}
END
{
printa(" %s (%s)\n%@d\n", @);
}ハード・ディスクからUSBドライブにデータをコピーするとき、前述のスクリプトを数秒間実行すると、次のような出力が得られます。
# dtrace -s copy.d
^C
sdc1 (/dev/sdc1)
value ------------- Distribution ------------- count
32 | 0
64 | 3
128 | 1
256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2257
512 | 1
1024 | 0
dm-00 (/dev/dm-00)
value ------------- Distribution ------------- count
128 | 0
256 | 1
512 | 0
1024 | 2
2048 | 0
4096 | 2
8192 |@@@@@@@@@@@@@@@@@@ 172
16384 |@@@@@ 52
32768 |@@@@@@@@@@@ 108
65536 |@@@ 34
131072 | 0
前述の出力から、USBドライブ(sdc1)がデバイスのリミッタとして機能していることがわかります。sdc1のスループットは256K/秒から512K/秒の間で、dm-00は8MB/秒から64MB/秒超までのいずれかでI/Oを提供しています。
ioの安定性
ioプロバイダは、DTraceの安定性メカニズムを使用してその安定性を記述します。これらの値を、次の表に示します。
| 要素 | 名前の安定性 | データの安定性 | 依存クラス |
|---|---|---|---|
|
プロバイダ |
発展中 |
発展中 |
ISA |
|
モジュール |
非公開 |
非公開 |
不明 |
|
関数 |
非公開 |
非公開 |
不明 |
|
名前 |
発展中 |
発展中 |
ISA |
|
引数 |
発展中 |
発展中 |
ISA |
安定性メカニズムの詳細は、「DTraceの安定性機能」を参照してください
fasttrapプロバイダ
fasttrapプロバイダは、ユーザー空間スレッド内の任意の命令の動的なインストゥルメンテーションを実行します。他のほとんどのDTraceプロバイダとは異なり、fasttrapプロバイダはシステム・アクティビティのトレース用に設計されていません。かわりに、このプロバイダは、DTraceコンシューマがfasttrapプローブをアクティブ化して情報をDTraceフレームワークにインジェクトする方法として意図されています。
ユーザー空間プログラムでの静的定義プローブの有効化について、詳細は、「ユーザー・アプリケーションの静的定義トレース」を参照してください。
fasttrapプローブ
fasttrapプロバイダは、ユーザー・レベルのプロセスがカーネルへの特定のDTraceコールを行うたびに起動する単一のプローブを使用できるようにします。プローブをアクティブ化するためのDTraceコールは使用できません
fasttrapの安定性
fasttrapプロバイダは、DTraceの安定性メカニズムを使用してその安定性を記述します。これらの値を、次の表に示します。
| 要素 | 名前の安定性 | データの安定性 | 依存クラス |
|---|---|---|---|
|
プロバイダ |
発展中 |
発展中 |
ISA |
|
モジュール |
非公開 |
非公開 |
不明 |
|
関数 |
非公開 |
非公開 |
不明 |
|
名前 |
発展中 |
発展中 |
ISA |
|
引数 |
非公開 |
非公開 |
不明 |
安定性メカニズムの詳細は、「DTraceの安定性機能」を参照してください。