Solaris 动态跟踪指南

数组

D 允许您定义整数和其他类型的变量,以便表示字符串和称为结构联合的复合类型。如果您熟悉 C 编程,那么应该很高兴,因为您可以在 D 中使用 C 中使用的任何类型。如果您不是 C 语言专家,也不要担心:第 2 章中描述了所有不同类型的数据类型。D 还支持一种称为关联数组的特殊类型的变量。关联数组与常规数组类似,因为它也将一组键与一组值相关联,但是在关联数组中,键不限于固定范围的整数。

D 关联数组可以由任何类型的一个或多个值的列表建立索引。各个键值一起构成元组,用于对数组建立索引以及访问或修改与该键对应的值。与给定关联数组一起使用的每个元组必须符合相同类型的签名;也就是说,每个元组键的长度必须相同,且具有同样顺序的相同键类型。整个数组中与给定关联数组的每个元素关联的值也是同一个固定类型。例如,以下 D 语句使用元组签名 [ string, int ] 定义一个 int 值类型的新关联数组 a,并将整数值 456 存储在该数组中:

a["hello", 123] = 456;

在定义数组之后,可以像访问任何其他 D 变量一样访问数组的元素。例如,以下 D 语句通过将值从 456 递增到 457 来修改 a 中先前存储的数组元素:

a["hello", 123]++;

尚未赋值的任何数组元素的值均设置为零。现在,我们学习在 D 程序中使用关联数组。键入以下程序,并将其保存在名为 rwtime.d 的文件中:


示例 1–3 rwtime.d:时间 read(2) 和 write(2) 调用

syscall::read:entry,
syscall::write:entry
/pid == $1/
{
	ts[probefunc] = timestamp;
}

syscall::read:return,
syscall::write:return
/pid == $1 && ts[probefunc] != 0/
{
	printf("%d nsecs", timestamp - ts[probefunc]);
}

trussrw.d 相同,执行 rwtime.d 时,指定 shell 进程的 ID。如果键入一些 shell 命令,您将会看到每个系统调用过程中已用的时间。在其他 shell 中键入以下命令,然后多次按回车键:


# dtrace -s rwtime.d `pgrep -n ksh`
dtrace: script 'rwtime.d' matched 4 probes
CPU     ID                    FUNCTION:NAME
  0     33                      read:return 22644 nsecs
  0     33                      read:return 3382 nsecs
  0     35                     write:return 25952 nsecs
  0     33                      read:return 916875239 nsecs
  0     35                     write:return 27320 nsecs
  0     33                      read:return 9022 nsecs
  0     33                      read:return 3776 nsecs
  0     35                     write:return 17164 nsecs
...
^C
#

要跟踪每个系统调用的已用时间,必须对进入 read(2)write(2) 以及从它们返回的状态进行检测,并对每个点进行时间采样。然后,在从给定的系统调用返回时,必须计算第一个时间标记和第二个时间标记之间的差异。对于每个系统调用可以使用独立的变量,但这可能会使程序在扩展到其他系统调用时变得很乱。相反,使用由探测器函数名称建立索引的关联数组则简单得多。以下是第一个探测器子句:

syscall::read:entry,
syscall::write:entry
/pid == $1/
{
	ts[probefunc] = timestamp;
}

此子句定义一个名为 ts 的数组,并将 DTrace 变量 timestamp 的值赋给相应的成员。此变量返回一个始终递增的纳秒计数器的值,与 Solaris 库例程 gethrtime(3C) 类似。保存进入时间标记后,对应的返回探测器将再次对 timestamp 采样,并报告当前时间与所保存值之间的差异:

syscall::read:return,
syscall::write:return
/pid == $1 && ts[probefunc] != 0/
{
	printf("%d nsecs", timestamp - ts[probefunc]);
}

返回探测器中的谓词要求,DTrace 正在跟踪相应的进程,且对应的 entry 探测器已触发,并为 ts[probefunc] 指定了一个非零值。此技巧在首次启动 DTrace 时可以消除无效的输出。如果在执行 dtrace 时,shell 已在 read(2) 系统调用中等待输入,将会触发 read:return 探测器,但前面的第一个 read(2)read:entry 不会触发,且 ts[probefunc] 的计算结果将为零,因为尚未对其赋值。