mcpField
S
stuckgo

stuckgo

本地服务
github.com/GillMacedo开发工具02 周前更新

关于 stuckgo

stuckgo 是面向 Go 开发者的 MCP 服务和命令行工具,可根据 goroutine 转储排查程序挂起。它会归类调用栈、排除常见空闲 goroutine、排列可能原因,并支持比较转储以调查泄漏,全程无需连接服务。

主要功能

  1. 01审查 Go goroutine 转储
  2. 02归类相同的调用栈
  3. 03排除常见的空闲 goroutine
  4. 04排列可能的阻塞原因
  5. 05比较两份转储以检查增长
  6. 06按严重程度输出 JSON 或使 CI 失败

适用场景

  1. 01根据转储排查挂起的 Go 服务
  2. 02比较转储以检查 goroutine 泄漏
  3. 03查找等待锁或数据库连接的 goroutine

MCP 相关

MCP 服务提供工具,可从文本或文件审查单份 goroutine 转储,也可比较同一程序的两份转储。它分析转储数据,不会连接服务。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"gillmacedo-stuckgo": {
"command": "npx",
"args": [
"-y",
"stuckgo"
]
}
}
}
启动命令npx -y stuckgo

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmstuckgo0.1.0

工具

2

收录的工具列表 · 更新于 2 周前

参数类型必填说明
afterstring否较晚时获取的 goroutine 转储文本
beforestring否较早时获取的 goroutine 转储文本
after_pathstring否包含较晚时获取的转储的文件路径
before_pathstring否包含较早时获取的转储的文件路径

详细介绍

stuckgo:这个 Go 程序为什么卡住了?

npm CI MIT license

一个 MCP 服务和命令行工具,可读取 Go goroutine 转储,并告诉你程序在等待什么。它能找到 goroutine 泄漏、锁堆积、数据库连接池耗尽,以及没有超时的 HTTP 调用。它只读取转储文本,因此绝不会连接你的服务。

服务卡住时可能有数千个 goroutine,其中大多数都很正常:监听器、读取循环、空闲工作线程。stuckgo 会将相同的堆栈分组,把通常处于空闲状态的堆栈单独列出,并对其余堆栈进行排序。当有 40 个 goroutine 在等待互斥锁时,它还会指出可能持有锁的对象。

你可以向 Agent 提问

  • “我们的 Go 服务卡住了。这是 goroutine 转储。”
  • “我们有 goroutine 泄漏吗?这是相隔 10 分钟的两份转储。”
  • “这个服务为什么没有可用的数据库连接了?”
  • “是什么在持有这个互斥锁?”

安装

作为 Claude Code 插件:

claude plugin marketplace add GillMacedo/stuckgo
claude plugin install stuckgo@stuckgo-marketplace

作为 MCP 服务,供任意客户端使用:

{"mcpServers": {"stuckgo": {"command": "npx", "args": ["-y", "[email protected]", "serve"]}}}

作为命令行工具,需使用 Node 18 或更高版本:

npx stuckgo review dump.txt

或者使用 Go 1.27 或更高版本:go install github.com/GillMacedo/stuckgo@latest。

用法

从导入了 net/http/pprof 的服务获取转储。这不会停止程序:

curl 'http://localhost:6060/debug/pprof/goroutine?debug=2' > dump.txt

kill -QUIT <pid> 也会输出转储,但会结束进程。崩溃输出也可以使用,GOTRACEBACK=all 会显示所有 goroutine,而不只是发生故障的 goroutine。

以下是 examples/ 中一个虚构服务的真实转储,运行时所在目录是本仓库的克隆:

$ go run . review examples/dumps/mutex-late.txt
43 goroutines, 1 normally idle

Groups:
      40  sync.Mutex.Lock, up to 2 min  main.(*store).Get (app/main.go:109)
       1  chan receive, up to 2 min  main.mutex (app/main.go:130)
       1  select, up to 2 min  main.(*store).Refresh (app/main.go:118)

HIGH   SG107 main.(*store).Get (app/main.go:109)
  40 goroutines waiting for a mutex, waiting up to 2 minutes. Possible holder, not proven: main.(*store).Refresh (app/main.go:118), in select.
  Try: Look at what the holder does while it holds the lock. Don't hold a lock across network calls, channel operations or slow work.

要检查是否存在泄漏,请比较相隔一段时间获取的两份转储。与只看一份转储相比,堆栈数量持续增长,且 goroutine ID 保持不变,是有力得多的证据:

$ go run . compare examples/dumps/leak-early.txt examples/dumps/leak-late.txt
Goroutines: 344 -> 637 (+293)

Growing:
    +293  342 -> 635  chan send  main.lookupPrice.func1 (app/main.go:79)

Same goroutine IDs in the same place in both dumps:
     342  chan send, up to 2 min  main.lookupPrice.func1 (app/main.go:79)

使用 Go 1.27 或更高版本时,Go 也可以自行检查:/debug/pprof/goroutineleak?debug=2。stuckgo 也能读取该输出。

添加 --json 可输出 JSON;添加 --fail-on high 可在 CI 中以 1 退出。使用 - 从标准输入读取。

它能发现什么

