Solaris 动态跟踪指南

第 4 章 D 程序结构

D 程序由一组子句组成,这些子句说明要启用的探测器和要绑定到这些探测器的谓词和操作。D 程序也可包含变量声明(如第 3 章中所介绍)和新类型定义(如第 8 章中所介绍)。本章正式介绍 D 程序的总体结构和用于构造与多个探测器匹配的探测器说明的功能。此外,还将讨论如何结合 D 程序使用 C 预处理程序 cpp

探测器子句和声明

如我们已提供过的示例所示,D 程序源文件由一条或多条探测器子句组成,这些子句说明 DTrace 启用的检测过程。每条探测器子句具有以下一般形式:

probe descriptions
/ predicate /
{
	action statements
}

谓词和操作语句列表可以省略。探测器子句外部的任何指令都称为声明。只可以在探测器子句外部使用声明。封闭的 { } 内部不允许使用声明,声明不能分布在如上所示的探测器子句的元素之间。可以使用空格分隔任何 D 程序元素,并缩进操作语句。

可以使用声明来声明 D 变量和外部 C 符号(如第 3 章中所讨论),或者定义要在 D 中使用的新类型(如第 8 章中所介绍)。称为 pragma 的特殊 D 编译器指令也可以出现在 D 程序中的任何位置(包括探测器子句的外部)。在以 # 字符开头的行上指定 D pragma。例如,D pragma 可用于设置运行时 DTrace 选项;有关详细信息,请参见第 16 章

探测器说明

每条 D 程序子句都以一个或多个探测器说明的列表开头,每个说明都采用以下常见形式:

提供器:模块:函数:名称

如果省略探测器说明中的一个或多个字段,D 编译器将从右向左解释指定的字段。例如,探测器说明 foo:bar 将匹配包括函数 foo 和名称 bar 的探测器,而不考虑探测器的提供器和模块字段的具体值。所以,探测器说明更准确地说是一种模式,可用于根据名称匹配一个或多个探测器。

应编写指定所有四个字段分界符的 D 探测器说明,以便可以在左侧指定所需的提供器。如果未指定提供器,在多个提供器以相同的名称发布探测器时可能会得到意外的结果。类似地,将来版本的 DTrace 可能包括新的提供器,这些提供器的探测器可能会无意中与所指定的探测器说明部分匹配。可以指定提供器,但保留所有模块、函数和名称字段为空,以与提供器的任何探测器匹配。例如,说明 syscall::: 可用于匹配 DTrace syscall 提供器发布的每个探测器。

探测器说明还支持与 shell globbing 模式匹配语法(如 sh(1) 中所述)类似的模式匹配语法。在将探测器与说明匹配之前,DTrace 将在每个说明字段中扫描字符 *?[。如果这些字符中的某一个出现在探测器说明字段中,并且前面没有 \,则会将该字段视为一种模式。说明模式必须与给定探测器的整个相应字段匹配。要成功匹配和启用探测器,完整的探测器说明必须与每个字段匹配。非模式的探测器说明字段必须与该探测器的相应字段完全匹配。空的说明字段将与任何探测器匹配。

下表中是探测器名称模式中可识别的特殊字符:

表 4–1 与字符匹配的探测器名称模式

符号 

说明 

*

匹配任何字符串,包括空字符串。 

?

匹配任何单个字符。 

[ ... ]

匹配任何一个封闭字符。使用 - 分隔的一对字符将匹配这对字符之间的任何字符,包括这对字符。如果 [ 之后的第一个字符是 !,则将与集合中未包括的任何字符匹配。

\

将下一个字符解释为其本身,不具有任何特殊含义。 

可以在探测器说明的任何或所有四个字段中使用模式匹配字符。通过在命令行中将模式与 dtrace -l 一起使用,也可以使用模式列出匹配的探测器。例如,命令 dtrace -l -f kmem_* 列出函数中名称以前缀 kmem_ 开头的所有 DTrace 探测器。

如果要为多个探测器说明或说明模式指定相同的谓词和操作,可以将说明放置在逗号分隔的列表中。例如,以下 D 程序将跟踪每次触发探测器时的时间标记,这些探测器与包含单词 "lwp" 或 "sock" 的系统调用入口关联:

syscall::*lwp*:entry, syscall::*sock*:entry
{
	trace(timestamp);
}

探测器说明也可以使用其整数探测器 ID 指定探测器。例如,子句:

12345
{
	trace(timestamp);
}

可用于启用由 dtrace -l -i 12345 报告的探测器 ID 12345。应始终使用人工可读的探测器说明编写 D 程序。装入和卸载 DTrace 提供器内核模块时或者在重新引导后,不能保证整数探测器 ID 仍然不变。

谓词

谓词是置于斜杠 / / 中的表达式,在触发探测器时进行计算,以确定是否应执行关联的操作。谓词是用于在 D 程序中生成更复杂的控制流的主要条件结构。您可以完全省略任何探测器的探测器子句的谓词部分,在此情况下,触发探测器时将始终执行操作。

谓词表达式可以使用前面说明的任何 D 运算符,可以引用任何 D 数据对象(如变量和常数)。谓词表达式的计算结果必须为整数或指针类型的值,以便可以将其视为 true 或 false。与所有 D 表达式一样,零值将解释为 false,任何非零值将解释为 true。

操作

探测器操作由分号 (;) 分隔的语句列表说明,括在花括号 { } 中。如果仅想说明在特定 CPU 中触发特定探测器而不跟踪任何数据,或不执行任何其他操作,可以指定一个内部不包含任何语句的空花括号集合。

C 预处理程序的用法

用于定义 Solaris 系统接口的 C 编程语言包括在 C 程序编译中执行一组初始步骤的预处理程序。C 预处理程序通常用于定义宏替换(在此情况下,C 程序中的某个标记将替换为另外一组预定义的标记),或者用于包括系统头文件的副本。可以通过指定 dtrace -C 选项将 C 预处理程序与 D 程序配合使用。此选项使 dtrace 首先执行程序源文件中的 cpp(1) 预处理程序,然后将结果传递到 D 编译器。C 编程语言中详细介绍了 C 预处理程序。

D 编译器自动装入与操作系统实现关联的 C 类型说明集,但也可以使用预处理程序来包括其他类型的定义,例如您自己的 C 程序中使用的类型。还可以使用预处理程序执行其他任务(如创建将扩展为 D 代码块和其他程序元素的宏)。如果将预处理程序与 D 程序一起使用,则只能使用包含有效 D 声明的文件。一般的 C 头文件仅包括类型和符号的外部声明,这些声明可以由 D 编译器正确解释。D 编译器无法解析包括其他程序元素(如 C 函数源代码)的 C 头文件,将生成相应的错误消息。