9.8 Ftrace 杂项与遗留问题(FAQ)
关于 ftrace,还有一些碎屑但非常重要的问题。与其把它们像扔垃圾一样列出来,不如用一种更符合我们直觉的方式——FAQ(常见问题解答)——来把这块拼图的最后几块补上。
这几个问题,都是我在实际踩坑过程中遇到过的「哎,怎么这时候就不行了?」的时刻。
那个 README 到底说了啥?
你可能一直在找一份 ftrace 的「快速上手指南」。其实它就藏在内核的源码树里,直接挂载在你的系统上。
直接 cat 它:
sudo cat /sys/kernel/tracing/README
这是一个 mini-HOWTO。你会看到一大段文字,里面包含了所有 ftrace 的基本用法。别指望它是本小说,但它确实是最权威的「备忘单」。
为什么我的文件跟你的不一样?
有时候你照着教程做,结果发现 /sys/kernel/tracing/ 下根本没有某个文件,或者某些选项死活不出现。
这很正常,别怀疑人生。
因为 tracefs 这个伪文件系统本身就是内核的一部分。你看到什么,完全取决于两件事:
- CPU 架构:通常 x86_64 是亲儿子,功能最全,更新最快。如果你在 ARM 或 RISC-V 上玩,有些高级功能可能还没移植过去。
- 内核版本:本文基于 5.10.60。如果你用的是 4.x,或者非常新的 6.x,接口可能会有变动。
如果你觉得缺了点什么,先去确认一下这两点。
我想要「实时数据」,能不能像看日志一样一直刷?
上一节我们提到了 trace 这个静态快照文件。但如果你想看那种「正在发生」的、流动的数据流,你需要用到 trace_pipe。
这就好比看视频:
trace:是一张照片(静态快照)。trace_pipe:是直播流。
你可以用 tail -f 或者任何自定义脚本去读它:
tail -f /sys/kernel/tracing/trace_pipe
而且因为它是「流」,你完全可以把它像水管一样接到别的工具上。比如,你只想看包含 "usb" 的 trace:
cat /sys/kernel/tracing/trace_pipe | grep usb
这在排查某个特定子系统的突发风暴时特别好用。
我能在代码里控制 ftrace 的开关吗?
这是个很硬核的需求。有时候你想在某个特定的驱动函数里,一检测到异常条件就立马关掉跟踪,免得后续的垃圾日志把关键信息冲掉。
当然可以。内核给你提供了两个 API(注意,它们是 GPL 导出的):
tracing_on():打开跟踪。tracing_off():关闭跟踪。
这其实就是往 tracing_on 那个伪文件里写 1 或 0 的代码版。你可以在内核模块里这么写:
/* 检测到故障点,立马冻结现场 */
tracing_off();
⚠️ 注意
还有一个层级更高的开关:/proc/sys/kernel/ftrace_enabled。
往这里写 0 会把整个 ftrace 系统关掉。这属于「核武器」级别的操作,别乱用。
系统崩了的时候,我的 trace 去哪了?
这是一个痛点,也是 ftrace 最强的地方。
想象一下场景:系统突然 panic 了,或者触发了 Oops。这时候你有强大的 kdump 和 crash 工具,它们能帮你保存内核崩溃时的内存快照。
但是——快照只有「死」的那一刻。它告诉你「死在这儿」,但没告诉你「怎么走到这儿的」。这就是所谓的「死后验尸」缺了病历本。
这时候就需要 ftrace_dump_on_oops 登场了。
这个机制的设计初衷非常简单:既然系统崩了没法写文件了,那我们就把 ftrace 缓冲区里的内容直接吐到控制台和内核日志里。因为 kdump 会把这些日志一并抓走,你就能在事后分析崩溃现场时,同时看到它生前的最后一段轨迹。
开启方式很简单,往 proc 文件里写个 1:
echo 1 > /proc/sys/kernel/ftrace_dump_on_oops
默认是 0(关闭)。
如果你想让它一启动就准备好,也可以在内核启动参数里加:
ftrace_dump_on_oops
这里有个高级玩法。如果你是多核系统,可能只关心触发 Oops 的那个 CPU 的 trace,不想被别的 CPU 的日志干扰。可以用这个参数:
ftrace_dump_on_oops=orig_cpu
这会只把「肇事 CPU」的 ftrace 缓冲区 dump 出来,清爽多了。
那些测量延迟的跟踪器是干什么的?
你在 available_tracers 里肯定见过一串奇怪的名字:irqsoff、preemptoff、wakeup……它们是 ftrace 里的「特种部队」,专门用来测系统延迟的。
为什么需要它们?
理想情况下,中断和抢占应该像闪电一样开闭。但现实中,驱动代码可能会不小心把中断关太久。一旦超过几十微秒,系统的实时性就完蛋了,音频会卡顿,网络会丢包。
几个关键角色:
irqsoff:专门抓「把中断关太久」的坏蛋。它会记录下硬件中断被禁用的最长时间,并且告诉你是在哪个函数里发生的。preemptoff:专门抓「禁止抢占太久」的情况。这也是一种延迟来源。preemptirqsoff:上面两者的集大成者,只要内核不能调度了(无论是关了中断还是关了抢占),它都管。wakeup/wakeup_rt:关注的是调度延迟。任务从醒来到真正跑起来,中间隔了多久?这对实时系统至关重要。
怎么用?
和普通 ftrace 一样,切换过去就行了:
echo irqsoff > /sys/kernel/tracing/current_tracer
然后使劲压你的系统。一旦有代码关中断时间过长,ftrace 就会抓出那个「罪魁祸首」的调用栈。
多长算「太长」?
对于硬件中断或内核抢占来说,超过 10 微秒 就开始值得警惕了;如果到了 毫秒 级别,那就是严重的事故。
很多驱动 bug 都是这么抓出来的——你以为那个自旋锁只持用了几行代码,结果里面藏了个 memcpy,还是 uncable 的,直接把中断关了几毫秒。
我能同时跑多个 trace 吗?
默认情况下,你只有一套全局的缓冲区。但 ftrace 有一个很酷的 Instances(实例) 模型。
你可以把 instances 目录想象成「虚拟机」。在这个目录下,你可以 mkdir 出任意多的子目录,每个子目录都是一套独立的 ftrace 环境。
cd /sys/kernel/tracing/instances
mkdir my_test_trace
现在你可以在 /sys/kernel/tracing/instances/my_test_trace/ 下面看到另一套完整的 trace、trace_pipe、tracing_on……
这有什么用?
太有用了。你想用 function_graph 跟踪网络栈,同时又想用 irqsoff 监控中断延迟。如果只有一个全局缓冲区,这两个会打架。有了 instances,你可以把它们隔离开,各干各的。
这是处理复杂系统问题的终极武器。Steven Rostedt(ftrace 的主要作者)有一个很棒的演讲专门讲这个,强烈建议去找来看看。