trace.Start()必须传入*os.File,不能传路径字符串或os.Stdout;需用os.Create创建文件并确保trace.Stop()执行以写入完整数据,否则go tool trace报错。

trace.Start() 传参必须是 *os.File,不能是路径字符串
很多人写 trace.Start("trace.out") 直接 panic 或静默失败——trace.Start 只接受 io.Writer,但传字符串会触发类型错误;更隐蔽的问题是传 os.Stdout 或 log.Writer(),混入日志文本导致二进制损坏,go tool trace 报 unknown magic number。
正确做法只有这一种:
- 用
os.Create("trace.out")得到*os.File - 直接传给
trace.Start(f),不要包装、不要转成io.MultiWriter -
trace.Stop()内部已调f.Close(),defer f.Close()不必须,但留着不冲突
trace.Stop() 必须执行,且不能被 os.Exit() 或 panic 跳过
trace.Stop() 不只是“关掉采集”,它负责 flush 缓冲区、写入文件尾部校验信息。如果程序提前退出(比如 os.Exit(0)、log.Fatal()、未 recover 的 panic),defer trace.Stop() 根本不执行,文件只剩 header,大小常为 48 字节或几百字节。
验证方式很简单:ls -l trace.out,有效文件至少 > 1KB;若只有几十字节,说明 trace.Stop() 没跑完。
立即学习“go语言免费学习笔记(深入)”;
可靠写法:
- 避免在
init()里调trace.Start()—— runtime 还没初始化,静默失败 - 别把
defer trace.Stop()放在main()开头就 defer,万一逻辑里有os.Exit()就废了 - 显式调用
trace.Stop()在业务逻辑之后,或用atexit类似逻辑兜底(Go 无原生 atexit,可用signal.Notify捕获SIGINT)
程序必须存活足够久,且触发调度/GC/I/O 才有数据
trace.Start() 启动后默认采样率约 10ms 一次,但只记录真实发生的运行时事件:goroutine 切换、GC、系统调用、channel 阻塞等。纯计算循环、空 select{}、秒级退出的程序,很可能只录到 ProcStart 和 GCStart 几个事件,go tool trace 显示 “No traces found”。
让 trace 有内容的最低成本办法:
- 起一个 goroutine +
time.Sleep(10 * time.Millisecond),强制触发调度 - 主 goroutine 至少
time.Sleep(100 * time.Millisecond),确保缓冲区 flush - 或主动触发 GC:
runtime.GC(),再等几十毫秒 - 别依赖“程序逻辑耗时长”——如果全是 CPU 密集型计算,没调度就没 trace 数据
go tool trace 打不开 trace.out?先看文件本身是否合格
90% 的 “打不开” 问题不是浏览器或命令错,而是文件无效。双击打开、拖进 Chrome、cat trace.out | go tool trace 全都不行——go tool trace 只接受完整二进制文件路径,且要求格式严格。
排查步骤:
- 运行
file trace.out,输出必须含Go execution trace - 确认路径正确:
go tool trace trace.out(Windows 用正斜杠或双反斜杠) - 大文件(>50MB)容易卡死,改用
go tool trace -http=:8080 trace.out启服务,Chrome 访问http://localhost:8080 - 绝对不要 gzip 压缩或重命名
.out为.gz——格式破坏即不可解析
真正难搞的点不在代码怎么写,而在你得意识到:trace 不是函数 profiler,它不记录你写的任何函数名,也不采样 CPU 时间。它只忠实地记下 runtime 干了什么——调度器有没有卡、GC STW 多久、syscall 为什么阻塞。想查 handler 耗时,该用 pprof;想串跨服务链路,该用 OpenTelemetry。


















