macos-load-doctor について
macOS Load Doctor は、システムとプロセスの測定結果に基づき、Mac の遅延や発熱の原因を調べるスキルです。プロセスの整理ではなく、リソースの所有者を特定し、共有マシンの権限境界を守る診断を重視します。
主な機能
- 01システム負荷とプロセスの調査
- 02親プロセスをたどった所有者特定
- 03リーク、ストーム、カスケードの分類
- 04メモリー圧力と活動の確認
- 05権限を尊重した対処ガイド
活用シーン
- 01複数アプリで同時に起きるタイムアウトの調査
- 02増え続ける子プロセスの追跡
- 03ビジーループとプロセスストームの判別
- 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 の動作が遅い、または熱くなっている原因を見つけ、所有者を特定し、共有マシンの境界を守って対処します。このスキルの役割は、正確で証拠に裏付けられた原因の特定であり、プロセスのクリーンアップではありません。
それ以外のすべてに優先するルール
マシン全体に及ぶ症状は、そうでないと証明されるまでは環境の問題です。 独立したアプリ、フック、エージェントがすべて同時にタイムアウトしたり、極端に遅くなったりした場合、どれも疑わしい対象ではありません。何かがそれらすべてを飢餓状態にしています。まずマシンを確認し、マシンに問題がないと分かってから個々の対象をデバッグしてください。
トリアージ(確認して実行、順番どおり)
1. まず必ず負荷を確認する
sysctl -n vm.loadavg; uptime
想定される出力は、たとえば { 3.12 4.01 5.20 } のような3つの数値です。絶対値ではなく、コア数と照らし合わせて読み取ってください。最新の 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 アプリやヘルパーの累積 CPU 時間が数日に及び、CPU 使用率が約 100% なら、一時的な急増ではなくビジーループです。
- 瞬間的な %CPU 別 — 今まさに CPU を使っているのは誰か? 累積値では薄まって見えなくなる、現在進行中の急増を検出します。
残存しているエージェント/MCP の子プロセスについては、無駄と判断する前に 状態を持つエージェントと 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. 修正を提案する前に、パターンを分類する
| パターン | 特徴 | 典型的な原因 |
|---|---|---|
| リーク候補 | 同等の処理が完了した後も残存する子プロセス数が増える、またはヘルパーが文書化されたライフサイクルを超えて残る | クリーンアップの欠落。リークと判断する前に、ワークロードとライフサイクルを確認してください |
| 処理の分岐/プール | 子プロセスの多さがアクティブなセッションに伴う、または設定済みの安定したプールと一致する | スレッドごとの所有は、リークではなくても大きなコストになる場合があります |
| 急増 | 実行時間が数秒のプロセスが多数あり、フォーク率が高い | スロットリングされていないループ(テストの再生、バッチスキャン、バックオフなしの再試行) |
| ビジーループ | 1つのプロセスが約 100% の CPU を使い、累積時間が数日に及ぶ | アプリがスリープせずにポーリングしている |
| 連鎖 | 負荷が高い一方で、疑わしいプロセスが分散している | 新しいプロセスが増えるたびに負荷を増幅するシステムサービス(セキュリティスキャン、ファイル同期、Spotlight) |
パターンによって修正方法は異なります。リークが確認された場合は、プロセスを生成する側の修正または再起動が必要です。急増にはループ側でのスロットリングが必要です。ビジーループにはアプリの再起動、またはスキャンの無効化が必要です。連鎖には、新しいプロセスを速くするのではなく、数を減らす必要があります。
5. 境界を守って行動する
共有マシンでは、診断は読み取り専用で行い、修復の実行権限は所有者にあります。
- 他のセッション、エージェント、ユーザーのプロセスを終了してはいけません。「サービスの再起動」がデーモンの子プロセスを終了する副作用を伴うなら、それは体裁を整えた同じ操作であり、プロセスの終了に該当します。
- 自分で行ってよいこと:自分のループをスロットルする、自分のプロセスの優先度を下げる、自分のバックグラウンドジョブを停止する。
- 他のセッション、エージェント、サービス、ユーザーのプロセスを終了または再起動するには、現在のユーザーの明示的な許可が必要です。関係者と所有権を調整しても、その合意が許可の代わりになることはありません。承認が必要な介入だけを保留し、それに依存しない許可済みの読み取り専用調査は続けてください。
- 名前が指定されたアラートについては、原因を特定する前に、記録された期間、測定単位、トリガーの計算方法を再構成してください。別のプロセスの負荷がより高いことは、このアラートが発生した理由の説明にはなりません。アラートの妥当性とマシンの実際のワークロードは、別々の結論として扱ってください。
- 診断または修復の依頼では、進捗報告だけでタスクを終えてはいけません。許可された調査によって競合する説明をまだ区別できるなら、原因が不明なまま終了回答を送らないでください。診断は、特定の仕組みが依頼された症状またはアラートを説明し、その説明と他の説明を区別する証拠が得られた時点で完了します。承認された修復は、独立した読み戻しまで続けてください。スナップショットだけを求められた場合は、そのスナップショットの取得後に終了できます。診断の進行中にステータスを尋ねられた場合は、調査を続けながら進捗を報告してください。明示的な一時停止の指示には従ってください。外部条件によって残りの調査がすべて妨げられている場合は、完了したと主張せず、未完了の事項として、その条件と不足している証拠を示してください。
- 現在のユーザーが介入を許可したら、それを実行して検証してください。すでに許可された操作を報告として差し戻すだけでは、完了したことになりません。
- 修復(自分または所有者によるもの)の後は、必ず読み戻しを行ってください。
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 のメモリガイドでは、空きメモリ、スワップ速度、ワイアードメモリ、キャッシュファイルに基づいてメモリ圧力を定義しています。実行中のエージェントがこれらのシグナルと観測されたワークロードを解釈します。センサスが機械的にメモリリークを分類するわけではありません。
よくあるリークのパターン
実際の調査出力を伴う事例は references/incident-playbook.md にあります。調査で見慣れないものが見つかった場合や、これから作成するレポートの参考事例が必要な場合は、そちらをお読みください。
- スレッドごとの MCP 起動プロセス:エージェントのランタイムが会話スレッドごとに、設定済みの MCP 一式を起動して終了させないため、数日かけて 1 つのデーモンの下に何百ものプロキシ/ヘルパープロセスが蓄積します。GUI を使う MCP サーバーは、さらに WindowServer に過剰な負荷をかけます。
- スロットリングされていないバッチループ:レート制限のないリプレイ/ファジング/移行ループは、マシンのほかの部分から見るとフォークボムと区別がつきません。同じ速度、同じ発熱、同じように連鎖するセキュリティスキャンの負荷が発生します。
- GUI のビジーループ:メニューバーアプリや設定アプリがスリープせずにポーリングすると、累積時間の調査を行うまで気づかれないまま、1 つのコアを何日も 100% 使用します。
- 再起動ループ:launchd ジョブが数秒ごとにクラッシュして再起動します。
launchctl listでは、直前の終了コードがゼロ以外になり、PID が変わり続けていることがわかります。
トラブルシューティング
- 負荷は高いのに、どの測定値も正常に見える:原因は短命のプロセスです。フォーク率を直接測定してください。数秒間隔で
ps -Ao pid=のスナップショットを 2 つ取得し、2 つ目にしか存在しない PID の数を数えます(comm -13 <(sort old.txt) <(sort new.txt) | wc -lの値を間隔で割ります)。毎秒数十件なら大量発生ですが、数件なら通常の入れ替わりです。 - 明らかに CPU 使用率が高い大きなプロセスが無関係に見える:WindowServer、ターミナル、画面共有クライアントの CPU 使用率が高い場合、実際の原因の下流にあることがよくあります(何百もの GUI アプリのコピーがそれぞれウィンドウサービスを必要としているなど)。さらに追跡を続けてください。
- すべての原因がシステムサービスを示している:これは連鎖的な負荷のパターンです。対処すべきなのはサービス自体ではなく、そのサービスに届く新しい処理の速度を下げることです。