深入linux c/c++调试工具之perf
目录
2、程序「感觉慢」,但 strace 看不出 syscall 瓶颈
(2)采样模式(perf record / perf top)
一、前言-perf 工具简介
perf 是 Linux 内核自带的 性能分析工具,基于内核的 perf_events 子系统,可采集 CPU 周期、指令、缓存缺失、分支预测失败、系统调用、函数调用链等事件,用于 定位热点函数、分析 CPU 瓶颈、排查性能退化。
二、使用场景——何时使用 perf
1、程序 CPU 占用高,不知道热点在哪
典型现象:top 显示进程 CPU 90%+,但不知道哪个函数耗 CPU;多线程程序某个线程跑满。
2、程序「感觉慢」,但 strace 看不出 syscall 瓶颈
典型现象:strace -c 显示 syscall 总耗时不高,程序仍然慢。
3、优化前后效果验证
典型现象:改了算法/数据结构,想量化提升;需要对比两个版本的 CPU 分布。
4、多线程 / 锁竞争 / 上下文切换问题
典型现象:CPU 不高但整体吞吐低;怀疑 spinlock、futex、线程切换过多。
5、缓存未命中、分支预测失败(深度优化)
典型现象:算法已优化,性能仍不理想;怀疑 cache miss、false sharing。
6、内核 + 用户态联合分析
典型现象:驱动、网络、存储程序慢;不确定时间耗在用户态还是内核态。
7、C/C++ 程序无源码时的性能摸底
典型现象:第三方 .so / 闭源二进制慢;只有符号表或部分符号。
三、核心原理——工作原理深度解析
perf 的核心是 Linux 内核的 perf_events 子系统:在内核中注册性能监控事件,按计数或采样方式采集数据,写入 perf.data,再由用户态工具解析展示。
3.1 基本架构

