一次离奇的 Go 项目故障排查实录 ------ 全程由 Claude Code 辅助排查
奇怪的故障
某个再普通不过的下午,我像往常一样在 Mac 上开发 Go 项目。写完了代码,go build 成功,一切正常------然后 ./server 启动,没有任何反应。
没有日志输出,没有端口监听,没有报错信息。进程是创建了,但像被施了定身术一样,RSS 始终只有 32 字节。
更诡异的是,我常用的 air(热重载工具)明明能正常运行------它也是 Go 编译的二进制。只有新编译的二进制跑不起来。
如果你也遇到过类似的情况,可能也会和我一样:先是怀疑代码有问题,然后怀疑 Go 版本,最后怀疑人生。
好在这次排查我全程使用了 Claude Code(Anthropic 推出的 AI 编程助手)来辅助诊断。就像有一个经验丰富的 SRE 坐在旁边,帮我一步步缩小范围、排除干扰项。下面就是这次 AI 辅助排查的完整过程。
排查之路:一次次排除,一步步逼近
第 1 步:验证编译
首先确认不是代码问题:
bash
go build ./... # 通过,零错误
go run ./cmd/server/ # 进程创建成功,但...没有任何输出
查看进程状态:
vbscript
PID STAT RSS COMMAND
92672 SN 32 server
RSS 只有 32 字节,这基本上等于进程创建后什么都没干。
第 2 步:查日志(Claude Code 自动发现)
我让 Claude Code 检查项目启动情况,它自动读取了 logs/debug.log,发现日志文件的时间戳没有更新------说明 InitLogger() 根本没执行到。
代码是卡在了某处,而且是在非常早期的初始化阶段。Claude Code 通过分析日志文件时间戳和进程内存状态,迅速将问题范围锁定在进程初始化阶段。
第 3 步:确认二进制文件是完整的(Claude Code 自动检查)
Claude Code 执行了 file 和 otool -l 命令来分析二进制文件:
二进制文件编译正确,Mach-O 结构完整,不是损坏文件。
第 4 步:排除 CGO
怀疑是 CGO 动态链接的问题:
bash
CGO_ENABLED=0 go build -o /tmp/testhello ./cmd/server/
结果:一样失败。排除 CGO。
第 5 步:排除代码签名
尝试手动签名:
bash
codesign -s - --force /tmp/testhello
结果:一样失败。排除签名问题。
第 6 步:排除隔离属性
macOS 会给从网络下载的文件打上 com.apple.quarantine 或 com.apple.provenance 属性。检查后发现新编译的二进制确实有 com.apple.provenance,但正常工作的 air 也有同样的属性------说明不是这个原因。
第 7 步:柳暗花明------发现 syspolicyd(Claude Code 的关键发现)
前面六步排除了所有常见的嫌疑,开始怀疑是系统级的问题。Claude Code 提出检查系统进程状态,敲了一行命令:
bash
ps aux | grep syspolicyd
结果令人震惊:
javascript
root 478 98.7% /usr/libexec/syspolicyd
CPU 占用 98.7%。 这个进程已经跑了 6 天 16 小时,基本处于卡死状态。
第 8 步:确认关联(Claude Code 给出根因链路)
发现 syspolicyd 异常后,Claude Code 立刻给出了完整的因果链解释:
syspolicyd 是 System Policy Daemon,它的职责之一就是验证新二进制文件的代码签名。当系统启动一个新进程时,内核会通过 amfid(Apple Mobile File Integrity)调用 syspolicyd 进行安全验证。如果 syspolicyd 已经卡死,验证请求就无法完成,进程就会停留在加载阶段,永远不会执行代码。
这也解释了为什么 RSS 只有 32 字节------二进制文件被 mmap 到了内存(产生了那 32 字节),但代码从未被执行。
这也解释了为什么 air 能正常工作------它已经加载完毕,不需要再次验证。
根本原因分析
ini
频繁 go build 新二进制
↓
amfid 频繁调用 syspolicyd 验证
↓
验证请求堆积,队列阻塞
↓
syspolicyd CPU 100%,陷入死循环
↓
新二进制加载时被卡在验证阶段
↓
进程 RSS=32,代码永不执行
触发条件:
- 长时间不重启------我这次 Mac 已连续运行 6 天 16 小时
- 频繁编译新二进制 ------开发过程中反复
go build产生大量新 Mach-O 文件 - 可能叠加因素------macOS 自动公证检查超时重试,进一步加剧问题
解决方案
方法 1:重启电脑(简单粗暴,验证有效)
bash
sudo shutdown -r now
重启后 syspolicyd 自然被重置,一切恢复正常。
方法 2:仅重启 syspolicyd(不需要重启电脑)
bash
sudo killall syspolicyd
系统会自动重新启动它,CPU 占用会回落到正常水平。这是最快的修复方式。
方法 3:确认问题是否已解决
bash
# 检查 syspolicyd CPU 占用
ps aux | grep syspolicyd
# 正常运行应在 0-1% CPU
如何预防?
- 每周重启一次 Mac ------macOS 的
syspolicyd长期运行后容易状态异常,定期重启是最简单的预防 - 开发时留意 CPU ------偶尔看一眼 Activity Monitor,发现
syspolicyd异常飙升及时处理 - 保持 macOS 更新------Apple 在后续版本中修复过类似问题
- 没事 kill 一下 ------发现 CPU 异常时立即
sudo killall syspolicyd,不等它卡死
一点感悟
AI 辅助排查的体验
这次排查全程使用 Claude Code 进行。整个过程就像是和一个经验丰富的 SRE 同事结对编程:
- 我只需要说"项目启动不了",它就自动执行
go build、go run、分析日志 - 发现二进制进程 RSS 只有 32 字节时,我甚至不需要手动算------它直接告诉我"进程创建后未执行任何代码"
- 一步步排除 CGO、签名、隔离属性后,它主动提出检查系统进程状态,最终锁定了
syspolicyd - 从发现问题到给出解决方案(
sudo killall syspolicyd),只花了不到 20 分钟
这让我深刻体会到:AI 辅助排查不是取代人的经验,而是把经验放大。 一个有经验的人查这个问题可能更快,但让 AI 帮你做那些重复性的 check、交叉验证、分析日志,可以极大加速排查过程。
回到技术本身
这次排查让我体会到:有时候问题不在你的代码里,而在你的操作系统里。
在 Go 项目开发中,当遇到编译成功但运行异常的情况时,大多数人第一反应是检查代码、检查配置、检查依赖。但如果这些都没问题,不妨看看系统层面------一个卡死的系统守护进程,就足以让你的二进制文件永远停在起跑线上。
macOS 的稳定性总体不错,但 syspolicyd 似乎是一个容易出问题的环节。尤其是对于 Go 开发者来说,频繁的编译行为会触发它的验证机制,把它搞崩溃的概率比一般人想象的要高。
如果你也遇到了 Go 二进制能编不能跑、RSS=32 的诡异现象,先别急着重装系统------查一下 syspolicyd,很可能就是它在作祟。
你有遇到过类似的 macOS 开发环境问题吗?欢迎留言分享你的排查经历。如果你也在用 Claude Code 辅助排查问题,欢迎聊聊你的体验。