上周线上出了一个怪问题一个后端服务进程突然CPU飙到100%请求全部超时但日志里没有任何报错。我第一反应是上去用pstack抓一下线程堆栈结果pstack输出出来一大片全是十六进制地址和函数符号看得我头皮发麻。正好那段时间我在折腾Claude Code顺手就做了一个叫pstack-claude的小项目把pstack采集到的堆栈文件丢给Claude Code去做自动分析。这篇文章就聊聊这个项目的完整思路、安装过程和实际效果适合也遇到读堆栈想哭、又正好想体验Claude Code的朋友。1. 为什么要在 Claude Code 里接一套 pstack 分析流程1.1 pstack 到底是干嘛的pstack是Linux下非常经典的进程诊断工具它做的事情一句话概括attach到一个正在运行的进程上把进程内所有线程的调用栈dump出来。很多发行版里它本质上是gdb的封装背后执行的动作差不多是“gdb attach 然后thread apply all bt”最后输出每个线程从当前执行位置到入口函数的一整条调用链。这个工具解决的场景很典型进程突然hang住、CPU飙高、死锁、或者某个线程卡在奇怪的地方。比如一个服务进程所有请求都超时了但业务日志又一切正常这时候pstack能告诉你进程到底在“忙什么”。它是产线事故排查里少数能拿到“第一现场”的手段比抓日志、看监控更接近问题本质。不过pstack也有让人头疼的地方线程一多输出就是几百行甚至上千行符号表不完整的时候看到的全是地址和动态库偏移再加上线程状态、锁等待、内核态调用纠缠在一起人工一行行读非常耗时。这也是我决定把它交给Claude Code的原因。1.2 Claude Code 怎么补上“人工读堆栈”的短板Claude Code是Anthropic官方推出的终端AI编程助手它可以在终端里直接执行命令、读取本地文件、然后结合上下文回答你的问题。这意味着它不只是个聊天机器人而是一个能“看到”你项目环境的真实工具。人工读pstack堆栈最痛苦的是不知道从哪看起。几兆的堆栈文件里到底哪个线程是关键哪些栈帧是噪音哪些调用序列指向锁竞争这些判断需要经验和上下文。Claude Code恰恰擅长这种“给一堆文本找出重点并归纳结构”的活儿它会按线程状态、函数调用层级、锁等待关系把堆栈重新组织成一份摘要再结合你补充的进程信息给出排查建议。pstack-claude这个项目就是想搭一条自动化的流水线pstack负责采集现场Claude Code负责分析和出报告。前者解决“拿不到数据”的问题后者解决“看不懂数据”的问题。两个工具都不新但把它俩串成一个可复用的命令行流程是我个人觉得最实用的部分。2. 环境准备安装 Claude Code 并跑通基础命令2.1 安装前的依赖检查Claude Code本身是个npm包所以最基础的要求是Node.js环境。我用的是Ubuntu 22.04台机器Node版本20.xnpm版本10.x。建议Node.js至少18以上太老的版本会出现依赖解析问题。先检查两个版本号node -v npm -v如果node命令不存在推荐用nvm安装和管理Node版本避免和系统包管理器里的版本冲突。装完之后确认npm registry能正常访问这个命令也是后续安装Claude Code是否能成功的前提。Windows用户需要注意Claude Code的设计一开始就是面向类Unix终端的在Windows上推荐使用WSL安装别直接塞进cmd或者PowerShell里。我在WSL里跑下来比较稳文件路径和pstack这类系统工具也都顺带兼容了。2.2 安装 Claude Code 的正确姿势安装本身很简单全局安装官方包npm install -g anthropic-ai/claude-code装完验证版本claude --version接下来运行claude命令会进入首次登录流程在终端里会生成一个授权链接用浏览器打开完成账号授权之后回到终端就能直接对话了。这里的坑主要是权限问题。如果之前用sudo装过Node或者npm的全局目录被root占用执行claude时可能会发现写不到npm prefix目录导致自动更新失败。后面第5章我会单独讲这里先记着尽量用普通用户权限做全局安装别全程sudo。从终端进入交互界面后随便问一个问题比如“你现在能不能读取当前目录下的文件”确认CLAUDE Code能正常响应就说明环境ok了。2.3 第一个会话把 pstack 结果交给 Claude 分析安装好后先准备一份测试用堆栈文件/tmp/stack.txt里面可以放一个简化版的pstack输出比如Thread 2 (process 11123): #0 0x00007f1234abcd in __futex_abstimed_wait_private () #1 0x00007f1234abcd in pthread_cond_timedwaitGLIBC_2.3.2 () #2 0x00007f1234abcd in std::condition_variable::wait_until() #3 0x00007f1234abcd in WorkerQueue::PopWithTimeout() #4 0x00007f1234abcd in WorkerThread::Run() #5 0x00007f1234abcd in start_thread()然后在项目目录里运行claude -p 请分析 /tmp/stack.txt 中的线程堆栈告诉我这个线程在等待什么可能的问题是什么-p是Claude Code的非交互打印模式适合脚本调用也适合快速的单次问答。你会发现它给出的回答结构相当清晰会把栈帧按调用链拆开指出pthread_cond_timedwait说明线程在等条件变量PopWithTimeout说明这是一个带超时的队列消费下一步应该去看队列里是否有任务积压。这种“先手工喂一个文件再问一句”的方式就是pstack-claude的雏形。3. 核心实现pstack-claude 脚本从零写起3.1 抓取堆栈前的系统配置pstack能工作依赖Linux的ptrace能力而很多系统的kernel.yama.ptrace_scope默认值是1意思是进程只能attach到自己的子进程上普通用户去attach别的用户进程会被拒绝。排查线上问题时最简单的方式是用与被排查进程相同的用户去执行pstack或者直接用sudosudo pstack pid如果想临时放开限制可以改内核参数但生产环境不建议长期关sudo sysctl -w kernel.yama.ptrace_scope0这个改动重启后就失效属于应急手段。另外容器环境下还要注意宿主机可能限制了ptrace系统调用docker启动参数里如果没给--cap-addSYS_PTRACE容器里的pstack也会失败。3.2 用 pstack 采集进程堆栈找到目标进程的PID假设进程名是order-serverpgrep -f order-server拿到PID之后直接采集pstack 12345 /tmp/stack-12345.txt不过我自己的经验是单次pstack往往不够。进程卡死很多时候是有波动的单拍可能刚好拍到一个正在正常处理任务的线程漏掉真正阻塞的线程。所以我一般会间隔3秒采样3次最后合并在一起分析for i in 1 2 3; do pstack $PID /tmp/stack-$PID.txt echo --- sample $i --- /tmp/stack-$PID.txt sleep 3 done还有一类特殊情况如果目标进程是Java应用pstack其实不太能直接看明白JVM内部的线程状态。这时候应该换jstack命令它的输出更贴合Java线程模型包含线程状态RUNNABLE、BLOCKED、WAITING等。pstack-claude脚本里最好自动判断进程类型是Java就调用jstack否则调用pstack。3.3 让 Claude Code 自动分析脚本的核心逻辑就是把“采集堆栈”和“调用Claude分析”串起来。我最初是这么写的#!/usr/bin/env bash PID$1 STACK_FILE/tmp/pstack-claude-$PID-$(date %s).txt if jps -l 2/dev/null | grep -q $PID; then jstack $PID $STACK_FILE else pstack $PID $STACK_FILE fi claude -p 你是Linux性能诊断专家。请分析以下线程堆栈文件$STACK_FILE 。要求 1. 按线程整理调用栈 2. 标出阻塞等待、锁竞争、热点函数 3. 给出下一步排查建议这个脚本最大的优点是可重复、可共享。你不需要每次手动复制粘贴堆栈文本一条命令就能完成从采集到分析的全流程。Claude Code如果配置了默认项目目录还能自动读取进程命令行、配置文件等额外上下文。另外一个思路是把分析流程做成Claude Code的Slash Command放到~/.claude/commands/pstack.md里你是Linux进程诊断专家针对用户提供的pstack或jstack堆栈文件按线程状态、调用栈特征、锁等待关系给出诊断结论。之后在Claude Code交互界面输入/pstack /tmp/stack-12345.txt就能触发固定格式的分析团队里的同学可以共用一套分析模板输出质量会更稳定。4. 真实案例分析一次“进程卡住”的完整排查4.1 现场还原有一回我值班接到告警说某个服务的响应时间从50ms涨到10秒进程还活着但看起来明显“卡”。我登录机器先看进程状态top -H -p pid看到有3个线程CPU占用接近100%业务日志没有异常慢查询、GC日志都不像有问题。当时我用pstack采了三份样本拿到的部分输出长这样Thread 12 (process 22123): #0 0x00007f3a123456 in syscall () #1 0x00007f3a123456 in futex_wait () #2 0x00007f3a123456 in __lll_lock_wait () #3 0x00007f3a123456 in pthread_mutex_lockGLIBC_2.2.5 () #4 0x00007f3a123456 in ConnectionPool::GetConnection() #5 0x00007f3a123456 in QueryRunner::Execute() #6 0x00007f3a123456 in Handler::Process() Thread 5 (process 22123): #0 0x00007f3a123456 in epoll_wait () #1 0x00007f3a123456 in EventLoop::Run() #2 0x00007f3a123456 in main()人工看一眼能看到第12线程卡在ConnectionPool::GetConnection明显是拿不到数据库连接。但为什么拿不到连接池里的连接去哪了单凭这一份堆栈说不清楚。4.2 pstack-claude 给出的诊断我把三份堆栈合并之后直接丢给Claude Code它的回答大概是这样分析线程12和线程5线程5在epoll_wait等于空闲等待不是阻塞源。线程12栈帧显示从Handler::Process - QueryRunner::Execute - ConnectionPool::GetConnection - pthread_mutex_lock等待的锁是连接池内部互斥锁。结合三次采样线程12全部卡在同一个锁上说明锁的持有时间过长或出现了长时间占用。建议查看连接池最大连接数是否被调低、数据库端是否有慢连接占满连接池另外检查是否有连接泄漏。这个结论等于直接帮我锁定了排查方向。它没有说“线程12卡住了”这种废话而是给出了锁等待的“上游链路”用户请求进了Handler到QueryRunner执行SQL再到连接池获取连接时卡住。于是我去查数据库连接数配置发现连接池最大连接数被之前同事临时调到了5而正常请求并发量远高于这个数连接一满后面的请求全部堵在获取连接这一步。问题就这么定位了。4.3 对比人工分析的收益我自己做了个不严谨的对比步骤人工操作pstack-claude理解堆栈内容花5分钟逐行翻译地址、函数名1分钟内输出结构化摘要定位阻塞链路需要脑补多个线程的先后关系自动按调用链串起关键线程给出下一步建议依赖个人经验积累提供可落地的排查命令和配置方向写复盘报告需要单独整理对话结果稍微改就能贴到工单当然它不是万能的符号表丢失、堆栈采样不完整的时候再聪明的模型也得靠人喂上下文。但作为第一轮筛选工具它的效率提升非常明显。5. 常见问题与避坑指南5.1 Claude Code 安装或升级报错排查报错auto-update failed: no write permission to npm prefix这个报错很典型npm全局包安装目录是root的当前用户没有写权限Claude Code想自动更新却写不进去。最简单的解法是把npm全局目录改成用户目录mkdir -p ~/.npm-global npm config set prefix ~/.npm-global export PATH~/.npm-global/bin:$PATH然后重新安装npm install -g anthropic-ai/claude-code之后claude的自动更新就会写到用户目录不会再碰权限问题。报错claude命令找不到通常是PATH没生效。如果是用npm安装成功但还是找不到命令先确认全局bin目录在哪个路径然后把它加到~/.bashrc或~/.zshrc里。升级失败或者反复提示有新版本Claude Code版本更新挺勤快的有时候自动更新会卡住。最简单的方式是手动重新全局安装一次让它覆盖旧版本npm install -g anthropic-ai/claude-codelatest5.2 pstack 抓不到堆栈怎么办如果是权限不足pstack会直接报错Permission denied这时候用同用户执行或者sudo。另一个常见原因是ptrace_scope限制前面第3章里已经给出了临时放开的办法。如果pstack能执行但输出只有地址没有函数名说明调试符号丢失。C/C进程需要用带debug信息的二进制或者安装对应的调试符号包比如debuginfod支持的情况下可以自动拉取。还有有些进程内部用了比较激进的系统调用过滤seccomp会直接拒绝ptrace attach这种情况下pstack无解只能从系统监控、日志和应用层埋点做侧面判断。对Java应用别再对着pstack的空输出发呆直接上jstack。jstack输出里能看到Java线程状态比如java.lang.Thread.State: BLOCKED定位锁问题比pstack直观得多。5.3 Claude Code 的分析结果不靠谱怎么办Claude Code再强也不可能凭一份裸堆栈解决所有问题。我自己遇到过它把一个正常的定时等待当成了死锁原因是没告诉它业务背景。这时不要急着喷模型而是给它更多上下文。可以这样补信息claude -p 这是一个Java订单服务的线程转储服务主要做订单状态流转最近出现大量请求超时。堆栈文件/tmp/stack.txt。请重点说明是否有线程持有锁以及GC线程是否异常信息越多分析越准。还有一个技巧是明确要求它“只基于堆栈文件和给出的上下文回答不要推测你不知道的具体业务逻辑”能有效减少脑补和幻觉。如果担心它胡编可以让它把每一条结论都对应到具体栈帧比如“哪个线程的哪个调用栈支持这个判断”这样就能人工快速复核。最后再分享一点个人体会pstack-claude这个项目做下来我最大的感受是AI编程助手的价值不一定是“替你做决定”更多是“帮你把原始材料变成可读性更高的中间产物”。读堆栈是个典型的苦活Claude Code能把几千行十六进制和函数名整理成一份像样的案情摘要我只需要在摘要上做判断。这套思路不只适用于pstack像日志分析、配置审计、错误堆栈归因都可以照着这个模式套。建议你也先从自己最痛的那个Linux排查场景开始写一个最小的脚本把Claude Code“喂”进去用起来之后再慢慢加细节。