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」,再到「看整个系统的时间线」。层层深入,像剥洋葱一样把性能问题扒干净。
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 四个功能的配合使用
单独用每个功能都有局限。我一般按这个流程来:
- 先用
perf top看看全局,找到最热的几个函数 - 再用
perf annotate深入热点函数,定位到具体代码行 - 用
perf script导出详细的事件流,分析调用关系和上下文 - 最后用
perf timechart画出时间线,看系统整体的调度和等待情况
记住:性能分析不是一次性的,是迭代的。改完代码再跑一遍,看看热点有没有转移。我见过太多人改了一处热点,结果把瓶颈推到了别的地方。
| 功能 | 适用场景 | 输出形式 | 我最常用的参数 |
|---|---|---|---|
| perf top | 实时监控、快速定位热点 | 终端实时刷新 | -U -p PID |
| perf annotate | 源码级热点分析 | 汇编+源码对照 | -s function_name |
| perf script | 事件流分析、自定义脚本处理 | 文本输出 | --time, -C |
| perf timechart | 系统调度、中断、空闲分析 | SVG可视化 | -o output.svg |
这四个功能用熟了,大部分性能问题都能在几分钟内定位。别光看文档,找个压测环境动手试试。嗯,相信我,你会爱上这种掌控感。