mcpField
D
debugging-code

debugging-code

github.com/almogbaku開発335575 回インストール28 週間前に更新
リポジトリ

debugging-code について

クラッシュや誤った出力、予期しない実行経路を調べるための対話型デバッグスキルです。dapでプロセスを再起動せずに実行状態を確認できます。

主な機能

  1. 01実行中の変数とコールスタックの確認
  2. 02行単位のステップ実行とブレークポイント
  3. 03条件付き・例外ブレークポイント
  4. 04実行中プロセスでの式の評価
  5. 05DAPバックエンド対応言語のサポート

活用シーン

  1. 01誤った値の発生源を追跡
  2. 02発生箇所が不明な例外の調査
  3. 03実行中またはリモートプロセスの確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/almogbaku/debug-skill --skill debugging-code

インストール後、Claude Code で/debugging-codeを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
advanced-techniques.md
installing-debuggers.md
scripts/
install-dap.sh

詳細

インタラクティブデバッガー

プログラムがクラッシュしたとき、誤った出力を生成したとき、または特定の状態に至った実行経路を正確に把握する必要があるときに使用します。print 文を追加して再実行しても、十分な速さで答えが得られない場合に役立ちます。

実行中のプログラムを任意の時点で一時停止し、その瞬間の変数の値やコールスタックを確認できます。1 行ずつステップ実行したり、次のブレークポイントまで進んだり、実行中のプロセスに対して任意の式を評価したりできます。いずれも再起動は不要です。

セットアップ

このスキルでは dap を使用します。これは、デバッガーと DAP Protocol 経由でやり取りするバックグラウンドデーモンを起動し、デバッガーの状態を維持する CLI ツールです。そのため、複数回呼び出して簡単に操作できます。

dap がインストールされていない場合(確認方法:command -v dap)、今すぐインストールしてください。 インストールを進める前に、ユーザーに確認または通知してください。

Homebrew からインストール(macOS)

brew install AlmogBaku/tap/dap

インストーラースクリプト:

bash scripts/install-dap.sh

ソースからインストール:

go install github.com/AlmogBaku/debug-skill/cmd/dap@latest

このツールはオープンソースで、GitHub で公開されています。ベストプラクティスに沿って保守されています。

Python、Go、Node.js/TypeScript、Rust、C/C++、および DAP をサポートするその他の言語にネイティブ対応しています。

デバッガーバックエンドが見つからない場合や、起動に失敗する場合は、references/installing-debuggers.md を参照してください。

すべてのコマンドとフラグについては、dap --help または dap <cmd> --help を実行してください。

セッションの開始

dap debug <file> は、デバッガーの下でプログラムを起動します。バックエンドはファイル拡張子から自動検出されます。

把握している内容に応じて、開始方法を選んでください。

  • 仮説がある場合 — バグが発生すると予想される場所にブレークポイントを設定します:dap debug script.py --break script.py:42
  • 条件付きブレークポイント — 条件が満たされたときだけ停止します:dap debug script.py --break "script.py:42:x > 5" (条件付きの指定は必ず引用符で囲んでください)
  • 複数ファイルのアプリ — モジュールをまたいでブレークポイントを設定します:--break src/api/routes.py:55 --break src/models/user.py:30
  • 仮説がなく、小規模なプログラムの場合 — エントリーポイントから実行を追います:dap debug script.py --stop-on-entry (大規模なプロジェクトでは避けてください。起動コードのノイズが多いため、代わりにブレークポイントを使って切り分けてください)
  • 例外が発生するが、場所が不明な場合 — dap debug script.py --break-on-exception raised(Python)/all(Go/JS)
  • リモートプロセス — dap debug --attach host:port --backend <name>
  • すでに実行中のプロセス(応答しないサーバー、発生中の問題など) — 再起動せずにアタッチします: dap debug --pid <PID> --backend <name>

    macOS + Go の注意点: dlv --pid を使用するには、SIP を無効にする必要があります(csrutil disable)。 プログラムをデバッガーの下で起動するか、リモートデバッガーにアタッチする方法を推奨します!

セッションの分離: --session <name> を指定すると、並行して動作するエージェント同士が干渉しません。 ヒント:利用可能な場合は、セッション ID(${CLAUDE_SESSION_ID})を使うとよいでしょう。

