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 1135
procの安定性
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 110
tick
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の安定性機能」を参照してください。