4. perf高级功能:perf top、perf annotate、perf script、perf timechart的使用

perf 这个工具,很多人用了一两年还停留在 perf stat 和 perf record 上。说实话,有点浪费。今天我把压箱底的四个高级功能拿出来聊聊——perf top、perf annotate、perf script 和 perf timechart。这些才是真正能帮你定位疑难杂症的利器。

核心思路:从「看谁在吃CPU」到「看哪一行代码在吃CPU」,再到「看整个系统的时间线」。层层深入,像剥洋葱一样把性能问题扒干净。

perf 高级功能四层递进 perf top 实时热点监控 perf annotate 源码级热点定位 perf script 事件脚本化分析 perf timechart 系统级时间线可视化 从「谁」→「哪行代码」→「什么事件」→「什么时候发生」 逐层深入,精准定位性能瓶颈

4.1 perf top:实时盯着CPU看

perf top 相当于 top 命令的增强版。它不只看进程,还能看到内核里哪个函数在吃CPU。我个人习惯在压测一开始就开着它,像监控仪表盘一样。

# 最基本的用法
perf top

# 只看用户态,不看内核
perf top -U

# 只看某个进程
perf top -p 12345

# 按CPU编号查看
perf top -C 0,1

输出长这样:

Samples: 1M of event 'cycles', 4000 Hz, Event count (approx.): 850312345678
Overhead  Shared Object       Symbol
  12.45%  [kernel]            [k] _raw_spin_unlock_irqrestore
   8.32%  libc-2.31.so        [.] __memcpy_avx_unaligned
   5.67%  myserver            [.] process_request
   3.21%  [kernel]            [k] tcp_v4_rcv

我的小技巧:按 z 键可以切换颜色高亮,按 E 键可以只看某个ELF文件里的符号。排查内存拷贝热点时,我经常盯着 __memcpy 系列函数看。

4.2 perf annotate:把热点精确到一行代码

光知道哪个函数热还不够。你想想看,一个函数几百行,到底是哪一行在拖后腿?perf annotate 就是干这个的。

用法很简单,先采样:

perf record -a -g -- sleep 10
perf annotate -s process_request

输出会显示汇编代码和对应的源码行,百分比标注在左边:

Percent |  Source code & Disassembly
------------------------------------
        │  int process_request(struct req *r) {
        │      if (!r) return -1;
  0.05% │      cmpq   $0x0, (%rdi)
        │      je      <...>
        │      for (int i = 0; i < r->count; i++) {
 12.30% │      mov    (%rdi), %eax
        │      test   %eax, %eax
        │      jle    <...>
        │          data[i] = transform(data[i]);
 45.20% │      movsd  (%rsi), %xmm0
        │      mulsd  %xmm1, %xmm0
        │      movsd  %xmm0, (%rsi)
        │      add    $8, %rsi
        │      dec    %eax
        │      jne    <...>
        │      }
  0.10% │      ret

避坑指南:我曾经在调试一个网络服务时,发现 perf annotate 显示 memcpy 占了60%的开销。一开始以为是内存拷贝太多,后来仔细看才发现——是编译器把结构体赋值优化成了 memcpy。嗯,这里要注意,别被汇编表象骗了。

4.3 perf script:把采样数据变成可读的脚本

perf script 是我最常用的调试工具之一。它把二进制的 perf.data 转成文本,方便你用 grep、awk 去分析。

# 先采样
perf record -a -g -- sleep 5

# 导出为文本
perf script > output.perf

# 只看某个CPU的事件
perf script -C 2

# 只看某个时间段的
perf script --time 10.0,20.0

输出的每一行都包含时间戳、进程名、PID、CPU编号、事件类型和调用栈:

myserver 12345 [002] 12345.678901: cycles:
        process_request+0x3a (/usr/bin/myserver)
        handle_connection+0x1f (/usr/bin/myserver)
        event_loop+0x45 (/usr/bin/myserver)
        main+0x12 (/usr/bin/myserver)
        __libc_start_main+0xf5 (/lib/libc-2.31.so)

实战场景:有一次线上服务偶发延迟,我采样了10分钟,然后用 perf script | grep -E "sched:sched_switch" | awk '{print $4}' | sort | uniq -c | sort -rn 快速找到了哪个进程在频繁切换上下文。说白了,文本分析比GUI工具灵活得多。

4.4 perf timechart:画出系统的时间线

这个功能知道的人不多,但真的很好用。perf timechart 能把CPU的调度、中断、进程状态画成一张SVG图。一眼就能看出CPU在忙什么、哪里在空转。

# 采样调度事件
perf record -e sched:sched_switch -a -g -- sleep 5

# 生成SVG图
perf timechart

# 指定输出文件名
perf timechart -o mychart.svg

生成的SVG图里,横轴是时间,纵轴是CPU核心。每个进程用不同颜色的条块表示,空闲状态是白色,中断是红色。我经常用它来排查「CPU利用率不高但吞吐上不去」的问题。

我的经验:有一次我发现一个4核系统,3个核在跑业务,1个核一直在处理软中断。用 perf timechart 一看,原来是网卡中断全部绑定到了CPU0上。调整 irqbalance 后,吞吐量直接提升了30%。

4.5 四个功能的配合使用

单独用每个功能都有局限。我一般按这个流程来:

  1. 先用 perf top 看看全局,找到最热的几个函数
  2. 再用 perf annotate 深入热点函数,定位到具体代码行
  3. 用 perf script 导出详细的事件流,分析调用关系和上下文
  4. 最后用 perf timechart 画出时间线,看系统整体的调度和等待情况

记住:性能分析不是一次性的,是迭代的。改完代码再跑一遍,看看热点有没有转移。我见过太多人改了一处热点,结果把瓶颈推到了别的地方。

功能 适用场景 输出形式 我最常用的参数
perf top 实时监控、快速定位热点 终端实时刷新 -U -p PID
perf annotate 源码级热点分析 汇编+源码对照 -s function_name
perf script 事件流分析、自定义脚本处理 文本输出 --time, -C
perf timechart 系统调度、中断、空闲分析 SVG可视化 -o output.svg

这四个功能用熟了,大部分性能问题都能在几分钟内定位。别光看文档,找个压测环境动手试试。嗯,相信我,你会爱上这种掌控感。

可可精品资料库