debugger: torb debug launches a native program through lldb-dap or gdb (step 4a of #42) #720

Closed
opened 2026-11-12 17:42:32 +00:00 by orion · 12 comments
Collaborator

Context

Step 4a of #42 (epic: debug native programs, DEBUGGER.md section 4). Steps 1 to 3 (#405, #469, #600) landed: torb build --debug writes a binary with a #line per statement and a layout table, and gdb and lldb have printers and formatters for it. Nothing yet connects an editor to them: torb debug only debugs a program in the VM. Step 4 of the epic is cut in two, because the launch and the relay are one reviewable PR and the translation of names in variables (4b) is the next: a session pinned here would otherwise be re-pinned at once.

Source

DEBUGGER.md sections 2, 4, 16, 18 and 20

Goal

An editor can launch a native program through torb debug and get breakpoints at TorbScript lines, stops with the TorbScript source and line, stepping and continue from lldb-dap or gdb --interpreter=dap, with torb debug relaying the Debug Adapter Protocol. Names in variables stay the debugger's own (s12_count) until step 4b.

Acceptance criteria

  • A launch with "native": true (new attribute in the table of DEBUGGER.md section 16; program as for torb run) makes torb debug run torb build --debug for that program as a child process, report its output as output events, and answer launch with an error naming the build failure if the build fails (no debugger is started then)
  • The adapter finds the native debugger in this order: lldb-dap (also lldb-dap-<N> and lldb-vscode where only those exist), else gdb 14 or newer started with --interpreter=dap; $TORB_DEBUG_NATIVE (lldb or gdb) forces one. Where none is found, launch fails with a message that names both and how to install them; the VM debugger and every existing session are unchanged
  • The native debugger is started with the printers of step 2 or the formatters of step 3 loaded (lldb: initCommands with command script import; gdb: -x), so values are already formatted by the layout table; where the script file is not found the session still runs and the adapter says so in an output event
  • Requests and events are relayed both ways as framed DAP (Content-Length), seq renumbered by the adapter as for the VM debuggee; initialize is forwarded and its capabilities answered with the native adapter's, launch is translated to the native adapter's own arguments (program = the built binary, args, cwd, env, stopOnEntry); disconnect and the end of standard input end the native debugger and the adapter
  • A session in tests/debug-native/ (new, run by a new tier B gate tools/debug-native.sh, which builds a fixture with --debug) sets a breakpoint on a statement line of src/main.trb, sees stopped with stackTrace frame source src/main.trb and that line, steps one line (next), sees the next line, continues and sees terminated with exit code 0, once per debugger that is installed. It is a separate gate and not part of tools/debug.sh (tier A), because it needs a C compiler and a debugger; where no debugger is installed it skips with a message unless DEBUG_NATIVE_REQUIRE names it, the way DEBUG_VALUES_REQUIRE does. Linux only
  • The tier B job of the forge CI runs the gate with DEBUG_NATIVE_REQUIRE: lldb once lldb-dap is confirmed on the runner image. This is measured first, in the PR: print command -v lldb-dap lldb-dap-18 lldb-vscode gdb and gdb --version in the CI log. If lldb-dap is not on the image, the packages: line of the workflow gains the package that has it (a PR change, no runner change), and the PR body says which
  • The packaged toolchain carries the scripts (tools/gdb/torb_printers.py, tools/lldb/torb_formatters.py): tools/package.sh adds them to the layout, and the adapter looks for them beside std/ and runtime/ as findCompilerSources does (today only tools/build-slot.sh and tools/build-units.sh ship)
  • DEBUGGER.md gets a slice row 10 (Done) and updates sections 4 (what is built), 16 (the native attribute) and 20 (what stays open: translation of names, docs and editor launch); docs/tooling/torb-debug.md documents native, the debugger lookup and $TORB_DEBUG_NATIVE; the skills/ copy is regenerated

Seed

No syntax, native, driver flag or looked-up std name changes. The adapter is TorbScript in compiler/src/debugger/; it uses Process, Environment and CurrentProcess as adapter.trb does today. No seed refresh needed (a refresh would stay Torben's).

Size

One PR, two Conventional Commits: feat(debugger): torb debug launches a native program through lldb-dap or gdb (adapter, lookup, scripts in the package, DEBUGGER.md, docs) and test(debugger): a native session per installed debugger in tier B (tests/debug-native/, tools/debug-native.sh, workflow line).

Dependencies

After #405, #469 and #600 (landed). Part of #42. Independent of #719 (workers; runtime/task.c, the debuggee) and of #41; both touch DEBUGGER.md sections 14, 18 and 20, so rebase after whichever lands first. Step 4b (the translation of s12_count to count in variables, a variables session) follows.

## Context Step 4a of #42 (epic: debug native programs, DEBUGGER.md section 4). Steps 1 to 3 (#405, #469, #600) landed: `torb build --debug` writes a binary with a `#line` per statement and a layout table, and gdb and lldb have printers and formatters for it. Nothing yet connects an editor to them: `torb debug` only debugs a program in the VM. Step 4 of the epic is cut in two, because the launch and the relay are one reviewable PR and the translation of names in `variables` (4b) is the next: a session pinned here would otherwise be re-pinned at once. ## Source [DEBUGGER.md sections 2, 4, 16, 18 and 20](https://git.torb.dev/torbscript/language/src/branch/main/docs/design/DEBUGGER.md) ## Goal An editor can launch a native program through `torb debug` and get breakpoints at TorbScript lines, stops with the TorbScript source and line, stepping and continue from `lldb-dap` or `gdb --interpreter=dap`, with `torb debug` relaying the Debug Adapter Protocol. Names in `variables` stay the debugger's own (`s12_count`) until step 4b. ## Acceptance criteria - [ ] A `launch` with `"native": true` (new attribute in the table of DEBUGGER.md section 16; `program` as for `torb run`) makes `torb debug` run `torb build --debug` for that program as a child process, report its output as `output` events, and answer `launch` with an error naming the build failure if the build fails (no debugger is started then) - [ ] The adapter finds the native debugger in this order: `lldb-dap` (also `lldb-dap-<N>` and `lldb-vscode` where only those exist), else `gdb` 14 or newer started with `--interpreter=dap`; `$TORB_DEBUG_NATIVE` (`lldb` or `gdb`) forces one. Where none is found, `launch` fails with a message that names both and how to install them; the VM debugger and every existing session are unchanged - [ ] The native debugger is started with the printers of step 2 or the formatters of step 3 loaded (lldb: `initCommands` with `command script import`; gdb: `-x`), so values are already formatted by the layout table; where the script file is not found the session still runs and the adapter says so in an `output` event - [ ] Requests and events are relayed both ways as framed DAP (`Content-Length`), `seq` renumbered by the adapter as for the VM debuggee; `initialize` is forwarded and its capabilities answered with the native adapter's, `launch` is translated to the native adapter's own arguments (`program` = the built binary, `args`, `cwd`, `env`, `stopOnEntry`); `disconnect` and the end of standard input end the native debugger and the adapter - [ ] A session in `tests/debug-native/` (new, run by a new tier B gate `tools/debug-native.sh`, which builds a fixture with `--debug`) sets a breakpoint on a statement line of `src/main.trb`, sees `stopped` with `stackTrace` frame source `src/main.trb` and that line, steps one line (`next`), sees the next line, continues and sees `terminated` with exit code 0, once per debugger that is installed. It is a separate gate and not part of `tools/debug.sh` (tier A), because it needs a C compiler and a debugger; where no debugger is installed it skips with a message unless `DEBUG_NATIVE_REQUIRE` names it, the way `DEBUG_VALUES_REQUIRE` does. Linux only - [ ] The tier B job of the forge CI runs the gate with `DEBUG_NATIVE_REQUIRE: lldb` once `lldb-dap` is confirmed on the runner image. This is measured first, in the PR: print `command -v lldb-dap lldb-dap-18 lldb-vscode gdb` and `gdb --version` in the CI log. If `lldb-dap` is not on the image, the `packages:` line of the workflow gains the package that has it (a PR change, no runner change), and the PR body says which - [ ] The packaged toolchain carries the scripts (`tools/gdb/torb_printers.py`, `tools/lldb/torb_formatters.py`): `tools/package.sh` adds them to the layout, and the adapter looks for them beside `std/` and `runtime/` as `findCompilerSources` does (today only `tools/build-slot.sh` and `tools/build-units.sh` ship) - [ ] DEBUGGER.md gets a slice row 10 (Done) and updates sections 4 (what is built), 16 (the `native` attribute) and 20 (what stays open: translation of names, docs and editor launch); `docs/tooling/torb-debug.md` documents `native`, the debugger lookup and `$TORB_DEBUG_NATIVE`; the `skills/` copy is regenerated ## Seed No syntax, native, driver flag or looked-up std name changes. The adapter is TorbScript in `compiler/src/debugger/`; it uses `Process`, `Environment` and `CurrentProcess` as `adapter.trb` does today. No seed refresh needed (a refresh would stay Torben's). ## Size One PR, two Conventional Commits: `feat(debugger): torb debug launches a native program through lldb-dap or gdb` (adapter, lookup, scripts in the package, DEBUGGER.md, docs) and `test(debugger): a native session per installed debugger in tier B` (`tests/debug-native/`, `tools/debug-native.sh`, workflow line). ## Dependencies After #405, #469 and #600 (landed). Part of #42. Independent of #719 (workers; `runtime/task.c`, the debuggee) and of #41; both touch DEBUGGER.md sections 14, 18 and 20, so rebase after whichever lands first. Step 4b (the translation of `s12_count` to `count` in `variables`, a variables session) follows.
Author
Collaborator

[loop:torbscript] intake+refine: filed from the refine of #42 (step 4a) and marked ready; goal, acceptance criteria, design record sections, seed impact (none) and size (one PR, two commits) are in the body. next: implementation

[loop:torbscript] intake+refine: filed from the refine of #42 (step 4a) and marked ready; goal, acceptance criteria, design record sections, seed impact (none) and size (one PR, two commits) are in the body. next: implementation
Author
Collaborator

[loop:torbscript] claim implementation by session:loop-torbscript-20261112 until 2026-11-12T23:50:00Z

[loop:torbscript] claim implementation by session:loop-torbscript-20261112 until 2026-11-12T23:50:00Z
Author
Collaborator

[loop:torbscript] checkpoint: design settled (relay in adapter.trb, lookup/translation in native.trb, interactive driver for tier B), implementing

[loop:torbscript] checkpoint: design settled (relay in adapter.trb, lookup/translation in native.trb, interactive driver for tier B), implementing
Author
Collaborator

[loop:torbscript] checkpoint: native lookup and launch translation (compiler/src/debugger/native.trb), findDebuggerScripts, and the relay in adapter.trb written; packaging, docs and the tier B session test next

[loop:torbscript] checkpoint: native lookup and launch translation (compiler/src/debugger/native.trb), findDebuggerScripts, and the relay in adapter.trb written; packaging, docs and the tier B session test next
Author
Collaborator

[loop:torbscript] checkpoint: both commits pushed (loop/720-native-debug), PR #723 opened with Refs #720, review requested from argus; waiting for CI, which is the first compile and the first native session run.

[loop:torbscript] checkpoint: both commits pushed (loop/720-native-debug), PR #723 opened with Refs #720, review requested from argus; waiting for CI, which is the first compile and the first native session run.
Author
Collaborator

[loop:torbscript] implementation: PR #723 (#723, branch loop/720-native-debug, head f24ba54, two commits, Refs #720) is open, review requested from argus; forge CI pending. The worker hit its time limit before CI finished, so the claim is released here.
next: waiting (forge CI on PR #723, then review or a fix on loop/720-native-debug)

[loop:torbscript] implementation: PR #723 (https://git.torb.dev/torbscript/language/pulls/723, branch loop/720-native-debug, head f24ba54, two commits, Refs #720) is open, review requested from argus; forge CI pending. The worker hit its time limit before CI finished, so the claim is released here. next: waiting (forge CI on PR #723, then review or a fix on loop/720-native-debug)
Author
Collaborator

[loop:torbscript] claim implementation by session:loop-torbscript-20261112 until 2026-11-13T04:30:00Z

[loop:torbscript] claim implementation by session:loop-torbscript-20261112 until 2026-11-13T04:30:00Z
Author
Collaborator

[loop:torbscript] implementation: pushed d738d73 to loop/720-native-debug (fix(debugger): map the line table to the package and end the native session). CI on PR #723 at d738d73 is still pending: tier A, tier B, conformance, playground, seed, archives and gates are running, and every finished job is green.

The tier B failure at dc60015 was tools/debug-native.sh (lldb). It had two causes:

  • The breakpoint never resolved. A --debug build names its files in #line by the package name plus the path below the package, and the C compiler runs in torb's working directory. So lldb looks for <cwd>/torbscript/debug-native/src/main.trb, while the editor sets the breakpoint on <project>/src/main.trb. Fix: sourceMapping in compiler/src/debugger/native.trb finds the nearest project.trb and maps <cwd>/<name> to the package directory. The adapter passes it to lldb-dap as sourceMap and to gdb as -iex "set substitute-path ...".
  • The adapter never ended after disconnect ("no end within 120 seconds"). The test editor kept the adapter's stdin open. Once the native debugger had answered the disconnect, end of input sent it a second disconnect instead of ending the adapter. Fix: tests/debug-native/session.trb now closes the adapter's input after the disconnect response, like a real editor. In the adapter, end of input now ends the adapter right away when the native session is already over (new nativeDisconnected flag, or nativeEnded).

next: waiting (forge CI on PR #723 at d738d73d5f)

[loop:torbscript] implementation: pushed d738d73 to loop/720-native-debug (fix(debugger): map the line table to the package and end the native session). CI on PR #723 at d738d73 is still pending: tier A, tier B, conformance, playground, seed, archives and gates are running, and every finished job is green. The tier B failure at dc60015 was `tools/debug-native.sh` (lldb). It had two causes: - **The breakpoint never resolved.** A `--debug` build names its files in `#line` by the package name plus the path below the package, and the C compiler runs in torb's working directory. So lldb looks for `<cwd>/torbscript/debug-native/src/main.trb`, while the editor sets the breakpoint on `<project>/src/main.trb`. Fix: `sourceMapping` in `compiler/src/debugger/native.trb` finds the nearest `project.trb` and maps `<cwd>/<name>` to the package directory. The adapter passes it to lldb-dap as `sourceMap` and to gdb as `-iex "set substitute-path ..."`. - **The adapter never ended after disconnect** ("no end within 120 seconds"). The test editor kept the adapter's stdin open. Once the native debugger had answered the disconnect, end of input sent it a second disconnect instead of ending the adapter. Fix: `tests/debug-native/session.trb` now closes the adapter's input after the disconnect response, like a real editor. In the adapter, end of input now ends the adapter right away when the native session is already over (new `nativeDisconnected` flag, or `nativeEnded`). next: waiting (forge CI on PR #723 at d738d73d5f365d7a9f2da73898480e8cfbc34c1a)
Author
Collaborator

[loop:torbscript] claim implement by session:loop-torbscript-20261113 until 2026-11-13T06:30:00Z (fix round for PR #723: the commit-message check fails on head 04cf8df4, type style is not allowed)

[loop:torbscript] claim implement by session:loop-torbscript-20261113 until 2026-11-13T06:30:00Z (fix round for PR #723: the commit-message check fails on head 04cf8df4, type `style` is not allowed)
Author
Collaborator

[loop:torbscript] implementation: fix round on PR #723. CI job "commit messages" was red because the head subject used type style, which is not an allowed Conventional Commit type here. Reworded only the head commit (content identical) to chore(debugger): format the native debugger unit tests and force-pushed loop/720-native-debug with lease. New head 6181aae813 (was 04cf8df4). Local tools/check-commits.sh 79b35cb9..HEAD exits 0; forge CI: "commit messages", "what the change touches" and "pdisc" are success, gates/bootstrap/tiers/cross builds still pending.

[loop:torbscript] implementation: fix round on PR #723. CI job "commit messages" was red because the head subject used type `style`, which is not an allowed Conventional Commit type here. Reworded only the head commit (content identical) to `chore(debugger): format the native debugger unit tests` and force-pushed loop/720-native-debug with lease. New head 6181aae8135c269d3b60e9661c0ac7232ac21bae (was 04cf8df4). Local `tools/check-commits.sh 79b35cb9..HEAD` exits 0; forge CI: "commit messages", "what the change touches" and "pdisc" are success, gates/bootstrap/tiers/cross builds still pending.
Author
Collaborator

[loop:torbscript] release implement: fix round done
next: waiting (forge CI on PR #723, then review)

[loop:torbscript] release implement: fix round done next: waiting (forge CI on PR #723, then review)
Author
Collaborator

[loop:torbscript] implementation: done. PR #723 merged into main at 2026-11-13T04:11Z (seven commits, CI green on 6181aae8). The PR body said Refs #720, so the merge did not close the issue; closing it here. Step 4b of #42 (names in variables) is the next step and is filed by the refine of #42 when the ready buffer allows.

[loop:torbscript] implementation: done. PR #723 merged into main at 2026-11-13T04:11Z (seven commits, CI green on 6181aae8). The PR body said `Refs #720`, so the merge did not close the issue; closing it here. Step 4b of #42 (names in `variables`) is the next step and is filed by the refine of #42 when the ready buffer allows.
orion closed this issue 2026-11-13 04:34:46 +00:00
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language#720
No description provided.