fix(#260): normalize Rust panic stops from 'breakpoint' to 'exception' - #273
Merged
Conversation
CodeLLDB implements the rust_panic filter as an internal breakpoint, so
a panic pause arrived as reason 'breakpoint' and callers following the
documented contract (lastStop.reason === 'exception') missed it.
Live capture (Windows/GNU, CodeLLDB 1.11.8) showed the stopped body is
only {allThreadsStopped, hitBreakpointIds, reason, threadId} — there is
no description/text to match on — so the discriminator is breakpoint-id
based: the internal filter breakpoint takes the next free id after user
breakpoints, and its hitBreakpointIds are disjoint from every user
breakpoint id.
- normalizeStopReason context gains userBreakpointIds, passed by the
session manager only when every user breakpoint has a known adapterId
(incomplete bookkeeping disables the inference).
- RustAdapterPolicy maps a 'breakpoint' stop to 'exception' when hit ids
are present and disjoint from the user set. No hit ids (the mislabeled
step completions from the issue #255 trace) or no user-id knowledge
keeps the raw reason — a missed panic is cosmetic, a false 'exception'
would mislead.
- exceptionInfo enrichment now also requires the adapter's RAW reason to
be 'exception': for a normalized panic CodeLLDB answers about the
internal breakpoint ({exceptionId: 'Breakpoint', description:
'breakpoint 1.1'}, confirmed live) — misleading, not enriching. The
panic message is available via get_output stderr.
- Policy comment documents that CodeLLDB 1.11.8 no longer advertises
rust_panic (only cpp_throw/cpp_catch) yet the filter still works; the
capability drift warning remains the early alarm.
- New examples/rust/panic_example + Rust launch trio in the
break-on-exceptions e2e (default pause at panic + exit 101 after
continue, explicit 'uncaught', 'none' opt-out).
Fixes #260
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #260. A Rust
panic!pause arrived withlastStop.reason: 'breakpoint'because CodeLLDB implements therust_panicfilter as an internal breakpoint. Callers following the documented crash-diagnosis contract (lastStop.reason === 'exception') missed panic pauses entirely. ThenormalizeStopReasonpolicy hook added in #270 is exactly the seam this needed.The discriminator (live-capture driven)
Live capture on Windows/GNU (CodeLLDB 1.11.8) killed the obvious approach: the panic stopped body is only
{"allThreadsStopped":true,"hitBreakpointIds":[2],"reason":"breakpoint","threadId":…}— no description/text to pattern-match. But the capture also showed the internal filter breakpoint takes the next free DAP breakpoint id after user breakpoints, so the discriminator is id-based:
hitBreakpointIds∩ user ids ≠ ∅'breakpoint''breakpoint'hitBreakpointIds'exception'(rawReason: 'breakpoint'preserved)Both sides must be known: the session manager passes
userBreakpointIdsonly when every user breakpoint has a knownadapterId; missing hit ids or incomplete bookkeeping keeps the raw reason. The asymmetry is deliberate — a missed panic keeps a cosmetic 'breakpoint' label, a false 'exception' would mislead agents.exceptionInfo enrichment guard
After normalization the #243 enrichment would have fired (CodeLLDB advertises
supportsExceptionInfoRequest), and live it answers about the internal breakpoint —{exceptionId: "Breakpoint", description: "breakpoint 1.1"}— misleading, not enriching. Enrichment is now additionally gated on the adapter's raw reason being'exception'. The panic message itself is available viaget_outputstderr (thread 'main' panicked at …), which the e2e asserts.Also
cpp_throw/cpp_catch;rust_panicstill works via legacy acceptance; the [FEATURE] Capture adapter initialize capabilities + best-effort exceptionInfo enrichment on exception stops #243 drift warning stays as the early alarm).examples/rust/panic_example+ Rust launch trio in the break-on-exceptions e2e: default (pause at panic, panic frame in stack, message in output, continue → exit 101), explicit'uncaught', and'none'opt-out (run to termination, exit 101).skills/debugging/references/rust.mdupdated: panic pauses now report'exception'.Testing
start_debuggingnow reportsreason: "exception",lastStop={reason: 'exception', rawReason: 'breakpoint'}Note for review: the Linux leg of the capture is delegated to ubuntu CI — if Linux CodeLLDB reported panics natively as
'exception'the hook no-ops and the e2e still passes; if it reported'breakpoint'without ids the e2e would fail visibly here.🤖 Generated with Claude Code