Solaris 动态跟踪指南

第 38 章 性能注意事项

由于 DTrace 会导致系统产生额外工作,因此启用 DTrace 始终会在某些方面影响系统性能。通常,这种影响可以忽略,但如果启用了许多会产生大量开销的探测器时,对系统的影响可能就会变得很严重。本章将介绍降低 DTrace 对性能影响的一些技术。

限制已启用的探测器

使用动态检测过程技术,DTrace 可对内核和任意用户进程提供非并行的跟踪范围。虽然此范围允许对系统行为进行彻底性的监测,但也可能会产生极大的探测影响。如果启用了大量的探测器,势必将对系统产生非常大的影响。因此,应根据解决问题时的实际需要来启用探测器。例如,在有更简明的启用项可以回答问题时,不应启用所有 FBT 探测器。例如,您可能只需要集中于所关注的特定模块或特定函数便可解决问题。

使用 pid 提供器时,应特别小心。因为 pid 提供器可以检测每条指令,而您可能会在应用程序中启用数百万个探测器,从而使目标进程速度变得非常缓慢。

必须为要回答的问题启用大量探测器的情况下,也可以使用 DTrace。启用大量的探测器可能会大大降低系统速度,但不会导致计算机出现致命故障。因此,应放心启用所需数量的探测器。

使用聚合

第 9 章中所讨论,DTrace 聚合允许以一种可伸缩的方式聚合数据。关联数组也可能会提供与聚合类似的功能。但是,作为全局通用变量的特性决定了它们无法提供可线性伸缩的聚合。因此,如有可能,应优先使用聚合而不是关联数组。建议您不要使用以下示例:

syscall:::entry
{
	totals[execname]++;
}

syscall::rexit:entry
{
	printf("%40s %d\n", execname, totals[execname]);
	totals[execname] = 0;
}

首选使用以下示例:

syscall:::entry
{
	@totals[execname] = count();
}

END
{
	printa("%40s %@d\n", @totals);
}

使用可高速缓存的谓词

DTrace 谓词用于过滤实验中不需要的数据,具体方法是仅跟踪指定的条件为 true 时的数据。启用许多探测器时,通常可使用标识特定线程或所关注线程(如 /self->traceme//pid == 12345/)形式的谓词。尽管对于多数探测器中的多数线程而言,许多谓词的计算结果为 false 值,但对成千上万个探测器进行这样的计算时,计算本身的开销也可能变得很大。为了降低此开销,如果谓词仅包含线程局部变量(例如,/self->traceme/)或不变变量(例如,/pid == 12345/),DTrace 将对谓词计算进行高速缓存。计算已高速缓存的谓词的开销要远远小于计算未高速缓存的谓词的开销,在谓词包含线程局部变量、字符串比较或其他开销相对较大的操作时,尤其如此。虽然谓词高速缓存对于用户是透明的,但它隐含了一些构造最优谓词的原则,如下表所示:

可高速缓存 

不可高速缓存 

self->mumble

mumble[curthread], mumble[pid, tid]

execname

curpsinfo->pr_fname, curthread->t_procp->p_user.u_comm

pid

curpsinfo->pr_pid, curthread->t_procp->p_pipd->pid_id

tid

curlwpsinfo->pr_lwpid, curthread->t_tid

curthread

curthread->any member, curlwpsinfo->any member, curpsinfo->any member

建议您不要使用以下示例:

syscall::read:entry
{
	follow[pid, tid] = 1;
}

fbt:::
/follow[pid, tid]/
{}

syscall::read:return
/follow[pid, tid]/
{
	follow[pid, tid] = 0;
}

首选使用线程局部变量的以下示例:

syscall::read:entry
{
	self->follow = 1;
}

fbt:::
/self->follow/
{}

syscall::read:return
/self->follow/
{
	self->follow = 0;
}

为进行高速缓存,谓词必须完全由可高速缓存的表达式组成。以下谓词均可进行高速缓存:

/execname == "myprogram"/
/execname == $$1/
/pid == 12345/
/pid == $1/
/self->traceme == 1/

以下示例使用了全局变量,因而无法高速缓存:

/execname == one_to_watch/
/traceme[execname]/
/pid == pid_i_care_about/
/self->traceme == my_global/