go tool trace 是 Go 自带的轻量级执行追踪器,记录调度事件而非底层寄存器;它不直接报死锁,但可定位阻塞点,需结合状态、调用栈和时间线分析卡顿与真死锁。
没有 go tool ptrace 这个命令,go 官方工具链里不存在它——你大概率是把 go tool trace 和 linux 的 ptrace 系统调用记混了。
go tool trace 能看什么、不能看什么
go tool trace 是 Go 自带的轻量级执行追踪器,它记录的是 Go 运行时调度事件(goroutine 创建/阻塞/唤醒、GC、系统调用等),不是底层寄存器或内核态堆栈。它对死锁诊断的价值在于:能定位“谁在等什么”,但**不直接报死锁**;死锁 panic 由运行时自己触发,trace 只帮你提前发现卡点。
常见误判场景:
- 看到某个 goroutine 长时间停在
chan send状态 → 不代表死锁,可能只是接收方还没就绪(比如刚启动、逻辑分支未走到) - 多个 goroutine 停在
sync.runtime_SemacquireMutex→ 锁争用严重,但只要还有一个 goroutine 在跑,就不会触发all goroutines are asleep - trace UI 中 “Goroutines” 标签页里大量 goroutine 显示为 “Runnable” 却迟迟不执行 → 往往是 P(processor)不足或 GOMAXPROCS 设置过低,不是死锁
真正卡死时,trace 文件里最该盯的三个信号
当程序已 panic 出 fatal error: all goroutines are asleep - deadlock!,说明所有 goroutine 全部阻塞且无 runnable。此时回溯 trace 文件(需在 panic 前已开启),重点筛:
- 状态为
chan receive或chan send的 goroutine,且持续时间 >100ms —— 尤其注意它们是否都卡在同一个ch地址上 - 调用栈中出现
runtime.gopark+runtime.chansend或runtime.chanrecv—— 这是 channel 阻塞的底层标记 - 没有一个 goroutine 处于
Running或Runnable状态,全部是Waiting或Sleeping—— 这是 runtime 判定死锁前的真实快照
注意:go tool trace 默认只采样 5 秒,若死锁发生在第 6 秒,就捕不到。实操中建议加 ?seconds=30 并手动触发 panic 前多等几秒。
为什么不用 pprof goroutine profile 就去翻 trace
pprof 的 /debug/pprof/goroutine?debug=2 给出的是**静态快照**:哪个 goroutine 卡在哪一行、等什么资源,一目了然;而 trace 提供的是**时间线行为**:它能告诉你这个 goroutine 是第 2.3 秒开始阻塞、第 4.1 秒被唤醒(或没被唤醒)、中间有没有被调度器短暂切换过。
二者互补,但优先级不同:
- 刚发现卡顿 → 先 curl
http://localhost:6060/debug/pprof/goroutine?debug=2,3 秒内出结果,直击卡点行号 - 卡顿反复出现、pprof 看不出明显瓶颈 → 再启
go tool trace,拉长时间窗口,观察 channel 收发节奏是否错配、是否有 goroutine 启动后立刻 sleep - trace 里看到某 goroutine 在
net/http.(*conn).serve里长期Waiting→ 实际可能是 TLS 握手超时或客户端断连未清理,和死锁无关,别浪费时间查 channel
真正难缠的不是 panic 那一刻,而是那些“没 panic 却永远不推进”的 case:goroutine 数稳定在 200,每个都卡在 select { case ,但 ch 永远没人 close、也没人 send。这种问题 trace 能暴露模式,pprof 只显示“停在这行”,看不出上下文依赖。











