Solaris 动态跟踪指南

输出格式化

系统调用跟踪是一种观察大多数用户进程行为的有效方法。如果您以前以管理员或开发者身份使用过 Solaris truss(1) 实用程序,则可能已了解到,该实用程序是一种用于解决问题的有用工具。如果以前从未使用过 truss,则可在某个 shell 中键入以下命令立即进行尝试:


$ truss date

您将会看到 date(1) 执行的所有系统调用的格式化跟踪,结尾紧跟一行输出。以下示例对前面的 rw.d 程序进行了改进,将其输出格式设置为与 truss(1) 更相似,从而便于您理解输出。键入以下程序,并将其保存在名为 trussrw.d 的文件中:


示例 1–2 trussrw.d:使用 truss(1) 输出格式跟踪系统调用

syscall::read:entry,
syscall::write:entry
/pid == $1/
{
	printf("%s(%d, 0x%x, %4d)", probefunc, arg0, arg1, arg2);
}

syscall::read:return,
syscall::write:return
/pid == $1/
{
	printf("\t\t = %d\n", arg1);
}

在此示例中,将每个谓词中的常数 12345 替换为标签 $1。使用此标签可将所关注的进程指定为脚本参数:编译脚本时,将用第一个参数的值替换 $1。要执行 trussrw.d,请使用 dtrace 选项 -q-s,后跟 shell 的进程 ID 作为最后一个参数。-q 选项指出,dtrace 应为静默模式,并取消前面示例中显示的标题行和 CPU 及 ID 列。因此,您将仅会看到显式跟踪的数据的输出。键入以下命令(将 12345 替换为 shell 进程的进程 ID),然后在指定的 shell 中多次按回车键。


# dtrace -q -s trussrw.d 12345
	                 = 1
write(2, 0x8089e48,    1)                = 1
read(63, 0x8090a38, 1024)                = 0
read(63, 0x8090a38, 1024)                = 0
write(2, 0x8089e48,   52)                = 52
read(0, 0x8089878,    1)                 = 1
write(2, 0x8089e48,    1)                = 1
read(63, 0x8090a38, 1024)                = 0
read(63, 0x8090a38, 1024)                = 0
write(2, 0x8089e48,   52)                = 52
read(0, 0x8089878,    1)                 = 1
write(2, 0x8089e48,    1)                = 1
read(63, 0x8090a38, 1024)                = 0
read(63, 0x8090a38, 1024)                = 0
write(2, 0x8089e48,   52)                = 52
read(0, 0x8089878,    1)^C
#

现在我们来更详细地检查 D 程序及其输出。首先,与前面的程序类似的一条子句检测 shell 的每次 read(2)write(2) 调用。但对于此示例,将使用一个新函数 printf() 来跟踪数据并以特定格式显示数据:

syscall::read:entry,
syscall::write:entry
/pid == $1/
{
	printf("%s(%d, 0x%x, %4d)", probefunc, arg0, arg1, arg2);
}

printf() 函数将跟踪数据的功能(就像先前使用的 trace() 函数一样)与以描述的特定格式输出数据和其他文本的功能组合在一起。printf() 函数会指示 DTrace 跟踪与第一个参数之后的每个参数关联的数据,然后使用第一个 printf() 参数说明的规则(称为格式字符串)来格式化结果。

格式字符串是包含任意数量的格式转换的规则字符串,其中的每个转换都以 % 字符开头,该字符说明如何设置对应的参数格式。格式字符串中的第一个转换对应于第二个 printf() 参数,第二个转换对应于第三个参数,依此类推。转换之间的所有文本将逐字列显。% 转换字符之后的字符说明要用于对应参数的格式。以下是 trussrw.d 中使用的三种格式转换的含义:

%d

将相应的值显示为十进制整数 

%s

将相应的值显示为字符串 

%x

将相应的值显示为十六进制整数 

DTrace printf() 的工作方式与 C printf(3C) 库例程或 shell printf(1) 实用程序类似。如果您以前对 printf() 完全不了解,请参阅第 12 章中对格式和选项的详细说明。即使您通过其他语言已经熟悉了 printf(),也应该认真阅读本章。在 D 中,将 printf() 作为内置功能提供,并且专为 DTrace 设计了一些新的格式转换供您使用。

为了帮助您编写正确的程序,D 编译器将会针对参数列表验证每个 printf() 格式字符串。请尝试将以上子句中的 probefunc 更改为整数 123。如果运行修改的程序,将会显示一条错误消息,通知您字符串格式转换 %s 不适用于整数参数:


# dtrace -q -s trussrw.d
dtrace: failed to compile script trussrw.d: line 4: printf( )
	   argument #2 is incompatible with conversion #1 prototype:
	        conversion: %s
	         prototype: char [] or string (or use stringof)
	          argument: int
#

要显示读或写系统调用及其参数的名称,请使用 printf() 语句:

printf("%s(%d, 0x%x, %4d)", probefunc, arg0, arg1, arg2);

以跟踪当前探测器函数和系统调用的前三个整数参数(可从 DTrace 变量 arg0arg1arg2 中获得)的名称。有关探测器参数的更多信息,请参见第 3 章read(2)write(2) 的第一个参数为文件描述符,以十进制格式显示。第二个参数为缓冲区地址,格式为十六进制值。最后一个参数为缓冲区大小,格式为十进制值。格式说明符 %4d 用于第三个参数,指示应使用 %d 格式转换显示该值,最小字段宽度为 4 个字符。如果该整数的宽度小于 4 个字符,printf() 将插入额外的空白以对齐输出。

要显示系统调用的结果并完成每一行输出,使用以下子句:

syscall::read:return,
syscall::write:return
/pid == $1/
{
	printf("\t\t = %d\n", arg1);
}

请注意,除 entry 外,syscall 提供器还为每个系统调用发布了一个名为return 的探测器。在 syscall return 探测器中,DTrace 变量 arg1 保存了系统调用的返回值。返回值的格式为十进制整数。格式字符串中以反斜杠开头的字符序列分别扩展为制表符 (\t) 和换行符 (\n)。这些转义序列可帮助您显示或记录难以键入的字符。D 支持与 C、C++ 和 Java 编程语言相同的一组转义序列。完整的转义序列列表请参阅第 2 章