关键在采样真实性、调用栈完整性和符号正确解析;perf record 必须加 -g 和 --call-graph dwarf,二进制需带调试信息,stackcollapse-perf.pl 要用 --all 保留全栈,火焰图用于快速聚焦问题而非替代深度分析。

直接用 perf + FlameGraph 生成可用的火焰图,关键不在“能不能画”,而在采样是否真实反映瓶颈、调用栈是否未被截断、符号是否能正确解析——这三点没对,图再好看也没用。
perf record 采样时必须加 -g 和 --call-graph dwarf
默认 perf record -e cycles 只记录函数入口,没有调用栈,火焰图会扁平成一层。必须显式开启调用图采集,且优先用 dwarf 模式:
-
perf record -F 99 -g --call-graph dwarf -p $(pidof your_program)(针对运行中进程) -
perf record -F 99 -g --call-graph dwarf ./your_binary(启动新进程) -
-F 99是经验频率,太高影响目标程序,太低(如 -F 10)可能漏掉短生命周期热点 - 不用
fp(frame pointer)模式:现代编译器(gcc/clang -O2+)默认不保留帧指针,fp会大量丢失栈帧;dwarf依赖调试信息,所以二进制得带-g编译
火焰图脚本要匹配 perf.data 的符号解析能力
FlameGraph 本身不解析符号,它依赖 perf script 输出的文本格式。如果 perf script 打印的是十六进制地址而非函数名,说明符号没加载成功:
- 确认二进制含调试信息:
file your_binary应含with debug_info;readelf -S your_binary | grep debug有输出 - 若用
strip清过符号,需保留.symtab和.debug_*段,或改用objcopy --only-keep-debug分离调试文件再用perf buildid-cache注入 - 内核模块或 JIT 代码(如 Java、Node.js)需额外处理:
perf script -F +pid,+tid看是否有[unknown],有则需启用对应语言的 perf 支持(如 Node.js 要--perf-basic-prof启动)
生成 SVG 时别跳过 stackcollapse-perf.pl 的 --all 配置
stackcollapse-perf.pl 默认丢弃非用户态栈帧(如内核中断、软中断),但某些性能问题恰恰卡在 sys_read → ext4_file_read_iter 这类路径上:
- 用
perf script | ./stackcollapse-perf.pl --all > out.folded,强制保留所有上下文 - 若目标程序是多线程,
--all还能避免线程 ID 混淆导致的栈合并错误 - 生成 SVG 后用浏览器打开,按
/键搜索关键词(如malloc、epoll_wait),比肉眼扫图快得多 - 注意:
--all会让 SVG 文件变大,但火焰图本质是交互式分析工具,不是汇报材料,大小无所谓
真正难的从来不是生成一张图,而是判断「为什么 memcpy 占比突然从 5% 跳到 60%」——这时候得回看 perf report -g 的交互式调用链,结合源码定位具体哪次调用触发了异常拷贝,火焰图只是帮你快速聚焦的那一眼。











