直接用 pprof + flamegraph.pl 三步生成火焰图:开启 HTTP 采样、pprof 下载解析、flamegraph.pl 渲染 SVG;注意避免符号缺失、格式错误及 profile 类型误用。

Go 程序 CPU 占用高但看不出哪段代码拖慢?直接上 pprof + 火焰图,不是“教程”,是定位问题的最小可行路径。
怎么快速生成 Go 火焰图(go tool pprof + flamegraph.pl)
火焰图本身不是 Go 内置功能,而是靠 pprof 采样 + 外部脚本渲染。核心链路就三步:
- 用
net/http/pprof或runtime/pprof开启性能采样(HTTP 方式最常用) - 用
go tool pprof下载并解析profile数据(默认是 30 秒 CPU 采样) - 用 Perl 脚本
flamegraph.pl把pprof输出转成 SVG 火焰图
示例命令流(假设服务跑在本地 :6060):
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30 # 按提示输入 'web' 会自动打开浏览器;想存为火焰图,改用: go tool pprof -http=:8080 http://localhost:6060/debug/pprof/profile?seconds=30 # 或导出原始数据再渲染: curl -s "http://localhost:6060/debug/pprof/profile?seconds=30" > cpu.pprof go tool pprof -raw cpu.pprof | ./flamegraph.pl > flame.svg
注意:flamegraph.pl 需单独下载(Brendan Gregg 原版),别用 npm 包装过的版本——Go 的 symbol 表解析容易出错。
立即学习“go语言免费学习笔记(深入)”;
为什么 runtime/pprof.StartCPUProfile 生成的文件打不开?
直接调用 StartCPUProfile 写出的是二进制格式,但 go tool pprof 默认期望的是 HTTP 接口返回的协议缓冲格式(或 -raw 模式下的文本堆栈)。常见报错如 failed to fetch any profiles 或 unrecognized profile format 就是这个原因。
- 要么改用
go tool pprof -raw your-cpu.pprof(需确保写入时用了WriteTo且未被截断) - 要么统一走 HTTP 接口:哪怕离线程序,也建议起个临时 HTTP server 注册
/debug/pprof/,更稳定 - 别手动修改
.pprof后缀名——扩展名不决定格式,内容头才决定
火焰图里全是 runtime.mcall、runtime.goexit?说明没采到业务代码
这是典型符号缺失或编译优化干扰。Go 默认开启 -ldflags="-s -w" 会剥离调试信息,导致 pprof 无法还原函数名。
- 编译时去掉
-s -w:go build -ldflags="" main.go - 确保运行时没被容器或 systemd 限制
perf_event_paranoid(Linux 上需 ≤ 2,否则内核拒绝采样) - 如果用 CGO,确认
gcc版本兼容(太新可能生成 DWARF 不被 Go pprof 识别) - 交叉编译的二进制,在目标机器上采样——本地分析远程 profile 容易丢符号
火焰图扁平、没调用栈深度?检查 runtime.SetBlockProfileRate 和 mutexprofile
CPU 火焰图只反映执行时间,IO 阻塞、锁竞争、GC 暂停这些不会出现在 CPU 图里。如果你看到“热点”很浅,但实际延迟高,大概率是其他类型瓶颈。
- 加
blockprofile 查协程阻塞:http://localhost:6060/debug/pprof/block?seconds=30 - 加
mutexprofile 查锁争用:http://localhost:6060/debug/pprof/mutex?seconds=30 -
runtime.SetBlockProfileRate(1)才能捕获细粒度阻塞事件(默认是 1e6,基本采不到) - GC 压力看
goroutineprofile 或gctrace=1日志,火焰图不体现
真正难的不是画图,是判断该采哪种 profile、在哪台机器上采、采多久——线上服务不敢随便开 30 秒 CPU 采样,得先用 5 秒试探,再结合日志和 metrics 锁定窗口。


















