参考来源与版权说明
这章解决什么问题
perf 是 Linux perf_events 子系统的官方前端。它把硬件性能计数器、内核软件事件、tracepoint、kprobe、uprobe 和 USDT 放进相近的命令模型,用来回答:CPU 时间花在哪些函数、每周期完成多少指令、线程为何频繁切换、哪条代码路径发起了 I/O。
真正需要掌握的不是几十个子命令,而是一条渐进路径:先用 stat 数事件并估计频率,再用 record 采样到 perf.data,之后用 report 做聚合、用 script 看时间顺序或供火焰图处理。
五条核心结论
- 事件源决定答案。 cycles/instructions 描述微架构,软件事件描述内核计数,tracepoint 与探针描述具体行为。
- perf stat 负责“多少”,perf record 负责“在哪里/何时”。 先计数通常更便宜,也能估算后续跟踪开销。
- 采样不是逐事件记录。 高频 PMC 和软件事件常按频率抽样;tracepoint 通常可按周期 1 记录全部事件,但事件太频繁时仍有开销。
- 栈和符号质量决定剖析价值。 帧指针、DWARF/LBR、调试符号和内核符号缺失,都会造成断栈或 unknown。
- 结果依赖处理器、内核与权限。 同名硬件事件的映射、精确采样能力、可用 tracepoint 和 perf 安全策略都可能不同。
核心模型与关键指标
可以把 perf 看成四层:
~~~text
事件源 → 收集方式 → 数据文件/实时流 → 汇总与可视化
PMC stat计数 文本计数 IPC、缓存命中、频率
软件事件 record采样 perf.data 热点、栈、火焰图
tracepoint perf.data 时间线、参数、延时
动态探针 trace实时 标准输出 临时验证
~~~
CPU 分析常看 cycles、instructions 和 IPC(instructions / cycles),再结合 cache-misses、branch-misses。调度问题常看 context-switches 和 sched 事件。数据质量还要看样本数、采样频率、是否丢事件、记录时长、CPU/PID/cgroup 范围以及符号解析比例。
<code>-F 99</code> 表示目标采样频率约为每秒 99 次,而 <code>-c 1</code> 表示每发生一次事件就触发记录;二者语义不同。内核会限制采样率和 perf 可占 CPU 比例,超载时可能节流。
事件的作用域也是测量模型的一部分。<code>-a</code> 代表全系统,<code>-p</code> 与 <code>-t</code> 分别限定进程和线程,<code>-G</code> 可面向 cgroup。全系统结果适合发现“谁在消耗”,进程结果适合验证某服务,但线程迁移、子进程以及容器边界都可能让范围漏数。硬件事件还可区分用户态和内核态。报告时应写清作用域,否则同一计数无法复现。
诊断步骤
- 用 <code>perf --version</code>、<code>perf list</code> 和只读 sysctl 值确认工具、事件与权限。
- 先跑 <code>perf stat</code>:确认目标事件确实发生,估算频率,并检查 IPC、切换率等大方向。
- 缩小范围:优先指定 PID、命令、cgroup 或短时全系统窗口,避免无边界收集。
- 对 CPU 热点用 49/99 Hz 采样并记录栈;对离散行为选稳定 tracepoint。
- 用 report 看聚合热点,用 script 看时间顺序、字段和元数据;必要时再生成火焰图。
- 核查符号、断栈和 lost 计数,并与 vmstat、pidstat、应用指标交叉验证。
常用工具与命令
以下命令默认不改变业务配置;record 会在当前目录生成 perf.data。很多系统需要 root、CAP_PERFMON 或放宽策略,命令选项也可能随版本改变。
~~~bash
# 列出本机实际支持的事件
perf list
sysctl kernel.perf_event_paranoid
# 统计全系统周期、指令与上下文切换,持续 10 秒
perf stat -e cycles,instructions,context-switches -a -- sleep 10
# 每秒输出一次 sched 切换计数
perf stat -e sched:sched_switch -a -I 1000
# 全系统 CPU 栈采样,99 Hz,30 秒
perf record -F 99 -a -g -- sleep 30
perf report --stdio -n
# 记录块请求 10 秒,再按时间顺序输出
perf record -e block:block_rq_issue -a -- sleep 10
perf script
# 保留头信息与稳定字段,便于后处理
perf script --header -F comm,pid,tid,cpu,time,event,ip,sym,dso,trace
~~~
若默认帧指针回溯损坏,可在测试环境尝试 <code>--call-graph dwarf</code>;它通常需要调试信息且数据量更大。事件过滤字段先查看对应 tracefs format 文件,切勿照搬另一内核的字段名。
如何解读结果
stat 中 cycles 与 instructions 可推导 IPC,但低 IPC 不是单一结论:它可能来自缓存/内存停滞、分支、超线程竞争或工作负载本身。先与同架构、同负载基线比较。事件计数若显示“not supported”或异常为零,应先怀疑事件映射和权限。
report 的 Overhead 是样本占比,不等于精确墙钟时间;热点函数要结合调用栈理解是谁触发。script 每行包含进程/线程、CPU、时间、事件与参数,适合发现阶段性行为;header 能保存主机、内核、CPU 和原始命令,是日后复盘的重要证据。
调度类问题可先比较上下文切换总率和每 CPU 分布,再记录 sched_switch/sched_wakeup 事件回答“谁睡眠、谁唤醒、在哪个 CPU”。块 I/O 也应先计数和看设备队列,再记录 issue/complete 时间线。这样可避免把一个高频但与用户延时无关的事件误当根因。
采样频率越高时间分辨率越好,但开销和数据量也增加。99 Hz 常用于避开与整百周期性任务的同步,同时保持较低成本;它不是所有场景的固定答案。
还要区分进程 ID、线程 ID 与命令名。多线程服务的热点可能只落在少数工作线程,短命线程也可能在汇总前退出。需要解释不均衡时,应保留 tid 与 CPU 字段,并把 perf 时间线和应用线程池、请求日志对齐,不能只按相同的进程名合并。
常见误判
- 把样本数量当作事件总数,忽略频率采样和周期采样的区别。
- 看见某函数样本多就直接修改它,未检查调用者和工作负载。
- 不保存 perf 版本、内核、命令和头信息,导致 perf.data 无法解释或迁移。
- 忽略 lost/节流、unknown 符号和断栈,仍把图当成完整事实。
- 在不同处理器上直接比较裸 PMC,或默认相信人类可读事件映射完全正确。
- 记录所有 CPU、所有高频事件且没有时限,使观测本身干扰系统。
五分钟自测
- 想知道“事件发生多少次”,优先用哪个子命令?
- <code>-F 99</code> 与 <code>-c 1</code> 的核心区别是什么?
- report 和 script 分别擅长哪类问题?
- CPU 栈出现大量 unknown 时先检查什么?
- 为什么先用 stat 估计 tracepoint 频率?
检查要点:stat;频率采样与逐事件周期;聚合热点与时间序列;符号/调试信息/栈遍历;判断 record 全量捕获的开销和数据规模。
本章小结
perf 的最小有效闭环是“list 确认事件 → stat 低成本计数 → record 短时采样 → report/script 解读 → 与其他指标验证”。每次都写明事件、范围、时长、采样语义和数据质量,才能把漂亮的火焰图变成可靠证据。
来源定位
| 主题 | PDF 页码 |
|---|---|
| 功能、子命令与事件概览 | 717-727 |
| 硬件/软件事件与采样语义 | 727-730 |
| tracepoint、kprobe、uprobe、USDT | 730-737 |
| stat、record 与栈遍历 | 737-743 |
| report、script、火焰图与 trace | 743-749 |
| 其他命令与文档 | 749-751 |
章节测验
共 5 道单选题。答错有解析,答对看来源页码。