译者序
在Linux系统上拿来追踪、调试的工具有好多,有内核态的、用户态的、网络、IO等等不同层次的工具。本文翻译自Linuxtracingsystems&howtheyfittogether-JuliaEvans,这是在学习Systemtap原理时找到的资料,文章中就简略讲了Linux的追踪系统,以及一点点来龙去脉,实际上工作中用到的大部份工具或多或少都是基于文章当中提及的某一种机制实现的。
我对于Linux追踪系统仍然很疑惑,有好几年了。strace、ltrace、kprobes、tracepoints、uprobes、ftrace、perf、eBPF,它们如何串联在一起的,又有哪些意义呢?
上周,我去了”PapersWeLove"1,后来我&Kamal到加拿大理工学院(LTTng项目开始的地方)与Suchakra一起出去玩,总算我想我理解了所有的这种部件是怎样组合在一起的了,或多或少吧。不过,在这篇文章中仍然会有一些错误,假如发觉了请让我晓得!(twitterID:b0rk)
这篇文章我要把strace摒弃(它是我最喜欢的工具),由于开支太高了-在这篇文章中我们只会讨论相对高效/低开支的追踪系统。这篇文章也不是关于样本采集器的(这是另外一个主题!)。只是追踪。
我上周学到的东西帮助我真正的了解了——你可以把linux追踪系统分拆为数据源(追踪数据的来源),搜集数据源的机制(类似ftrace)和追踪后端(以交互式方法搜集/剖析数据的工具)。整体看起来还有些零散和模糊,但起码是一个更容易理解的方式。
以下是我们即将讨论的内容:
搜集"真香"数据的机制后端所以我应当使用哪些样的追踪工具希望这篇文章有用!
这一直有点复杂,但以这样的形式分解它,确实有助于我理解(谢谢BrendanGregg在Twitter上建议这样分解!)
图片版本
这儿有6个草图,汇总了这篇文章的内容:
能追踪哪些?
你似乎想要追踪几种不同类型的事情:
以上所有事情都是可能的,不过追踪环境实际上是十分复杂的。
数据源:kprobes,tracepoints,uprobes,dtraceprobes等等
Okay,让我们说说数据源!这是最有意思的部份——有太多地方可以获取程序的数据。
我要把它们分成“probes”(kprobes/uprobes)和“tracepoints”(tracepoints/ltng-ust)。实际上,我觉得我没有使用正确的术语,这有2个不同的概念能帮助你去理解。
kprobes
接出来是kprobes!那是哪些?下边这段话来自LWN的一篇文章:
KProbes是一种Linux内核调试机制,也可以拿来监控生产系统内部的风波。能够用它来发觉性能困局、记录特定的风波、追踪问题等等。
复述一下,基本上kprobes能让你在运行时动态的改变Linux内核的汇编代码(例如,插入额外的汇编指令),追踪一个指令哪些时侯被调用。我以为kprobes就是追踪linux内核函数调用,实际上它可以追踪内核中的任意指令以及检测寄存器。神奇吧?
BrendanGregg有个krpobe脚本,可以拿来玩一玩krpboes。
比如!让我们用kprobes追踪,我们笔记本上正在打开的文件。我执行了下边这条命令:
$ sudo ./kprobe 'p:myopen do_sys_open filename=+0(%si):string'
以后,我的笔记本立刻输出正在打开的文件。干净整齐!
你会注意到kprobes插口本身有点冗长–比如,你必须晓得给do_sys_open的filename参数是在%si寄存器中,还要取消对这个表针的引用,并告诉kprobes系统它是一个字符串。
我觉得kprobes在以下3种情况中很有用:
你正在追踪一个系统调用。所有系统调用都有对应的内核函数,例如:do_sys_open。你正在调试一些网路栈或则文件IO的性能问题,且足够了解被调用的内核函数,对你追踪它们很有帮助(不是不可能!虽然linux内核也是代码)。你是一名内核开发者又或则在试着调试不常发生的内核bug!(我不是一名内核开发者).
uprobes
Uprobes有点像kprobes,只不过它不是检查内核函数的,而是检查用户空间函数的(比如malloc)。可以阅读brendangregg在2015年发布的一篇文章.
我对uprobes工作原理的理解是:
你决定想要去追踪在libc中的malloc函数你要求linux内核为你追踪libc中的malloc函数Linux找到libc被加载到显存中的副本(应当只有一份,共享给所有进程),之后改变malloc的代码便捷被追踪Linux用某种办法把数据传递给你(稍后我们会晤如何”要求linux”以及”用某种办法获取数据”的原理)
你可以用它做的一件事情是监视其他人在她们的bash终端中输入的内容,很炫哝!
[email protected]~/c/perf-tools> sudo ./bin/uprobe 'r:bash:readline +0($retval):string'
Tracing uprobe readline (r:readline /bin/bash:0x9a520 +0($retval):string). Ctrl-C to end.
bash-10482 [002] d... 1061.417373: readline: (0x42176e <- 0x49a520) arg1="hi"
bash-10482 [003] d... 1061.770945: readline: (0x42176e <- 0x49a520) arg1=(fault)
bash-10717 [002] d... 1063.737625: readline: (0x42176e <- 0x49a520) arg1="hi"
bash-10717 [002] d... 1067.091402: readline: (0x42176e <- 0x49a520) arg1="yay"
bash-10717 [003] d... 1067.738581: readline: (0x42176e <- 0x49a520) arg1="wow"
bash-10717 [001] d... 1165.196673: readline: (0x42176e <- 0x49a520) arg1="cool-command"
USDT/dtraceprobes
USDT全称为”用户级静态定义的追踪”,“USDT探针”和“dtrace探针”是一个东西(有点超乎我的预料!)。你似乎早已听过BSD/Solaris上dtrace,虽然你也能在Linux上用dtrace探针。虽然,它是一种曝露自定义风波的方式。举个事例,如果你编译正确的话,Python3是有dtrace探针的。
python.function.entry(str filename, str funcname, int lineno, frameptr)
假如有一个就能消费dtrace探针的工具(比如:eBPF/systemtap),和支持dtrace的Python版本,你就可以手动的追踪Python函数调用。很棒对吧!(但是这是一种”假设”————并不是所有编译的Python都支持dtrace探针的,在Ubuntu16.04中的Python就不支持)
如何判定支不支持dtrace探针,依照Python文档,虽然你可以用readelf工具在二补码文件中查找的“stap”字符串。
readelf -S ./python | grep .note.stapsdt
[30] .note.stapsdt NOTE 0000000000000000 00308d78
# sometimes you need to look in the .so file instead of the binary
readelf -S libpython3.3dm.so.1.0 | grep .note.stapsdt
[29] .note.stapsdt NOTE 0000000000000000 00365b68
readelf -n ./python
假如想阅读更多关于dtrace的内容,你可以阅读2004年的论文,不过我不晓得是不是最好的参考。
kerneltracepoints
Tracepoints也在Linux内核中(这有篇LWN的文章)。这个方式是MathieuDesnoyers(他来自Montreal!)写的。简单来讲,有一个TRACE_EVENT宏,它能让你定义追踪点,像下边这样(它对UDP失败队列做些事情?)
TRACE_EVENT(udp_fail_queue_rcv_skb,
TP_PROTO(int rc, struct sock *sk),
TP_ARGS(rc, sk),
TP_STRUCT__entry(
__field(int, rc)
__field(__u16, lport)
),
我不太明白它是如何工作的(我觉得它涉及面很广),但基本上追踪点:
lttng-ust
我还不太了解LTTng,但我的理解是,以上4种东西(dtraceprobes,kprobes,uprobes,tracepoints)在某种程度上都须要内核的支持。lttng-ust是一个追踪系统,它能让你把追踪探针编译到你的程序中,并且所有追踪都发生在用户空间。意味着它更快开支更小,由于你何必做上下文切换。我还没使用过LTTng,所以这种就是我要说的内容。
搜集”真香”数据的机制
要理解你拿来搜集&剖析追踪数据的后端工具,理解把内核中的追踪数据取下来的基础机制是极其重要的。(这就只有ftrace、perf_events、eBPF、systemtap、lttng5个工具)。让我们从ftrac,perf_events,eBPF开始,实际上它们是Linux内核的一部份。
ftrace
里面的./kprobe和./uprobe脚本?它们都使用ftrace从内核中获取数据。Ftrace有点像一种名称插口,直接使用它有点痛楚。在/sys/kernel/debug/tracing/内有个文件系统,能让你从内核中获得各色各样的追踪数据。
与ftrace交互的基本方式是:
把数据写入在/sys/kernel/debug/tracing/中的文件从/sys/kernel/debug/tracing/中的文件读出数据
Ftrace支持:Kprobes、Tracepoints、Uprobes,我觉得就是这样。
基于下边Ftrace的输出很难做剖析:
bash-10482 [002] d... 1061.417373: readline: (0x42176e <- 0x49a520) arg1="hi"
bash-10482 [003] d... 1061.770945: readline: (0x42176e <- 0x49a520) arg1=(fault)
bash-10717 [002] d... 1063.737625: readline: (0x42176e <- 0x49a520) arg1="hi"
bash-10717 [002] d... 1067.091402: readline: (0x42176e <- 0x49a520) arg1="yay"
bash-10717 [003] d... 1067.738581: readline: (0x42176e <- 0x49a520) arg1="wow"
bash-10717 [001] d... 1165.196673: readline: (0x42176e <- 0x49a520)
perf_events
从内核中获取数据的第二种方式,是使用perf_event_open系统调用。原理是:
调用perf_event_open系统调用内核写把风波到一个在用户空间的环型缓冲区中,应用程序可以从中读取数据
我晓得的惟一的一件事情是,你可以以这些方法读取tracepoints。这也就是执行sudoperftrace命令时做的事情(每位系统调用都有一个tracepoints)。
eBPF
eBPF是一种十分先进的获取数据的形式。它的工作原理。
你写一段“eBPF程序”(一般是C语言,或则用一种生成这些程序的工具)要求内核把探针附着到kprobe/uprobe/tracepoint/dtrace探针“eBPF程序”把数据输出到一个eBPFmap/ftrace/perf缓冲区你拥有了自己的数据!
eBPF特别好,由于它是Linux的一部份(不用安装内核模块),但是你可以定义自己的程序,去做任何你想做的奇怪的事情,因而它十分强悍。平常是通过bcc后端间接使用它,稍后再讨论bcc。不过eBPF只在较新的内核上可用(选择内核的那个版本,取决于想把eBPF程序附着到哪些样的数据源)
不同的内核版本提供不同的eBPF特点,以下是一份挺好的总结:
sysdig
Sysdig是一个内核模块+追踪系统。它能让你追踪系统调用和一些其他的事情?我发觉她们的站点有点难用,不过我觉得这个文件包含所有sysydig支持的风波列表。Sysdig会告诉你正在打开文件描述符,但不告诉你TCP栈的内部细节。
systemtap
我对SystemTap的工作原理有点不清楚,所以我们从构架文档开始
你决定要追踪一个kprobe编撰一个“systemtap程序”,把它编译到一个内核模块中内核模块在插入后,在激活时会从你的内核模块中调用代码创建kprobes(调用register_kprobe)内核模块(使用relayfsorsomething)复印输出到用户空间
SystemTap支持:tracepoints、kprobes、uprobes、USDT
支持许多东西!在这篇选择一个linux追踪工具的文章中对systemtap有更多的描述。
LTTng
LTTng(linuxtracing:thenextgeneration)是出自Montreal(伦敦高等理工大学的实验室)!!,让我超级高兴。前几天,我见过一个被叫做追踪罗盘的了不起的demo工具linux系统应用,它从LTTng读取数据。在执行tar-zxfsomefile.tar.gz命令时,它可以显示程序与系统调用之间的所有sched_switch变化过程,但是真的可以以一种特别清晰的形式听到正在发生的事情。
LTTng的缺点(类似SystemTap)是,你必须安装一个内核模块能够使内核的部份正常工作。使用lttng-ust时发生的一切都在用户空间,但是没有内核模块(今译:systemtap会编译出一个内核模块)。
后端
Okay!该谈后端了!我要把她们根据机制(如何将数据从内核中取下来)分类,理解上去更容易一点。
perf后端
这里惟一简单的后端是perf。
perftrace迅速为你追踪系统调用。简单,喜欢。实际上linux命令大全,现今perftrace是那些后端当中的惟一一个我每天都在使用的工具。(ftrace这个东西十分强悍,但也更无法使用)
ftrace后端
Ftrace用上去有点痛楚,因而也有各色各样的后端工具可以帮到你。我还没发觉最好的工具,不过可以从下边那些开始:
eBPF后端:bcc
我惟一了解的是bcc框架:。它才能让你编撰eBPF程序,帮你把它们插入到内核中,之后它会帮你把数据从内核中读下来,让你可以用Python脚本来处理。这框架十分容易上手。
假如你好奇tcpdump中的BPF和eBPF的关系,可以瞧瞧前几天我写过一篇它们之间关系的文章。不过我觉得最简单的是把它们看作是两个东西就好,由于eBPF强悍得多。
bcc有点古怪,由于你要在Python程序内部写一个C程序,不过倒是有好多事例可以参考。前几天Kamal和我第一次用bcc编撰了一个特别简单的程序。
LTTng&SystemTap后端
LTTng&SystemTap都有一套它们自己的工具,我不太了解。说真的————这个被叫做追踪罗盘的图形工具linux内核打印调用栈,它似乎真的很强悍。它消费一种LTTng形成的数据,也称CTF(“通用追踪格式”)追踪格式。
所以我应当使用哪些样的追踪工具
我如今的看法是(你应当晓得,我只是刚才弄清楚它们怎样组装在一起的,我不是专家):
希望这篇文章有用
我真的很高兴,如今我了解了(大部份)所有的组件是怎样组装在一起的,这篇文章是我在爵士音乐节上的一场免费的演出中写的,听着布鲁斯过的很愉快。
如今linux内核打印调用栈,我晓得它们是怎样组合在一起的,我觉得我可以更轻松地跟进追踪后端的进展!
BrendanGregg的博客中有大量的这种主题中的细节——如果你有兴趣了解linux追踪生态中的改进(始终在变化!),那是最好的博客。
谢谢AnnieCherkaev,HaroldTreen,IainMcCoy,DavidTurner阅读这篇文章的草稿。
1.评注:PapersWeLove是一个计算机科学论文网站,时常组织线下活动