Skip to main content

dap-debugging

Diagnose runtime state with persistent DAP debugger sessions — breakpoints, expression eval, and stepping across Go, Python, TypeScript/JavaScript, and native binaries

インストールへ移動

ソース情報

リポジトリ
getkimchi/kimchi
ソースの最終更新活動
2026年8月26日 14:11
検出された SKILL.md の言語
英語
スター
2,224
フォーク
138

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

ファイルエクスプローラー
5 ファイル

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
dap-debugging
description
Diagnose runtime state with persistent DAP debugger sessions — breakpoints, expression eval, and stepping across Go, Python, TypeScript/JavaScript, and native binaries
triggers
["a test or build fails repeatedly and static reasoning stalls","a bug cannot be explained by reading code alone","user asks to debug a program or inspect a runtime value"]
category
harness
state
active
version
1
# Debugging with DAP Tools Use this skill when you need **runtime state** — a value at a line, the exception that actually threw, which code path ran — and reading the source no longer answers the question. Per-language launch config, source mapping, and expression syntax rules live in reference files alongside this skill. Load the one matching your debuggee before launching: `skill_view name="dap-debugging" file_path="references/go.md"`, `references/python.md`, `references/typescript.md`, or `references/native.md`. ## The debugging loop Debugging is a search: you are locating the first place observable state diverges from intended state. Each iteration narrows the search. 1. **Reproduce and name the symptom.** Run the program normally first (`bash`). Write down the *visible wrong thing*: wrong output, exception, hang. The symptom anchors every breakpoint you set. 2. **State your one-line hypothesis.** "The cache returns a stale entry after eviction." One line, about *data*. If you cannot, read more code — debugging without a hypothesis is random walking. 3. **Probe the boundary where the symptom appears.** Breakpoint at the observable wrong behavior (see placement rules below), inspect state. Ask: does it match expectations here? 4. **Move backward along the data flow.** State is wrong at your probe → the corruption happened earlier. Find where the bad value was *written* (assignment site, mutation, argument pass) and probe there. Repeat until you reach a probe where state is **correct** — the divergence is between the last correct and first incorrect probe. 5. **Minimize the step distance.** Steps between your last-correct and first-wrong probes may still be large. Step (`step_over` / `step_in`) through that window while re-evaluating the suspect expression. The statement where the expression flips from right to wrong is usually the bug — or one line away from it. 6. **Fix, then verify at the same breakpoint.** Re-run to the same probe with the fix and confirm state is now correct. Do not skip the verification pass. Give up on a thread after ~3 empty iterations: if three probes reveal no new divergence, your hypothesis is stale — stop, re-read the code around the data flow, form a new one, or report what you ruled out. **Never escalate into longer debugging sessions with fewer details each time**; that pattern produces cost without signal. ## Where to set breakpoints Placement is the highest-leverage decision. Prefer the probe that splits the remaining search space, not the nearest line. - **The symptom line itself** — the `return`, `print`, `write`, or UI update that surfaces the wrong behavior. Confirms inputs to the symptom. - **The decision point** — the `if`/`switch`/loop where behavior diverges. Evaluating the condition plus its operands tells you which side of the truth table the program took and why. - **Function entry of the wrong-returning function** — check *arguments in* against *expectation*. If arguments are already wrong, the bug is in the caller; move up the stack (see `debug_backtrace` frames). - **The mutation point** — the line that writes the corrupted value: assignment, `append`/`push`, map insert, struct field set, callback registration. If you don't know which of several writers is guilty, use `debug_watch_change` on the expression to catch the changer red-handed. - **Error handling boundaries** — `catch`/`except` blocks, `if err != nil` guards, `.catch()` chains where the error path diverges from the happy path. - **Loop boundaries** — first and last iteration where behavior changes; evaluate the loop invariant and the index. - **Async/join boundaries** — where a promise resolves, a goroutine joins, a message is received: the value crossing the boundary is the suspect. Avoid: - Library/framework interiors you didn't write — probe *your* call into them and *their* call back into you. - Lines that execute millions of times (inner hot loops) — a conditional probe via `debug_watch_change`, or a breakpoint at the loop's *exit*, is cheaper than stepping through. - Comment-only or declaration-only lines — many adapters resolve those to the next statement, which may be a different block than you expect. ## Finding the interesting state Once stopped, the question is "which of the 40 visible variables is the interesting one?" - **Start from the hypothesis variable.** The one you named in step 2. `debug_eval` it first. Wrong → keep digging here. Right → the interesting state is elsewhere; don't wander. - **Follow the data backward, not the code forward.** For each value that is wrong, ask *who could have written it*. Evaluate its immediate producers (arguments at call site, fields of its container). This walks you along the causality chain instead of dumping everything. - **Inspect structures, not just primitives.** The bug is usually a field you didn't think to check. `debug_locals` expands nested fields one to two levels; for deeper, eval explicit field paths (`a.b.c`); check the language reference for expansion limits (dlv: 64 elements, 2 levels). - **Compare expected vs actual, explicitly.** At every probe, write both down. The *shape* of the discrepancy is the clue: off-by-one → loop bounds; stale value → caching/ordering; truncated → pagination/limits; wrong type → conversion site; `nil`/`None`/`undefined` → initialization or error swallow. - **Cross the frame boundary when the caller is suspect.** Use `debug_backtrace`, then inspect a higher frame's locals to see the arguments that produced this state. - **When you don't know *where*** the mutation happens, don't random- breakpoint: `debug_watch_change({file, line, expression})` at a stable line and let it report the changer. - **When you don't know *what* should run**, probe the dispatcher: eval the flag/route/method table that selects the code path at the decision point. - **Structured values and `variables_reference`.** `debug_locals` and `debug_eval` tag expandable values with `[ref N]`. Refs are valid only while paused at the *current* stop and adapters may expire them on resume — call `debug_set_variable(..., variables_reference: N)` or expand children immediately, not after `debug_continue`. ## Tool selection cheat sheet ### One-shot (fire-and-forget) — default choice - **`debug_state_at({file, line, evaluated?})`** — the workhorse probe: breakpoints + run + locals + backtrace + evaluated expressions + output in one result. Use it for loop steps 3–4 above. - **`debug_last_error({program})`** — runs with exception breakpoints; returns exception type/message plus locals + backtrace at the throw site. Start here when the symptom is a crash. - **`debug_watch_change({file, line, expression})`** — reports old vs new for an expression across stops: "who changed this". - **`debug_trace_calls({program})`** — marker parsing **only**: returns records the program printed as `__KIMCHI_TRACE__<json>`. Nothing is instrumented for you; no markers means "not instrumented" — add markers or use `debug_state_at`. ### Interactive (stateful) — multi-stop investigations 1. `debug_launch({program})` → `session_id`. 2. `debug_set_breakpoint({session_id, source, line})` — set several along the suspected data flow at once; cheaper than relaunching. 3. `debug_continue({session_id})` → next stop. 4. `debug_locals` / `debug_eval` / `debug_backtrace` — inspect. 5. `step_over` / `step_in` / `step_out` — narrow the window (loop step 5). 6. `debug_terminate({session_id})` when done — **always terminate**; orphan sessions hold adapter processes. Rules: `step_*` auto-completes a pending launch. js-debug nested sessions (`startDebugging`) route to the child transparently. Default timeout is 30 s; cold builds (first Go launch compiles the stdlib) need a larger `timeout_ms`. ## Failure playbooks - **"Debuggee terminated before reaching a stop"** — breakpoint never hit: check source mapping in the language reference (compiled paths vs build paths), verify the line actually executes (the code may be inlined or dead), or run `debug_last_error` if the process crashed on the way. - **Breakpoint `verified: false`** — path mismatch between what you passed and what the adapter sees; use the concrete path forms in the language reference. - **`debug_eval` errors but the variable exists** — expression-syntax limits in that adapter (dlv: no method calls, especially on unexported fields). Simplify to a bare field path, or inspect via `debug_locals`. - **Values look optimized-out / locals missing (native, release builds)** — rebuild with debug info (`-g`, debug profile); see references/native.md. - **Empty or contradictory sessions** — terminate and report what you've ruled out rather than launching again with less detail. ## Humility rules - The debugger shows state, not cause. Infer cause from the *difference* between two probes, never from one. - A failed hypothesis is useful output — record what you ruled out so the next attempt (human or agent) doesn't repeat it. - Do not present guesses from reading locals as confirmed behavior; say "state at file:line showed X, which means Y because Z was W".
GitHubで見る