3.2 两种主要工作模式
(1)计数模式(perf stat)
- 对指定事件 全程计数
- 输出汇总:cycles、instructions、cache-misses 等
- 低开销,适合看整体指标
(2)采样模式(perf record / perf top)
- 每隔 N 个事件(如每 4000 个 cycles) 采一次样
- 记录:PC(程序计数器)、进程、线程、调用栈
- 统计哪些函数出现频率高 → 热点
3.3 事件来源
| 类型 | 说明 | 示例 |
|---|---|---|
|
Hardware(PMU) |
CPU 硬件性能计数器 |
cycles, instructions, cache-misses |
|
Software |
内核软件事件 |
context-switches, page-faults |
|
Tracepoint |
内核静态跟踪点 |
syscalls:sys_enter_open, sched:sched_switch |
|
Probe |
动态探针(kprobe/uprobe) |
指定函数入口/返回 |
3.4 调用栈如何获得?(-g 的关键)
C/C++ 程序要看到 函数名 而非地址,需要:
- 编译时加
-g(保留 DWARF 调试信息) - perf record 加
-g(采集 call graph) - 系统允许读取符号(
/proc/sys/kernel/perf_event_paranoid)
四、命令参数——常用选项详解
命令用法 usage: perf [--version] [--help] [OPTIONS] COMMAND [ARGS]
| 参数 | 说明 |
| annotate | 读取 perf.data(由 perf record 生成),并显示 带热点标注的源码/汇编 |
| archive | 根据 perf.data 中的 build-id 打包目标文件,便于 离线分析 |
| bench | 基准测试套件 的通用框架 |
| buildid-cache | 管理 build-id 缓存 |
| buildid-list | 列出 perf.data 文件中的 build-id |
| c2c | 共享数据 C2C/HITM 分析器(分析多核缓存行竞争、伪共享等) |
| config | 读取/设置 perf 配置文件 中的变量 |
| data | 与 perf.data 数据文件 相关的处理操作 |
| diff | 读取多个 perf.data,显示 差异性能报告(优化前后对比) |
| evlist | 列出 perf.data 中记录的 事件名称 |
| ftrace | 内核 ftrace 功能的简易封装 |
| inject | 过滤并 向事件流注入额外信息 |
| kallsyms | 在 运行中的内核 里搜索符号 |
| kmem | 跟踪/测量 内核内存 相关特性 |
| kvm | 跟踪/测量 KVM 客户机(Guest OS) |
| list | 列出所有可用的 符号化事件类型 |
| lock | 分析 锁相关事件(锁竞争等) |
| mem | 分析 内存访问 行为 |
| record | 运行命令并将性能数据 记录到 perf.data |
| report | 读取 perf.data 并 显示性能分析报告 |
| sched | 跟踪/测量 调度器 特性(延迟等) |
| script | 读取 perf.data 并以 脚本/文本形式 输出跟踪内容 |
| stat | 运行命令并收集 性能计数器统计 |
| test | 运行 自检/健全性测试 |
| timechart | 将负载运行期间的 系统整体行为可视化(时间线图表) |
| top | 系统性能实时分析 工具(类似 top,但看热点) |
| version | 显示 perf 可执行文件版本 |
| probe | 定义新的 动态跟踪点(kprobe/uprobe 等) |
| trace | 受 strace 启发 的工具(syscall 跟踪,开销通常低于 strace) |
五、使用实战——实际案例分析
代码实例:perf_demo.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>
/* 热点1:CPU 密集 */
static volatile double sink;
static void cpu_hot(void)
{
double s = 0;
int i = 0;
for (i = 0; i < 50000000; i++)
s += sin(i * 0.000001);
sink = s;
}
/* 热点2:内存拷贝 */
static void mem_hot(void)
{
int i = 0;
char *a = malloc(4 * 1024 * 1024);
char *b = malloc(4 * 1024 * 1024);
if (!a || !b) return;
for (i = 0; i < 100; i++)
memcpy(b, a, 4 * 1024 * 1024);
free(a);
free(b);
}
/* 非热点:sleep IO 等待 */
static void io_wait(void)
{
sleep(1);
}
int main(void)
{
printf("perf demo pid=%d\n", getpid());
cpu_hot();
mem_hot();
io_wait();
printf("done, sink=%f\n", (double)sink);
return 0;
}
1、perf stat——整体性能画像
perf stat -d ./perf_demo
sleep(1) 期间 几乎不占 cycles(perf stat 主要统计运行态)
大量 cycles 在 cpu_hot + mem_hot
下一步 → perf record -g 看具体函
2、perf record + report——定位热点函数
perf record -g -F 997 ./perf_demo
perf report --stdio -n --sort dso,symbol | head -30
| 阶段 | 现象 | 对应源码 |
|---|---|---|
|
热点1 |
|
|
|
热点2 |
|
|
|
非热点 |
|
IO 等待不占 CPU |
3、perf top——实时看谁在跑
./perf_demo &
perf top -p $! -g
运行初期 sin、cpu_hot 条目飙升
随后 memcpy 占比上升
sleep 阶段 perf top 样本几乎不
4、perf annotate——热点到源码行
perf record -g ./perf_demo
perf annotate -s cpu_hot
精确到 哪一行汇编/源码 最耗 CPU,指导优化(如减少 sin 调用、查表法)。
六、常见问题——疑难解答
1、perf report 只显示地址,没有函数名
原因:编译未加 -g;二进制被 strip;权限/paranoid 限制
2、提示 Permission denied / Access denied
原因: kernel.perf_event_paranoid 过严,或需 root 采系统级事件。
3、调用栈不完整或显示 [unknown]
原因:栈回溯方式不合适(frame pointer 被优化掉);缺少 -g 或 -fno-omit-frame-pointer
4、perf 结果「看起来」和 top 不一致
原因:perf 默认采 cycles,top 看 %CPU;多线程、采样频率、off-CPU 时间影响
七、总结——要点回顾
-
perf 跟踪的是 CPU 性能事件和热点函数,回答「时间花在哪些函数/指令上」,而不是「发起了哪些 syscall」。
-
原理是 perf_events 采样/计数,开销通常低于 ptrace 型 strace,适合 短时性能分析,但仍不建议 7×24 全开。
-
C/C++ 分析三板斧:
perf stat ./app # 整体指标
perf record -g ./app # 录热点
perf report # 看报告
-
务必
-g编译 +perf record -g,必要时-fno-omit-frame-pointer,否则调用栈残缺。 -
与 strace 配合最高效: strace 排除 syscall/IO 问题 → perf 定位 CPU 热点 → gdb 深入具体函数。
更多推荐


所有评论(0)