strace比go tool trace更适查系统级阻塞,因它直接挂钩内核syscall entry/exit,不依赖go运行时,能捕获cgo调用、runtime自身失败及所有线程阻塞点,并显示具体系统调用、参数与errno。

Go 程序卡死、超时、文件打不开、网络连不上?别急着翻源码或加日志——先用 strace 看它到底在内核层干了什么。 它比任何 Go 自带工具都更底层、更真实,尤其适合诊断那些 pprof 和 go tool trace 都看不到的阻塞点(比如 openat 权限拒绝、connect 被防火墙静默丢包、epoll_wait 永久挂起)。
为什么 strace 比 go tool trace 更适合查系统级阻塞
go tool trace 只能捕获 Go 运行时感知到的系统调用(即通过 runtime.syscall 封装的那部分),且依赖 Go 的调度器上报。而 strace 直接挂钩内核 syscall entry/exit,不经过 Go 运行时——这意味着:
- 它能看到 CGO 调用的原生 libc 函数(如
getaddrinfo、pthread_create) - 它能暴露 Go runtime 自身的系统调用失败(比如
mmap返回ENOMEM导致 panic 前的最后一步) - 它不受 goroutine 调度影响:哪怕整个程序卡在
read上、所有 goroutine 都阻塞,strace仍能准确定位那个没返回的调用 -
go tool trace的Syscall blocking profile只显示“有 goroutine 在 syscall”,但不会告诉你调用了什么、参数是什么、errno 是多少——strace全部给你
追踪正在运行的 Go 服务:-p + -f + -ttt 最小可行组合
线上 Go 服务响应变慢,ps aux | grep myapp 找到 PID 后,直接附加:
strace -p 12345 -f -ttt -o strace.log 2>&1
关键参数说明:
-
-p 12345:必须,附加到已有进程(不是启动新进程) -
-f:必须,Go 程序大量使用clone创建线程(如 netpoller、timer thread),不加-f会漏掉这些子线程的 syscall -
-ttt:强烈推荐,输出从 epoch 开始的微秒级时间戳,方便和应用日志对齐(比如你看到某条read耗时 3.2 秒,就能去查同一毫秒的应用日志) -
-o strace.log:必须重定向,否则刷屏无法分析
注意:strace -p 会暂停目标进程短暂时间(纳秒级),但频繁 attach/detach 可能干扰生产环境;建议单次采集 10–30 秒后 Ctrl+C 中断。
过滤关键系统调用:-e trace= 降低噪音
默认输出包含上千行无关的 rt_sigreturn、clock_gettime,直接看 grep 效率低。用 -e trace= 精确聚焦:
- 查文件问题:
strace -e trace=open,openat,stat,fstat,read,write -p 12345 - 查网络问题:
strace -e trace=socket,connect,bind,listen,accept,sendto,recvfrom -p 12345 - 查进程/线程问题:
strace -e trace=clone,fork,vfork,execve,wait4 -p 12345 - 查阻塞点(最常用):
strace -e trace=%file,%network,%process -p 12345(%file是预设宏,等价于上面所有文件相关调用)
重点看返回值:正常是数字(如 openat(...)=3),错误是 -1 加 errno(如 openat(...)= -1 ENOENT (No such file or directory))。ENOENT、EACCES、ETIMEDOUT、ECONNREFUSED 这些就是根因线索。
Go 程序特殊注意事项:静态链接、容器权限、CGO 干扰
Go 默认静态链接,strace 依然有效(它作用于 ELF loader 层,不依赖动态符号表)。但以下情况需额外处理:
- 容器内运行:Docker/K8s 默认禁用
ptrace,启动容器时要加--cap-add=SYS_PTRACE,否则strace -p报错Operation not permitted - CGO_ENABLED=0 时没问题;但若启用了 CGO(比如用了
cgo调用 OpenSSL),strace仍能追踪,只是部分 libc 调用(如getaddrinfo)可能被封装成更复杂的 syscall 序列,需结合-y(显示文件路径)和-s 1024(增大字符串截断长度)看清参数 - Go 1.20+ 引入了
io_uring支持(如net.Conn.Read可能走io_uring_enter),strace能看到,但需确认内核版本 ≥5.10 且编译时启用,否则 fallback 到传统read/epoll
真正容易被忽略的是:Go 程序的 stderr 可能被重定向(比如 2>/dev/null),导致 strace 输出也消失——务必用 -o 显式指定文件,不要依赖终端重定向。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











