火焰图 CPU 性能分析教程
火焰图(Flame Graph)是由 Brendan Gregg 发明的性能可视化方法,能够将 CPU 采样数据转化为直观的交互式图形。通过火焰图,你可以一眼看出哪些函数消耗了最多的 CPU 时间。本文将介绍在搬瓦工 VPS 上从数据采集到火焰图生成的完整流程。
一、火焰图的工作原理
火焰图的核心概念:
- Y 轴:表示调用栈深度,从下到上是函数的调用链。
- X 轴:按函数名字母排序,宽度代表该函数在采样中出现的比例。
- 宽度:函数方块越宽,表示它消耗的 CPU 时间越多。
- 颜色:随机的暖色系,仅用于视觉区分,不代表特殊含义。
最宽的"平台"就是性能瓶颈所在。
二、环境准备
# 安装 perf(数据采集工具)
apt update
apt install linux-tools-common linux-tools-$(uname -r) -y
# 下载 FlameGraph 工具集
git clone https://github.com/brendangregg/FlameGraph.git /opt/FlameGraph
# 添加到 PATH(可选)
echo 'export PATH=$PATH:/opt/FlameGraph' >> ~/.bashrc
source ~/.bashrc
三、CPU 火焰图生成
3.1 采集数据
# 全系统 CPU 采样 30 秒
perf record -F 99 -a -g -- sleep 30
# 针对特定进程采样
perf record -F 99 -p PID -g -- sleep 30
# 使用 DWARF 调用栈(更准确,适合没有帧指针的程序)
perf record -F 99 -p PID --call-graph dwarf -- sleep 30
3.2 生成火焰图
# 导出采样数据
perf script > out.perf
# 折叠调用栈
/opt/FlameGraph/stackcollapse-perf.pl out.perf > out.folded
# 生成 SVG 火焰图
/opt/FlameGraph/flamegraph.pl out.folded > cpu-flamegraph.svg
# 一条命令完成
perf script | /opt/FlameGraph/stackcollapse-perf.pl | /opt/FlameGraph/flamegraph.pl > cpu-flamegraph.svg
3.3 自定义火焰图样式
# 设置标题
/opt/FlameGraph/flamegraph.pl --title "Nginx CPU Profile" out.folded > nginx-cpu.svg
# 设置宽度
/opt/FlameGraph/flamegraph.pl --width 1200 out.folded > wide.svg
# 反向火焰图(从上到下,也叫冰柱图)
/opt/FlameGraph/flamegraph.pl --reverse out.folded > icicle.svg
# 过滤特定函数
grep "mysql" out.folded | /opt/FlameGraph/flamegraph.pl > mysql-only.svg
四、不同类型的火焰图
4.1 Off-CPU 火焰图
Off-CPU 火焰图显示进程被阻塞等待的时间分布,帮助发现 I/O、锁等待等问题。
# 使用 BPF 工具采集 Off-CPU 数据
apt install bpfcc-tools -y
# 采集 Off-CPU 数据
offcputime-bpfcc -df -p PID 30 > off-cpu.stacks
# 生成 Off-CPU 火焰图(使用蓝色系)
/opt/FlameGraph/flamegraph.pl --color=io --title "Off-CPU Time" off-cpu.stacks > offcpu.svg
4.2 内存分配火焰图
# 记录内存分配事件
perf record -e kmem:kmalloc -a -g -- sleep 10
perf script > mem.perf
/opt/FlameGraph/stackcollapse-perf.pl mem.perf > mem.folded
/opt/FlameGraph/flamegraph.pl --color=mem --title "Memory Allocation" mem.folded > memory.svg
4.3 差分火焰图
对比优化前后的性能变化:
# 采集优化前的数据
perf record -F 99 -a -g -- sleep 30
perf script > before.perf
/opt/FlameGraph/stackcollapse-perf.pl before.perf > before.folded
# 执行优化操作...
# 采集优化后的数据
perf record -F 99 -a -g -- sleep 30
perf script > after.perf
/opt/FlameGraph/stackcollapse-perf.pl after.perf > after.folded
# 生成差分火焰图
/opt/FlameGraph/difffolded.pl before.folded after.folded | \
/opt/FlameGraph/flamegraph.pl > diff.svg
差分火焰图中红色表示性能恶化(时间增加),蓝色表示性能改善(时间减少)。
五、读图技巧
- 找最宽的方块:这是 CPU 时间消耗最多的函数,优先优化。
- 找宽阔的平台:函数自身(不含子函数)消耗大量 CPU 的地方。
- 注意栈深度:过深的调用栈可能暗示递归或低效的抽象层。
- 搜索功能:SVG 火焰图支持在浏览器中搜索函数名。
- 点击缩放:点击某个函数可以将其放大为全宽查看细节。
六、不同语言的火焰图
Java 应用
# 使用 async-profiler
wget https://github.com/async-profiler/async-profiler/releases/latest/download/async-profiler-linux-x64.tar.gz
tar xzf async-profiler-linux-x64.tar.gz
# 生成火焰图
./async-profiler/bin/asprof -d 30 -f flamegraph.html PID
Node.js 应用
# 使用 0x 工具
npm install -g 0x
0x my-app.js
# 或使用 perf + --perf-basic-prof 标志
node --perf-basic-prof my-app.js &
perf record -F 99 -p $! -g -- sleep 30
Python 应用
# 安装 py-spy
pip install py-spy
# 生成火焰图
py-spy record -o profile.svg --pid PID --duration 30
七、自动化采集脚本
cat > /usr/local/bin/flame-profile.sh <<'EOF'
#!/bin/bash
DURATION=${1:-30}
OUTPUT=${2:-"flamegraph-$(date +%Y%m%d-%H%M%S).svg"}
FLAMEGRAPH_DIR="/opt/FlameGraph"
echo "Profiling for ${DURATION} seconds..."
perf record -F 99 -a -g -- sleep "$DURATION"
echo "Generating flame graph..."
perf script | \
"$FLAMEGRAPH_DIR/stackcollapse-perf.pl" | \
"$FLAMEGRAPH_DIR/flamegraph.pl" --title "CPU Profile (${DURATION}s)" > "$OUTPUT"
echo "Flame graph saved to: $OUTPUT"
echo "Open in browser: python3 -m http.server 8080"
EOF
chmod +x /usr/local/bin/flame-profile.sh
# 使用:采集 60 秒数据
flame-profile.sh 60 my-profile.svg
总结
火焰图是定位 CPU 性能瓶颈最直观有效的方法,建议与 perf 工具 配合使用。对于更动态的追踪需求可以使用 BPFtrace,系统调用层面的分析可以使用 strace。选购搬瓦工 VPS 请查看 全部方案,购买时使用优惠码 NODESEEK2026 可享受 6.77% 的折扣,通过 bwh81.net 访问官网。