Solaris 动态跟踪指南

数据记录操作

数据记录操作包含核心 DTrace 操作。缺省情况下,这些操作中的每一个都会将数据记录到主体缓冲区,但每个操作也可以用于将数据记录到推理缓冲区。有关主体缓冲区的更多详细信息,请参见第 11 章。有关推理缓冲区的更多详细信息,请参见第 13 章。本节中的说明仅是指定向缓冲区,表示如果操作执行 speculate(),数据将记录到主体缓冲区或推理缓冲区。

trace()

void trace(expression)

最基本的操作是 trace() 操作,此操作将 D 表达式用作其参数并跟踪结果到定向缓冲区。以下语句为 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,将常量用作其第二个参数 nbytestracemem()addr 指定的地址复制 nbytes 指定长度的内存到定向缓冲区中。

printf()

void printf(string format, ...) 

trace() 一样,printf() 操作将跟踪 D 表达式。但是,printf() 允许详细设置 printf(3C) 样式的格式。与 printf(3C) 一样,参数包含 format 字符串,后跟任意数量的参数。缺省情况下,将跟踪参数到定向缓冲区。然后,dtrace(1M) 将根据指定的格式字符串对参数进行格式化,以便输出。例如,可将 trace() 中的前两个 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 章。如果未提供 formatprinta() 将仅跟踪指示使用缺省格式处理和显示指定聚合的 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() 将分配指定的字符串空间量,并使用该空间直接从内核中执行地址到符号的转换。这种直接的用户符号转换当前仅在版本 1.5 和更高版本的 Java 虚拟机中可用。Java 地址到符号转换使用 Java 类和方法名注释包含 Java 帧的用户栈。如果无法转换这类帧,帧将仅显示为十六进制地址。

以下示例跟踪不包含字符串空间的栈,因此将不会进行 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 栈帧仅显示为十六进制地址。以下示例说明了对包含非零字符串空间的 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 栈帧的符号栈帧信息。该输出中仍然有一些十六进制帧,这是因为一些函数为静态函数,并在应用程序符号表中没有条目。无法对这些帧进行转换。

非 Java 帧的 ustack() 符号转换发生在记录栈数据之后。因此在执行符号转换之前,对应的用户进程可能会退出,这将使栈帧转换无法进行。如果用户进程在执行符号转换之前退出,则 dtrace 将发出一条警告消息,后跟十六进制栈帧,如下例所示:


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

第 33 章中介绍了减小此问题影响的方法。

最后,由于事后 DTrace 调试器命令无法执行帧转换,因此将 ustack()ring 缓冲区策略同时使用总是会生成原始 ustack() 数据。

以下 D 程序显示了未指定 strsizeustack() 示例:

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

要在 Netscape Web 浏览器 .netscape.bin(缺省 Solaris 安装)中运行此示例,请使用以下命令:


# 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 帧转换。