mcpField
M
macos-load-doctor

macos-load-doctor

github.com/daymade云与 DevOps1.5k49 次安装今天更新
代码仓库

关于 macos-load-doctor

macOS Load Doctor 是用于诊断 Mac 变慢或发热原因的技能,通过系统与进程测量提供证据。它侧重追踪资源使用的归属,并遵守共享机器的授权边界,而非以清理进程为目标。

主要功能

  1. 01系统负载与进程盘点
  2. 02追踪进程父链及归属
  3. 03分类泄漏、风暴与级联负载
  4. 04检查内存压力与活动
  5. 05遵循授权边界的处理指引

适用场景

  1. 01调查多个应用同时超时
  2. 02追踪持续增长的子进程
  3. 03区分忙循环与进程风暴
  4. 04排查系统变慢时的内存压力

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/daymade/claude-code-skills --skill macos-load-doctor

安装后在 Claude Code 中输入/macos-load-doctor确认已加载。

附带文件references/incident-playbook.mdscripts/load_census.sh

详细介绍

macOS 负载诊断

找出导致 Mac 变慢或发热的原因,追溯责任归属,并在共享机器的边界内采取行动。本技能的目标是给出准确、有证据支持的归因,而不是清理进程。

首要规则

在证实并非环境问题之前,应将影响整个设备群的症状视为环境问题。 当多个相互独立的应用、钩子或 Agent 同时开始超时或运行缓慢时,它们都不是嫌疑对象——一定有某种因素在让它们集体资源不足。先检查机器;只有机器状况正常后,才调试个别对象。

分诊(按顺序读取并执行)

1. 始终先检查负载
sysctl -n vm.loadavg; uptime

预期结果:三个数字,例如 { 3.12 4.01 5.20 }。根据核心数来判断,而不是看绝对值:在现代 Mac 上,个位数表示负载平稳,几十表示繁忙;数百意味着运行队列的任务数是可用核心数的数倍,机器上的所有进程都会受影响——钩子会超时、按键会延迟、推送会停滞。如果负载达到数百,但 CPU 列中没有明显的罪魁祸首,通常意味着有数百个小进程,而不是一个大进程。

2. 进程普查——运行随附脚本
LOAD_DOCTOR_DIR='<absolute directory containing this SKILL.md>'
bash "$LOAD_DOCTOR_DIR/scripts/load_census.sh"

从已加载的 Skill 条目中解析该目录;调用方的项目目录不是 Skill 目录。普查结果中的各项读数回答不同的问题:

  • 按父进程聚合(PPID 聚合) — 谁有大量子进程? 数量较大说明存在扇出和资源开销,但不能证明有泄漏。在有限时间段内,结合所有者当前的工作负载和预期生命周期,对比进程的创建、退出和数量。稳定的进程池或活跃会话的辅助进程可能完全正常;若要认定发生泄漏,需要有证据表明子进程的存续时间超过了其服务对象的工作时长,或相同工作完成后保留的进程数量仍在增长。不要通过终止会话来人为制造证据;应使用已有的生命周期记录或自行测试。
  • 按累计 CPU 时间 — 谁已经连续消耗 CPU 数小时? GUI 应用或辅助进程若以约 100% 的 CPU 运行数天,说明存在忙循环,而不是短暂峰值。
  • 按瞬时 %CPU — 谁现在正在消耗 CPU? 可捕捉到累计视图会弱化的当前风暴。

对于保留的 Agent/MCP 子进程,在将其视为无用进程前,请阅读有状态 Agent 和 MCP 子进程。

3. 追溯责任归属

沿着嫌疑进程的父进程链向上追踪,直到找到明确的责任方:

ps -o pid,ppid,etime,command -p <pid>     # then repeat on its PPID
launchctl list | awk '$2 != "-" && $2 != "0"'   # jobs with abnormal last-exit codes

launchctl 筛选结果也会打印标题行,而且许多系统守护进程会一直处于 -9(SIGKILL)状态——这是常见的背景噪声,不代表崩溃循环。判断是否为重启循环,要看多次运行之间任务的 PID 是否一直变化,而不是看某个单独的状态值。

归因可以确定是哪个产品或会话拥有该进程、它是否应当长期运行(守护进程),还是应当短期运行(忘记退出的辅助进程),以及谁有权停止它。

4. 提出修复方案前,先判断问题类型
类型特征常见原因
疑似泄漏可比工作完成后,保留的子进程数量仍在增长;或辅助进程的存续时间超过其文档所述的生命周期缺少清理;认定为泄漏前,先确认工作负载和生命周期
扇出/进程池大量子进程与活跃会话相关,或对应一个配置稳定的进程池按线程分配进程可能开销很大,但不一定是泄漏
风暴大量进程的运行时间只有几秒,且派生速率很高未限速的循环(测试回放、批量扫描、未采用退避机制的重试)
忙循环单个进程以约 100% 的 CPU 运行,累计时间已达数天应用持续轮询却没有休眠
级联负载很高,但嫌疑进程分散某个系统服务放大了每个新进程的影响(安全扫描、文件同步、Spotlight)

