ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
DTrace ユーザーガイド Oracle Solaris 10 8/11 Information Library (日本語) |
この節では、DTrace の「投機トレース」について説明します。投機トレース機能では、一時的にデータをトレースし、このデータをトレースバッファーに「コミット」するか「破棄」するかを決めることができます。重要でないイベントを除去する手段としては、主に「述語」を使用します。述語は、そのプローブイベントがユーザーにとって重要かどうか、プローブの起動時にわかっている際に有用です。プローブの起動後まで、そのプローブイベントが重要かどうかわからない場合には、述語はあまり有用ではありません。
あるシステムコールが一般的なエラーコードを出力してときどき異常終了する場合、エラー条件の原因となっているコードパスを調べることをお勧めします。投機トレース機能を使用すれば、1 つ以上のプローブ位置で一時的にデータをトレースしたあと、データを主バッファーにコミットするかどうかは、別のプローブ位置で決めることができます。結果的に、重要な出力だけがトレースデータとして保持されるため、事後処理の必要がありません。
表 4-1 DTrace 投機関数
|
speculation() 関数は、投機バッファーを割り当て、投機識別子を返します。以後、 speculate() 関数の呼び出し時には、この投機識別子を使用します。投機識別子の値がゼロの場合、この投機識別子は常に無効です。ただし、speculate()、commit()、discard() のいずれかの関数に渡すことは可能です。speculation() の呼び出しに失敗した場合、dtrace コマンドは、次のようなメッセージを返します。
dtrace: 2 failed speculations (no speculative buffer space available)
投機を使用するには、すべてのデータ記録アクションの実行前に、speculation() から返された識別子を speculate() 関数に渡す節を使用してください。speculate() と同じ節に含まれるすべてのデータ記録アクションは、投機的にトレースされます。1 つの D プローブ節内で、データ記録アクションのあとに speculate() を呼び出すと、D コンパイラから、コンパイル時エラーが返されます。1 つの節には、投機トレース要求だけ、またはそれ以外のトレース要求だけを含めることができます。同じ節にこれらの両方を含めることはできません。
集積アクション、破壊アクション、exit アクションは、投機的に処理することはできません。これらのいずれかのアクションを speculate() と同じ節に含めると、コンパイル時エラーが発生します。speculate() 関数のあとに、speculate() 関数を使用することはできません。1 つの節で使用できる投機は 1 つだけです。speculate() 関数 1 個以外に何も含まれない節では、デフォルトのアクション (有効化されたプローブ ID のみをトレースする) が投機的にトレースされます。
speculation() 関数を使用するときには、通常は、speculation() 関数の結果をスレッド固有変数に割り当てます。そのあとは、このスレッド固有変数を、ほかのプローブの述語や speculate() の引数として使用します。
例 4-5 speculation() 関数の一般的な使用例
syscall::open:entry { self->spec = speculation(); } syscall::: /self->spec/ { speculate(self->spec); printf("this is speculative"); }
投機のコミットは、commit() 関数を使って行います。投機バッファーをコミットすると、このバッファー内のデータが主バッファーにコピーされます。指定した投機バッファーに、主バッファーには収まり切らない量のデータが含まれている場合、データはコピーされず、バッファーの欠落カウントの値が大きくなります。バッファーを複数の CPU 上で投機的にトレースした場合、コミット操作を実行した CPU 上の投機データはすぐにコピーされますが、その他の CPU 上の投機データは、commit() の実行後にコピーされます。
コミットが実行されている間は、その投機バッファーを後続の speculation() 呼び出しで使用できません。各 CPU の投機バッファーが対応する CPU の主バッファーに完全にコピーされるまで待つ必要があります。コミット中のバッファーに speculate() 関数呼び出しの結果を書き込もうとすると、データは破棄されます。このとき、エラーは生成されません。その後の commit() や discard() の呼び出しも、エラーを返さずに失敗します。commit() 関数が含まれる節に、データ記録アクションを含めることはできません。しかし、同じ節に複数の commit() 呼び出しを含めて、複数のバッファーを同時にコミットすることは可能です。
投機を破棄するには、discard() 関数を使用します。discard() 関数を呼び出している CPU 上でのみ投機がアクティブにされている場合は、そのバッファーを、後続の speculation() 関数呼び出しですぐに使用できます。複数の CPU 上で投機がアクティブにされている場合、破棄されたバッファーは、discard() の呼び出し後に、後続の speculation() 関数呼び出しで使用できるようになります。speculation() 関数を呼び出したときに、使用可能な投機バッファーが存在しない場合は、次のような dtrace メッセージが表示されます。
dtrace: 905 failed speculations (available buffer(s) still busy)
投機は、特定のコードパスを明らかにするために使用できます。open() が異常終了したときは、open(2) システムコールのコードパスをすべて表示できます。次の例を参照してください。
例 4-6 specopen.d: 異常終了した open() のコードフロー
#!/usr/sbin/dtrace -Fs syscall::open:entry, syscall::open64:entry { /* * The call to speculation() creates a new speculation. If this fails, * dtrace(1M) will generate an error message indicating the reason for * the failed speculation(), but subsequent speculative tracing will be * silently discarded. */ self->spec = speculation(); speculate(self->spec); /* * Because this printf() follows the speculate(), it is being * speculatively traced; it will only appear in the data buffer if the * speculation is subsequently commited. */ printf("%s", stringof(copyinstr(arg0))); } fbt::: /self->spec/ { /* * A speculate() with no other actions speculates the default action: * tracing the EPID. */ speculate(self->spec); } syscall::open:return, syscall::open64:return /self->spec/ { /* * To balance the output with the -F option, we want to be sure that * every entry has a matching return. Because we speculated the * open entry above, we want to also speculate the open return. * This is also a convenient time to trace the errno value. */ speculate(self->spec); trace(errno); } syscall::open:return, syscall::open64:return /self->spec && errno != 0/ { /* * If errno is non-zero, we want to commit the speculation. */ commit(self->spec); self->spec = 0; } syscall::open:return, syscall::open64:return /self->spec && errno == 0/ { /* * If errno is not set, we discard the speculation. */ discard(self->spec); self->spec = 0; }
このスクリプトを実行すると、次の例のような結果が出力されます。
# ./specopen.d dtrace: script './specopen.d' matched 24282 probes CPU FUNCTION 1 => open /var/ld/ld.config 1 -> open 1 -> copen 1 -> falloc 1 -> ufalloc 1 -> fd_find 1 -> mutex_owned 1 <- mutex_owned 1 <- fd_find 1 -> fd_reserve 1 -> mutex_owned 1 <- mutex_owned 1 -> mutex_owned 1 <- mutex_owned 1 <- fd_reserve 1 <- ufalloc 1 -> kmem_cache_alloc 1 -> kmem_cache_alloc_debug 1 -> verify_and_copy_pattern 1 <- verify_and_copy_pattern 1 -> file_cache_constructor 1 -> mutex_init 1 <- mutex_init 1 <- file_cache_constructor 1 -> tsc_gethrtime 1 <- tsc_gethrtime 1 -> getpcstack 1 <- getpcstack 1 -> kmem_log_enter 1 <- kmem_log_enter 1 <- kmem_cache_alloc_debug 1 <- kmem_cache_alloc 1 -> crhold 1 <- crhold 1 <- falloc 1 -> vn_openat 1 -> lookupnameat 1 -> copyinstr 1 <- copyinstr 1 -> lookuppnat 1 -> lookuppnvp 1 -> pn_fixslash 1 <- pn_fixslash 1 -> pn_getcomponent 1 <- pn_getcomponent 1 -> ufs_lookup 1 -> dnlc_lookup 1 -> bcmp 1 <- bcmp 1 <- dnlc_lookup 1 -> ufs_iaccess 1 -> crgetuid 1 <- crgetuid 1 -> groupmember 1 -> supgroupmember 1 <- supgroupmember 1 <- groupmember 1 <- ufs_iaccess 1 <- ufs_lookup 1 -> vn_rele 1 <- vn_rele 1 -> pn_getcomponent 1 <- pn_getcomponent 1 -> ufs_lookup 1 -> dnlc_lookup 1 -> bcmp 1 <- bcmp 1 <- dnlc_lookup 1 -> ufs_iaccess 1 -> crgetuid 1 <- crgetuid 1 <- ufs_iaccess 1 <- ufs_lookup 1 -> vn_rele 1 <- vn_rele 1 -> pn_getcomponent 1 <- pn_getcomponent 1 -> ufs_lookup 1 -> dnlc_lookup 1 -> bcmp 1 <- bcmp 1 <- dnlc_lookup 1 -> ufs_iaccess 1 -> crgetuid 1 <- crgetuid 1 <- ufs_iaccess 1 -> vn_rele 1 <- vn_rele 1 <- ufs_lookup 1 -> vn_rele 1 <- vn_rele 1 <- lookuppnvp 1 <- lookuppnat 1 <- lookupnameat 1 <- vn_openat 1 -> setf 1 -> fd_reserve 1 -> mutex_owned 1 <- mutex_owned 1 -> mutex_owned 1 <- mutex_owned 1 <- fd_reserve 1 -> cv_broadcast 1 <- cv_broadcast 1 <- setf 1 -> unfalloc 1 -> mutex_owned 1 <- mutex_owned 1 -> crfree 1 <- crfree 1 -> kmem_cache_free 1 -> kmem_cache_free_debug 1 -> kmem_log_enter 1 <- kmem_log_enter 1 -> tsc_gethrtime 1 <- tsc_gethrtime 1 -> getpcstack 1 <- getpcstack 1 -> kmem_log_enter 1 <- kmem_log_enter 1 -> file_cache_destructor 1 -> mutex_destroy 1 <- mutex_destroy 1 <- file_cache_destructor 1 -> copy_pattern 1 <- copy_pattern 1 <- kmem_cache_free_debug 1 <- kmem_cache_free 1 <- unfalloc 1 -> set_errno 1 <- set_errno 1 <- copen 1 <- open 1 <= open 2