openclaw-test-heap-leaks について
Vitest テストのメモリ増加を調査し、ランタイムのリーク修正を検証するスキルです。プロファイルやヒープ証拠を用いて、モジュールグラフの保持とアプリケーションのリークを区別します。
主な機能
- 01同じテスト設定でメモリ増加を再現
- 02CPU・ヒーププロファイル・スナップショットを収集
- 03モジュール保持とランタイムリークを分類
- 04プロセス単位でヒープスナップショットを比較
- 05専用ハーネスでランタイム修正を検証
活用シーン
- 01Vitest ワーカーのメモリ増加を調査
- 02ヒープスナップショットで保持オブジェクトを追跡
- 03クロージャリークの修正を確認
- 04テスト振り分けとクリーンアップの変更を選択
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ openclaw/ openclaw --skill openclaw-test-heap-leaksインストール後、Claude Code で/openclaw-test-heap-leaksを入力して読み込まれたか確認します。
同梱ファイルagents/openai.yamlscripts/heapsnapshot-delta.mjs
詳細
OpenClaw テストのヒープリーク
テストのメモリ調査には、このスキルを使用してください。ヒープスナップショットを利用できる場合は、RSS だけから推測しないでください。スナップショット名の差分はトリアージの証拠として扱い、リテイナーまたはドミネーターによる裏付けが得られるまでは、確証とみなさないでください。
現在のテストパフォーマンス関連コマンドと証拠の確認先については、最初に ../openclaw-test-performance/SKILL.md を読んでください。
ランタイムの修正(ゲートウェイなどの長時間稼働するサービスにおけるクロージャーリークなど)については、以下のランタイムの修正を検証するを参照してください。これはユニットテストのプロファイリングワークフローではなく、専用のハーネスを使用します。
ワークフロー
-
まず、失敗する状況を再現します。
-
実際のエントリーポイントとワーカーの予算を合わせます。設定ごとの最大 RSS と上位ファイルの処理時間を含む、広範な unit-fast のベースラインを取得するには、次を実行します。
pnpm test:perf:groups \ --config test/vitest/vitest.unit-fast.config.ts \ --allow-failures \ --output .artifacts/test-perf/unit-fast-memory.json -
疑わしいファイルがある場合は、ワーカーを 1 つにしてそのファイルを再実行し、経過時間と RSS の証拠を収集します:
/usr/bin/time -l pnpm test <file> --maxWorkers=1 --reporter=verbose。 -
現在の
pnpm testの実行計画はscripts/test-projects.mtsによって作成されます。出力された Vitest 設定またはシャードを記録し、レポートが設定またはワーカー予算に固有の場合は、その実行条件を維持してください。
-
-
利用可能な中で最も強力なヒープの証拠を収集します。
pnpm test:perf:profile:runner -- --output-dir .artifacts/test-perf/vitest-runner-profile -- <file>を実行し、CPU プロファイルとユニットテストランナーのサンプリングヒーププロファイルを取得します。DevTools でヒーププロファイルを開き、割り当て量の大きいファミリーを調べてください。- サンプリングによる
.heapprofileの出力は.heapsnapshotではありません。スナップショット差分ヘルパーに渡さないでください。 - 専用ハーネスまたは既知の別の生成元が、繰り返し
.heapsnapshotファイルを出力する場合は、.agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjsを使って同一 PID のスナップショットを比較します。 - スナップショットもヒーププロファイルも利用できない場合、RSS の増加は有用なトリアージの証拠ですが、リークの分類は確定できないものとして扱ってください。
-
修正方法を選ぶ前に、増加の種類を分類します。
- 増加の大部分を Vite/Vitest が変換したソース文字列、
Module、system / Context、バイトコード、ディスクリプター配列、またはプロパティマップが占めている場合は、長時間稼働するワーカーでモジュールグラフが保持されている可能性が高いと考えてください。 - 増加の大部分をアプリのオブジェクト、キャッシュ、バッファー、サーバーハンドル、タイマー、モック状態、sqlite の状態、または同様のランタイムオブジェクトが占めている場合は、クリーンアップまたはライフサイクルのリークである可能性が高いと考えてください。
- 名前が曖昧な場合は、確信のある分類を避け、DevTools で差分の大きい項目のリテイナー/ドミネーターを調べてください。
- 増加の大部分を Vite/Vitest が変換したソース文字列、
-
適切なレイヤーを修正します。
- 共有ワーカーで変換済みモジュールが保持されている可能性が高い場合:
- 所有する Vitest 設定と
scripts/test-projects.test-support.mjsのプロセス分割を調べます。同じ実行条件での証拠から、共有ワーカーでの保持が原因だと分かった場合に限り、そこでプロセスの寿命またはプロジェクトの所有範囲を修正してください。 test/vitest/vitest.unit-fast-isolated.config.tsは、新しいモジュールグラフが必要な、状態を持つことが監査済みのテスト向けです。一般的なメモリホットスポットのリストとして使用しないでください。- 実際のリークの場合:
- 問題の原因となっているテスト、またはランタイムのクリーンアップ経路にパッチを適用します。
afterEach/afterAllの不足、モジュールリセットの漏れ、グローバル状態の保持、解放されていない DB ハンドル、またはファイルの実行後も残るリスナー/タイマーを探してください。
-
最も直接的な証拠で検証します。
- 同じグループ化コマンドまたは対象を絞ったコマンドを再実行し、最大 RSS の傾向または OOM が改善したことを確認します。
- 専用の生成元がヒーププロファイルまたはスナップショットを出力する場合は、同じ生成元を再実行し、同等の成果物を比較します。
- 振り分けまたは設定を変更した場合は、想定した Vitest 設定またはシャードが開始し、対象のテストが完了することを確認します。
ヒューリスティック
- 何でもリークと呼ばないでください。分離されていない共有 Vitest プロジェクトでの増加は、アプリケーションオブジェクトのリークではなく、ワーカーの存続期間に関する問題である場合があります。
scripts/test-projects.mts、scripts/test-group-report.mts、scripts/run-vitest-profile.mtsは、現在の実行、グループ別 RSS、プロファイルの各エントリーポイントです。[test] starting ...の行から、再現対象の Vitest 設定またはシャードを特定できます。.artifacts/vitest-shard-timings.jsonには、スケジューリング用の設定/シャードごとの実行時間が保存されています。ファイル単位のメモリホットスポットや動作マニフェストではありません。- 同じワーカー PID で複数の間隔にわたり同じ保持オブジェクト群が増加する場合は、直感ではなくスナップショットを信頼し、判断が難しい呼び出しについてはリテイナーの証拠で確認してください。
スナップショットの比較
- 直接比較:
node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs before.heapsnapshot after.heapsnapshot
- 1 つのレーン内で、PID ごとに最も古い/新しいスナップショットを自動選択:
node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs --lane-dir <snapshot-directory>
- 便利なフラグ:
--top 40--min-kb 32--pid 16133
最初に、増加量が大きい上位の差分を確認してください。モジュール変換アーティファクトが大きく増加している場合は、共有プロセスの存続期間またはプロジェクトの所有権に関する問題が示唆されます。ランタイムオブジェクトが大きく増加している場合は、実際のリークが示唆されます。名前だけでは判断できない場合は、根本原因を断定する前に、DevTools で同じスナップショットのペアを開き、上位の行にあるリテイナー/ドミネーターを調べてください。
ランタイム修正の検証(テストメモリではない場合)
上記のワークフローは、Vitest ワーカーのメモリ増加を診断するためのものです。ランタイム/クロージャーの修正によってキャプチャされた状態が実際に解放されることを検証するには、専用のハーネスを使用してください。
pnpm leak:embedded-run—scripts/embedded-run-abort-leak.tsを実行します。runEmbeddedAttemptを模した関数スコープで、中断した実行を N 回ループし、ヒープスナップショットを書き出します。また、追跡対象インスタンスのカウントにFinalizationRegistryを使用し、RSS の差分と合わせて、保持の増加について PASS/FAIL の判定を報告します。
モード:
closure-extracted(デフォルト)— 本番修正の形(ヘルパーをモジュールスコープに配置)。closure-inline— 修正前の形(ランナースコープ内にクロージャーを配置)。感度チェックとして使用してください。これがパスした場合、ハーネスを壊したのであって、バグを修正したのではありません。synthetic-leak— モジュールレベルのバケットを介して意図的に保持します。実際の修正で PASS を信頼する前に、ハーネスがリークを検出できることの確認に使用してください。
スナップショットは .tmp/embedded-run-abort-leak/ に出力されます。前述と同じスクリプトで差分を取ってください。
node .agents/skills/openclaw-test-heap-leaks/scripts/heapsnapshot-delta.mjs \
.tmp/embedded-run-abort-leak/baseline-*.heapsnapshot \
.tmp/embedded-run-abort-leak/batch-N-*.heapsnapshot --top 30
別のランタイムリークを修正する場合は、このハーネスを改造するのではなく、別のハーネスを追加してください。フィクスチャ関数は、汎用的な中断ループではなく、リークが発生する関数のレキシカルスコープを模倣する必要があります。
出力に関する要件
このスキルを使用する場合は、次の内容を報告してください。
- 正確な再現コマンド。
- 再現した Vitest 設定または対象、およびスナップショットがある場合は比較した PID。
- ヒーププロファイルまたはスナップショット差分で確認できた、主要な保持オブジェクト群。
- 問題が実際のリークの可能性が高いのか、共有ワーカーに保持されたモジュールの増加の可能性が高いのか、またリテイナー/ドミネーターで確認できたかどうか。
- 具体的な修正または影響を軽減するパッチ。
- 検証できた内容、およびスナップショットのオーバーヘッドによって検証できなかった内容。