perf是Linux内核自带的性能分析利器,在RHEL体系里做CPU热点定位几乎绕不开它。但真正上手时,perf record这一步往往会卡住:要么直接报权限错误,要么采样跑完了发现数据文件里几乎没内容,要么报告里全是十六进制地址,看不到函数名。这篇文章把perf record采样的原理和常见故障一次性讲清楚,帮你把排查思路理顺。

先搞懂perf record到底在做什么
perf record的核心机制是周期性采样。它通过内核提供的perf_events子系统,向CPU的硬件性能计数器(PMU)或软件事件注册一个采样事件,比如最常见的CPU时钟事件cpu-clock或硬件事件cycles。内核按照设定的频率(默认大约每秒4000次,由-F参数控制)触发中断,把当时正在执行的指令指针(RIP寄存器)以及调用链记录下来,写入内核环形缓冲区,perf再把缓冲区数据落盘成perf.data文件。
理解这个流程后,很多故障就很好解释了。比如采样数据为空,可能是目标进程在那个时间段根本没跑到CPU上;符号显示不出来,是因为perf只负责抓地址,函数名的还原要靠二进制文件里的符号表和调试信息;权限报错,则是因为读取硬件计数器需要内核授权。下面这张表总结了几个关键环节和对应的知识点:
| 环节 | 涉及组件 | 常见故障表现 |
|---|---|---|
| 事件注册 | PMU / perf_events | Permission denied |
| 采样记录 | 内核环形缓冲区 | perf.data为空或极小 |
| 符号还原 | ELF符号表、调试信息 | 报告中只有十六进制地址 |
| 调用链采集 | 帧指针/dwarf | 调用栈断裂、火焰图不完整 |
权限类故障:Permission denied怎么破
在RHEL默认配置下,普通用户执行perf record经常碰到这样的报错:
$ perf record -F 99 -g -p 12345 sleep 10 Error: You may not have permission to collect stats. Consider adjusting /proc/sys/kernel/perf_event_paranoid setting
这个提示指向的是内核参数perf_event_paranoid。RHEL默认值通常是2或4,含义分别是:2表示仅允许用户空间测量自己,4表示完全禁止非root用户使用perf。临时调整可以用sysctl -w kernel.perf_event_paranoid=1,想永久生效就写入/etc/sysctl.d/99-perf.conf。要注意的是,如果这台机器是虚拟机,还可能遇到另一个问题:云厂商或虚拟化平台没有透传PMU,硬件事件根本不可用。
验证硬件事件是否可用,执行perf list | grep -i cycles,如果列表里找不到硬件事件,说明PMU被屏蔽了。这时候可以退而求其次,使用软件事件采样:
# 使用cpu-clock软件事件代替硬件cycles事件 perf record -F 99 -e cpu-clock -g -p 12345 -- sleep 10
软件事件的精度略低于硬件事件,但定位热点函数完全够用,这是在虚拟化环境里最常用的替代方案。另外,如果开启了SELinux enforcing模式,某些场景下还会拦截perf的调用,可以用ausearch -m avc -ts recent确认是否有相关拒绝记录。
符号缺失:报告里全是地址没有函数名
采样成功只是第一步,报告的可读性取决于符号信息。perf report里看到大量类似0x00007f8a3b2c1d40的条目,基本就是符号缺失。内核符号缺失比较好解决,RHEL默认安装了kernel-debuginfo仓库配置但未必安装了包,执行debuginfo-install kernel即可。用户态程序则需要安装对应的debuginfo包,或者确认程序编译时没有strip。
还有一个容易被忽略的点:perf report是在读取时解析符号的,而不是在record时。这意味着你可以先在无符号环境采样,把perf.data拷贝到另一台装了debuginfo的机器上分析,前提是两台机器的软件版本一致。如果目标程序是自己编译的,记得加-g保留调试信息,最好再加-fno-omit-frame-pointer保留帧指针,否则-g采到的调用链会断裂。
# 查看内核符号是否正常加载 perf report -i perf.data --stdio | head -30 # 手动指定符号路径(程序在容器内时常用) perf report --symfs=/path/to/rootfs -i perf.data
容器场景尤其麻烦:目标进程跑在容器里,宿主机上没有对应的二进制路径。这时可以用--symfs指向容器根文件系统在宿主机上的位置,比如/var/lib/docker/overlay2/xxx/merged,或者干脆把perf也放进容器里跑。
采样数据异常:perf.data为空或采样率上不去
采完发现文件只有几KB,报告里几乎没有样本,常见原因有三个。第一,目标进程本身CPU占用很低,采样时间内确实没什么可采的,先确认一下进程是否真的在消耗CPU。第二,采样频率设得太低或者被系统限制了,检查/proc/sys/kernel/perf_cpu_time_max_percent和perf_event_max_sample_rate,当内核认为采样占用CPU过多时会自动降频,dmesg里能看到相关日志。
# 查看内核是否限制了采样率 cat /proc/sys/kernel/perf_event_max_sample_rate # 查看perf自身统计,确认采样是否被节流 perf record -F 99 -g -p 12345 sleep 10 perf report -D | head
第三种情况是采样命令本身的用法问题。比如用perf record -p PID指定了错误进程,或者采样窗口太短还没覆盖到问题时段。线上CPU间歇性飙高的场景,建议用-o分文件持续采样,配合循环脚本保留最近N个文件,等故障复现后立刻有现场数据。
实战流程:从采样到火焰图
排查思路理顺后,完整流程其实不复杂。先确认环境(paranoid参数、事件可用性),再针对进程采样,最后生成可视化报告。生产环境如果装不了FlameGraph工具,可以直接用perf自带的脚本功能:
# 1. 调整权限 sudo sysctl -w kernel.perf_event_paranoid=1 # 2. 对目标进程采样30秒 sudo perf record -F 99 -e cpu-clock -g -p $(pgrep -f myapp) -o perf.data -- sleep 30 # 3. 用内置脚本输出折叠调用栈 sudo perf script -i perf.data > out.stacks # 4. 没装FlameGraph时,直接看文本报告定位热点 sudo perf report -i perf.data --stdio --sort symbol
如果机器能联网或允许内部源,装上flamegraph包后,把perf script的输出交给stackcollapse-perf.pl和flamegraph.pl两步处理就能得到火焰图。火焰图的好处是直观,宽度代表采样占比,一眼就能看出热点集中在哪条调用路径上。
最后提醒两个细节:采样频率不是越高越好,99Hz是社区推荐值,太高会明显影响被测系统性能;采样得到的perf.data包含内存地址等敏感信息,生产环境用完记得及时清理。把这些套路记住,RHEL上用perf record做性能定位基本就不会再卡壳了。
perf record性能采样RHEL性能分析修改时间:2026-09-07 11:12:57