Solaris 动态跟踪指南

谓词

D 语言和其他编程语言(如 C、C++ 和 Java 编程语言)之间的一个主要差别是没有控制流结构(如 if 语句和循环)。D 程序子句以单个直线语句列表的形式编写,用于跟踪固定数量的可选数据。D 使用称为谓词的逻辑表达式,提供了根据条件跟踪数据和修改控制流的功能,谓词可用于为程序子句添加前缀。触发探测器时,在执行与相应的子句关联的任何语句之前,将计算谓词表达式。如果谓词计算结果为 true(由任何非零值表示),则执行语句列表。如果谓词计算结果为 false(由零值表示),则不执行任何语句并忽略探测器触发。

为下一个示例键入以下源代码,并将其保存在名为 countdown.d 的文件中:

dtrace:::BEGIN
{
	i = 10;
}

profile:::tick-1sec
/i > 0/
{
	trace(i--);
}

profile:::tick-1sec
/i == 0/
{
	trace("blastoff!");
	exit(0);
}

此 D 程序使用谓词实现一个 10 秒的递减计数计时器。执行时,countdown.d 从 10 开始递减计数,然后显示一条消息并退出:

# dtrace -s countdown.d
dtrace: script 'countdown.d' matched 3 probes
CPU     ID                    FUNCTION:NAME
	0  25499                       :tick-1sec        10
	0  25499                       :tick-1sec         9
	0  25499                       :tick-1sec         8
	0  25499                       :tick-1sec         7
	0  25499                       :tick-1sec         6
	0  25499                       :tick-1sec         5
	0  25499                       :tick-1sec         4
	0  25499                       :tick-1sec         3
	0  25499                       :tick-1sec         2
	0  25499                       :tick-1sec         1
	0  25499                       :tick-1sec   blastoff!
# 

此示例使用 BEGIN 探测器将整数 i 初始化为 10 以开始递减计数。接下来,与前一个示例相同,程序使用 tick-1sec 探测器实现每秒触发一次的计时器。请注意,在 countdown.d 中,tick-1sec 探测器描述用在两条不同的子句中,每条子句使用一个不同的谓词和操作列表。谓词是置于封闭斜杠 / / 之间的逻辑表达式,出现在探测器名称之后、括住子句语句列表的大括号 { } 之前。

第一个谓词测试 i 是否大于零,以指示计时器是否仍在运行:

profile:::tick-1sec
/i > 0/
{
	trace(i--);
}

关系运算符 > 表示大于,如果为 false 则返回整数值零,如果为 true 则返回一。D 中支持所有 C 关系运算符;在第 2 章中可以找到完整的列表。如果 i 不为零,则脚本将跟踪 i,然后使用 -- 运算符以一为减量递减。

如果 i 恰好等于零,则第二个谓词使用 == 运算符返回 true,指示递减计数已完成:

profile:::tick-1sec
/i == 0/
{
	trace("blastoff!");
	exit(0);
}

当递减计数完成时,hello.dcountdown.d 使用称为字符串常量、引在双引号中的一个字符序列来显示最终消息,这一点与第一个示例类似。然后使用 exit() 函数退出 dtrace 并返回到 shell 提示符。

如果返回来查看 countdown.d 的结构,将会看到通过创建两个具有相同的探测器描述但具有不同谓词和操作的子句,有效地创建了以下逻辑流:

i = 10
once per second,
	if i is greater than zero
		trace(i--);
	otherwise if i is equal to zero
		trace("blastoff!");
		exit(0);

如果您希望使用谓词编写复杂的程序,请首先尝试按照此方式将您的算法直观化,然后将条件结构的每个路径转换为独立的子句和谓词。

现在,我们将谓词与一个新的提供器(syscall 提供器)组合在一起,创建第一个真实的 D 跟踪程序。syscall 提供器允许在进入任何 Solaris 系统调用或从该系统调用返回时启用探测器。以下示例使用 DTrace 对 shell 执行的每次 read(2)write(2) 系统调用进行观察。首先打开两个终端窗口,一个用于 DTrace,另外一个包含您将查看的 shell 进程。在第二个窗口中,键入以下命令以获取此 shell 的进程 ID:


# echo $$
12345

现在返回到第一个终端窗口并键入以下 D 程序,然后将其保存在名为 rw.d 的文件中。键入程序时,用响应 echo 命令列显的 shell 的进程 ID 替换 12345

syscall::read:entry,
syscall::write:entry
/pid == 12345/
{

}

请注意,由于该程序仅用于跟踪探测器触发通知而不用于跟踪任何其他数据,因此将 rw.d 的探测器子句的主体保留为空。完成 rw.d 键入后,使用 dtrace 开始编写实验脚本,然后转到第二个 shell 窗口键入一些命令,并在每个命令后按回车键。键入时,您应在第一个窗口中看到 dtrace 报告所出现的探测器触发,与以下示例类似:


# dtrace -s rw.d
dtrace: script 'rw.d' matched 2 probes
CPU     ID                    FUNCTION:NAME
	0     34                      write:entry 
	0     32                       read:entry 
	0     34                      write:entry 
	0     32                       read:entry 
	0     34                      write:entry 
	0     32                       read:entry 
	0     34                      write:entry 
	0     32                       read:entry 
...

您现在会注意到,shell 执行 read(2)write(2) 系统调用从终端窗口中读取字符并回显结果!此示例包括许多到现在为止已描述的概念和一些新概念。首先,要采用相同方式检测 read(2)write(2),脚本应使用包含多个探测器描述的单条探测器子句,并用逗号分隔这些描述,如下所示:

syscall::read:entry,
syscall::write:entry

为了获得较好的可读性,每个探测器描述均独占一行。当然,并非一定按此方式编写,但这样做可使脚本可读性更好。接下来,脚本定义一个谓词,该谓词仅与 shell 进程执行的那些系统调用匹配:

/pid == 12345/

该谓词使用预定义的 DTrace 变量 pid,其计算结果始终为与触发相应探测器的线程关联的进程 ID。DTrace 为各种有用的项目(如进程 ID)提供了许多内置的变量定义。以下是一些 DTrace 变量的列表,您可以使用这些变量来编写第一个 D 程序:

变量名 

数据类型 

含义 

errno

int

系统调用的当前 errno

execname

string

当前进程的可执行文件的名称 

pid

pid_t

当前进程的进程 ID 

tid

id_t

当前线程的线程 ID 

probeprov

string

当前探测器描述的提供器字段 

probemod

string

当前探测器描述的模块字段 

probefunc

string

当前探测器描述的函数字段 

probename

string

当前探测器描述的名称字段 

现在,您已经编写了一个实际的检测程序,可尝试通过更改要检测的进程 ID 和系统调用探测器,对系统上运行的不同进程试验该程序。然后,可以进行一次更简单的更改,将 rw.d 转变为一个非常简单的系统调用跟踪工具(如 truss(1))版本。空的探测器描述字段充当通配符,可与任何探测器匹配,因此将程序更改为以下新源代码可跟踪 shell 执行的任何系统调用:

syscall:::entry
/pid == 12345/
{

}

尝试在 shell 中键入一些命令(如 cdlsdate),然后查看 DTrace 程序报告的内容。