跳到主要内容

9.11 LTTng 与 Trace Compass:高层视角的上帝模式

进场:换一种视角看内核

到现在为止,我们主要是在和 Ftrace 以及它的衍生品打交道。Ftrace 很棒,它是内核自带的,几乎零依赖,不仅能看还能改。但有时候,你需要的不只是一个「手电筒」,而是一套「全景监控系统」。

这就是 LTTng (Linux Trace Toolkit - next generation) 登场的时候。

LTTng 和我们之前聊的工具不太一样。Ftrace 像是一个精简的瑞士军刀,而 LTTng 则像是一台重型工业级 CT 扫描仪。它不仅盯着内核,还能同时盯着用户空间的应用和库,专门为多核并行系统和实时系统设计。如果你在调试那种「跑了十分钟才出现一次卡顿」的诡异问题,或者需要分析多核之间的复杂交互,LTTng 的能力会非常恐怖。

LTTng 的历史相当久远,最早版本(LTT)可以追溯到 2005 年,现在的 LTTng 是活跃维护的下一代版本。它开源,协议自由(LGPL/GPL/MIT 都有),而且在高性能计算领域名声在外。

但这套工具的文档也是出了名的详尽。由于篇幅限制,我没法把它的所有手册都搬运过来。如果你对「什么是 Tracing(跟踪)」以及它和「Profiling(性能分析)」的本质区别感兴趣,强烈建议去刷一遍 LTTng 官网的文档,那是真正的教科书级别。

上号:LTTng 的安装与快速上手

既然我们要在内核里用,第一步自然是安装。

如果你用的是 Ubuntu 20.04,不想折腾源码包,直接用 apt 就能搞定:

sudo apt install lttng-tools lttng-modules-dkms -y

⚠️ 仓库版本提醒 不过要注意,仓库里的版本通常不是最新的。在我写这段代码的时候,用这个方法装到的是 2.11 和 2.12 版本的模块,而官方最新的稳定版已经到了 2.13。大部分情况下这没问题,但如果你发现某些新特性没有,可能就是版本差了一代。

装好之后,建议先去 LTTng 官网把《Quick start guide》过一遍。虽然它能追踪用户空间,但作为一个内核调试的书,我们这里只关心 Kernel Tracing

核心流程:LTTng 的五步走

用 LTTng 记录一次内核追踪会话,逻辑其实非常清晰。虽然它有很多参数,但核心流程只有五步(全程需要 root 权限):

第一步:建立会话

我们需要告诉 LTTng:「我要开始干活了,数据存这里」。

lttng create <session-name> --output=~/my_lttng_traces/

如果不加 --output 参数,它默认会扔在 ~/lttng_traces/ 目录下。

第二步:启用事件

LTTng 的核心思想是「事件」。你需要明确告诉它你想抓什么。这里为了演示简单(也为了别把硬盘撑爆),我们开启所有内核事件:

lttng enable-event --kernel --all

⚠️ 暴力开启的代价 这只是演示。在生产环境,--all 是个危险动作。所有的 syscall、所有的调度器事件、所有的网络包进出……全部都会被记录下来。如果是高负载服务器,日志文件增长的速度会让你怀疑人生。实际使用时,请务必指定具体的事件类或通道。

第三步:开始记录

准备好了?按开关。

lttng start

现在,去干你想干的事。复现那个 bug,或者跑那个压力测试。

第四步:停止记录

lttng stop

这一步其实不是必须的——因为下一步的 destroy 会自动停止它。但如果你想在同一次会话里分批次记录,stop 可以让你暂停一下。

第五步:销毁会话

lttng destroy

这里的「销毁」听起来有点吓人,但别担心——它只是销毁 LTTng 守护进程里的会话配置,不会删除你已经保存下来的原始 trace 数据。

最后,如果你想让非 root 用户也能看这些数据,记得改一下权限:

sudo chown -R $(whoami):$(whoami) ~/my_lttng_traces

实战:再次追踪那个 Ping 包

为了让你有个直观的感受,我写了一个简单的 Bash 脚本,把上面的步骤封装了一下。你可以在这里找到它:ch9/lttng/lttng_trc.sh。这个脚本只是为了让你的生活更轻松一点(我也只做了轻量测试,不保证在所有环境下都完美)。

它的用法很简单:

$ cd <lkd_src>/ch9/lttng ; sudo ./lttng_trc.sh
Usage: lttng_trc.sh session-name program-to-trace-with-LTTng|0
1st parameter: name of the session
2nd parameter, ...:
If '0' is passed, we just do a trace of the entire system (all kevents),
else we do a trace of the particular process (all kevents).
Eg. sudo ./lttng_trc.sh ps1 ps -LA
[NOTE: other stuff running _also_ gets traced (this is non-exclusive)].

让我们继续拿 ping 开刀。这几乎是我们的老朋友了:

sudo ./lttng_trc.sh ping1 ping -c1 packtpub.com

脚本会跑一圈,然后你会发现,除了屏幕输出,它还在 /tmp 目录下生成了一个压缩包(比如 lttng_ping1_08Mar22_1104.tar.gz)。这就是你的「黑匣子」记录,你可以把它拷贝到另一台机器上慢慢分析,完全不用在生产环境上挂着沉重的图形界面。

命令行分析:Babeltrace 的洪流

拿到了数据,怎么看?

LTTng 自带了一个叫 Babeltrace 2 的命令行工具。顾名思义,它是巴别塔的翻译——把 LTTng 那种二进制的、高效的 CTF(Common Trace Format)格式,翻译成人类能读的文本。

你可以参考官方文档来学习它的详细用法。但我必须给你打个预防针:输出量是惊人的

