3. perf 工具基础:perf stat、perf record、perf report 的基本用法与输出解读
perf 是 Linux 内核自带的性能剖析工具,基于内核的 Performance Events 子系统,能够以极低开销采集 CPU 周期、缓存未命中、分支预测错误、上下文切换等硬件/软件事件。本章聚焦三个最核心的子命令:perf stat(计数统计)、perf record(采样记录)和 perf report(报告展示),它们是火焰图数据采集的前置基础。
3.1 perf stat:事件计数与性能快照
perf stat 用于对目标程序运行期间的关键性能事件进行计数汇总,适合快速评估程序是否存在明显的 CPU 绑定、缓存抖动或上下文切换开销。
基本用法
# 统计 ls 命令的硬件/软件事件
perf stat ls
# 统计指定 PID 的进程,持续 10 秒
perf stat -p 12345 sleep 10
# 统计整个系统所有 CPU 的事件,持续 5 秒
perf stat -a sleep 5
输出解读
以下是一次典型的 perf stat ls 输出(已做中文注释):
Performance counter stats for 'ls':
1.234567 task-clock (msec) # 0.123 CPUs utilized
12 context-switches # 9.723 K/sec
2 cpu-migrations # 1.621 K/sec
45 page-faults # 36.456 K/sec
3,456,789 cycles # 2.801 GHz
1,234,567 instructions # 0.36 insn per cycle
123,456 branches # 100.012 M/sec
4,567 branch-misses # 3.70% of all branches
0.010234567 seconds time elapsed
关键指标速查:
- task-clock:程序实际占用的 CPU 时间(毫秒),反映 CPU 利用率。
- context-switches:上下文切换次数,过高可能意味着锁竞争或 I/O 等待。
- cpu-migrations:进程在 CPU 间迁移次数,频繁迁移会污染缓存。
- page-faults:缺页异常次数,大量缺页暗示内存访问模式不佳。
- instructions per cycle (IPC):每周期指令数,IPC < 0.5 通常表示内存瓶颈或分支预测差。
- branch-misses %:分支预测失败率,超过 5% 可能影响流水线效率。
自定义事件选择
使用 -e 参数可以指定需要统计的事件,支持硬件事件、软件事件和 tracepoint 事件:
# 统计 L1 数据缓存未命中与最后一级缓存未命中
perf stat -e L1-dcache-load-misses,LLC-load-misses ./myapp
# 统计系统调用次数(软件事件)
perf stat -e syscalls:sys_enter_read,syscalls:sys_enter_write ./myapp
实战建议:在火焰图分析前,先用
perf stat 做一次“体检”。如果 IPC 极低且缓存未命中率很高,应优先关注内存访问优化,而不是盲目采样 CPU 热点。
3.2 perf record:采样记录与性能数据采集
perf record 以固定频率对 CPU 进行采样,记录每个采样点正在执行的函数地址(或符号),生成 perf.data 文件。这是生成火焰图的数据来源。
基本用法
# 以 99 Hz 频率采样 ls 命令(默认频率)
perf record ls
# 以 99 Hz 频率采样,持续 30 秒
perf record -F 99 sleep 30
# 采样整个系统所有 CPU,持续 60 秒
perf record -a -F 99 sleep 60
# 采样指定 PID 的进程及其子线程
perf record -F 99 -p 12345 -- sleep 10
采样频率选择
| 场景 | 推荐频率 | 说明 |
|---|---|---|
| CPU 密集型应用 | 99 Hz 或 199 Hz | 避免与内核调度周期(100 Hz)共振,减少采样偏差 |
| I/O 密集型或短生命周期进程 | 499 Hz 或 997 Hz | 提高采样密度,捕获短暂热点 |
| 全系统采样(生产环境) | 49 Hz 或 99 Hz | 降低采样开销,避免影响业务 |
| 内核态深入分析 | 999 Hz 或更高 | 需配合 --call-graph dwarf 获取完整调用栈 |
调用栈采集模式
火焰图依赖完整的调用栈信息,perf record 通过 --call-graph 参数控制栈展开方式:
# 使用 DWARF 调试信息展开调用栈(最准确,但开销较大)
perf record -F 99 --call-graph dwarf ./myapp
# 使用帧指针(Frame Pointer)展开(要求编译时加 -fno-omit-frame-pointer)
perf record -F 99 --call-graph fp ./myapp
# 使用 LBR(Last Branch Record)硬件展开(Intel CPU,开销极低)
perf record -F 99 --call-graph lbr ./myapp
常见陷阱:如果程序使用
-fomit-frame-pointer 编译(GCC 默认开启优化),--call-graph fp 将无法获取完整调用栈,此时应改用 --call-graph dwarf 或重新编译程序。火焰图生成工具 FlameGraph 要求调用栈深度至少 3 层以上才有分析价值。
3.3 perf report:采样报告与热点分析
perf report 读取 perf.data 文件,以交互式 TUI 或文本形式展示采样结果,帮助定位最热的函数和调用路径。
基本用法
# 交互式 TUI 界面(默认)
perf report
# 输出纯文本报告
perf report --stdio
# 按 CPU 或线程分组展示
perf report --stdio --sort cpu
perf report --stdio --sort pid
输出解读(文本模式)
# Samples: 10K of event 'cycles:ppp'
# Event count (approx.): 3456789012
#
# Overhead Command Shared Object Symbol
# ........ ....... ................. .........................
25.34% myapp myapp [.] compute_dense
12.10% myapp myapp [.] matrix_multiply
8.45% myapp libc-2.31.so [.] __memcpy_avx_unaligned
5.67% myapp myapp [.] sort_array
4.32% myapp [kernel.kallsyms] [k] __do_softirq
3.89% myapp [kernel.kallsyms] [k] tick_sched_timer
...
列含义:
- Overhead:该符号占用的采样比例,即 CPU 时间占比。
- Command:进程名。
- Shared Object:符号所在的共享库或可执行文件。
- Symbol:函数名或内核符号。
[.]表示用户态,[k]表示内核态。
调用链展开
在交互式 TUI 中,选中一个热点函数后按 Enter 可以展开其调用链(Caller / Callee),查看该函数被谁调用以及它调用了哪些子函数:
# 选中 compute_dense 后的调用链示例
- 25.34% compute_dense
- 18.20% matrix_multiply
load_data
compute_block
- 4.12% memory_prefetch
prefetch_l1
- 3.02% loop_overhead
branch_check
火焰图关联:
perf report 的调用链视图与火焰图的“自上而下”展示逻辑一致。在生成火焰图前,建议先用 perf report --stdio 确认采样数据质量——如果大量采样落在 [unknown] 或 __libc_start_main 等非业务函数,说明调用栈展开配置需要调整。
3.4 perf 数据采集与火焰图生成流程
下图展示了从 perf 采样到火焰图生成的完整数据流:
3.5 实战案例:快速定位 CPU 热点
假设有一个名为 cpu_hog 的程序运行缓慢,我们按以下步骤进行 perf 分析:
- 第一步:perf stat 快速体检
perf stat ./cpu_hog # 输出显示 IPC = 0.28,branch-misses = 12.3%,说明存在严重的分支预测失败和内存瓶颈 - 第二步:perf record 采样
perf record -F 99 --call-graph dwarf ./cpu_hog # 采样 10 秒后自动退出,生成 perf.data - 第三步:perf report 查看热点
perf report --stdio # 发现 40% 的 CPU 时间集中在 binary_search 函数,且调用链显示其被 sort_and_search 频繁调用 - 第四步:导出数据准备火焰图
perf script > out.perf stackcollapse-perf.pl out.perf > out.folded flamegraph.pl out.folded > flame.svg
分析结论:通过 perf 三步曲,我们快速锁定了
binary_search 函数是 CPU 热点,且其分支预测失败率高达 12%。后续优化方向包括:将二分查找替换为哈希查找,或使用 __builtin_expect 优化分支预测。
3.6 常见问题与避坑指南
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
perf.data 中大量 [unknown] 符号 |
缺少调试符号或调用栈展开失败 | 安装 debuginfo 包;改用 --call-graph dwarf |
| 采样开销过高(CPU 占用 > 5%) | 采样频率过高或全系统采样 | 降低 -F 频率至 49 Hz;使用 -p 指定目标进程 |
| perf report 显示调用栈深度不足 | 帧指针被优化掉或 DWARF 解析不完整 | 编译时加 -fno-omit-frame-pointer;增大 --call-graph dwarf 的栈大小 |
| perf stat 统计的事件计数为 0 | 事件名称错误或权限不足 | 使用 perf list 查看可用事件;以 root 权限运行 |
火焰图底部出现大量 __libc_start_main |
采样点落在程序启动/退出阶段 | 增加采样时长,或使用 --delay 跳过启动阶段 |
安全提醒:在生产环境使用
perf record -a 全系统采样时,务必先评估开销。建议在低峰期以 49 Hz 频率短时间采样(不超过 30 秒),并配合 --pid 或 --cpu 参数缩小范围。
3.7 本章小结
- perf stat:事件计数工具,用于快速评估 IPC、缓存命中率、上下文切换等宏观指标。
- perf record:采样记录工具,通过
-F控制频率,--call-graph控制调用栈展开方式。 - perf report:报告展示工具,支持交互式下钻和文本输出,是定位热点函数的第一道关口。
- 三个命令构成 perf 分析的基础工作流:stat 体检 → record 采样 → report 分析,为后续火焰图生成提供高质量数据。