4. perf top 与 perf annotate:实时热点监控
在性能分析中,快速定位当前系统的 CPU 热点是第一步。perf top 和 perf annotate 是 Linux perf 子系统中用于实时监控和深度分析的两个核心工具。前者提供系统级或进程级的实时热点概览,后者则能将热点精确到汇编指令级别,帮助开发者理解 CPU 到底在“忙什么”。
4.1 perf top:实时性能监控仪表盘
perf top 类似于 top 命令,但它监控的是 CPU 周期事件(或用户指定的事件),并按采样频率排序显示最热的函数或符号。它不需要预先录制数据,直接附加到系统或进程上,适合快速排查现网问题。
4.1.1 基本用法与参数
| 参数 | 说明 | 示例 |
|---|---|---|
-e <event> |
指定采样事件,默认是 cycles |
perf top -e cache-misses |
-p <pid> |
仅监控指定进程 | perf top -p 1234 |
-K |
仅显示内核空间符号 | perf top -K |
-U |
仅显示用户空间符号 | perf top -U |
-s <sort> |
按指定列排序(如 comm) |
perf top -s comm |
-n |
显示采样计数,而非百分比 | perf top -n |
perf top 时,建议先使用 -p 绑定到目标进程,避免对整个系统造成不必要的采样开销。如果怀疑是内核问题,使用 -K 过滤内核符号,可以快速定位驱动或调度器热点。
4.1.2 输出字段解读
默认输出包含以下列:
- Overhead:该符号占用的 CPU 周期百分比,是判断热点的核心指标。
- Shared Object:符号所在的共享库或可执行文件。
- Symbol:函数名或符号名。如果无法解析,显示为十六进制地址。
4.1.3 实时热点监控流程
典型工作流:
- 运行
perf top观察系统整体热点。 - 发现某个进程(如
nginx)占用过高,按Ctrl+C中断。 - 使用
perf top -p $(pgrep -x nginx)聚焦该进程。 - 记录下最热的函数名(如
ngx_http_process_request)。 - 进入
perf annotate阶段进行指令级分析。
4.2 perf annotate:深入汇编级热点
当 perf top 或 perf record 定位到热点函数后,perf annotate 可以反汇编该函数,并将采样计数标注到每一条汇编指令上。这是理解 CPU 流水线停顿、缓存未命中、分支预测错误等微架构问题的关键手段。
4.2.1 使用方式
perf annotate 通常与 perf record 配合使用,但也可以直接对 perf top 的当前热点进行标注(通过交互式界面)。
# 录制数据
perf record -e cycles -a -g -- sleep 10
# 生成标注报告,默认显示最热的函数
perf annotate
# 指定具体函数
perf annotate --symbol do_sys_open
4.2.2 输出解读
标注输出包含三部分:
- Percent:该指令占函数总采样数的百分比。高百分比指令就是热点指令。
- Disassembly of function:函数名和源文件信息。
- Source code & Assembly:如果编译时加了
-g调试信息,会交错显示源码和汇编。
-fno-omit-frame-pointer,可能导致符号解析失败或调用链不完整。建议在编译目标程序时使用 -g -fno-omit-frame-pointer。
4.3 从 perf top 到 perf annotate 的完整链路
下图展示了从实时监控到指令级分析的完整数据流:
4.4 实战案例:定位 nginx 用户态热点
假设一台 Web 服务器 CPU 使用率持续 90% 以上,我们通过以下步骤定位热点:
步骤 1:实时监控
# 监控整个系统,按 CPU 周期排序
perf top -U -p $(pgrep -x nginx)
输出显示 ngx_http_process_request 占用 35%,ngx_http_upstream_send_response 占用 20%。
步骤 2:录制数据
# 录制 30 秒数据
perf record -e cycles -p $(pgrep -x nginx) -g -- sleep 30
步骤 3:指令级标注
# 对最热函数进行标注
perf annotate --symbol ngx_http_process_request
标注结果示例:
Percent | Disassembly of ngx_http_process_request
------------------------------------------------
| push %rbp
| mov %rsp,%rbp
12.5 | mov 0x20(%rdi),%rax ; 热点:读取请求结构体
8.3 | test %rax,%rax
5.2 | je <+0x40>
20.1 | mov (%rax),%rcx ; 热点:解引用指针
15.0 | cmp $0x200,%rcx ; 热点:比较操作
... | callq ngx_http_parse_line
分析结论:从标注可以看出,mov 0x20(%rdi),%rax 和 mov (%rax),%rcx 两条指令占用了超过 30% 的采样。这通常意味着 CPU 在等待内存加载(缓存未命中)。优化方向:检查请求结构体的内存布局,尝试使用 prefetch 指令或调整数据结构对齐。
4.5 常用技巧与避坑
| 场景 | 推荐做法 | 常见错误 |
|---|---|---|
| 内核热点分析 | 使用 perf top -K 或 perf record -e cycles:k |
忘记加 -k 导致采样到大量用户空间噪声 |
| JIT 代码(如 Java) | 需要 perf-map-agent 生成符号映射 |
直接分析看到的是 [JIT] 或地址,无法定位到 Java 方法 |
| 短生命周期进程 | 使用 perf record -e cycles -p $(pgrep -x cmd) 并配合 --timestamp |
进程退出后采样数据为空 |
| 汇编标注不准确 | 确保编译时使用 -fno-omit-frame-pointer 和 -g |
使用 strip 后的二进制导致标注偏移 |
4.6 小结
perf top 和 perf annotate 构成了从宏观到微观的性能分析闭环:
- perf top:快速定位热点函数,适合在线排查。
- perf annotate:深入指令级,揭示 CPU 微架构瓶颈。
- 两者结合,可以在几分钟内完成从“系统卡顿”到“某条指令缓存未命中”的完整诊断。