Linux perf script 与 FlameGraph 的热点函数可视化自动化脚本

1次阅读

perf script 输出不能直接喂给 flamegraph.pl,因其输出为原始采样事件流,而 flamegraph.pl 仅接受折叠格式(每行一个分号分隔的调用+次数),否则生成空白或错误 svg。

Linux perf script 与 FlameGraph 的热点函数可视化自动化脚本

perf script 输出为什么不能直接喂给 flamegraph.pl?

因为 perf script 默认输出的是原始采样事件流(含时间戳、PID、符号地址、调用栈等杂项),而 flamegraph.pl 只认“折叠后”的调用栈格式:每行一个栈,函数名用分号分隔,末尾是抽样次数。不折叠就生成空白或报错 SVG。

  • 常见错误现象:flamegraph.pl out.perf-script 生成的 SVG 里全是空白,或者只有一条扁平的「[unknown]」横条
  • 真正该喂进去的是 stackcollapse-perf.pl 处理后的文件,比如 out.perf-folded
  • 如果你跳过折叠步骤,flamegraph.pl 会把每一行当成一个独立函数,完全丢失调用关系

怎么写一个安全可用的自动化脚本?

核心就三步:record → script → fold → svg,但必须加容错和路径控制,否则在 CI 或多用户环境容易炸。

  • perf record -g -F 99 -p $PID -- sleep 10 比裸跑命令更稳,避免因进程提前退出导致 perf.data 不完整
  • perf script 后建议加 2>/dev/NULL,某些内核版本会在 stderr 吐 warning 干扰管道
  • 折叠前先检查 /tmp/perf-$PID.map 是否存在(Node.js 等 JIT 语言需要它还原符号),缺失时火焰图里会出现大量 [jitted] 或十六进制地址
  • 生成 SVG 时用绝对路径写入,比如 $(pwd)/flamegraph-$(date +%s).svg,防止同名覆盖或权限问题

为什么 -g 参数漏掉就白忙活?

-g 是开启调用图(call graph)采集的开关,没它 perf record 只记下当前 CPU 正在执行的函数,不记录“是谁调的它”。没有调用栈,火焰图就只剩一层——根本看不出瓶颈在哪儿。

  • 错误示范:perf record -e cycles -p 1234 → 火焰图所有函数都悬浮在底部,无父子关系
  • 正确写法必须带 -g,且推荐搭配 -F 99(不是越高越好,1000Hz 在高负载机器上可能失真)
  • 注意:-g 依赖内核的 frame pointer 支持;若程序用 -fomit-frame-pointer 编译(如很多 Go 程序),需改用 --call-graph dwarf,但开销更大

生产环境跑这个脚本要注意什么?

perf 本身是轻量采样,但连续高频采集 + 生成 SVG 仍可能短暂推高 CPU 和磁盘 IO,尤其在容器或低配实例上。

  • 别在高峰期对关键进程跑 sleep 30,用 --duration 5 快速抓点(BCC 的 profile 工具更适合长期监控)
  • 生成的 perf.data 文件默认存当前目录,大应用采样 10 秒可能超百 MB,记得 rm -f perf.data 清理
  • 如果目标进程是 Java,得额外加 -e cpu-clock,java:vm:gc 等事件,单纯 cpu-clock 会漏掉 safepoint 停顿

真正的坑不在命令写错,而在你信了“一次采样全看清”——火焰图只反映 CPU 时间分布,IO 等待、锁竞争、GC 暂停这些非 CPU 开销,得换 off-cpu 火焰图或 perf lock 等专项工具。

text=ZqhQzanResources