Solaris 動的トレースガイド

破壊アクション

一部の 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);}'