Solaris 動的トレースガイド

第 10 章 アクションとサブルーチン

trace()printf() といった D 関数を呼び出すことで、DTrace の 2 種類のサービスを呼び出すことができます。 サービスのうち 1 つは、データをトレースしたり DTrace 外部の状態を変更したりする「アクション」です。もう 1つは、DTrace 内部の状態にのみ影響を及ぼす「サブルーチン」です。この章では、これらのアクションとサブルーチンを定義し、その構文と意味について説明します。

アクション

アクションには、DTrace プログラムと DTrace 外部のシステムの相互作用を促す働きがあります。もっとも一般的なアクションは、DTrace バッファーへデータを記録するアクションです。そのほかにも、現在のプロセスを停止するアクション、現在のプロセス上で特定のシグナルを発行するアクション、トレースをすべて停止するアクションなどがあります。こうしたアクションのうち、明確な方法でシステムに変更を加えるものを「破壊アクション」と呼びます。破壊アクションは、明示的に有効化されている場合にだけ使用できます。データの記録アクションは、デフォルトで、「主バッファー」にデータを記録します。主バッファーとバッファーポリシーの詳細については、第 11 章バッファーとバッファリングを参照してください。

デフォルトアクション

節には、任意の数のアクションと変数操作を含めることができます。節に何も指定しないと、「デフォルトアクション」が実行されます。デフォルトアクションでは、有効なプローブ ID (EPID ) が主バッファー内にトレースされます。EPID は、特定の述語とアクションによる特定のプローブの特定の有効化を識別します。DTrace コンシューマは、この EPID から、アクションを引き起こしたプローブを特定できます。実際、データのトレース時には必ず、EPID を指定して、コンシューマにトレース対象のデータを伝える必要があります。デフォルトアクションが EPID のトレースのみになっているのは、このためです。

デフォルトアクションを使用すると、dtrace(1M) コマンドの使用が簡単になります。たとえば、次のコマンド例では、タイムシェアリング (TS) スケジューリングモジュール内のすべてのプローブが有効化され、デフォルトアクションが実行されます。


# dtrace -m TS

このコマンドを実行すると、次のような出力が得られます。


# dtrace -m TS
dtrace: description 'TS' matched 80 probes
CPU     ID                    FUNCTION:NAME
  0  12077                 ts_trapret:entry 
  0  12078                ts_trapret:return 
  0  12069                   ts_sleep:entry 
  0  12070                  ts_sleep:return 
  0  12033                  ts_setrun:entry 
  0  12034                 ts_setrun:return 
  0  12081                  ts_wakeup:entry 
  0  12082                 ts_wakeup:return 
  0  12069                   ts_sleep:entry 
  0  12070                  ts_sleep:return 
  0  12033                  ts_setrun:entry 
  0  12034                 ts_setrun:return 
  0  12069                   ts_sleep:entry 
  0  12070                  ts_sleep:return 
  0  12033                  ts_setrun:entry 
  0  12034                 ts_setrun:return 
  0  12069                   ts_sleep:entry 
  0  12070                  ts_sleep:return 
  0  12023                  ts_update:entry 
  0  12079             ts_update_list:entry 
  0  12080            ts_update_list:return 
  0  12079             ts_update_list:entry 
...

データ記録アクション

DTrace のコアアクションは、データ記録アクションで構成されています。これらのアクションのデフォルトの動作は、主バッファーへのデータの格納ですが、投機バッファーへのデータの格納も可能です。主バッファーの詳細については、第 11 章バッファーとバッファリングを参照してください。投機バッファーについては、第 13 章投機トレースを参照してください。この節では「指定バッファー」という語を使用して、データの記録先が主バッファーか投機バッファー (アクションが speculate() の後ろに指定されている場合) であることを示しています。

trace()

void trace(expression)

もっとも基本的なアクションは、D 式 expression を引数とし、指定バッファーに結果をトレースする trace() アクションです。以下に、trace() アクションの例を示します。

