Appearance
perf 版本能力差异 —— 同一命令,新旧内核差别有多大
更新时间:2026-08-13
这个文档要回答的问题
perf 跟内核源码一起发布(在 linux/tools/perf/ 下),所以它的版本号就是内核版本号——perf --version 打出的 4.18.0 说明这是随 4.18 内核一起编译的 perf,而不是独立的软件版本。这个特性导致一个很现实的问题:
同一句 perf 命令,在 CentOS 7(内核 3.10)、Ubuntu 18.04(4.15)、Ubuntu 22.04(5.15)、新内核(6.x)上跑出来的能力和结果可能完全不同。
本文档要回答:不同版本内核的 perf,能力差异到底在哪几条主线上?实验里哪些"perf 报错"其实是版本太老?新版本多了什么,老版本要用什么替代?
前提:为什么 perf 必须跟随内核
perf 是内核 perf_event_open 性能事件子系统的用户态前端(原理见 perf-internals.md)。它依赖三样内核提供的东西,而这三样都在随内核演进而变:
- 事件能力:硬件 PMU 事件名、软件事件、tracepoint 都在
/sys/kernel/下由内核暴露,老内核没有新 tracepoint(如 io_uring、BPF 相关事件); - 内核接口:
perf_event_open的系统调用参数、perf_event_attr结构体字段会加新字段,老内核不认识新字段; - 前端工具代码:perf 本身的子命令、语法、解析器也在
tools/perf里持续开发,新增子命令靠内核源码打包分发。
所以 perf 的能力差异有两个正交来源:
- 内核提供什么(后端能力,取决于运行的内核)
- perf 用户态带了什么(前端能力,取决于编译时打包的 tools/perf 代码)
绝大多数情况下两者版本一致(同源码树),但也可以出现"新 perf 跑在旧内核"或反过来——这也是很多"装了新 perf 却报 <not supported>"的根源。

