DTrace ユーザーガイド

投機トレース

この節では、DTrace の「投機トレース」について説明します。投機トレース機能では、一時的にデータをトレースし、このデータをトレースバッファーに「コミット」するか「破棄」するかを決めることができます。重要でないイベントを除去する手段としては、主に「述語」を使用します。述語は、そのプローブイベントがユーザーにとって重要かどうか、プローブの起動時にわかっている際に有用です。プローブの起動後まで、そのプローブイベントが重要かどうかわからない場合には、述語はあまり有用ではありません。

あるシステムコールが一般的なエラーコードを出力してときどき異常終了する場合、エラー条件の原因となっているコードパスを調べることをお勧めします。投機トレース機能を使用すれば、1 つ以上のプローブ位置で一時的にデータをトレースしたあと、データを主バッファーにコミットするかどうかは、別のプローブ位置で決めることができます。結果的に、重要な出力だけがトレースデータとして保持されるため、事後処理の必要がありません。

投機インタフェース

以下の表に、DTrace 投機関数を一覧します。

表 4–1 DTrace 投機関数

関数名 

引数 

説明 

speculation

なし 

新しい投機バッファーの識別子を返す 

speculate

ID 

同一節内の残りの部分を、指定された ID の投機バッファーにトレースする 

commit

ID 

指定された ID の投機バッファーをコミットする 

discard

ID 

指定された ID の投機バッファーを破棄する 

投機の作成

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