すべてのフラグ、バックエンド、使用例については、dap debug --help を実行してください。

デバッグの考え方

ソースコードを読むだけでは根本原因を検証できない場合は、デバッガーを使ってください。 デバッガーを使えば、実際に起きていること、つまり実際の値、実際の実行経路、実際の状態を観察できます。 それが期待される動作と異なるとき、バグを見つけたことになります。

2 回失敗したら考え直してください。 同じ場所で 2 つの仮説が外れた場合、頭の中のモデルが間違っています。 コードを読み直し、ブレークポイントを変えて、まったく異なる仮説を立ててください。

段階的に進めてください。 手早く仮説を検証するには、まず dap eval を使います。ノイズを絞り込むには、条件付きブレークポイントを使ってください。インタラクティブな制御が必要な場合にのみ、通常のブレークポイントとステップ実行に切り替えます。

ユーザーの操作経路を再現してください。 ユーザーフローをデバッグする場合は、コードが通ると予想される経路に沿ってブレークポイントを設定します。 compute() が呼び出されるはずなのに一度も呼ばれない場合、バグがあるのは呼び出し元です。compute() ではなく、呼び出すはずだった処理を確認してください。

print 文ではなくブレークポイントを設定してください。 何かを出力したくなったら、代わりにブレークポイントを設定してください。

状態を把握する

すべての dap 実行コマンドは、現在の位置、ソース、ローカル変数、コールスタック、出力を含むコンテキスト全体を自動的に返します。停止するたびに、次の点を確認してください。

  • ローカル変数の値は想定どおりですか?
  • コールスタックは想定したコードパスを示していますか?
  • ここまでの出力に、予期しないことが表れていますか?

スタックをさかのぼって因果関係を追います。 フレーム 0 の値が間違っている場合は、dap eval "<expr>" --frame 1 を確認し、呼び出し元から渡された値を調べます。値が最初に間違ったものになったフレームが見つかるまで、さらに上へさかのぼってください(--frame 2、--frame 3)。そこが症状ではなく、バグの発生源です。

停止時の出力例:

Stopped at compute() · script.py:41
  39:   def compute(items):
  40:       result = None
> 41:       return result
Locals: items=[]  result=None
Stack:  main [script.py:10] → compute [script.py:41]
Output: (none)

ブレークポイントに到達する前にプログラムが終了する場合:

Program terminated · Exit code: 1

→ ブレークポイントをより前に移動するか、--stop-on-entry を付けて再起動してください。

仮説を立てる

ブレークポイントを設定する前に、「Y なので、バグは X にあると考えています」と考えます。よい仮説は反証可能です。次の観察によって、仮説を確認または否定できます。まだ仮説がありませんか? ブレークポイントを 2 つ設定して二分探索し、調査範囲を絞るか、上記の開始時の戦略を参照してください。

戦略的にブレークポイントを設定する

  • 問題が現れる場所ではなく、問題が始まる場所に設定します
  • 80 行目で例外が発生する場合、根本原因はその前にあります。もっと前から調べてください
  • はっきりしない場合は、二分探索します。--break f:20 --break f:60 を使い、状態が間違っているのが前半か後半かを確認して調査範囲を半分に絞ります

ブレークポイントを設定する場所:

  • 境界 — データが形式、表現、モジュールの境界を越える場所です。ここでは状態が最もクリーンです
  • 状態遷移 — 破損した値を代入または変更する行です
  • 誤った分岐 — 不正な経路につながった入力を持つ条件です
  • アンチパターン — ライブラリコード内ではなく、呼び出し箇所に設定します。処理が頻繁に繰り返されるループでは、無条件のブレークポイントを使わず、条件を指定してください
セッション中のブレークポイントの管理

調査が進んだら、疑わしいコードのより深い位置にブレークポイントを追加し、役目を終えたものを削除してください。再起動せずに、段階的に範囲を絞り込めます。

dap continue --break app.py:50              # add breakpoint deeper, then continue
dap continue --remove-break app.py:20       # drop a breakpoint you're done with
dap break add app.py:42 app.py:60           # add multiple breakpoints at once
dap break list                              # see what's set
dap break clear                             # start fresh

ブレークポイントが無効な行に設定されている場合や、アダプターによって位置が調整された場合、dap は出力で警告します。

条件付きブレークポイント

