You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit eceeaaf
Browse filesBrowse the repository at this point in the historyBrowse files
authored
fix(attach): report a CodeLLDB attach's initial break-in stop as a pause (#817) (#821)
* fix(attach): report a CodeLLDB attach's initial break-in stop as a pause (#817)
On Windows a cpp/cobol attach by PID reported its first stop as
`reason: "exception"` with "Exception 0x80000003 encountered at
address …": the DebugBreakProcess break-in Windows injects to stop the
target, on a helper thread. CodeLLDB stops the target itself when the
attach did not opt out of stopOnEntry, so no pause intent was ever armed
and the normalizer's attach rule (#597) could not fire.
- session-manager-core: the stop-reason context's stopOnEntry is read per
mode — a launch stops on entry only when asked (=== true), an attach
stops unless told not to (!== false), which is what the attach
controller and the adapters (CodeLLDB's `stopOnEntry ?? true`) apply.
A default attach reaches the core as `stopOnEntry: undefined`.
- lldb-policy-shared: the first stop of an attach that asked to stop, with
no pause in flight and the 0x80000003 break-in code in the description,
is 'pause'; rawReason 'exception' and the description stay beside it.
A break-in met later, or first by a stopOnEntry:false attach, stays an
exception.
Measured (Windows 11, CodeLLDB 1.11.8, examples/cpp/pause_test.cpp),
through a nested mcp-debugger paused inside its own stop handler: the
stopped body is {allThreadsStopped, description, reason, threadId} and
arrives 2 ms before the attach response; the context read
{sessionMode:'attach', firstStop:true, pausePending:false,
stopOnEntry:false}. After: lastStop {reason:'pause',
rawReason:'exception'}, get_stack_trace stopReason 'pause'.
Closes#817
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* fix(attach): read the attach's stopOnEntry as the adapter receives it; review follow-ups (#817)
Review of #821:
- adapterConfig.stopOnEntry wins the proxy launcher's merge on the wire
(issue #336) but the core's mode facts only saw the top-level parameter,
so an attach with `adapterConfig: { stopOnEntry: false }` and no
top-level value made the core read "asked to stop" while CodeLLDB
resumed — a genuine first __debugbreak() would have been relabelled a
pause. The attach controller now folds a boolean adapterConfig.stopOnEntry
into the dapLaunchArgs the core sees; the verification gate keeps
reading the top-level parameter (the existing warning says so).
- The per-mode stopOnEntry is computed once, with the other mode facts,
and the auto-continue decision reads it too.
- The POSIX SIGSTOP remark is hedged: that shape is not measured for this
stop (the cpp attach e2e logs it off Windows).
- docs/cobol/README.md points at the C/C++ guide for the attach stop's
wording, as #814 did for the detach mechanism.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: JF <john.franklin@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
**A CodeLLDB attach's initial stop is reported as a pause, not an exception** — on Windows, attaching to a process by PID (C/C++ and COBOL) reported the target's post-attach stop as `lastStop.reason: "exception"` / `stopReason: "exception"` with `Exception 0x80000003 encountered at address …`: that is the `DebugBreakProcess` break-in Windows injects to stop the target for the attach, on a helper thread, and nothing in the program threw. CodeLLDB stops the target itself when the attach did not opt out of `stopOnEntry`, so the session layer never armed the pause intent the shared normalizer keyed on; the first stop of such an attach is now `pause`, with the adapter's own reason kept as `rawReason: "exception"` and its description beside it. The stop-reason context's `stopOnEntry` now reads an attach the way the attach controller and the adapters do — stop unless told not to — so a real `__debugbreak()` met first by an attach that passed `stopOnEntry: false` stays an exception (#817)
- On Windows the attach stop is reported on the break thread the OS injects (exception `0x80000003`), a pause may land on a runtime worker thread: when the reported thread has no COBOL frame, the shim reports the stop on the thread that is inside the COBOL program (the description names both), so `get_stack_trace` shows the program on first contact. Breakpoint, step, entry and runtime-error stops are always on the right thread and are left alone.
147
147
- A batch job is usually paused inside libcob or a `C$SLEEP`/I/O call, a frame with no COBOL source: `get_scopes`, `get_local_variables` and `evaluate_expression` then serve the nearest COBOL program up the stack (the scope names say whose and how far up, `WORKING-STORAGE of PAYROLL (0000-MAIN, 3 frames up)`), so the data division is visible on first contact; `get_stack_trace` shows the COBOL frame's paragraph.
148
148
- When the client that started the session goes away (the server or the proxy dies), the shim detaches: an attached process is never terminated the way a launched one is.
149
-
- The target is held paused after attach (`stopOnEntry` is a top-level `attach_to_process` parameter and defaults to `true`; pass `false` to resume immediately).
149
+
- The target is held paused after attach (`stopOnEntry` is a top-level `attach_to_process` parameter and defaults to `true`; pass `false` to resume immediately). That initial stop is reported as `lastStop.reason: "pause"`; on Windows the adapter's own words stay beside it as `rawReason: "exception"` and the `0x80000003` description (issue #817; the mechanism is in the C/C++ guide's attach section).
150
150
- Recognised `adapterConfig` keys: `processId`/`pid`, `program` (CodeLLDB's explicit-binary hint; a relative path is resolved against `cwd` before it reaches CodeLLDB), `cwd`, `waitFor`, `manifestDirs`, `engineScopes`, `sources`, `dialect`, `format`, `copybookDirs`, `cobcFlags`, `runtimeChecks`, `forceRebuild`, `initCommands`, `preRunCommands`, `postRunCommands`, `exitCommands`, `targetCreateCommands`, `processCreateCommands`, `expressions`, `sourceMap`, `sourceLanguages`, `relativePathBase`, `breakpointMode`. The manifest keys (`sources` … `forceRebuild`, `manifestDirs`, `engineScopes`, `cwd`) are consumed by the adapter and never appear in the attach request, and are not reported as ignored. Unlisted keys are still forwarded, with a warning naming them.
151
151
- The regeneration runs before CodeLLDB is spawned, under the attach's `timeout` (default 30 s) less a margin; a cached translate (unchanged sources and options) is instant, a first one is a cobc run.
152
152
-`detach_from_process` leaves the target running. On Windows a stopped target is resumed (DAP `continue`) before the detaching `disconnect` — on detach, on `close_debug_session` and on shutdown — and the detach result reports `data.resumedBeforeDetach: true`: the engine is CodeLLDB, whose Windows detach otherwise kills a target whose last resume was a step (issue #763; the mechanism is in the C/C++ guide's attach section).
Copy file name to clipboardExpand all lines: docs/cpp/README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -49,7 +49,7 @@ Compile with **`-gdwarf-4 -O0`**: full debug info, no optimization (optimized co
49
49
attach_to_process sessionId=... processId=<pid>
50
50
```
51
51
52
-
- The target is held paused after attach (`stopOnEntry` defaults to `true` for attach in every language, not just C/C++; pass `false` to resume immediately).
52
+
- The target is held paused after attach (`stopOnEntry` defaults to `true` for attach in every language, not just C/C++; pass `false` to resume immediately). That initial stop is reported as `lastStop.reason: "pause"` (`stopReason: "pause"` in `get_stack_trace`). On Windows it is the `DebugBreakProcess` break-in Windows injects to stop the target, which CodeLLDB reports as an `0x80000003` exception on a helper thread — the adapter's own words stay beside the pause as `rawReason: "exception"` and `description: "Exception 0x80000003 encountered at address …"`, and the anchored thread is the program's (issue #817). A `stopOnEntry: false` attach has no initial stop, so a break-in met first there (a real `__debugbreak()`) is reported as the exception it is.
53
53
-`detach_from_process` leaves the target running. On Windows a stopped target is resumed (DAP `continue`) before the `disconnect` — on detach, on `close_debug_session` and on shutdown — and the detach result reports `data.resumedBeforeDetach: true`: CodeLLDB's Windows detach resumes each thread with the resume state of its last stop, and after a `step_over`/`step_into` that state is still "stepping", so the target would single-step with no debugger attached and exit with `STATUS_SINGLE_STEP` (`0x80000004`) (issue #763). A refused continue leaves `resumedBeforeDetach: false` and a `data.warning`.
54
54
-**Linux**: `kernel.yama.ptrace_scope=1` (the default on many distros) only allows attaching to child processes. For arbitrary processes: `sudo sysctl kernel.yama.ptrace_scope=0` (temporary) or run the server with `CAP_SYS_PTRACE`.
55
55
-**Windows**: attach requires same-or-higher privilege than the target.
Copy file name to clipboardExpand all lines: docs/tool-reference.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1235,6 +1235,7 @@ When the requested pause has not landed by the time the tool answers (an idle No
1235
1235
1236
1236
**Notes:**
1237
1237
-`state` is `"paused"` only once a stopped event has actually been observed; otherwise the attach reports `"running"`. When a requested post-attach pause is accepted but its stopped event has not arrived within the bounded wait, the response is successful with `state: "running"` and `pending: true` (at the top level and in `data`) and the `message` names the pending pause; the late stopped event is the only transition to `paused`, and every paused session has a `lastStop`.
1238
+
- The initial stop of an attach is reported as `lastStop.reason: "pause"` (`stopReason: "pause"` in `get_stack_trace`) by every adapter, whether the debugger paused the target on request or, as CodeLLDB does, stopped it itself. On Windows, CodeLLDB reports that stop as the `DebugBreakProcess` break-in — an `0x80000003` exception on a helper thread — so the adapter's own words stay beside the pause as `lastStop.rawReason: "exception"` and `description: "Exception 0x80000003 encountered at address …"` (issue #817). Only the first stop of an attach that did not pass `stopOnEntry: false` is read this way: a break-in met later, or first by an attach that opted out of the initial stop, is a real `__debugbreak()` and keeps `reason: "exception"`.
1238
1239
- When `processId` was used, the message reads `Attached to process PID <pid>` instead.
1239
1240
- The response `warning` reports two distinct `adapterConfig` key outcomes (issues #450/#466): keys the adapter's attach transform genuinely drops (e.g. Python's ptvsd-era `localRoot`/`remoteRoot` — use `pathMappings`) are named as **ignored**, while keys mcp-debugger doesn't recognize are **forwarded to the adapter as-is** and named with an edit-distance suggestion for near-misses (`pathMapping (did you mean pathMappings?)`) — "ignored" means dropped, "forwarded as-is" means the adapter still sees them. The same field also carries the launch-style warning for function breakpoints still unverified at attach (issue #308).
1240
1241
- js-debug attach honors `adapterConfig` too: `localRoot`/`remoteRoot`/`sourceMaps`/`skipFiles`/`continueOnAttach` and other js-debug attach options reach the debugger (issue #466). Two keys are defaulted when absent (issue #655): `resolveSourceMapLocations: ["**", "!**/node_modules/**"]` (js-debug's own attach default collapses to "resolve everywhere", which applied every dependency's `.js.map` and produced phantom `../src/*.ts` frames) and `cwd` (the server's working directory, or the workspace root in container mode — js-debug needs a base path before it resolves any source map's relative `sources`, even ones next to the generated file). Pass your own value, including `resolveSourceMapLocations: null`, to override. `skipFiles` is deliberately left at js-debug's default: blackboxing `node_modules` would turn a `pause_execution` that lands in framework code on an idle server into the endless step-chase of issue #513 — `get_stack_trace` hides those frames instead.
'[SessionManager] adapterConfig.stopOnEntry reaches the adapter but does not affect post-attach pause verification; prefer the top-level stopOnEntry parameter'
118
+
'[SessionManager] adapterConfig.stopOnEntry reaches the adapter (and is what the stop-reason reading of the '+
119
+
'attach\'s initial stop follows) but does not affect post-attach pause verification; prefer the top-level '+
120
+
'stopOnEntry parameter'
119
121
);
120
122
}
121
123
// Same rules as every per-request 'timeout' override: positive finite
@@ -186,9 +188,16 @@ export class AttachController {
186
188
// The actual attach logic will be handled by the adapter via dapLaunchArgs
187
189
constplaceholderPath='attach://remote';
188
190
189
-
// Pass attach config through dapLaunchArgs with special request type
191
+
// Pass attach config through dapLaunchArgs with special request type.
192
+
// stopOnEntry as the adapter will receive it: adapterConfig wins the
193
+
// merge the proxy launcher applies (issue #336), and the core's mode
194
+
// facts — the stop-reason reading of the attach's initial stop (issue
195
+
// #817) and the RUNNING projection — must follow the value that reached
196
+
// the debugger, not a top-level default the adapter never saw. The
197
+
// verification gate below keeps reading the top-level parameter.
0 commit comments