用 perf 进行 Linux 性能分析:火焰图生成与热点定位

性能问题从来都是后端工程师绕不开的一道坎。当线上服务突然变慢、CPU 飙高,或者某个接口的 P99 延迟莫名其妙地翻倍时,你需要的不是猜测,而是精确的定位工具。在 Linux 生态中,perf 就是那把最锋利的刀——它内置于内核源码树,几乎零成本安装,却能提供从硬件事件到函数调用栈的全方位洞察。

这篇文章将带你从零开始掌握 perf 的核心用法,重点放在火焰图的生成和热点函数的定位上。读完之后,你应该能独立完成一次完整的性能归因分析。

perf 是什么,为什么选它

perf 是 Linux 内核自带的性能分析工具集,基于 perf_events 子系统实现。它的核心能力包括:

  • 硬件性能计数器采样:CPU 周期、指令数、缓存命中/未命中、分支预测失败等
  • 软件事件追踪:上下文切换、缺页异常、CPU 迁移等
  • 函数级采样与调用栈记录:这是火焰图的数据来源
  • 动态探针(kprobe/uprobe):无需修改代码即可追踪内核或用户态函数

相比 gprofValgrind 这类工具,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.plflamegraph.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 轴是调用栈深度,从下往上是调用关系。颜色本身没有特殊含义,只是为了区分不同的栈帧。

火焰图怎么读

读火焰图有几个关键技巧:

  1. 看顶部最宽的矩形:这些是实际消耗 CPU 的叶子函数。如果某个函数很宽但下面没有子调用,说明它自身就是热点
  2. 看“平顶”:如果某个函数在顶部有很宽的平顶,说明它的内部逻辑(而非子函数调用)消耗了大量 CPU
  3. 看调用链:从底部向上追踪,理解热点是如何被触发的
  4. 搜索特定函数:在 SVG 中按 Ctrl+F 可以搜索函数名,匹配的矩形会被高亮

一个常见的误区是只关注最宽的矩形。实际上,如果一个函数很宽但它的子函数更宽,问题可能出在子函数。火焰图的价值在于展示完整的调用上下文,而非孤立的热点排名。

热点定位的实战思路

拿到火焰图后,如何从视觉信息转化为可操作的优化点?以下是几个典型场景:

场景一:用户态 CPU 热点

如果火焰图顶部集中在某个业务函数,比如 json_parseencrypt_data,说明该函数的计算逻辑本身是瓶颈。优化方向包括算法改进、缓存结果、或者用更高效的库替换。

场景二:内核态开销

如果顶部出现大量 copy_user_enhanced_fast_string__memcpy_raw_spin_lock,说明瓶颈可能在系统调用、内存拷贝或锁竞争上。这时需要结合 perf stat 查看上下文切换和缓存 miss 率:

sudo perf stat -p 12345 -- sleep 10

输出会包含 context-switchescache-missescycles 等关键指标。

场景三: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 性能分析:火焰图生成与热点定位

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