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 采样到火焰图生成的完整数据流:

perf 数据采集与火焰图生成流程 perf 数据采集 → 火焰图生成流程 perf record perf.data perf report 采样写入 读取分析 perf script 折叠栈文本 FlameGraph 导出采样数据 格式转换 生成 SVG 核心命令链: perf record -F 99 --call-graph dwarf ./app → perf script → stackcollapse-perf.pl → flamegraph.pl 其中 stackcollapse-perf.pl 和 flamegraph.pl 来自 Brendan Gregg 的 FlameGraph 工具集。

3.5 实战案例:快速定位 CPU 热点

假设有一个名为 cpu_hog 的程序运行缓慢,我们按以下步骤进行 perf 分析:

  1. 第一步:perf stat 快速体检
    perf stat ./cpu_hog
    # 输出显示 IPC = 0.28,branch-misses = 12.3%,说明存在严重的分支预测失败和内存瓶颈
  2. 第二步:perf record 采样
    perf record -F 99 --call-graph dwarf ./cpu_hog
    # 采样 10 秒后自动退出,生成 perf.data
  3. 第三步:perf report 查看热点
    perf report --stdio
    # 发现 40% 的 CPU 时间集中在 binary_search 函数,且调用链显示其被 sort_and_search 频繁调用
  4. 第四步:导出数据准备火焰图
    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 分析,为后续火焰图生成提供高质量数据。