每种情况的修复方法都不一样:确认发生泄漏时,应修复或重启进程生成器;发生风暴时,应在循环处限速;出现忙循环时,应重新启动应用或禁用其扫描;出现级联时,应减少新进程,而不是让它们运行得更快。

5. 在边界内行动

在共享机器上,诊断只读;修复由负责人负责。

  • 绝不终止其他会话、Agent 或用户的进程。所谓“重启服务”,如果副作用是杀死守护进程的子进程,那只是用更好听的说法包装了同一操作——仍然算作终止进程。
  • 你可以自行执行的操作:限制自己的循环频率、调整自己进程的优先级、停止自己的后台任务。
  • 终止或重启其他会话、Agent、服务或用户的进程,需要获得当前用户的明确授权。可以与同事协调责任归属,但他们的同意并不构成该授权。只暂停需要审批的干预;继续进行不依赖该审批的已授权只读调查。
  • 对于命名告警,在判定原因前,先还原其记录的时间窗口、计量单位和触发计算方式。另一个进程负载更高,并不能解释该告警为何触发。应分别说明告警是否有效,以及机器的实际负载情况。
  • 对于诊断或修复请求,进度报告不代表任务结束。如果仍有已授权的探查可以区分相互竞争的解释,就不要在原因尚未查明时发送结案答复。只有找到能解释所请求症状或告警的具体机制,并取得能够区分这些解释的证据,诊断才算完成;获批的修复还须通过独立回读来确认。单独要求快照的请求可在提供快照后结束;在诊断进行中询问状态时,应提供进度更新并继续调查。明确要求暂停时,应遵从。如果外部条件阻止了所有剩余探查,应将其列为阻碍,并说明尚缺的证据,不要声称任务已完成。
  • 当前用户授权干预后,应执行并验证;将已获授权的操作作为另一份报告交还,不算完成。
  • 任何修复后(无论由你还是负责人执行),都要回读:重新运行 sysctl -n vm.loadavg 和进程清点,然后通过实际使用该功能的使用方来验证用户原本需要的能力。禁用服务可能会减轻负载,却同时移除了该能力:在功能恢复之前,应报告为缓解措施。重新加载配置前,应确认其影响范围是否涵盖其他线程或服务。比较目标与非目标辅助进程在操作前后的情况;主进程 PID 未变,并不能证明其他辅助进程未受影响。

内存证据

对于内存问题,应在统计进程数的同时测量内存压力和活动情况:

sysctl vm.swapusage
vm_stat -c 4 2
ps -axo pid,ppid,rss,command

读取 vm_stat 打印的页面大小;其第一行是累计值,后续行显示时间间隔内的活动情况。现有交换空间分配量并不能证明当前正在发生抖动:应检查采样时间窗口内的换入/换出活动,并报告该时间窗口。RSS 总量可能会重复计算共享的常驻页面,因此不代表唯一的物理内存。对于可疑进程,使用 vmmap -summary <pid> 区分其报告的物理占用量与 RSS;单独保留报告的 GPU/IOKit 分配量,不要悄悄将不同口径的计数相加。仅凭大量分配或压缩内存快照,无法确定哪些对象被保留,也不能据此认定存在内存泄漏。

Apple 的内存指南将内存压力定义为根据可用内存、交换速率、固定内存和缓存文件进行评估。执行诊断的 Agent 会结合这些信号与观察到的工作负载进行判断;进程清点不会自动判定是否存在内存泄漏。

常见泄漏模式

包含实际探测输出的案例见 references/incident-playbook.md——当清查发现你从未见过的情况时,或需要为即将撰写的报告寻找先例时,请阅读该文档。

  • 每个线程启动 MCP 进程:Agent 运行时会为每个对话线程启动完整配置的 MCP 集合,且从不回收这些进程;几天内,数百个代理/辅助进程便会累积在同一个守护进程下。带 GUI 的 MCP 服务还会给 WindowServer 造成巨大负载。
  • 未限速的批处理循环:没有速率限制的重放/模糊测试/迁移循环,对机器上的其他部分来说与 fork 炸弹无异——速率相同、发热相同,还会导致安全扫描负载级联增加。
  • GUI 忙循环:菜单栏应用或设置应用在轮询时没有休眠——单个核心连续数天保持 100% 占用,直到清查累计时间时才会被发现。
  • 反复重启循环:launchd 任务每隔几秒就崩溃并重启——launchctl list 会显示非零的上次退出代码,以及不断变化的 PID。

故障排除

  • 负载很高,但每次读数看起来都正常:可疑进程的生命周期很短——直接测量 fork 速率:间隔几秒获取两次 ps -Ao pid= 快照,统计只出现在第二次快照中的 PID 数量(comm -13 <(sort old.txt) <(sort new.txt) | wc -l,再除以时间间隔)。每秒几十个说明发生了风暴;只有几个则属于正常波动。
  • 显而易见的高 CPU 进程其实无辜:WindowServer、终端或屏幕共享客户端的 CPU 占用率很高,往往是实际原因的下游表现(数百个 GUI 应用副本都需要窗口服务)。继续追查。
  • 所有线索都指向系统服务:这是级联现象——解决方法是降低到达该服务的新任务速率,而不是处理服务本身。

评论

支持 Markdown