动态追踪技术中篇,关于 DTrace、SystemTap 和 火焰图的那点事。
DTrace 与 SystemTap
说到动态追踪就不能不提到 DTrace(1) 。DTrace 算是现代动态追踪技术的鼻祖了,它于 21 世纪初诞生于 Solaris 操作系统,是由原来的 Sun Microsystems 公司的工程师编写的。可能很多同学都听说过 Solaris 系统和 Sun 公司的大名。
最初产生的时候,我记得有这样一个故事,当时 Solaris 操作系统的几个工程师花了几天几夜去排查一个看似非常诡异的线上问题。开始他们以为是很高级的问题,就特别卖力,结果折腾了几天,最后发现其实是一个非常愚蠢的、某个不起眼的地方的配置问题。自从那件事情之后,这些工程师就痛定思痛,创造了 DTrace 这样一个非常高级的调试工具,来帮助他们在未来的工作当中避免把过多精力花费在愚蠢问题上面。毕竟大部分所谓的“诡异问题”其实都是低级问题,属于那种“调不出来很郁闷,调出来了更郁闷”的类型。
应该说 DTrace 是一个非常通用的调试平台,它提供了一种很像 C 语言的脚本语言,叫做 D。基于 DTrace 的调试工具都是使用这种语言编写的。D 语言支持特殊的语法用以指定“探针”,这个“探针”通常有一个位置描述的信息。你可以把它定位在某个内核函数的入口或出口,抑或是某个用户态进程的 函数入口或出口,甚至是任意一条程序语句或机器指令上面。编写 D 语言的调试程序是需要对系统有一定的了解和知识的。这些调试程序是我们重拾对复杂系统的洞察力的利器。Sun 公司有一位工程师叫做 Brendan Gregg,他是最初的 DTrace 的用户,甚至早于 DTrace 被开源出来。Brendan 编写了很多可以复用的基于 DTrace 的调试工具,一齐放在一个叫做 DTrace Toolkit(2) 的开源项目中。Dtrace 是最早的动态追踪框架,也是最有名的一个。
DTrace 的优势是它采取了跟操作系统内核紧密集成的一种方式。D 语言的实现其实是一个虚拟机(VM),有点像 Java 虚拟机(JVM)。它的一个好处在于 D 语言的运行时是常驻内核的,而且非常小巧,所以每个调试工具的启动时间和退出时间都很短。但是我觉得 DTrace 也是有明显缺点的。其中一个让我很难受的缺点是 D 语言缺乏循环结构,这导致许多针对目标进程中的复杂数据结构的分析工具很难编写。虽然 DTrace 官方声称缺少循环的原因是为了避免过热的循环,但显然 DTrace 是可以在 VM 级别上面有效限制每一个循环的执行次数的。另外一个较大的缺点是,DTrace 对于用户态代码的追踪支持比较弱,没有自动的加载用户态调试符号的功能,需要自己在 D 语言里面声明用到的用户态 C 语言结构体之类的类型。
DTrace 的影响是非常大的,很多工程师把它移植到其他的操作系统。比方说苹果的 Mac OS X 操作系统上就有 DTrace 的移植。其实近些年发布的每一台苹果笔记本或者台式机上面,都有现成的 dtrace 命令行工具可以调用,大家可以去在苹果机器的命令行终端上尝试一下。这是苹果系统上面的一个 DTrace 的移植。FreeBSD 操作系统也有这样一个 DTrace 的移植。只不过它并不是默认启用的。你需要通过命令去加载 FreeBSD 的 DTrace 内核模块。Oracle 也有在它自己的 Oracle Linux 操作系统发行版当中开始针对 Linux 内核进行 DTrace 移植。不过 Oracle 的移植工作好像一直没有多少起色,毕竟 Linux 内核并不是 Oracle 控制的,而 DTrace 是需要和操作系统内核紧密集成的。出于类似的原因,民间一些勇敢的工程师尝试的 DTrace 的 Linux 移植也一直距离生产级别的要求很远。
相比 Solaris 上面原生的 DTrace,这些 DTrace 移植都或多或少的缺乏某些高级特性,所以从能力上来说,还不及最本初的 DTrace。
DTrace 对 Linux 操作系统的另一个影响反映在 SystemTap(3) 这个开源项目。这是由 Red Hat 公司的工程师创建的较为独立的动态追踪框架。SystemTap 提供了自己的一种小语言(4),和 D 语言并不相同。显然,Red Hat 自己服务于非常多的企业