trace(execname);
trace(curlwpsinfo->pr_pri);
trace(timestamp / 1000);
trace(`lbolt);
trace("somehow managed to get here");

tracemem()

void tracemem(address, size_t nbytes)

tracemem() アクションは、最初の引数として D 式 address、2 番目の引数として定数 nbytes をとります。tracemem() は、address で指定されたアドレスから nbytes で指定された長さのメモリーを、指定バッファーにコピーします。

printf()

void printf(string format, ...) 

printf() アクションは、trace() と同じように、D 式をトレースします。ただし、printf() では、複雑な printf(3C) 形式の書式が使用されます。printf(3C) の場合と同じく、パラメータは format 文字列と任意の数の引数です。デフォルトでは、これらの引数が指定バッファーにトレースされます。その後、指定された書式設定文字列に従って、これらの引数に dtrace(1M) の出力書式が設定されます。たとえば、trace() の最初の 2 つの trace() の例を、単一の printf() 内で組み合わせて使用すると、以下のようになります。

printf("execname is %s; priority is %d", execname, curlwpsinfo->pr_pri);

printf() の詳細については、第 12 章出力書式を参照してください。

printa()

void printa(aggregation)
void printa(string format, aggregation)

printa() アクションでは、集積体に書式を設定して表示できます。集積体の詳細については、第 9 章集積体を参照してください。format を省略した場合、printa() は、aggregation で指定された集積体の処理を行い、デフォルトの書式で表示せよという DTrace コンシューマへの指令をトレースする以外に、何も行いません。format を指定した場合、集積体に指定された書式が設定されます。printa() の書式文字列については、第 12 章出力書式を参照してください。

printa() は、DTrace コンシューマに集積体を処理させる指令をトレースするだけです。カーネル内で集積体の処理を行うことはありません。したがって、printa() のトレース指令から実際に指令が処理されるまでの時間は、バッファー処理に影響を及ぼす要因によって変化します。こうした要因には、集積間隔、バッファリングポリシー、バッファーの切り替え間隔 (バッファリングポリシーが switching である場合) などがあります。これらの要因の詳細については、第 9 章集積体第 11 章バッファーとバッファリングを参照してください。

stack()

void stack(int nframes)
void stack(void)

stack() アクションは、カーネルスタックトレースを指定バッファーに記録します。カーネルスタックの深さは、nframes で指定します。nframes を省略した場合、stackframes オプションで指定された数のスタックフレームが記録されます。次に例を示します。


# dtrace -n uiomove:entry'{stack()}'
  CPU     ID                    FUNCTION:NAME
    0   9153                    uiomove:entry 
                genunix`fop_write+0x1b
                namefs`nm_write+0x1d
                genunix`fop_write+0x1b
                genunix`write+0x1f7

    0   9153                    uiomove:entry 
                genunix`fop_read+0x1b
                genunix`read+0x1d4

    0   9153                    uiomove:entry 
                genunix`strread+0x394
                specfs`spec_read+0x65
                genunix`fop_read+0x1b
                genunix`read+0x1d4
   ...

stack() アクションは、集積体キーとしても使用できるという点で、その他のアクションとは少し異なっています。


# dtrace -n kmem_alloc:entry'{@[stack()] = count()}'
dtrace: description 'kmem_alloc:entry' matched 1 probe
^C

                rpcmod`endpnt_get+0x47c
                rpcmod`clnt_clts_kcallit_addr+0x26f
                rpcmod`clnt_clts_kcallit+0x22
                nfs`rfscall+0x350
                nfs`rfs2call+0x60
                nfs`nfs_getattr_otw+0x9e
                nfs`nfsgetattr+0x26
                nfs`nfs_getattr+0xb8
                genunix`fop_getattr+0x18
                genunix`cstat64+0x30
                genunix`cstatat64+0x4a
                genunix`lstat64+0x1c
                  1

                genunix`vfs_rlock_wait+0xc
                genunix`lookuppnvp+0x19d
                genunix`lookuppnat+0xe7
                genunix`lookupnameat+0x87
                genunix`lookupname+0x19
                genunix`chdir+0x18
                  1

                rpcmod`endpnt_get+0x6b1
                rpcmod`clnt_clts_kcallit_addr+0x26f
                rpcmod`clnt_clts_kcallit+0x22
                nfs`rfscall+0x350
                nfs`rfs2call+0x60
                nfs`nfs_getattr_otw+0x9e
                nfs`nfsgetattr+0x26
                nfs`nfs_getattr+0xb8
                genunix`fop_getattr+0x18
                genunix`cstat64+0x30
                genunix`cstatat64+0x4a
                genunix`lstat64+0x1c
                  1
    ...

ustack()

void ustack(int nframes, int strsize)
void ustack(int nframes)
void ustack(void)

ustack() アクションは、指定バッファーにユーザースタックトレースを記録します。ユーザースタックの深さは、nframes で指定します。nframes を省略した場合、ustackframes オプションで指定された数のスタックフレームが記録されます。ustack() では、プローブ起動時の呼び出しフレームのアドレスを特定できますが、DTrace コンシューマがユーザーレベルで ustack() アクションを処理しないかぎり、スタックフレームはシンボルに翻訳されません。strsize にゼロ以外の値が指定されている場合、ustack() は指定された量の文字列空間を割り当て、これを使ってカーネルから直接、アドレスからシンボルへの翻訳を行います。このユーザーシンボルの直接翻訳機能は、現在、Java 仮想マシンのバージョン 1.5 以降でしか提供されていません。Java のアドレスからシンボルへの翻訳が行われると、Java クラスとメソッド名を持つ Java フレームが含まれているユーザースタックに、注釈が付けられます。翻訳できなかったフレームは、16 進アドレスで表されます。

以下の例では、文字列空間を持たないスタックをトレースしているため、Java のアドレスからシンボルへの翻訳は行われません。


# dtrace -n syscall::write:entry'/pid == $target/{ustack(50, 0); 
    exit(0)}' -c "java -version"
dtrace: description 'syscall::write:entry' matched 1 probe
java version "1.5.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta3-b58)
Java HotSpot(TM) Client VM (build 1.5.0-beta3-b58, mixed mode)
dtrace: pid 5312 has exited
CPU     ID                    FUNCTION:NAME
  0     35                      write:entry 
              libc.so.1`_write+0x15
              libjvm.so`__1cDhpiFwrite6FipkvI_I_+0xa8
              libjvm.so`JVM_Write+0x2f
              d0c5c946
              libjava.so`Java_java_io_FileOutputStream_writeBytes+0x2c
              cb007fcd
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb000152
              libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_
                          pnMmethodHandle_pnRJavaCallArguments_
                          pnGThread__v_+0x187
              libjvm.so`__1cCosUos_exception_wrapper6FpFpnJJavaValue_
                          pnMmethodHandle_pnRJavaCallArguments_
                          pnGThread__v2468_v_+0x14
              libjvm.so`__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_
                          pnRJavaCallArguments_pnGThread __v_+0x28
              libjvm.so`__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_
                          pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_
                          ArgumentPusher_pnGThread__v_+0x180
              libjvm.so`jni_CallStaticVoidMethod+0x10f
              java`main+0x53d

Java 仮想マシンの C と C++ のスタックフレームは、C++ 符号化シンボル名でシンボリックに表現されます。Java スタックフレームは、16 進アドレスで表現されます。以下は、ゼロでない文字列空間で ustack() を呼び出す例です。


# dtrace -n syscall::write:entry'/pid == $target/{ustack(50, 500); exit(0)}'
      -c "java -version"
dtrace: description 'syscall::write:entry' matched 1 probe
java version "1.5.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta3-b58)
Java HotSpot(TM) Client VM (build 1.5.0-beta3-b58, mixed mode)
dtrace: pid 5308 has exited
CPU     ID                    FUNCTION:NAME
  0     35                      write:entry 
              libc.so.1`_write+0x15
              libjvm.so`__1cDhpiFwrite6FipkvI_I_+0xa8
              libjvm.so`JVM_Write+0x2f
              d0c5c946
              libjava.so`Java_java_io_FileOutputStream_writeBytes+0x2c
              java/io/FileOutputStream.writeBytes
              java/io/FileOutputStream.write
              java/io/BufferedOutputStream.flushBuffer
              java/io/BufferedOutputStream.flush
              java/io/PrintStream.write
              sun/nio/cs/StreamEncoder$CharsetSE.writeBytes
              sun/nio/cs/StreamEncoder$CharsetSE.implFlushBuffer
              sun/nio/cs/StreamEncoder.flushBuffer
              java/io/OutputStreamWriter.flushBuffer
              java/io/PrintStream.write
              java/io/PrintStream.print
              java/io/PrintStream.println
              sun/misc/Version.print
              sun/misc/Version.print
              StubRoutines (1)
              libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_
                          pnMmethodHandle_pnRJavaCallArguments_pnGThread
                          __v_+0x187
              libjvm.so`__1cCosUos_exception_wrapper6FpFpnJJavaValue_
                          pnMmethodHandle_pnRJavaCallArguments_pnGThread
                          __v2468_v_+0x14
              libjvm.so`__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle
                          _pnRJavaCallArguments_pnGThread__v_+0x28
              libjvm.so`__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI
                          _jobject_nLJNICallType_pnK_jmethodID_pnSJNI
                          _ArgumentPusher_pnGThread__v_+0x180
              libjvm.so`jni_CallStaticVoidMethod+0x10f
              java`main+0x53d
              8051b9a

この出力例から、Java スタックフレームのシンボリックなスタックフレーム情報を確認できます。いくつかの関数が静的関数で、アプリケーションシンボルテーブルにエントリがないので、この出力にはまだ 16 進フレームが含まれています。これらのフレームを翻訳することはできません。

Java フレーム以外のフレームの ustack() シンボル翻訳は、スタックデータの記録に行われます。このため、シンボル翻訳の実行前に対応するユーザープロセスが終了してしまい、スタックフレーム翻訳ができない可能性があります。シンボル翻訳の前にユーザープロセスが終了した場合、次の例のように、dtrace からの警告メッセージと、16 進スタックフレームのリストが表示されます。


  dtrace: failed to grab process 100941: no such process
                c7b834d4
                c7bca85d
                c7bca1a4
                c7bd4374
                c7bc2628
                8047efc

この問題に対処するためのテクニックについては、第 33 章ユーザープロセスのトレースを参照してください。

最後に、事後 DTrace デバッガコマンドはフレーム翻訳を実行できないので、ring バッファーポリシーで ustack() を使用すると、常に生の ustack() データが返されることになります。

以下の D プログラムの例では、ustack() が使用されていますが、strsize の指定は省略されています。

syscall::brk:entry
/execname == $$1/
{
	@[ustack(40)] = count();
}

デフォルトの Solaris インストールで、Netscape の Web ブラウザ .netscape.bin を指定してこのプログラムを実行するには、次のコマンドを使用します。


# dtrace -s brk.d .netscape.bin
dtrace: description 'syscall::brk:entry' matched 1 probe
^C
                libc.so.1`_brk_unlocked+0xc
                88143f6
                88146cd
                .netscape.bin`unlocked_malloc+0x3e
                .netscape.bin`unlocked_calloc+0x22
                .netscape.bin`calloc+0x26
                .netscape.bin`_IMGCB_NewPixmap+0x149
                .netscape.bin`il_size+0x2f7
                .netscape.bin`il_jpeg_write+0xde
                8440c19
                .netscape.bin`il_first_write+0x16b
                8394670
                83928e5
                .netscape.bin`NET_ProcessHTTP+0xa6
                .netscape.bin`NET_ProcessNet+0x49a
                827b323
                libXt.so.4`XtAppProcessEvent+0x38f
                .netscape.bin`fe_EventLoop+0x190
                .netscape.bin`main+0x1875
                   1

                libc.so.1`_brk_unlocked+0xc
                libc.so.1`sbrk+0x29
                88143df
                88146cd
                .netscape.bin`unlocked_malloc+0x3e
                .netscape.bin`unlocked_calloc+0x22
                .netscape.bin`calloc+0x26
                .netscape.bin`_IMGCB_NewPixmap+0x149
                .netscape.bin`il_size+0x2f7
                .netscape.bin`il_jpeg_write+0xde
                8440c19
                .netscape.bin`il_first_write+0x16b
                8394670
                83928e5
                .netscape.bin`NET_ProcessHTTP+0xa6
                .netscape.bin`NET_ProcessNet+0x49a
                827b323
                libXt.so.4`XtAppProcessEvent+0x38f
                .netscape.bin`fe_EventLoop+0x190
                .netscape.bin`main+0x1875
                  1
    ...

jstack()

void jstack(int nframes, int strsize)
void jstack(int nframes)
void jstack(void)

jstack()ustack() の別名で、jstackframes オプションでスタックフレーム数を指定し、jstackstrsize オプションで文字列空間サイズを指定します。デフォルトでは、jstacksize はゼロ以外の値になります。つまり、jstack() を使用すると、Java フレーム翻訳が適切に行なわれるようにスタックがトレースされます。

破壊アクション

一部の DTrace アクションは、明確な方法でシステムの状態に変更を加えることから、破壊アクションと呼ばれます。破壊アクションは、明示的に有効にしないと使用できません。dtrace(1M)-w オプションを指定すると、破壊アクションを有効にできます。dtrace(1M) で、破壊アクションを明示的でない方法で有効にしようとすると、dtrace は失敗し、次のようなメッセージが表示されます。


dtrace: failed to enable 'syscall': destructive actions not allowed

プロセス破壊アクション

一部の破壊アクションは、特定のプロセスだけに影響を及ぼします。こうしたアクションを実行できるのは、dtrace_proc 権限か dtrace_user 権限を持つユーザーだけです。DTrace のセキュリティー権限については、第 35 章セキュリティーを参照してください。

stop()

void stop(void)

stop() アクションは、有効なプローブを起動するプロセスが次にカーネルを出るとき、proc(4) アクションを使用したときのように強制的に停止します。stop() アクションによって停止させられたプロセスを再開するには、prun(1) ユーティリティーを使用します。stop() アクションでは、任意の DTrace プローブポイントで、プロセスを停止できます。このアクションを使用すると、単純なブレークポイントでは捕捉するのが難しい特定の状態のプログラムを捕捉し、そのプロセスに mdb(1) などの従来のデバッガを接続できます。gcore(1) ユーティリティーを使用すれば、停止したプロセスの状態をあとで分析できるようにコアファイルに保存することもできます。

raise()

void raise(int signal)

raise() アクションは、現在実行中のプロセスに、指定されたシグナルを送信します。このアクションは、kill(1) コマンドでプロセスにシグナルを送る処理とよく似ています。raise() アクションでは、プロセスの実行中の特定のポイントでシグナルを送信できます。

copyout()

void copyout(void *buf, uintptr_t addr, size_t nbytes)

copyout() アクションは、指定されたバッファー (buf) から、現在のスレッドに関連付けられたプロセスのアドレス空間内の指定されたアドレス (addr) へ、指定されたバイト数 (nbytes) をコピーします。ユーザー空間アドレスが、現在のアドレス空間内の有効なフォルトインページに対応していない場合、エラーが生成されます。

copyoutstr()

void copyoutstr(string str, uintptr_t addr, size_t maxlen)

copyoutstr() アクションは、指定された文字列 (str) を、現在のスレッドに関連付けられたプロセスのアドレス空間内の指定されたアドレス (addr) へコピーします。ユーザー空間アドレスが、現在のアドレス空間内の有効なフォルトインページに対応していない場合、エラーが生成されます。文字列長は、strsize オプションで設定された値までになります。詳細については、第 16 章オプションとチューニング可能パラメータを参照してください。

system()

void system(string program, ...) 

system() アクションは、program で指定されたプログラムを、シェルで実行したときのようにして実行します。program 文字列には、printf() または printa() の書式変換を含めることができます。その書式変換に合った引数を指定する必要があります。有効な書式変換の詳細については、第 12 章出力書式を参照してください。

以下は、毎秒 1 回 date(1) コマンドを実行する例です。


# dtrace -wqn tick-1sec'{system("date")}'
 Tue Jul 20 11:56:26 CDT 2004
 Tue Jul 20 11:56:27 CDT 2004
 Tue Jul 20 11:56:28 CDT 2004
 Tue Jul 20 11:56:29 CDT 2004
 Tue Jul 20 11:56:30 CDT 2004

以下は、先ほどの例よりもアクションの使い方が複雑になった例です。この例では、program 文字列内で、printf() 変換と、パイプのような従来のフィルタリングツールが使用されています。

#pragma D option destructive
#pragma D option quiet

proc:::signal-send
/args[2] == SIGINT/
{
	printf("SIGINT sent to %s by ", args[1]->pr_fname);
	system("getent passwd %d | cut -d: -f5", uid);
}

このスクリプトを実行すると、次のような出力が得られます。


# ./whosend.d
SIGINT sent to MozillaFirebird- by Bryan Cantrill
SIGINT sent to run-mozilla.sh by Bryan Cantrill
^C
SIGINT sent to dtrace by Bryan Cantrill

指定されたコマンドは、起動するプローブのコンテキストでは実行されません。実行されるのは、system() アクションの詳細情報が格納されているバッファーがユーザーレベルで処理されたときです。この処理がどのようにしていつ行われるのかは、バッファリングポリシーによって異なります。詳細については、第 11 章バッファーとバッファリングを参照してください。デフォルトのバッファリングポリシーでは、バッファー処理間隔は switchrate オプションで指定されます。次の例のように、明示的に switchrate をチューニングして、デフォルト値の 1 秒よりも長くした場合、system() 内で遅延が発生します。

#pragma D option quiet
#pragma D option destructive
#pragma D option switchrate=5sec

tick-1sec
/n++ < 5/
{
	printf("walltime  : %Y\n", walltimestamp);
	printf("date      : ");
	system("date");
	printf("\n");
}

tick-1sec
/n == 5/
{
	exit(0);
}

このスクリプトを実行すると、次のような出力が得られます。


# dtrace -s ./time.d
 walltime  : 2004 Jul 20 13:26:30
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:31
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:32
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:33
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:34
date      : Tue Jul 20 13:26:35 CDT 2004

walltime の値は異なっているが、date の値は一致している点に注目してください。これは、date(1) コマンドが、system() アクションが記録されたときではなく、バッファーが処理されたときにだけ実行されるからです。

カーネル破壊アクション

システム全体に影響を及ぼす破壊アクションもあります。これらのアクションは、システム上のすべてのプロセスに影響を及ぼすだけでなく、影響を受けるシステムのネットワークサービスによっては、その他のシステムにも暗黙的または明示的に影響を及ぼします。したがって、これらのアクションを使用するときは、細心の注意を払ってください。

breakpoint()

void breakpoint(void)

breakpoint() アクションは、カーネルブレークポイントを設定して、システムを停止し、カーネルデバッガに制御を移します。カーネルデバッガは、アクションを引き起こした DTrace プローブを表す文字列を発行します。たとえば、次の内容を実行するとします。


# dtrace -w -n clock:entry'{breakpoint()}'
dtrace: allowing destructive actions
dtrace: description 'clock:entry' matched 1 probe

SPARC 版 Solaris 環境では、コンソールに次のメッセージが表示されます。


dtrace: breakpoint action at probe fbt:genunix:clock:entry (ecb 30002765700)
Type  'go' to resume
ok

x86 版 Solaris 環境では、コンソールに次のメッセージが表示されます。


dtrace: breakpoint action at probe fbt:genunix:clock:entry (ecb d2b97060)
stopped at      int20+0xb:      ret
kmdb[0]:

プローブ記述に続くアドレスは、DTrace 内の有効化制御ブロック (ECB) のアドレスです。このアドレスを使って、ブレークポイントアクションを引き起こすプローブ有効化の詳細情報を得ることができます。

breakpoint() アクションの操作を間違うと、このアクションが必要以上に何度も呼び出される可能性があります。この問題が発生すると、ブレークポイントアクションを引き起こす DTrace コンシューマを終了することもできなくなってしまう場合があります。そのような場合は、カーネル整数変数 dtrace_destructive_disallow を 1 に設定してください。この設定では、マシン上のすべての破壊アクションが拒否されます。この設定は、この特別な状況以外では適用しないでください。

dtrace_destructive_disallow の設定方法は、厳密には、使用しているカーネルデバッガによって異なります。SPARC システム上で OpenBoot PROM を使用している場合は、w! を使用します。


ok 1 dtrace_destructive_disallow w!
ok

w? を使って、変数が設定されたことを確認します。


ok dtrace_destructive_disallow w?
1
ok

go と入力して、処理を続行します。


ok go

x86 または SPARC システム上で kmdb(1) を使用している場合は、書式設定 dcmd であるスラッシュ (/) を入力し、続けて 4 バイトの書き込み修飾子 (W) を指定します。


kmdb[0]: dtrace_destructive_disallow/W 1
dtrace_destructive_disallow:    0x0             =       0x1
kmdb[0]:

:c と入力して、処理を続行します。


kadb[0]: :c

続行後、破壊アクションを再度有効にするには、mdb(1) を使って dtrace_destructive_disallow を明示的にゼロに戻す必要があります。


# echo "dtrace_destructive_disallow/W 0" | mdb -kw
dtrace_destructive_disallow:    0x1             =       0x0
#

panic()

void panic(void)

panic() アクションを引き起こすと、カーネルパニックが起こります。このアクションは、必要に応じて強制的にシステムのクラッシュダンプを実行するときに使用します。このアクションをリングバッファリングや事後分析と組み合わせることで、問題を把握できます。詳細は、第 11 章バッファーとバッファリング第 37 章事後トレースを参照してください。パニックアクションを使用すると、パニックの原因となったプローブを示すパニックメッセージが表示されます。次に例を示します。


  panic[cpu0]/thread=30001830b80: dtrace: panic action at probe
  syscall::mmap:entry (ecb 300000acfc8)

  000002a10050b840 dtrace:dtrace_probe+518 (fffe, 0, 1830f88, 1830f88,
    30002fb8040, 300000acfc8)
    %l0-3: 0000000000000000 00000300030e4d80 0000030003418000 00000300018c0800
    %l4-7: 000002a10050b980 0000000000000500 0000000000000000 0000000000000502
  000002a10050ba30 genunix:dtrace_systrace_syscall32+44 (0, 2000, 5,
    80000002, 3, 1898400)
    %l0-3: 00000300030de730 0000000002200008 00000000000000e0 000000000184d928
    %l4-7: 00000300030de000 0000000000000730 0000000000000073 0000000000000010

  syncing file systems... 2 done
  dumping to /dev/dsk/c0t0d0s1, offset 214827008, content: kernel
  100% done: 11837 pages dumped, compression ratio 4.66, dump
  succeeded
  rebooting...

syslogd(1M) も、リブート時にメッセージを発行します。


  Jun 10 16:56:31 machine1 savecore: [ID 570001 auth.error] reboot after panic:
  dtrace: panic action at probe syscall::mmap:entry (ecb 300000acfc8)

クラッシュダンプのメッセージバッファーには、プローブのほか、panic() アクションの原因となった ECB も含まれます。

chill()

void chill(int nanoseconds)

chill() アクションには、DTrace を指定された期間 (nanoseconds ナノ秒間) 待つ働きがあります。chill() は、主にタイミング関連の問題の調査に使用します。たとえば、このアクションを使って、競合しているウィンドウを開いたり、定期イベントを同期させたり、定期イベントの同期を解除したりできます。DTrace プローブコンテキストでは、割り込みは無効になります。このため、chill() を使用すると、割り込み遅延、スケジュール遅延、およびディスパッチ遅延が発生します。このように、chill() は、システム全体に思いがけない影響を及ぼすことがあるので、乱用は避けてください。システムアクティビティは定期的な割り込み処理に依存します。このため、DTrace は、CPU 上で 1 秒間隔のうち 500 ミリ秒間を超えて chill() アクションを実行することを拒否します。最大 chill() 間隔を超過すると、DTrace は、次の例のように、不正な操作を表すエラーを返します。


# dtrace -w -n syscall::open:entry'{chill(500000001)}'
dtrace: allowing destructive actions
dtrace: description 'syscall::open:entry' matched 1 probe
dtrace: 57 errors
CPU     ID                    FUNCTION:NAME
dtrace: error on enabled probe ID 1 (ID 14: syscall::open:entry): \
  illegal operation in action #1

chill() への複数の呼び出しによる合計、または単一のプローブを使用する複数の DTrace コンシューマによる合計が、この時間制限を超えた場合も同様です。たとえば、次のコマンドでも、同じエラーが生成されます。


# dtrace -w -n syscall::open:entry'{chill(250000000); chill(250000001);}'

特殊なアクション

この節では、データ記録アクションでも破壊アクションでもないアクションについて説明します。

投機アクション

投機トレース関連のアクションには、speculate()commit()、および discard() があります。これらのアクションについては、第 13 章投機トレースを参照してください。

exit()

void exit(int status)

exit() アクションでは、トレースをただちに終了できます。また、DTrace コンシューマに、トレースを終了し、最終処理を行い、指定された状態 status で exit(3C) を呼び出すように指示できます。exit() は状態をユーザーレベルに戻すという点でデータ記録アクションに分類されます。ただし、その他のデータ記録アクションとは異なり、exit() を投機的にトレースすることはできません。exit() は、バッファーポリシーとは関係なく、DTrace コンシューマを終了させます。exit() はデータ記録アクションなので、落とされることがあります。

exit() を呼び出すと、ほかの CPU 上ですでに実行中の DTrace アクションだけが最後まで実行されます。どの CPU 上でも、新しいアクションは実行されません。この規則の唯一の例外は、END プローブの処理です。END プローブは、DTrace コンシューマが exit() アクションを処理し、トレースを終了するように指示したあとで呼び出されます。

サブルーチン

サブルーチンは、通常、DTrace の内部状態にのみ影響を及ぼすという点で、アクションとは異なっています。このため、「破壊サブルーチン」というようなものは存在しません。また、サブルーチンがデータをバッファー内でトレースすることもありません。サブルーチンには、セクション 9F と 3C のインタフェースに類似したものが多数含まれています。対応するサブルーチンの詳細については、Intro(9F)Intro(3) のマニュアルページを参照してください。

alloca()

void *alloca(size_t size)

alloca() は、スクラッチ空間から size バイトを割り当て、割り当てられたメモリーへのポインタを返します。必ず 8 バイトのバイト列を持つポインタが返されます。スクラッチ空間は、節の開始から完了までの間しか有効ではありません。alloca() で割り当てられたメモリーは、節の完了時に割り当て解除されます。使用できるスクラッチ空間が不足している場合、メモリーの割り当ては行われず、エラーが生成されます。

basename()

string basename(char *str)

basename() は、basename(1) に相当します。このサブルーチンは、指定された文字列のコピーから成る文字列を生成します。ただし、/ で終わる接頭辞は付きません。返される文字列には、スクラッチメモリーからメモリーが割り当てられます。したがって、節が完了すると、この文字列は無効になります。使用できるスクラッチ空間が不足している場合、basename は実行されず、エラーが生成されます。

bcopy()

void bcopy(void *src, void *dest, size_t size)

bcopy() は、src がポイントするメモリーから dest がポイントするメモリーへ、size バイトをコピーします。すべてのコピー元のメモリーはスクラッチメモリーの外部、すべてのコピー先のメモリーはスクラッチメモリーの内部に存在していなければなりません。この条件が満たされない場合、コピーは行われず、エラーが生成されます。

cleanpath()

string cleanpath(char *str)

cleanpath() は、str で指定されたパスのコピーから成る文字列を生成します。ただし、特定の重複要素は排除されます。特に、パス内の「/./」要素は削除され、「/../」要素は縮められます。パス内の「/../」は、シンボリックリンクを考慮せずに縮められます。このため、cleanpath() を使用すると、有効なパスが縮められ、短い無効なパスが返されることがあります。

たとえば、str が「/foo/../bar」で、/foo/net/foo/export のシンボリックリンクになっている場合、bar があるのは / ではなく /net/foo であるのに、cleanpath() は文字列「/bar」を返します。この問題が発生するのは、cleanpath() が起動プローブのコンテキストで呼び出されるからです。起動プローブのコンテキストでは、完全なシンボリックリンク解決は行われず、任意の名前は使用できません。返される文字列には、スクラッチメモリーからメモリーが割り当てられます。したがって、節が完了すると、この文字列は無効になります。使用できるスクラッチ空間が不足している場合、cleanpath は実行されず、エラーが生成されます。

copyin()

void *copyin(uintptr_t addr, size_t size)

copyin() は、指定されたユーザーアドレス addr から DTrace スクラッチバッファーに、指定されたサイズ size バイトをコピーし、このバッファーのアドレスを返します。ユーザーアドレスは、現在のスレッドに関連付けられたプロセスの空間に含まれるアドレスであると見なされます。最終的なバッファーポインタは、必ず 8 バイトのバイト列を持つことになります。指定されたアドレスは、現在のプロセス内のフォルトインページに対応している必要があります。アドレスがフォルトインページに対応していない場合や、使用できるスクラッチ空間が不足している場合は、NULL が返され、エラーが生成されます。copyin のエラーが発生する可能性を減らすテクニックについては、第 33 章ユーザープロセスのトレースを参照してください。

copyinstr()

string copyinstr(uintptr_t addr)

copyinstr() は、指定されたユーザーアドレス addr から DTrace スクラッチバッファーに、NULL で終了する C 文字列をコピーし、このバッファーのアドレスを返します。ユーザーアドレスは、現在のスレッドに関連付けられたプロセスの空間に含まれるアドレスであると見なされます。文字列長は、strsize オプションで設定された値以下に制限されます。詳細については、第 16 章オプションとチューニング可能パラメータを参照してください。copyin の場合と同じく、指定されたアドレスは、現在のプロセス内のフォルトインページに対応している必要があります。アドレスがフォルトインページに対応していない場合や、使用できるスクラッチ空間が不足している場合は、NULL が返され、エラーが生成されます。第 33 章ユーザープロセスのトレース エラーが発生する可能性を減らすテクニックについては、Chapter 33, User Process Tracingを参照してください。

copyinto()

void copyinto(uintptr_t addr, size_t size, void *dest)

copyinto() は、指定されたユーザーアドレス addr から、dest で指定された DTrace スクラッチバッファーに、指定されたサイズ size バイトをコピーします。ユーザーアドレスは、現在のスレッドに関連付けられたプロセスの空間に含まれるアドレスであると見なされます。指定されたアドレスは、現在のプロセス内のフォルトインページに対応している必要があります。アドレスがフォルトインページに対応していない場合や、コピー先のメモリーの一部がスクラッチ空間内にない場合、コピーは行われず、エラーが生成されます。第 33 章ユーザープロセスのトレース エラーが発生する可能性を減らすテクニックについては、Chapter 33, User Process Tracingを参照してください。

dirname()

string dirname(char *str)

dirname() は、dirname(1) に相当します。このサブルーチンは、str で指定されたパス名の最後のレベルを除くすべてのレベルから成る文字列を生成します。返される文字列には、スクラッチメモリーからメモリーが割り当てられます。したがって、節が完了すると、この文字列は無効になります。使用できるスクラッチ空間が不足している場合、dirname は実行されず、エラーが生成されます。

msgdsize()

size_t msgdsize(mblk_t *mp)

msgdsize() は、mp がポイントしているデータメッセージ内のバイト数を返します。詳細については、msgdsize(9F) のマニュアルページを参照してください。msgdsize() がカウントするのは、M_DATA 型のデータブロックだけです。

msgsize()

size_t msgsize(mblk_t *mp)

msgsize() は、mp がポイントしているメッセージ内のバイト数を返します。msgdsize() がデータのバイト数だけを返すのに対し、msgsize() はメッセージ内の全バイト数を返します。

mutex_owned()

int mutex_owned(kmutex_t *mutex)

mutex_owned() は、mutex_owned(9F) の実装です。mutex_owned() は、呼び出しスレッドが指定されたカーネル相互排他ロックを所有している場合は、ゼロ以外の値を返します。指定された適応型相互排他ロックの所有者が存在しない場合は、ゼロを返します。

mutex_owner()

kthread_t *mutex_owner(kmutex_t *mutex)

mutex_owner() は、指定された適応型カーネル相互排他ロックの現在の所有者のスレッドポインタを返します。指定された適応型相互排他ロックの所有者が存在しない場合や、指定された相互排他ロックがスピン相互排他ロックである場合、mutex_owner()NULL を返します。mutex_owned(9F) のマニュアルページを参照してください。

mutex_type_adaptive()

int mutex_type_adaptive(kmutex_t *mutex)

mutex_type_adaptive() は、指定されたカーネル相互排他ロックが MUTEX_ADAPTIVE 型 (適応型) の場合はゼロ以外、MUTEX_ADAPTIVE 型でない場合はゼロを返します。次のいずれかの条件が満たされている場合、相互排他ロックは適応型になります。

相互排他ロックの詳細については、mutex_init(9F) のマニュアルページを参照してください。Solaris カーネル内のほとんどの 相互排他ロックは適応型です。

progenyof()

int progenyof(pid_t pid)

progenyof() は、呼び出しプロセス (一致したプローブを引き起こしているスレッドに関連付けられたプロセス) が指定されたプロセス ID pid の子孫にあたる場合、ゼロ以外の値を返します。

rand()

int rand(void)

rand() は、擬似乱数整数を返します。返される値は、弱い擬似乱数であり、暗号化アプリケーションでは使用できません。

rw_iswriter()

int rw_iswriter(krwlock_t *rwlock)

rw_iswriter() は、指定された読み取り/書き込みロック rwlock が書き込み側に保持されている場合、または書き込み側から要求されている場合、ゼロ以外の値を返します。ロックが読み取り側だけで保持されていて、書き込み側がブロックされていない場合、またはロックがまったく保持されていない場合、rw_iswriter() はゼロを返します。rw_init(9F) のマニュアルページを参照してください。

rw_write_held()

int rw_write_held(krwlock_t *rwlock)

w_write_held() は、指定された読み取り/書き込みロック rwlock が書き込み側によって保持されている場合、ゼロ以外の値を返します。ロックが読み取り側だけで保持されている場合やまったく保持されていない場合、rw_write_held () はゼロを返します。rw_init(9F) のマニュアルページを参照してください。

speculation()

int speculation(void)

speculation() は、speculate() で使用する投機トレースバッファーを予約し、このバッファーの識別子を返します。詳細については、第 13 章投機トレースを参照してください。

strjoin()

string strjoin(char *str1, char *str2)

strjoin() は、str1str2 の連結から成る文字列を生成します。返される文字列には、スクラッチメモリーからメモリーが割り当てられます。したがって、節が完了すると、この文字列は無効になります。使用できるスクラッチ空間が不足している場合、strjoin は実行されず、エラーが生成されます。

strlen()

size_t strlen(string str)

strlen() は、指定された文字列 str の長さ (バイト単位) を返します。このとき、終端の NULL バイトは除外されます。