跳过导航链接 | |
退出打印视图 | |
DTrace 用户指南 Oracle Solaris 10 8/11 Information Library (简体中文) |
本节针对推理跟踪讨论了 DTrace 工具。推理跟踪是试探性地跟踪数据并决定是将数据提交到某个跟踪缓冲区还是放弃数据的一种能力。用于过滤掉不需关注的事件的主要机制是谓词机制。如果在触发探测器时,您知道该探测事件是否为需要关注的事件,则谓词很有用。如果直到探测器触发之后,您才能知道是否需要关注某个给定的探测器事件,则谓词不太适合处理这类情况。
如果系统调用偶尔失败并返回某个常见的错误代码,则您可能需要检查导致错误情况的代码路径。您可以使用推理跟踪工具在一个或多个探测位置试探性地跟踪数据,然后决定是否将这些数据提交到另一个探测位置的主体缓冲区。最终得到的跟踪数据仅包含需要关注的输出,并且不需要进行后处理。
表 4-1 DTrace 推理函数
|
speculation() 函数分配推理缓冲区并返回一个推理标识符。在对 speculate() 函数的后续调用中将使用该推理标识符。值为零的推理标识符始终无效,但是可以传递给 speculate()、commit() 或 discard()。如果对 speculation() 的调用失败,则 dtrace 命令将生成类似于下例的一条消息。
dtrace: 2 failed speculations (no speculative buffer space available)
要使用某个推理,请在任何数据记录操作之前使用一个子句将已从 speculation() 返回的标识符传递给 speculate () 函数。将对包含 speculate() 的子句中的所有数据记录操作进行推理跟踪。如果在 D 探测子句中对 speculate() 的调用位于数据记录操作之后,D 编译器将会生成编译时错误。子句中可以包含推理跟踪请求或非推理跟踪请求,但不能同时包含两者。
不能对聚集操作、破坏性操作和 exit 操作进行推理跟踪。如果试图在包含 speculate() 的子句中采用这其中的某种操作,将导致编译时错误。speculate() 函数不能跟在前一个 speculate() 函数之后。每个子句中只允许使用一个推理。只包含一个 speculate() 函数的子句将对缺省操作进行推理跟踪,缺省操作被定义为仅跟踪已经启用的探测器 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() 之后的某个时间复制。
在每个基于 CPU 的推理缓冲区完全复制到其对应的基于 CPU 的主体缓冲区之前,正被提交的推理缓冲区不可供后续的 speculation() 调用使用。试图将 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