Solaris 动态跟踪指南

pid 提供器

使用 pid 提供器,可以跟踪进程中的任何指令。与大多数其他提供器不同,pid 探测器是基于 D 程序中的探测器说明根据需要创建的。因此,除非您亲自启用,否则 dtrace -l 输出中不会列出任何 pid 探测器。

用户函数边界跟踪

对于 pid 提供器,最简单的操作模式是作为与 fbt 提供器类似的用户空间。以下示例程序将跟踪通过单个函数产生的所有函数进入和返回。$1 宏变量(命令行中的第一个操作数)是要跟踪的进程的进程 ID。$2 宏变量(命令行中的第二个操作数)是要从中跟踪所有函数调用的函数的名称。


示例 33–1 userfunc.d:跟踪用户函数的进入和返回

pid$1::$2:entry
{
	self->trace = 1;
}

pid$1::$2:return
/self->trace/
{
	self->trace = 0;
}

pid$1:::entry,
pid$1:::return
/self->trace/
{
}

键入以上示例脚本,并将其保存在名为 userfunc.d 的文件中,然后通过 chmod 将其转换为可执行文件。此脚本生成的输出与以下示例类似:


# ./userfunc.d 15032 execute
dtrace: script './userfunc.d' matched 11594 probes
  0  -> execute                               
  0    -> execute                             
  0      -> Dfix                              
  0      <- Dfix                              
  0      -> s_strsave                         
  0        -> malloc                          
  0        <- malloc                          
  0      <- s_strsave                         
  0      -> set                               
  0        -> malloc                          
  0        <- malloc                          
  0      <- set                               
  0      -> set1                              
  0        -> tglob                           
  0        <- tglob                           
  0      <- set1                              
  0      -> setq                              
  0        -> s_strcmp                        
  0        <- s_strcmp                        
...

pid 提供器只能用于已在运行的进程。您可以使用 $target 宏变量(请参见第 15 章)以及 dtrace -c-p 选项来创建和抓取所关注的进程,并使用 DTrace 对其进行检测。例如,以下 D 脚本可用于确定通过特定主题进程对 libc 执行的函数调用的分布:

pid$target:libc.so::entry
{
	@[probefunc] = count();
}

要确定由 date(1) 命令执行的此类调用的分布,请将脚本保存在名为 libc.d 的文件中并执行以下命令:


# dtrace -s libc.d -c date
dtrace: script 'libc.d' matched 2476 probes
Fri Jul 30 14:08:54 PDT 2004
dtrace: pid 109196 has exited

  pthread_rwlock_unlock                                             1
  _fflush_u                                                         1
  rwlock_lock                                                       1
  rw_write_held                                                     1
  strftime                                                          1
  _close                                                            1
  _read                                                             1
  __open                                                            1
  _open                                                             1
  strstr                                                            1
  load_zoneinfo                                                     1

...
  _ti_bind_guard                                                   47
  _ti_bind_clear                                                   94

跟踪任意指令

您可以使用 pid 提供器跟踪用户函数中的任何指令。pid 提供器将根据需要,为函数中的每条指令创建探测器。每个探测器的名称即函数中与其对应的,以十六进制整数表示的指令的偏移量。例如,要启用与 bar.so 模块的 foo 函数中的偏移量为 0x1c 的指令(位于 PID 为 123 的进程中)关联的探测器,可使用以下命令:


# dtrace -n pid123:bar.so:foo:1c

要启用函数 foo 中的所有探测器(包括每条指令的探测器),可使用以下命令:


# dtrace -n pid123:bar.so:foo:

此命令展示了一种功能非常强大的、用于调试和分析用户应用程序的方法。由于很难重现不经常发生的错误,因此对其进行调试可能并不容易。通常,您可以在出现故障后确定问题,但这对于重构代码路径已太迟。以下示例演示了如何将 pid 提供器与推理跟踪(请参见第 13 章)进行结合,以便通过跟踪函数中的每条指令来解决该问题。


示例 33–2 errorpath.d:跟踪用户函数调用错误路径

pid$1::$2:entry
{
	self->spec = speculation();
	speculate(self->spec);
	printf("%x %x %x %x %x", arg0, arg1, arg2, arg3, arg4);
}

pid$1::$2:
/self->spec/
{
	speculate(self->spec);
}

pid$1::$2:return
/self->spec && arg1 == 0/
{
	discard(self->spec);
	self->spec = 0;
}

pid$1::$2:return
/self->spec && arg1 != 0/
{
	commit(self->spec);
	self->spec = 0;
}

执行 errorpath.d 将会生成与以下示例类似的输出:


# ./errorpath.d 100461 _chdir
dtrace: script './errorpath.d' matched 19 probes
CPU     ID                    FUNCTION:NAME
  0  25253                     _chdir:entry 81e08 6d140 ffbfcb20 656c73 0
  0  25253                     _chdir:entry
  0  25269                         _chdir:0
  0  25270                         _chdir:4
  0  25271                         _chdir:8
  0  25272                         _chdir:c
  0  25273                        _chdir:10
  0  25274                        _chdir:14
  0  25275                        _chdir:18
  0  25276                        _chdir:1c
  0  25277                        _chdir:20
  0  25278                        _chdir:24
  0  25279                        _chdir:28
  0  25280                        _chdir:2c
  0  25268                    _chdir:return