9.2 内核跟踪技术全景图
先别急着敲命令。
我们手里有了工具,也有了目录,但在真正上手 ftrace 之前,最好先退一步,把我们要打交道的这套东西看清楚。如果你以为跟踪工具只是一个单纯的「记录器」,那你很容易在后续的配置选项里迷路。
内核跟踪的世界里充满了缩写和子系统,它们看起来像是一堆散落的零件。但实际上,这是一台设计精密的机器。理解了这台机器的构造——数据从哪里产生、怎么收集、怎么展示——后面所有那些看似神秘的配置文件,就都只是顺理成章的连接件了。
数据源与前端——揭开工具的层
首先,一个最基本的问题:我们要跟踪的数据,到底从哪来?
你在上一节装了 lttng,改了内核参数,但你没看到「数据」本身。数据藏在内核的运行细节里。要抓到它,我们需要数据源。
内核里最常见的几大类数据源,我们其实并不陌生:
- Tracepoints(跟踪点):这是内核开发者预先埋好的「钩子」。内核里有些关键位置(比如调度器发生切换时、中断到来时),开发者写好了代码告诉系统「这里发生了一件事」。这些跟踪点的列表就躺在
/sys/kernel/tracing/events/下。- 我们在第 4 章折腾 Kprobes 时其实已经碰过这个了——那是「动态 kprobes」,相当于你自己在没有钩子的地方临时打了个洞。
- Kprobes / Uprobes:这不仅仅是调试工具,也是跟踪的强力数据源。Kprobes 负责内核空间,Uprobes 负责用户空间,它们能在几乎任何地址上拦截执行流。
- LTTng 模块与 USDT:LTTng 有自己的一套内核模块和用户空间探针,这是为了更高效、更低延迟地抓取数据。
有了数据源,这只是第一步。原始数据是散落在内核内存里的,我们需要一种基础设施把它们提取出来。
这就是为什么你的系统上有 /sys/kernel/tracing 这个目录。这是 tracefs,它是 ftrace 的「控制面板」。工具通过读写这些伪文件来告诉内核:开始记录、停止记录、只记录 schedule 这个函数、或者记录得再详细点。
最后,作为人类,直接去 cat 那些二进制的缓冲区文件是一种折磨。所以我们还需要前端。
这就构成了整个 Linux 跟踪技术的三层金字塔:
- 底层:数据源。内核产生的信号。
- 中层:基础设施。像 ftrace、tracefs、perf_events 这些机制,负责把信号从内核里捞出来,整理好。
- 顶层:前端/工具。像
trace-cmd、KernelShark,或者是我们在第 4 章用过的那些perf-tools脚本。它们替你敲底层的繁杂命令,把数据变成你能看懂的图表。
这里有个很重要的认知转变:这些技术并不是在互相竞争。
以前可能会想「我是该用 ftrace 还是 perf?」,但现在的答案是「全都要」。Julia Evans 有一篇非常经典的博客文章《Linux tracing systems & how they fit together》,如果你还没看过,强烈建议去搜来看看。她画的那张图把这一切讲得清清楚楚:现代 Linux 的跟踪技术是在共享代码、共享想法。底层的 ftrace 数据,可以通过 perf 这类工具看,也可以通过 trace-cmd 看,甚至可以通过 LTTng 的生态去分析。
这正是内核大佬 Steven Rostedt(ftrace 的原作者)一直在倡导的「统一跟踪平台」愿景。他曾经展示过一张图(就是下面这张),展示了这些技术是如何错综复杂但又和谐共存地交织在一起的:
[图 9.1 – Linux 跟踪基础设施示意图] (原书图示:展示了 Tracepoints/Probes 作为数据源,通过 tracefs/debugfs 等接口,连接到 ftrace/perf 等底层工具,再向上支撑起各种前端脚本和 GUI 工具)
[图 9.2 – 丰富的 Linux 跟踪生态系统] (原书图示:Steven Rostedt 的幻灯片,展示了底层技术如何被上层工具复用)
看到了吗?底层的「肉」是一样的,只是上面的「表皮」不同。这意味着我们学 ftrace 的时候,其实是在学一套通用的语言。
进入 Ftrace 的领地
好了,理论架构图看完了。现在让我们把脚迈进门里。
我们这一章的主角是 ftrace。它是 Linux 内核自带的跟踪器,名字里的 f 最初代表 function(函数),因为它最早就是为了跟踪内核函数调用图设计的。但现在的 ftrace 已经远不止于此,它是一个通用的跟踪引擎。
在 Linux 内核 4.1 之后,ftrace 的标准操作界面是通过一个叫做 tracefs 的虚拟文件系统来实现的。
别被这个名字吓到,你可以把它理解为一种专门用来「传输控制命令」的文件系统。就像 /proc 传的是进程信息,/sys 传的是硬件参数,tracefs 传的是跟踪控制指令。
挂载点检查
通常情况下,现代 Linux 发行版(尤其是使用 Systemd 的)在启动的时候会自动帮你把 tracefs 挂载好。
你可以用这个命令来验证一下它的存在:
mount | grep "^tracefs"
如果一切正常,你应该会看到类似这样的输出:
tracefs on /sys/kernel/tracing type tracefs (rw,nosuid,nodev,noexec,relatime)
tracefs on /sys/kernel/debug/tracing type tracefs (rw,nosuid,nodev,noexec,relatime)
注意到了吗?这里有两个挂载点。
/sys/kernel/tracing:这是新标准(4.1+ 内核)。无论你的debugfs是否被允许挂载,这个点都应该在。/sys/kernel/debug/tracing:这是旧传统。以前ftrace是挂在debugfs下面的一棵子树。
关于生产环境的一个坑
为什么要搞两个挂载点?这不仅仅是为了怀旧。
想象一下你在跑生产环境的机器,安全策略比较严。系统管理员为了安全,可能会通过内核配置 CONFIG_DEBUG_FS_DISALLOW_MOUNT 禁止挂载 debugfs。因为 debugfs 里面暴露了太多内核 internals,对于生产机来说确实是个风险。
但在生产环境下调试性能问题又是刚需。所以,内核把 tracefs 独立出来了。即使 debugfs 被禁了,/sys/kernel/tracing 依然可以单独挂载,让你只接触到跟踪相关的接口,而碰不到那些敏感的内核调试信息。
这就是为什么我们在本书中(假设你用的是较新的 5.10 内核)会默认使用 /sys/kernel/tracing 作为工作目录。
如果你在非常老的系统上(老于 4.1)操作,你可能只有 /sys/kernel/debug/tracing 这一条路。虽然路径不同,但里面的文件内容是一样的。在接下来的章节里,当我们提到「切换到 tracing 目录」时,我们的默认动作是:
cd /sys/kernel/tracing
或者,如果你想确保自己在当前系统上找到正确的路径,可以简单粗暴一点:
cd /sys/kernel/debug/tracing 2>/dev/null || cd /sys/kernel/tracing
这个目录就是我们接下来所有的魔法发源地。在这个目录下,每一个文件都不是用来存数据的,而是控制台上的一个开关、旋钮或者显示屏。
准备好了吗?把手放在键盘上。我们下一节就要开始拨弄这些开关了。