线上有一台Linux机器突然进程卡死、CPU打满,你第一时间会做什么?大部分人就是top看负载,然后赶紧抓调用栈。调用栈这东西,抓是一回事,读是另一回事——几十个线程的栈堆在终端里,互相嵌套,锁的持有关系要人肉理清,稍微复杂一点就得盯半天。我前阵子把Claude Code引入了这个流程,做了个叫pstack-claude的实践项目,让AI编码助手直接吃栈输出、给诊断结论,效率和准确率都提升了一大截。这篇文章就把这套工作流的完整玩法、工具选型、提示词设计和踩过的坑全盘写出来,适合做后端、SRE、平台开发的读者,也适合那些想用Claude Code提升日常排查效率但对AI工具还不太熟的人。
1. pstack-claude到底是做什么的:把“读调用栈”这件事交给AI编码助手
1.1 一个经常发生在凌晨的诊断场景
先说个我在项目里反复遇到的场景:凌晨两点,监控弹出来说某个服务请求延迟飙升,CPU倒是正常。我ssh上去之后,top看到20个线程里有几个状态是D或者S,但看不出问题。传统做法是先ps -L -p PID拿到所有线程号,然后手动挑几个可疑线程,用gdb附加去看bt。运气好的话,一眼就能看到某个线程卡在锁上;运气不好,十几个线程全在锁上等待,就得画图理清谁持有谁、谁等谁。
这个梳理过程非常耗时,因为调用栈的文本其实信息密度很高,但不直观。一次线上问题从抓栈到定位根因,花20到40分钟是常态,其中一多半时间不是在“找”问题,而是在“翻译”栈帧。pstack-claude的出发点很简单:把“翻译栈帧+模式识别”这一步交给Claude Code,人来负责判断和验证。
1.2 为什么“AI读栈”是真的能落地,而不是噱头
我一开始也怀疑过,AI读代码写代码是一回事,读调用栈是另一回事。但实际用下来发现,调用栈反而是AI特别擅长处理的输入。原因有三点:
- 调用栈是文本,而且结构非常规律,每一帧的函数名、偏移、文件行号都在,AI不需要理解线上环境就能做模式识别。
- 死锁、锁顺序反转、线程池耗尽、阻塞IO这几类经典问题,在栈上都有非常固定的“形态”。比如两个线程互相等待,栈上必然出现双向的锁等待关系,这种模式人眼要扫半天,AI几秒钟就能列出来。
- Claude Code跑在终端里,可以直接读取我采集好的栈文件,也可以直接跑命令重新抓一次,整个对话过程比把文本复制到Web聊天框要顺滑得多。
说白了,pstack-claude不是一个多高深的算法项目,它是一套“人机协作的故障诊断工作流”。人要做的三件事:采集栈、给背景、复验结论。剩下大量体力活交给AI。
1.3 这套工作流适合谁、不适合谁
如果你的日常工作涉及Linux进程异常排查,比如服务卡死、线程阻塞、内存异常、性能热点分析,那这套工作流几乎可以直接照搬。但要注意,它对基础能力有最低要求:至少得看得懂top和ps的输出,知道线程号和进程号的区别,否则AI给你的诊断结论你也无从验证。
反过来,如果你是刚接触Linux的新手,我建议先别急着上AI。先把gdb的基本操作、线程栈的阅读方法练出来,再用Claude Code做辅助。原因很简单:AI的结论需要人复核,你如果连栈都读不利索,就失去了最后一道防线。pstack-claude是放大你的排查能力,不是替代你的排查能力。
2. 先把Claude Code装进终端:安装、VSCode与WSL环境适配
2.1 Claude Code的安装路径选择
装了这么多次,我推荐的路径就两种。第一种是npm全局安装,适合已经用Node生态的开发者:
npm install -g @anthropic-ai/claude-code claude --version装完直接在你想要分析的项目目录下执行claude,就能启动一个交互式终端。之所以推荐npm方式,是因为后续升级方便,claude自身支持在线更新,你不需要手动管版本。
第二种是官方提供的curl管道脚本安装,适合不想装Node的机器。但我要提醒一点:curl管道脚本对目录权限要求更敏感,装完很容易遇到我后面要讲的npm prefix权限报错,所以我自己在开发机上一直用npm方式,只有在一次性容器或临时环境里才用脚本方式。容器场景也可以直接拉官方镜像anthropic/claude-code,进去就是现成环境,适合做CI集成,但不适合日常调试,因为容器里通常没有对应服务的完整运行环境。
2.2 在VSCode和WSL里跑起来的关键设置
我日常是在Windows上写代码、在WSL里跑Linux服务,所以Claude Code的安装要落在WSL的Linux一侧,而不是Windows侧。操作上没什么玄机:在WSL终端里执行npm安装,然后确认claude命令在PATH里。真正容易忽略的是,Windows Terminal默认打开的是Windows侧的PATH,如果你在WSL里装完发现claude找不到,先检查你打开的终端是不是WSL会话,而不是PowerShell。
VSCode场景我建议装官方的Claude Code扩展,然后在集成终端里启动claude。这样做的最大好处是,Claude Code能直接感知你当前打开的项目结构,你让它“看看worker.c的第42行”,它能直接定位文件并读出来,上下文是连续的。我的经验是,不要在VSCode的GUI输入框里跟它长篇对话,而是把集成终端当作主交互界面,用命令行方式处理,响应更干净,也方便保留历史。
另外提一句Windows上的一个环境依赖:部分Claude桌面组件在Windows上运行需要开启“虚拟机平台”功能,出现相关提示时,去“启用或关闭Windows功能”里勾上虚拟机平台和适用于Linux的Windows子系统,重启就好。这个跟WSL的底层虚拟化是同一套依赖,开发环境建议提前确认好状态,别等装完再折腾。
2.3 最常见的安装后报错:npm prefix权限问题
装完Claude Code以后,我最常被问到的问题是更新时报错:
Auto-update failed: no write permission to npm prefix这个错的意思是:npm的全局安装目录对当前用户没有写权限,Claude Code想在线更新自己,结果写不进去。排查链路非常简单:
npm config get prefix如果输出的路径在/usr/local或者系统目录下面,基本就是root创建的目录。最简单的办法是切换到nvm管理Node,nvm会把prefix指向用户目录,之后重装Claude Code就不会有这个问题。如果你不想动Node管理方式,也可以直接把当前用户变成npm全局目录的属主:
sudo chown -R $(whoami) "$(npm config get prefix)/lib/node_modules"不过这个操作只建议在单用户开发机上做,多用户机器上还是用nvm隔离更干净。这里多说一句:这个报错不是Claude Code的bug,而是npm全局安装的通用权限问题,你装任何全局CLI工具都可能碰到,所以排查思路可以复用。
3. 栈从哪来:pstack命令的现状和更可靠的采集工具链
3.1 传统pstack命令正在被替代
既然项目叫pstack-claude,很多人以为我会靠传统pstack命令抓栈。实际用过就知道,传统pstack在这套工作流里是可有可无的。它的原理是调用gdb底层能力打印指定进程的栈信息,但在很多现代Linux发行版里并没有预装,需要自己装gdb后才能用。而且它对Java进程、Python进程的支持很差,对高并发多线程进程的栈输出也经常缺线程信息,抓一次还可能让被附加的进程短暂停顿。
所以我现在的态度是:pstack适合“快速瞄一眼”,不适合“完整诊断”。真要让我拿一份稳定的栈文件去喂AI,我宁可优先用下面几个工具,它们的输出更规整、符号解析更完整,AI读起来也不容易误判。
3.2 用一张表选对抓栈工具
| 工具 | 适用场景 | 优点 | 常见坑 |
|---|---|---|---|
pstack | 快速查看单个进程粗略栈 | 命令简单,免gdb语法 | 部分发行版未预装,线程支持弱,输出不全 |
gdb | C/C++/Go等多线程程序完整栈 | 功能最强,可批量打所有线程栈 | 附加进程有短暂停顿,生产环境低峰期用 |
eu-stack | 轻量抓栈,依赖少 | 来自elfutils,输出干净,适合批量 | 对某些动态库符号解析弱,需要符号文件 |
py-spy | Python进程无侵入抓栈 | 不用改代码不需要重启服务 | 有权限要求,D状态进程可能抓不到 |
实际选择上,我会先判断进程类型。C/C++服务优先gdb -p PID -batch -ex "thread apply all bt";Python服务优先py-spy dump --pid PID;如果是混着用的服务,就两种都抓,让Claude Code对着两份栈对比着看。不要一上来就用pstack,它的输出拿到AI面前经常因为缺符号而让AI瞎猜。
3.3 我日常使用的采集脚本
为了让整套流程可复现,我写了一个简单的采集脚本,放在项目目录的tools/下。它会按PID抓取所有线程栈,并保存带时间戳的文件,避免现场被覆盖:
#!/usr/bin/env bash PID=$1 OUT_DIR=${2:-/tmp/stack_dump} mkdir -p "$OUT_DIR" TS=$(date +%Y%m%d_%H%M%S) # 通用多线程栈 gdb -p "$PID" -batch -ex "thread apply all bt" > "$OUT_DIR/${TS}_gdb.txt" 2>&1 # Python进程附加分析 if command -v py-spy >/dev/null && py-spy top --pid "$PID" >/dev/null 2>&1; then py-spy dump --pid "$PID" > "$OUT_DIR/${TS}_pyspy.txt" 2>&1 fi ls -lh "$OUT_DIR/${TS}"_*用的时候直接bash tools/stack_dump.sh <PID>。这里有两个心得:第一,抓之前先top -H -p PID看哪个线程CPU最高,把这个线程号记下来,后面在栈文件里按线程号定位,效率能翻倍;第二,生产环境抓栈尽量放在低峰期,因为gdb附加会让进程短暂停顿,千万不要在用