Linux跟踪工具在内核的发展就像是肥皂剧一样,剧情婉转,主题反复并且相互竞争。本文试图总结当前Linux中众多跟踪工具现状和历史原因。
Attention: 编者对于以下内容并非亲历者(主要来自LWN和maillist),如有歪曲请拍砖。以下部分虽属八卦,但是,学习一些模块的设计方案和历史进程是我学习方法论的一部分,从这些纠结的方案和博弈中可以看到很多权衡和利弊帮助更好的获得其中的思想,以下文字是连带产物。学习使用这些性能工具推荐以下资源。
Why Trace the kernel?
很多人在接触这类工具时可能会感到困惑,内核代码早就已经优化的非常好了并且基本不会有什么Bug,那么为什么还需要插入这么多探针去查看内核的状态? 其实这种工具在生产环境中被很多人使用去跟踪问题,比如系统此时无法理解的行为(CPU居高,IO忙),这时候系统管理员需要利用这种工具在不干扰正常系统运作的情况下去Linux这个庞大的内核中查找原因。
Dtrace Envy和SystemTap
首先,我们先来介绍Dtrace Envy。Linux社区中过去有一个名词叫Dtrace Envy,从字母上可以发现Linux社区对Dtrace的深恶痛绝,不仅是Dtrace在以前的Linux没有相应如此强大的实现,而且Dtrace经常被Sun公司用来作营销手段的噱头(同样的还有ZFS)与Linux进行对比。
那时还是07年,大家在提到Solaris系统时,Dtrace总是在人们关注的前几个特征。而SystemTap当时还在几个大公司Red Hat、Intel、IBM和日立内部使用,Dtrace的拥护者会嘲讽SystemTap只是个山寨货,而SystemTap的粉丝认为SystemTap是个独立发展的项目,并且以超越Dtrace为目标。
这两个工具都是在内核中插入探针,当一个线程执行碰到探针时,预先定义的脚本(D语言脚本或者SystemTap脚本)就会运行。
Dtrace使用了大量的预先定义的探针插入到Solaris内核中,这种方式称为静态探针,这些探针都在Dtrace的文档中做了详述。并且一些简单的通配符(*)可以用来实现多个探针的选择。Sun宣称无用的探针并不会增加太多的负载。
SystemTap没有采用Dtrace的方式,估计是SystemTap没有人有这么大精力去实现大量的静态探针放到内核中(Linus会发飙的)。SystemTap采用的动态探针(kprobes实现),它是在内核运行时被动态插入。SystemTap采用脚本语言来定义一些动作并且打开一些启用的探针来(SystemTap的探针库)插入到内核中,没有用到的探针不会插入。
从上述的差异可以看到Dtrace的静态探针是有优势的,首先Dtrace大量的探针是被预定义并且可以被很好的文档化(这对于系统管理员是非常好的,它们通常不熟悉内核源码),而SystemTap是可以在内核的任意位置,这就需要开发者需要熟悉内核的源码并且把探针放到合适的位置。这就可能带来安全性的问题,一些不恰当的探针放置是会带来系统崩溃的(这种情况非常少),即使非常少的位置是敏感的,但也足以使使用者感到不便,需要时时担心可能的不恰当触发。当然,SystemTap也有少量的预定义探针。
接着我们比较两者的语言,Dtrace采用的D语言同样继承了安全的原则,它运行在虚拟机中并且带有安全检测,并且拒绝控制语句(只有三元操作符),但在探针上可以附着表达式(相对于一个”if”)。而SystemTap同样采取了大刀阔斧的姿态,不仅有完备的控制语句,SystemTap脚本(STP)直接翻译成C,并且在SystemTap脚本中可以嵌入C。可以想象,在这里面如果出现了死循环就基本意味着crash了。
Kernel summit 2008的故事
08年,虽然SystemTap有了长足的发展,但是内核社区和SystemTap项目团队仍然没有一个很好的沟通和协商。SystemTap仍然没有进入普通用户的工具范畴,在kernel summit中,Matthew Wilcox表示Dtrace已经对Linux造成了足够的威胁Ksummit-2008-discuss DTrace,Linux通常被认为是新技术的前沿,但是当Sun把Solaris开放后,一系列的新特征已经领先Linux,比如ZFS和Dtrace。
即使当时SystemTap和Btrfs被认为是Linux对抗的利器,但是,在社区还是有人对SystemTap表示失望,James Bottomley(iSCSI贡献者)在邮件中提到Ksummit-2008-discuss DTrace,就好像在Winodws用户期望在Linux有Outlook一样,SystemTap只不过是Solaris用户到Linux期望有一个一样的工具,同时,James Bottomley表示SystemTap非常难用并且造成系统的崩溃。另一个Ted Ts’o同样赞成SystemTap非常难以使用,但是他期望在SystemTap增加更多的”tapsets”来帮助对内核不熟悉的人更好的使用SystemTap。
SystemTap的开发者Frank Ch. Eigler表示SystemTap早已考虑性能、难以使用的问题并且正在解决中,他认为内核社区缺少与SystemTap团队的沟通,并且提出了一系列的建议希望内核团队参与到SystemTap与kernel的整合中来Frank Ch. Eigler fche at redhat.com。
但是,Ted Ts’o同样回应称SystemTap只会将”tapsets”的开发任务交给内核,而不去思考如何让SystemTap变得易用并且更容易与内核整合,他同时指责SystemTap光想着让企业发行版使用,而不去努力改善与内核的问题Theodore Tso tytso at mit.edu。
Frank Ch. Eigler表示大部分系统管理员是不会去使用SystemTap去思考内核的性能追踪,SystemTap通常是被整合到PostgreSQL这种基础软件中来使用,因此,SystemTap更加注重对此类问题的解决。
同时,社区中有人期望把Dtrace移植到Linux中,但是这也困难重重,先不说Dtrace移植到Linux主线的技术问题,光光是CDDL协议就是不容于Linux的。好吧,接下来就是各种水友开始讨论CDDL与GPL的协议问题(一群程序员开始Google。。。)
最后还是Linus强调了最重要的一点,这个同时也是针对utrace的:SystemTap的实现是不被内核开发者喜欢的,因为它创造、利用内核模块的实现是不同于内核的,对于SystemTap的细节如锁实现和使用等等都是off kernel的。它希望SystemTap在实现设计上能离内核越来越近。
ptrace
在介绍utrace之前首先需要介绍ptrace。它是Linux内核跟踪工具的化石,过去(07年以前)很长一段时间里,大部分人利用它来调试应用或者内核模块,但是它的问题也很多,低效(需要内核/用户态切换)、实现难以维护(架构设计混乱)、用户难以使用(接口复杂)、一个进程只能被一个进程跟踪(使用信号方法跟踪进程,信号使得目标只能wait一个进程)、会改变跟踪进程的状态(进程父子关系)、打断系统调用并且一直有大量的安全问题存在。
ptrace的维护者在utrace出现之后就已经在呼吁让ptrace基于utrace实现,因为ptrace代码是混乱的,散乱在各个架构中,维护者早已经对横跨多个架构的代码厌倦了。
我们需要注意到gdb和strace都是基于ptrace,这个原因也导致了ustrace迟迟不能进入内核主线。
utrace
utrace在07年的时候进入内核的视线(Introducing utrace),它的目的是完全取代ptrace在用户态跟踪的作用,utrace只是实现了一个在内核中的engine,它提供了一个框架供更多的人基于它来实现对用户程序的控制任务(Trace,Debug,user-mode-linux)。
utrace的体系架构可以参考玩转 utrace。
utrace自07年在LWN中提出,然后迅速沉寂消失在视线之外,在09又重新希望进入内核主线中,当时Red Hat和Fedora kernels其实已经支持utrace很多年了。utrace进入内核最重要的问题是在内核中没有真正的用户(以utrace为框架的实现),utrace如果只是一个engine就没有进入内核的必要。
Frank Eigler(utrace开发者)提供了ftrace(下面会提到)基于utrace的实现,但是内核认为这个实现只是个插件罢了,而另外一个utrace真正的用户ptrace-over-utrace是被看做utrace进入内核的关键,但是有很多人以不成熟来反对,虽然ptrace有大量的ugly code并且难以维护但是毕竟已经存在这么多年了,取代ptrace是一个高风险的项目并且谁来做这个清理工作,如果不做清理工作两者同时进入内核会带来两个ptrace实现带来更多的问题,还有就是utrace并不是被所有架构支持,如果ptrace使用utrace的实现而删除自己的实现在其他架构中(ARM和MIPS)就无法使用ptrace调用。并且ptrace-over-utrace如果想替代原来的ptrace实现,必须需要有一些killer features(通过utrace的框架还是能实现很多的),Nesterov和McGrath这两个ptrace的维护者站出来表示自己更愿意维护ptrace-over-utrace的方案,并且乐意去推出新的代码。
问题之二是utrace会阻碍SystemTap进入内核(当时有很多人期望SystemTap尽快进入内核),因为utrace没有一个系统调用而是内核里的API(内核接口会不稳定),SystemTap利用了utrace的许多ABI来实现用户态的跟踪,如果utrace进入内核,SystemTap在主线外可能就导致在用户态跟踪上一团糟(主线与非主线最重要的问题就是版本兼容性)并且需要寻找其他的方式。(PS:现在 systemtap 可以直接使用新的主流内核中的 kprobes 和基于 inode 的uprobes 机制,不再依赖非主流的 utrace 补丁。同时它也可以直接使用主流内核中的 perf 静态探针 by agentzh)
在09年的Linux 2.6.30进入主线失败后,utrace就准备好了长期在主线外的命运,Red Hat不得不承担起utrace的维护任务并且长期在自己的内核中保持。目前来说,utrace和SystemTap像是一对难兄难弟。
LTTng
LTTng是另外一个动态跟踪工具,它整合了内核和用户态跟踪,对于大量的跟踪事件流有非常高的性能,并且有一系列的分析和抓取工具。
对于目前的Linux内核来说,LTTng只不过是众多Tracing工具的一个,它制造了太多重复的工作比如Rring Buffer(内核中已经有两个Ring buffer被perf和ftrace使用),并且自己的系统调用接口(内核已经有此类的接口),增加LTTng意味着更混乱的Tracing ABI在内核中。
相比较而言,LTTng进入内核比SystemTap和utrace更困难。
ftrace
相比前面的SystemTap、utrace和LTTng,ftrace就幸运多了。ftrace是用来帮助开发者和系统设计者寻找系统负载的原因,它可以用来调试和分析内核中的负载和性能原因。
ftrace之所以能顺利进入内核中,主要还是因为它充分利用了内核中既有的设计和其他组件,它利用了debugfs、kprobes,uprobes等等,并且因为其简单的静态探针和安全性,ftrace的内核之路顺利而平坦。
Tracepoint
tracepoint是在内核中预定义的一系列跟踪点并且提供了hook让使用者可以调用函数来进行分析,tracepoint在有probe attach时会在每次执行到tracepoint调用相应的函数。
tracepoint在08年的时候被置入内核中,ftrace也同样使用tracepoint作为tracer使用。
kprobe
kprobes在Linux 2.6.9主线上实现,它主要在内核模块中被使用,在模块init 函数中安装probe然后在exit时释放。
更多的kprobe可以在Documents/kprobes.txt找到。
对于kprobe来说,更多的时候是被内核模块开发者使用。因为kprobe比较麻烦且相对于SystemTap这类工具来说,kprobe只有较少的使用空间。
uprobe
实现了动态的介入用户程序的运行并且不打断的收集信息,它的原理与kprobe类似,都是在探针函数的第一个字节用breakpoint指令替代,当CPU运行到breakpoint指令时,uprobes得到通知并找到相应uprobe来执行对于的函数。
小结
目前Linux内核上众多跟踪、性能工具的实现其实非常混乱,像latencyTop、powertop、usbmon、blktrace都是好用的工具,但是每个工具都需要自己的hooks在内核,这对于内核而言是十分蛋疼的事情。而在Solaris中,powertop这类是在Dtrace之上实现的,不需要改变内核。
从Linux性能工具的发展可以看到,upstream kernel和商用kernel,其他系统(Solaris)的博弈和权衡是十分激烈的。对于Linux社区而言,Linus对于代码的把控是十分必要的,在11年kernel summit之后,开发者已经普遍赞成收敛系统调用的增加,这对于LTTng,utrace的进入内核希望更小了。