性能问题从来都是后端工程师绕不开的一道坎。当线上服务突然变慢、CPU 飙高,或者某个接口的 P99 延迟莫名其妙地翻倍时,你需要的不是猜测,而是精确的定位工具。在 Linux 生态中,perf 就是那把最锋利的刀——它内置于内核源码树,几乎零成本安装,却能提供从硬件事件到函数调用栈的全方位洞察。
这篇文章将带你从零开始掌握 perf 的核心用法,重点放在火焰图的生成和热点函数的定位上。读完之后,你应该能独立完成一次完整的性能归因分析。
perf 是什么,为什么选它
perf 是 Linux 内核自带的性能分析工具集,基于 perf_events 子系统实现。它的核心能力包括:
- 硬件性能计数器采样:CPU 周期、指令数、缓存命中/未命中、分支预测失败等
- 软件事件追踪:上下文切换、缺页异常、CPU 迁移等
- 函数级采样与调用栈记录:这是火焰图的数据来源
- 动态探针(kprobe/uprobe):无需修改代码即可追踪内核或用户态函数
相比 gprof、Valgrind 这类工具,perf 的优势在于极低的开销(采样模式下通常低于 5%)和对生产环境的友好性。你不需要重新编译程序,也不需要重启服务,直接在运行中的进程上采集数据即可。
安装与基础检查
大多数发行版的内核已经包含了 perf 的核心功能,但用户态工具需要单独安装:
# Debian / Ubuntu
sudo apt install linux-tools-common linux-tools-$(uname -r)
# RHEL / CentOS / Fedora
sudo yum install perf
# Arch
sudo pacman -S perf
安装完成后,先做一个快速检查:
perf --version
如果提示内核版本不匹配,通常是因为 linux-tools 的版本和当前运行内核不一致。确保安装的是与 uname -r 对应的版本。
另外,出于安全考虑,许多发行版默认将 perf_event_paranoid 设置为较严格的值。你可以查看当前设置:
cat /proc/sys/kernel/perf_event_paranoid
这个值的含义如下:
-1:几乎无限制0:允许访问 CPU 事件,但限制内核采样1:允许内核采样2:仅允许用户态采样3:完全禁止非特权用户使用
为了进行完整的性能分析,通常需要临时调整为 -1:
sudo sysctl -w kernel.perf_event_paranoid=-1
生产环境中请根据安全策略谨慎设置,分析完成后恢复原值。
第一次 perf 采样
假设你有一个 CPU 密集型的进程,PID 为 12345。最基础的采样命令是:
sudo perf record -F 99 -p 12345 -g -- sleep 30
逐项解释这些参数:
-F 99:采样频率为每秒 99 次。选择 99 而非 100 是为了避免与系统定时器产生锁步效应-p 12345:指定目标进程 PID-g:记录调用栈(call graph),这是生成火焰图的关键-- sleep 30:采集持续 30 秒,--后面的命令作为采集的时间控制器
采集完成后,当前目录会生成一个 perf.data 文件。你可以先用 perf report 快速查看:
sudo perf report -i perf.data
这会打开一个交互式界面,按开销从高到低列出所有采样到的函数。你可以展开每个函数查看调用链,找到真正的热点路径。
如果只想看某个特定进程或某个特定符号的开销,可以用:
sudo perf report -i perf.data --stdio | head -50
以文本模式输出前 50 行,适合在脚本中快速筛查。
生成火焰图
perf report 的交互界面虽然强大,但在团队协作和趋势对比中不够直观。火焰图(Flame Graph)由 Brendan Gregg 发明,是目前最流行的性能可视化方式。
获取 FlameGraph 工具
git clone https://github.com/brendangregg/FlameGraph.git
cd FlameGraph
这个仓库里包含了一系列 Perl 脚本,核心是 stackcollapse-perf.pl 和 flamegraph.pl。
从 perf.data 到 SVG
完整流程只需要两条命令:
# 第一步:将 perf.data 转换为折叠栈格式
sudo perf script -i perf.data | ./stackcollapse-perf.pl > out.folded
# 第二步:生成 SVG 火焰图
./flamegraph.pl out.folded > flamegraph.svg
用浏览器打开 flamegraph.svg,你会看到一张横向的火焰图。每个矩形代表一个函数,宽度表示它在采样中出现的比例。Y 轴是调用栈深度,从下往上是调用关系。颜色本身没有特殊含义,只是为了区分不同的栈帧。
火焰图怎么读
读火焰图有几个关键技巧:
- 看顶部最宽的矩形:这些是实际消耗 CPU 的叶子函数。如果某个函数很宽但下面没有子调用,说明它自身就是热点
- 看“平顶”:如果某个函数在顶部有很宽的平顶,说明它的内部逻辑(而非子函数调用)消耗了大量 CPU
- 看调用链:从底部向上追踪,理解热点是如何被触发的
- 搜索特定函数:在 SVG 中按 Ctrl+F 可以搜索函数名,匹配的矩形会被高亮
一个常见的误区是只关注最宽的矩形。实际上,如果一个函数很宽但它的子函数更宽,问题可能出在子函数。火焰图的价值在于展示完整的调用上下文,而非孤立的热点排名。
热点定位的实战思路
拿到火焰图后,如何从视觉信息转化为可操作的优化点?以下是几个典型场景:
场景一:用户态 CPU 热点
如果火焰图顶部集中在某个业务函数,比如 json_parse 或 encrypt_data,说明该函数的计算逻辑本身是瓶颈。优化方向包括算法改进、缓存结果、或者用更高效的库替换。
场景二:内核态开销
如果顶部出现大量 copy_user_enhanced_fast_string、__memcpy 或 _raw_spin_lock,说明瓶颈可能在系统调用、内存拷贝或锁竞争上。这时需要结合 perf stat 查看上下文切换和缓存 miss 率:
sudo perf stat -p 12345 -- sleep 10
输出会包含 context-switches、cache-misses、cycles 等关键指标。
场景三:off-CPU 分析
标准火焰图只展示 on-CPU 时间。如果进程大量时间在等待 I/O 或锁,火焰图会显得很“矮”,无法反映真实瓶颈。这时需要用到 off-CPU 分析:
sudo perf record -e sched:sched_switch -p 12345 -g -- sleep 30
这会记录调度切换事件,生成的火焰图展示的是进程被换出 CPU 的原因和位置。结合 on-CPU 和 off-CPU 两张图,才能得到完整的性能画像。
一些实用技巧
按进程过滤:如果系统上运行着多个进程,可以用 -p 指定 PID,或者用 --comm 按进程名过滤。
采集所有 CPU:不加 -p 时,perf 默认采集所有 CPU。可以用 -a 显式指定,配合 -C 限定 CPU 核心。
限制采样深度:调用栈太深时,可以用 --call-graph dwarf,8192 调整栈大小,或者用 --call-graph fp 使用帧指针(更快但可能不完整)。
差分火焰图:Brendan Gregg 还提供了 difffolded.pl 脚本,可以对比两个时间段的火焰图,直观展示性能退化或改善的位置。这对 A/B 测试和版本回归分析非常有用。
定期采集:在生产环境中,可以配置定时任务定期采集 30 秒的 perf 数据并归档。当性能问题发生时,你就有历史数据可以对比。
小结
perf 加火焰图的组合,是 Linux 性能分析中最实用的技能之一。它不需要修改代码,不需要重启服务,几乎可以在任何生产环境中使用。核心流程可以概括为:perf record 采样 → perf script 导出 → stackcollapse-perf.pl 折叠 → flamegraph.pl 可视化。
掌握这套工具后,你面对性能问题时就不再是“猜”,而是有数据支撑的“定位”。当然,工具只是手段,真正重要的是理解系统的行为模式——火焰图告诉你哪里热,但为什么热、怎么优化,仍然需要你对代码和系统有深入的理解。
未经允许不得转载:任鹏个人博客 » 用 perf 进行 Linux 性能分析:火焰图生成与热点定位


朋友圈点赞图在线生成源码