条件が真の場合にのみ停止します。ループ、頻繁に実行される処理、特定の入力値を調べるときに不可欠です。 構文:"file:line:condition"(必ず引用符で囲みます)。

dap debug app.py --break "app.py:42:i == 100"            # skip 99 iterations, stop on the one that matters
dap debug app.py --break "app.py:30:user_id == 123"      # reproduce a user-specific bug
dap continue --break "app.py:50:len(items) == 0"         # catch the empty-list case mid-session
不変条件ブレークポイント

条件付きブレークポイントを実行時アサーションとして使い、問題が起きた瞬間に停止します。

dap debug app.py --break "bank.py:68:balance < 0"          # catch the overdraft
dap debug app.py --break "pipe.py:30:type(val) != int"     # type violation

実行の追跡

停止するたびに、何を疑っているかに応じて次の進め方を選んでください。

3 回を超えて連続でステップ実行するなら、さらにステップを進めるのではなく、ブレークポイントが必要です。

dap step                         # step over — trust this call, advance to next line
dap step in                      # step into — suspect what's inside this function
dap step out                     # step out — you're in the wrong place, return to caller
dap continue                     # jump to next breakpoint
dap continue --to file:line      # run to line (temp breakpoint, auto-removed)
dap context                      # re-inspect current state without stepping
dap output                       # drain buffered stdout/stderr without full context
dap inspect <var> --depth N      # expand nested/complex objects
dap pause                        # interrupt a running/hanging program
dap restart                      # restart with same args and breakpoints
dap threads                      # list all threads
dap thread <id>                  # switch thread context

停止するたびに現在の file:line が表示されるため、常に現在位置を把握できます。

dap eval "<expr>" を使うと、ステップ実行せずに実行中の状態を調べられます。

dap eval "len(items)"
dap eval "user.profile.settings"
dap eval "expected == actual"       # test hypothesis on live state
dap eval "self.config" --frame 1    # frame 1 = caller (may be a different file)

副作用のあるメソッドを呼び出す eval 式は避けてください。プログラムの状態を変更し、デバッグセッションを破損させる可能性があります。修正を意図的にテストする場合を除き、読み取り専用のアクセスにとどめてください。

先へ進む

永続的なブレークポイントを設定せずに特定の行をすばやく確認するには、dap continue --to file:line を使います。一度だけ停止し、その後は消える一時的なブレークポイントです。ブレークポイントのライフサイクルを管理せずに、「50 行目での x の状態だけを確認したい」場合に便利です。

高度なシナリオ

ハング、並行処理のバグ、深くネストした状態、ループの二分探索などの高度なシナリオについては、${CLAUDE_SKILL_DIR}/references/advanced-techniques.md を参照してください。

チュートリアル

バグ:compute() が None を返す

Hypothesis: result not assigned before return
→ dap debug script.py --break script.py:41
  Locals: result=None, items=[]   ← wrong, and input is also empty

New hypothesis: caller passing empty list
→ dap eval "items" --frame 1      → []   ← confirmed
→ dap step out                    → caller at line 10, no guard for empty input
→ dap continue --break script.py:8 --remove-break script.py:41
  ← narrowing: add breakpoint at data source, drop the one we're done with
  Stopped at main():8, items loaded from config as []

Root cause: missing guard. Fix → dap stop.

仮説がない場合(例外が発生したが、場所が不明):

Exception: TypeError, location unknown
→ dap debug script.py --break-on-exception raised
  Stopped at compute():41, items=None
Root cause: None passed where list expected.

修正を検証する

バグの発生箇所で一時停止中に、eval を使って、提案する修正の式を現在の状態に対してテストします。eval で動作するなら、コードでも動作します。その後、コードを編集し、dap restart でエンドツーエンドの動作を確認してください。

修正を適用したら、同じシナリオを再実行して検証します。dap restart は同じ引数とブレークポイントで再実行するため、すばやくフィードバックを得られます。バグを発見したときと同じブレークポイントで正しい動作を確認するまでは、修正が機能すると決めつけないでください。

クリーンアップ

通常、プログラムの終了時またはアイドルタイムアウト後に、dap セッションは自動的に終了します。 アプリが正常に終了しなかった場合(デバッグ中に強制終了した場合など)は、dap stop で手動で終了できます。

コメント