规则发现内容
SG101Go 自身的“所有 goroutine 都在休眠”死锁
SG102panic 或致命错误,以及发生位置
SG103Go 的泄漏分析报告为泄漏的 goroutine
SG104对 nil 通道的发送或接收操作,这类操作永远不会返回
SG105等待空闲 database/sql 连接的 goroutine
SG106等待时间过长的出站 HTTP 调用
SG107大量 goroutine 堵在互斥锁上,并列出可能持锁的 goroutine
SG108尚未完成的 WaitGroup,以及它启动了什么
SG109卡在通道发送操作上的 goroutine,这是常见的泄漏情况
SG110大量等待通道接收或 select 的 goroutine
SG111长时间等待网络 I/O
SG112长时间运行的系统调用
SG113长时间等待 sync.Cond.Wait
SG114大量 goroutine 在同一行休眠
SG115在多个调用栈中启动了大部分 goroutine 的单个函数
SG116大量正在运行或等待 CPU 的 goroutine
SG117大量 goroutine 在 debug=1 转储中停在同一行,但转储不会说明原因

严重程度取决于涉及的 goroutine 数量及其等待时长。这些阈值是经验判断。

MCP 工具

工具功能
go_goroutine_review从文本或文件中审查单份转储
go_goroutine_compare比较同一程序的两份转储

限制

  • 发现结果只是线索。 转储只反映某一时刻的情况。请结合代码检查每项发现。
  • 它无法证明是谁持有锁。 Go 的转储不会提供此信息。“可能持锁者”是指同一类型方法中的另一个 goroutine。
  • 单份转储无法证明存在泄漏, 除非它来自 Go 自身的泄漏分析。空闲工作池看起来和泄漏一样,因此请比较两份转储。
  • debug=1 显示的信息较少。 它只提供计数,没有等待时长或 goroutine ID,也会隐藏 goroutine 等待的大部分信息。条件允许时请使用 debug=2。
  • 等待时长只是粗略估计。 Go 只会打印一分钟或更长时间的等待,而且从垃圾回收时开始计时,因此没有显示时长并不意味着等待时间短。
  • 它无法查看 CPU 使用情况。 如果程序运行缓慢,但没有 goroutine 停滞,请获取 CPU 性能分析文件。
  • 通常处于空闲状态的调用栈列表很短: Go 运行时、net/http、database/sql、gRPC 和 golang.org/x/net/http2。其他库的后台循环会以组的形式出现,但很少被列为发现结果。
  • 转储可能包含文件路径和程序中的值。stuckgo 会在本机运行,但 MCP 客户端会将工具结果发送给其模型。

常见问题

如何在 Go 中查找 goroutine 泄漏? 间隔一段时间获取两份转储,然后运行 stuckgo compare。如果某个调用栈数量不断增加,而 goroutine ID 保持不变,就值得调查。

如何从正在运行的 Go 程序获取 goroutine 转储? 导入 net/http/pprof,然后获取 /debug/pprof/goroutine?debug=2。请确保该端点不对外公开。

goroutine 转储中的 [chan send, 5 minutes] 是什么意思? 该 goroutine 在向一个没有接收者的通道发送数据时,已经阻塞了约 5 分钟。SG109 会检测这种情况。

为什么我的 Go 服务没有可用的数据库连接? 等待在 database/sql.(*DB).conn 中的 goroutine 表明连接池已满。SG105 会将其标出。请检查是什么占用了连接,例如执行缓慢的查询或未关闭的 Rows。

这和 panicparse 或 goroutine-inspect 有什么不同? panicparse 和 goroutine-inspect 可帮助用户阅读和探索转储。stuckgo 会为 Agent 提供按优先级排序的发现结果,可以比较两份转储,并排除通常处于空闲状态的内容。

测试情况

  • examples/programs/ 中的实际程序: SG101 到 SG109 的所有规则,以及 SG111、SG114 和 SG117,都在 examples/dumps/ 中那些确实卡住的程序转储上触发了。大多数规则也在 Go 1.22 上运行过。
  • 实际服务: examples/dumps/real/ 中处于空闲状态的 Prometheus 3.7.3 和 etcd 3.6.5,触发的结果均不超过低级别。Prometheus 会产生两个低级别的 WaitGroup 发现,这在该服务中属于正常情况。
  • 仅有编写的示例: SG110、SG112、SG113、SG115 和 SG116。
  • MCP 工具调用通过 MCP Inspector 进行。

目前尚未在生产环境的事故中使用过,也未在全新的 Claude 会话中使用过。

贡献

欢迎大家贡献,包括使用 AI Agent 的人。请参阅 CONTRIBUTING.md。适合新手的议题列在这里。

Pull Request 的处理流程
  1. 创建 PR。 Fork 仓库,在分支上进行开发,并将改动控制在一项。标题应说明用户会看到什么变化,因为它会成为变更日志条目:feat: 会列在“新增”下,fix: 会列在“修复”下。
  2. 自动检查。 代码改动需要附带测试,并且 make test 必须通过。超过 30 行的改动需要关联一个尚未关闭的议题(Closes #<number>),且 PR 的改动行数不得超过 800 行。如果缺少任何内容,系统会发表评论,告知需要修正之处。
  3. 审查。 Claude 会自动审查每个 PR。代码 PR 还会经过安全审查,并在沙盒中测试。随后,审查者会合并 PR 或要求修改。涉及 CI、Agent 设置、npm 或 Python 依赖、供应商代码或构建文件的 PR 无法通过这种方式合并,系统会发表评论说明原因。安全审查未通过的内容也同样无法合并。
  4. 发布。 影响用户安装内容的合并改动会在大约两小时内发布为新版本,并附带 GitHub Release;如果项目有 npm 包,也会一并发布。文档和 CI 改动会等到下一次发布。

AI 使用说明

本项目主要由 Claude Code 生成,并由维护者审查。

关于我

我是 Giulliano Macedo,在 GoTech Solutions 开发开发者工具。欢迎关注或联系我:

如果 stuckgo 找到了你的程序卡住的原因,点个星标可以帮助其他人发现它。


由 Giulliano Macedo、GoTech Solutions 构建 · LinkedIn · X

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。