一些 DTrace 操作具有破坏性,因为它们会以某种明确定义的方式更改系统的状态。如果未显式启用,则无法使用这些破坏性操作。使用 dtrace(1M) 时,可以使用 -w 选项启用破坏性操作。如果尝试在 dtrace(1M) 中启用破坏性操作,但不显式启用这些操作,则 dtrace 将失败,并显示一条与以下示例类似的消息:
dtrace: failed to enable 'syscall': destructive actions not allowed |
一些破坏性操作仅对特定进程具有破坏性。具有 dtrace_proc 或 dtrace_user 权限的用户可以使用这些操作。有关 DTrace 安全权限的详细信息,请参见第 35 章。
void stop(void)
当触发已启用的探测器的进程接下来退出内核时,stop() 操作将强制停止该进程,就像由 proc(4) 操作停止一样。prun(1) 实用程序可用于恢复由 stop() 操作停止的进程。stop() 操作可用于在任何 DTrace 探测器位置停止进程。此操作可用于在特定状态下,捕获难以使用简单断点获取的程序,然后将一个传统调试器(如 mdb(1))附加至该进程。您也可以使用 gcore(1) 实用程序将已停止进程的状态保存在一个核心转储文件中,供以后进行分析。
void raise(int signal)
raise() 操作将指定的信号发送至当前正在运行的进程。此操作与使用 kill(1) 命令向进程发送信号类似。raise() 操作可用于在进程执行过程中的一个准确时间点发送信号。
void copyout(void *buf, uintptr_t addr, size_t nbytes)
copyout() 操作在与当前线程关联的进程的地址空间中,从 buf 指定的缓冲区中复制 nbytes 到 addr 指定的地址。如果用户空间地址与当前地址空间中有效的错误页面不对应,则将生成错误。
void copyoutstr(string str, uintptr_t addr, size_t maxlen)
copyoutstr() 操作在与当前线程关联的进程的地址空间中,将 str 指定的字符串复制到 addr 指定的地址。如果用户空间地址与当前地址空间中有效的错误页面不对应,则将生成错误。字符串长度限于 strsize 选项指定的值。有关详细信息,请参见第 16 章。
void system(string program, ...)
system() 操作将导致执行 program 指定的程序,就像将该程序作为输入提供给 shell 一样。program 字符串可以包含任何 printf()/printa() 格式转换。指定的参数必须与格式转换匹配。有关有效的格式转换的详细信息,请参阅第 12 章。
以下示例每秒运行 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 显式地调整到高于其缺省值(一秒),则将会看到 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 值相同。此结果反映了一个事实,即仅当处理缓冲区而不是记录 system() 操作时,才会执行 date(1) 命令。
一些破坏性操作对整个系统都具有破坏性。由于这些操作将会影响系统中的每个进程,以及任何其他隐式或显式依赖于受影响系统的网络服务的系统,因此使用这些操作时必须非常小心。
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 中启用控制块 (enabling control block, 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),请将 4 字节的写入修饰符 (W) 与 / 格式设置 dcmd 一起使用:
kmdb[0]: dtrace_destructive_disallow/W 1 dtrace_destructive_disallow: 0x0 = 0x1 kmdb[0]: |
继续使用 :c:
kadb[0]: :c |
要在继续之后重新启用破坏性操作,需要使用 mdb(1) 将 dtrace_destructive_disallow 显式重置为 0:
# echo "dtrace_destructive_disallow/W 0" | mdb -kw dtrace_destructive_disallow: 0x1 = 0x0 # |
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。
void chill(int nanoseconds)
chill() 操作将导致 DTrace 显示指定的纳秒数。chill() 主要用于查找可能与计时相关的问题。例如,您可以使用此操作打开竞争情况窗口,或者使定期事件相互之间同步或异步。由于处于 DTrace 探测器上下文中时禁止中断,因此使用任何 chill() 都将引起中断延迟、调度延迟和分发延迟。因此,chill() 可能会导致意外的系统影响,不应毫无限制地使用。由于系统活动依赖于定期的中断处理,如果在任何给定 CPU 的每一秒间隔内,chill() 操作需要的时间超过 500 毫秒,DTrace 将拒绝执行。如果超过最大 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);}' |