go tool pprof 本身不直接生成火焰图,必须配合 flamegraph.pl 或 Graphviz 的 dot 工具;Go 1.21+ 默认隐藏「Flame Graph」按钮,需手动访问 http://localhost:8080/ui/flamegraph,且依赖足够采样(≥5秒)、完整符号表及本地 dot 或 Perl 环境。

go tool pprof 本身不直接生成火焰图,必须配合 flamegraph.pl 或系统级 dot(Graphviz)才能输出可读的 SVG。Web 界面里点“Flame Graph”按钮经常失效,不是你操作错,是它依赖外部条件没满足。
为什么 go tool pprof -http 打不开火焰图按钮
Go 1.21+ 默认隐藏火焰图入口,即使启动了 go tool pprof -http=:8080 cpu.pprof,左上角菜单也不显示「Flame Graph」。这不是 bug,是设计变更。
- 必须手动访问
http://localhost:8080/ui/flamegraph(注意路径不是 /debug/pprof/ui/flamegraph) - 该页面前端渲染,不调后端,但前提是
cpu.pprof文件里有足够采样(至少 5 秒以上活跃 CPU 时间) - 如果页面空白或提示
no profile data,大概率是采集时长太短、服务根本没跑起来,或 HTTP 接口被防火墙/反向代理拦截 - 更关键的是:这个 UI 会尝试调用本地
dot命令生成 SVG——没装 Graphviz 或dot不在PATH,页面就静默失败,不会报错
go tool pprof -raw -lines 导出的栈全是 [unknown]
这是符号表被 strip 的铁证,和采样逻辑无关。编译时加了 -ldflags="-s" 或 -ldflags="-w",pprof 就无法把内存地址映射回函数名,所有栈帧坍缩成顶层 [unknown] 或 ?。
- 重新编译,去掉 strip 参数:
go build -o server ./main.go(不带任何-ldflags) - 验证符号是否还在:
nm -C server | head -n 5,看到函数名说明 OK;全是十六进制地址说明已丢失 - 若安全合规必须 strip,可用
go build -buildmode=pie -ldflags="-linkmode external"保留部分调试信息,但效果不如原生符号稳定 - Go 1.22+ 的
go tool pprof --symbols强制恢复符号成功率很低,别依赖它
用 flamegraph.pl 渲染前必须确认格式对路
火焰图只认一种输入:折叠栈(folded stack)文本格式,每行是一个分号拼接的调用路径,例如:main.main;http.Serve;net/http.(*conn).serve。其他输出(如 -text、-top)都不能喂给 flamegraph.pl。
- 正确导出命令:
go tool pprof -raw -lines ./server cpu.pprof > stacks.txt - 必须加
-raw:跳过 pprof 内部符号解析与聚合,保留原始采样栈帧 - 必须加
-lines:带上行号,方便后续定位到具体代码行 -
flamegraph.pl必须用 Brendan Gregg 原版(GitHub 上brendan-gregg/FlameGraph),别用 npm 包装的版本——Go 的 DWARF 符号解析容易出错 - 渲染命令:
./flamegraph.pl stacks.txt > flame.svg,打开 SVG 即可查看
线上服务跑 ?seconds=30 很危险
CPU profiling 是基于 setitimer 的信号中断实现,每秒默认采样 100 次。30 秒就是 3000 次上下文切换,对高 QPS 服务来说,P99 延迟可能翻倍,甚至触发熔断。
立即学习“go语言免费学习笔记(深入)”;
- 生产环境采样时长要按需设:压测后分析可设 30 秒;日常巡检建议 5–10 秒足矣
- 避免在业务端口旁直接调
/debug/pprof/profile,最好单独起一个 debug 端口(如127.0.0.1:6061)并限制 IP 访问 - 别用
runtime/pprof.StartCPUProfile硬编码启停——程序提前退出会导致 profile 截断;HTTP 接口方式更可靠 - 如果看到
too many open files错误,是因为net/http/pprof注册了全部 handler(包括高频的/trace),按需 unregister 非必要项
runtime.futex 可能只是 GC STW 或 goroutine 频繁阻塞唤醒,不是锁竞争问题——得交叉看 go tool trace 的 goroutine 分析,否则优化方向容易跑偏。


