三根主线:版本差异到底差在哪
perf 的能力演进可以归成三根主线,实验里遇到的版本坑基本都落在其中:
| 主线 | 差的是什么 | 老版本(3.10 典型) | 新版本(5.x/6.x) |
|---|---|---|---|
| ① 子命令覆盖 | 有没有某个子命令 | 没有 perf c2c、perf mem 弱、perf trace 简陋 | 子命令齐全且持续增强 |
| ② 语法/插桩 | 同一命令的写法、能否挂某种探针 | perf probe 不支持 C++ ::、不支持 USDT 挂载 | :: 语法、USDT、动态类型解析齐全 |
| ③ 事件/计数 | 能采哪些指标 | 无 io_uring 等新 tracepoint,PMU 事件少 | tracepoint 丰富、metric group、JSON 输出 |
主线①:子命令覆盖差异
perf 的子命令随版本逐步加入。老内核(尤其 CentOS 7 的 3.10)缺一批后续才有的子命令:
bash
perf c2c # False Sharing(缓存伪共享)检测——4.x 引入
perf trace # syscall 追踪(比 strace 轻)——早期版本简陋,后期才成熟
perf mem # 内存访问延迟定位——早期版本能力很弱
perf daemon # 后台收集——5.x 引入
perf inject # 注入/处理 perf.data(如补丁栈、timestamp)——4.x 后常用
perf ftrace # 直接驱动 ftrace——5.x 引入判断方法:
perf list(列出当前内核+perf 支持的所有事件)、perf help(列出所有子命令)、perf --version。老版本跑perf c2c会直接报"command not found"或"invalid command",而不是功能异常。
主线②:语法与插桩差异(实验实证最多)
这条线在仓库的 perf-probe 实验里踩得最狠,全部是真实版本坑:
perf probe的 C++ 成员函数语法:perf 3.10 不支持Worker::compute(int)这种::写法,报There is non-digit char in line number(老版本把:误当行号分隔符);只能喂 mangled 名_ZN6Worker7computeEi。新版 perf(≥4.x)才支持 C++ 风格定义。见 cpp-member-probe.md 第 4 节。- USDT 静态预埋点挂载:3.10 内核的 perf 无法实际挂载 USDT 点(
perf probe --add 'usdt_demo:work_enter'报错、换地址挂又<not supported>),但readelf -n能看到预埋的stapsdtnote——预埋成功、挂载受限。升级内核(≥4.x)或改用 SystemTap/bpftrace 才能挂。见 theory.md 和 README.md。 --call-graph栈展开方式:老 perf 主要靠帧指针(-fno-omit-frame-pointer);新 perf 的--call-graph dwarf(DWARF 调试信息展开,准但开销大)和--call-graph lbr(CPU 硬件 LBR,开销小)在 4.x 后才成熟。老版本在-O2下火焰图容易全是[unknown],新版本有更多手段。见 perf.md 常见坑。
排查口诀:
perf probe/perf trace报奇怪的解析错误时,第一件事perf --version。很多"perf 坏了"其实是"perf 太老"。
主线③:事件与计数差异
老内核缺后续内核引入的 tracepoint 和事件,能采的指标范围窄:
| 差异 | 老版本(3.10) | 新版本(5.x/6.x) |
|---|---|---|
| tracepoint 覆盖 | 缺新子系统的事件 | io_uring、BPF、更多调度/块设备事件 |
| PMU 事件名 | 事件名少,依赖 /sys/bus/event_source | 事件名丰富,perf stat 支持 metric group |
| 输出形态 | 纯文本 | 支持 --json(5.x 起 perf stat/perf report) |
| 采样元数据 | 基本 | --weight(指令延迟)、perf mem 记录内存延迟 |
一张表看懂:核心能力按版本的分界线
下面按内核大版本给能力划粗线。具体到小版本有细微出入,以 perf --version + perf list 实测为准,这张表是"能 vs 不能"的可靠分界:
| 能力 | 3.10(CentOS 7 典型) | 4.x(Ubuntu 16/18 典型) | 5.x(Ubuntu 20/22 典型) | 6.x(新内核) |
|---|---|---|---|---|
perf stat / record / report / top | ✅ | ✅ | ✅ | ✅ |
perf list 硬件/软件/tracepoint 事件 | ✅ | ✅ | ✅ | ✅ |
帧指针调用栈(--call-graph fp) | ✅ | ✅ | ✅ | ✅ |
perf probe uprobe 动态插桩 | ✅(语法有限) | ✅(支持 ::) | ✅ | ✅ |
perf probe C++ :: 语法 | ❌ | ✅ | ✅ | ✅ |
perf probe USDT 挂载 | ❌ | ✅ | ✅ | ✅ |
perf trace(syscall 追踪) | ⚠️ 简陋 | ✅ 增强 | ✅ | ✅ |
perf c2c(False Sharing) | ❌ | ✅ | ✅ | ✅ |
perf mem(内存延迟) | ⚠️ 弱 | ⚠️ 增强 | ✅ | ✅ |
--call-graph dwarf / lbr | ⚠️ 受限 | ✅ | ✅ | ✅ |
perf stat --json | ❌ | ❌ | ✅ | ✅ |
| io_uring 等新 tracepoint | ❌ | ⚠️ 部分 | ✅ | ✅ |
perf ftrace / perf daemon | ❌ | ❌ | ✅ | ✅ |
读表方法:✅ 表示"该大版本基本可用",❌ 表示"基本没有",⚠️ 表示"有但能力弱/受限于配套"。判别的最终标准永远是
perf --version+ 实际perf list,本表给的是粗分界。
版本差异的实际影响:三类坑
把仓库里踩过的和常见的情况归纳成三类,方便对照排查:
坑一:命令/语法不存在 —— 报"invalid command"或解析错误
bash
$ perf c2c record # 3.10: 子命令不存在
Fatal: Perf c2c is not a command # 或 similar
$ perf probe --add 'Worker::compute(int)' # 3.10: 语法不被支持
There is non-digit char in line number应对:先 perf --version 确认版本。语法类错误可查 cpp-member-probe.md;命令缺失则看 perf help 列出的子命令,或考虑升级内核/用替代工具(perf c2c 缺 → 用 perf record -e cache-misses 间接看;perf trace 缺 → 用 strace)。
坑二:命令在,但事件/插桩 <not supported>
bash
$ perf stat -e <某新tracepoint> ... # 老内核没有该事件
<not supported>应对:事件由内核暴露,perf 前端再新也没用。perf list 看这台机器到底有哪些事件,别照搬网上的新事件名。USDT 挂载的 <not supported> 是典型(见 theory.md)。
坑三:能力存在但表现差
- 老版本
-O2下火焰图大量[unknown](没有 dwarf/lbr 栈展开手段)→ 需-fno-omit-frame-pointer重编; - 老版本
perf mem/perf c2c结果粗糙或依赖特定硬件 → 数据可信度打折。
版本差异的缓解策略
| 策略 | 适用场景 | 做法 |
|---|---|---|
| 升级内核 | 需要新子命令/新 tracepoint/USDT | 最彻底,但涉及生产环境升级成本 |
| 用替代工具 | 缺某个子命令但不便升级 | 见下方替代表 |
perf list 先行 | 不确定某事件是否支持 | 先 perf list 确认,再写命令 |
| 老内核实测为准 | 已锁定在老环境(如 CentOS 7) | 以本机实测为准,文档标注版本限制 |
| 换追踪体系 | 需要比 perf 更现代的能力 | SystemTap / bpftrace(依赖新内核) |
子命令缺失的替代方案:
| 缺失的 perf 子命令 | 替代工具 | 备注 |
|---|---|---|
perf c2c | perf record -e cache-misses 间接看 / 结合 /proc 伪共享分析 | c2c 是专门的 False Sharing 定位 |
perf trace | strace(见 strace.md) | strace 开销更高但老内核也有 |
perf mem | 结合 PMU 事件 + latency-measurement.md | 手动近似 |
perf probe USDT | SystemTap / bpftrace | 需对应内核版本支持 |
与本仓库实验的对应
仓库里已经实打实踩过版本差异的实验:
- perf-probe 系列(CentOS 7.9 / 内核 3.10)——最密集的版本坑实证:
perf probe的::语法、USDT 挂载、perf stat <not supported>全是版本问题,文档 §0.2 专门声明了环境边界。 - cpp-member-probe.md —— 专门记录"perf 版本决定语法",§7 结论点明先
perf --version。 - theory.md —— USDT "零开销待命"是否成立取决于 perf/内核版本,3.10 实测挂不上。
这些实验的共性启示:写文档/跑实验前先记录
perf --version+ 内核版本,否则同一篇教程在旧内核上照抄会报一堆莫名其妙的错。
一句话总结
perf 的版本 = 内核版本,能力差异集中体现在子命令覆盖、probe 语法/插桩、事件与计数三条主线上——老内核(3.10 典型)缺 c2c/trace 增强、不支持
::语法和 USDT 挂载、tracepoint 覆盖窄;排查 perf 报错的第一反应应是perf --version+perf list,而不是怀疑 perf 坏了。