刚才那个单次 ping 的操作,如果用 Babeltrace 2 导出成文本,生成了 超过 123,000 行的信息。

这就像是你把整座大楼的水管图纸铺满了整个足球场。虽然信息全在那里,但靠肉眼看这些文本流,眼睛会瞎掉的。这就是为什么我们需要图形界面。

图形化可视化:Trace Compass 的上帝视角

如果用命令行是「在地面上找管道」,那 Trace Compass 就是让你「坐卫星看全貌」。

Trace Compass 是基于 Eclipse 的一个项目(Eclipse RCP),也是 LTTng 官方推荐的图形化查看器。它的安装非常简单,去官网下载解压即可。

安装好后,启动 Trace Compass,菜单栏选择 File | Open Trace...,然后选中你刚才保存 trace 数据的目录。它会自动解析,然后给你呈现一个极其专业的控制台。

屏幕会被分成好几块。最上面是一个巨大的时间轴,中间是事件列表,下面是详细信息。一开始看可能会有点晕,因为它确实提供了太多维度的信息了。

精准定位:过滤的艺术

面对大海一样的数据,我们要学会钓鱼。在刚才那个 ping 的例子里,为了找到那一瞬间的网络包传输,我做了两件事:

  1. 锁定 CPU:我知道 ping 刚好在 CPU 6 上跑了(可能是因为调度器刚好把它扔那了),所以在 CPU 列的搜索框里输入 6
  2. 锁定事件:在 Contents 列输入 icmp

Trace Compass 会立刻帮你把噪音过滤掉,只剩下你关心的那根针。

在上面的截图中,你可以看到一个高亮的行:net_dev_queue。这代表着网络设备准备发送队列。右键点击这一行,Copy to Clipboard,贴出来你会看到一段非常详细的文本:

Timestamp Channel CPU Event type Contents TID Prio PID Source
18:39:11.274 970 channel0_6 6 net_dev_queue
skbaddr=0xffff8a4c30fb5c00, len=98, name=eno2, network_header_
type=_ipv4, network_header=ipv4=[version=4, ihl=5, tos=0,
tot_len=84, id=0x2950, frag_off=16384, ttl=64, protocol=_
icmp, checksum=0xe6db, saddr_padding=[], saddr=[192, 168, 1,
16], daddr_padding=[], daddr=[104, 22, 0, 175], transport_
header_type=_icmp, transport_header=icmp=[type=8, code=0,
checksum=393, gateway=720897]] 3932722 20 3932722

这种细节,对于一个网络驱动开发者来说,就是金矿。你能看到 skb 的地址、包长、网卡名字,甚至是 IPv4 头里的每一个 bit 和 ICMP 的 payload。这就是运行时参数的价值。

颜色的力量:一眼看懂系统状态

Trace Compass 最强的地方在于它的颜色编码

它在时间轴上把进程的状态涂上了不同的颜色。你不需要读 log,只要看图:

  • 蓝色:系统调用。
  • 绿色:用户空间代码正在跑。
  • 黄色:正在 blocked,比如等待 I/O。

看回刚才那个截图,最左侧 ping 进程的起始位置是褐色。鼠标放上去一看,原来是 sched_switch,它在等 CPU 调度(非阻塞等待)。而最右侧的那一长条柠檬黄,则是它在阻塞等待 I/O(具体是 power_cpu_idle 事件,睡了 14.6 微秒)。

这种可视化,能让你瞬间理解系统的「呼吸节奏」。

没有银弹:工具的选择与妥协

写到这儿,本章关于内核追踪的旅程就要告一段落了。

我们见识了 Ftrace 这个轻量级的忍者,玩了 trace-cmd 和 KernelShark 这对黄金搭档,又体验了 perf-tools 脚本的便捷,最后还在 LTTng 和 Trace Compass 的宏大图景里遨游了一圈。

你会发现一个事实:没有完美的工具

  • Trace Compass 的可视化很棒,一眼就能看出系统在干什么,但它在展示上下文细节(比如具体的延迟中断标志、抢占深度)这方面,就不如 KernelShark(以及背后的 Ftrace latency format)来得细腻。
  • Ftrace 什么都有,甚至能在内核崩了的时候帮你把内存里的 buffer 打出来(ftrace_dump_on_oops),但它在处理海量多核数据时的易用性,又不如 LTTng。

早在 1975 年,Fred Brooks 就在《人月神话》里告诉过我们:没有银弹

这就是为什么你需要把所有这些工具都装进你的工具箱里。在不同的场景下,针对不同的问题,选择最锋利的那把刀。

下一章,我们要面对一个更沉重的话题:Kernel Panic。别慌,有了今天的调试利器,哪怕是内核崩溃,我们也能从尸体里读出点东西来。


本章回响

现在,让我们回到本章开头埋下的那个伏笔。

还记得吗,我们一开始就在纠结:为什么内核开发者需要这种「显微镜」级别的能力?

现在答案很清楚了:因为现代系统太复杂了。

当你的代码运行在成千上万行内核代码、几十个并发进程、无数个中断上下文的交织中时,「直觉」是最不可靠的东西。只有 Tracing(跟踪)能把这种混沌的动态过程,凝固成可视化的、可分析的数据。

无论是通过 trace_pipe 看着实时数据流过屏幕,还是在 Trace Compass 里拖动时间轴,你其实都是在做同一件事——消除不确定性。这正是系统级调试的本质。

下一章,当屏幕变成红色,当 /dev/kmsg 里抛出 OopsStack dump,不要惊慌。那是系统在向你求救,而我们今天学到的这些工具,就是你破译求救信号的密码本。