Repository navigation
Conversation
`--inspect-brk` and `BUN_INSPECT=<url>?break=1` asked the parser to put a `debugger;` statement in front of the entry. That statement pauses only when the client enabled pause on debugger statements, it is not there for an entry the parser does not read (`// @bun`, a compiled executable), and it moves every line of the entry down by one. The first entry load of the process now asks the debugger to pause when the code of the entry starts to run (JSC `schedulePauseAtEntry`). The request is taken back when the entry did not run or loads again. The parser option, the one-shot flag of the transpiler paths and their slot in the transpiler cache hash are gone.
…ever it runs The request is scheduled before the preloads, so a preload that imports the entry does not lose the pause. It is not withdrawn when the load returns, so a patched Module.runMain that loads the entry later does not lose it either. The next load of an entry withdraws a request that was not used. WebKit 5d73d6291c: only the module code, the program code, or the wrapper of a CommonJS module takes the request, and the pause keeps its reason when a breakpoint is at the same place.
…y starts WebKit cd9249323d: the request ends when the top-level code of the entry starts to run, with a pause or without one. Before, an entry that started while breakpoints were inactive left the request pending, and a later call of a function of a CommonJS entry could take it. The test for the two debugger-statement cases is now two tests, and the compiled-executable tests run after the other tests.
The adapter puts a breakpoint of its own at the start of a file that has breakpoints, and resumes when that one pauses. The tests run it with stopOnEntry and check that the stop on entry shows, with and without a breakpoint of the user in the entry. Also: the tests of this file have a 30 s ceiling in debug and ASAN builds, where each inspector process needs a second to start. The zsh completions describe --inspect-brk as --help does.
Collaborator
Author
|
Updated 4:33 AM PT - Oct 7th, 2026
❌ @robobun, your commit 2647ed2 has 2 failures in
🧪 To try this PR locally: bunx bun-pr 44691That installs a local version of the PR into your bun-44691 --bun |
Collaborator
Author
|
Status. Draft. Blocked on oven-sh/WebKit#776: the WebKit pin is the preview build of that PR. How it was reproduced. The script of #32591, unchanged, prints PR: #44691 |
…tor tests With a client connected, the VM of the inspector server is not destroyed at exit. Under BUN_DESTRUCT_VM_ON_EXIT and LeakSanitizer an inspectee then reports two leaks of that VM (its Bun.stderr and the upgrade Request) and exits with 134, also under --inspect-wait. These tests check the exit code of the inspectee.
Collaborator
Author
|
The four comments that the review flagged are one line each now (2647ed2). The threads are resolved. |
This branch has not been deployed
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.
Fixes #32591
Blocked on oven-sh/WebKit#776: a draft until the pin is a merged commit.
Problem
--inspect-brkorBUN_INSPECT=<url>?break=1, the parser addsdebugger;as line 1 of the entry (src/js_parser/parse/parse_entry.rs:1184). Each line moves down by one in the reported script, so a breakpoint by line pauses one statement early.Debugger.setPauseOnDebuggerStatements. An entry that the parser does not print (// @bun, a compiled executable) gets none.Fix
reload_entry_pointasks JSC to pause when the top-level code of the entry starts (Debugger__schedulePauseAtEntry, [JSC] The debugger can pause when a given script starts to run WebKit#776).PauseOnNextStatement: 58 of 58 probe cells, 20 before.test/cli/inspect/inspect-brk.test.ts(33 tests, 30 fail on 1.4.3-canary.1). Also the other 8 files oftest/cli/inspect/.Background
Debugger::callEventorwillExecuteProgramwhen a function or a module starts. The request makes that call pause for one script.debugger;on line 1 beside the first statement (Fix inspector breakpoints landing one line early with --inspect-brk #32592). It keeps the opt-in and the entries with no pause.Downsides
Debugger.setBreakpointsActivestill gets no pause.Notes
What the reporter of #32591 sees. The script of the issue, unchanged, prints
BUG REPRODUCEDon 1.4.3-canary.1: the first pause is the line-5 breakpoint atmodule code@5:1withpayloadin its temporal dead zone. With this change the script of the entry has no injected line, and the first pause is the start pause atmodule code@1:1("reproduced": false). That client never saw a start pause, because it does not sendDebugger.setPauseOnDebuggerStatements. After oneDebugger.resumethe breakpoint on line 5 pauses withpayload.totalequal to 10. The test "the entry has the text it has under --inspect-wait" does exactly this.For a maintainer to decide.
Debugger.setBreakpointsActive? It does. JSC compiles debug hooks only while breakpoints are active, so without them nothing pauses: notDebugger.pause, not adebuggerstatement, not this. The client of Inspector Protocol does not emit Debugger.paused events #24254 (withInspector.initializedadded) still gets no pause.docs/runtime/debugger.mdxnow says which commands a client sends. The question is asked in [JSC] The debugger can pause when a given script starts to run WebKit#776 too.--inspect-brkthrough a CDP adapter. node: inspector/CDP + bun inspect v26 compat (+70 tests) #35396 works around the injected statement: it starts an interactivebun inspectchild with--inspect-wait, because with--inspect-brk"an interactive session stops twice on the first line", and its source-map code falls forward from the injected line. If this lands first, those workarounds can go, and a test of theirs that counts the injected line needs an update. If they land first, this PR makes that update.The Bun side.
Debugger::schedule_pause_at_entry(src/jsc/Debugger.rs) runs at the start ofreload_entry_pointandreload_entry_point_for_test_runner, before the preloads. A preload that imports the entry, and a patchedModule.runMainthat loads the entry in a later tick, still pause. Only the first entry load of the process schedules: a--hotreload and the second test file do not pause. The next entry load withdraws a request that was not used.set_breakpoint_on_first_line, the same field ofParseOptions, the process-wide one-shot flagset_break_point_on_first_line()and its two readers, and the slot in the features hash of the runtime transpiler cache (EXPECTED_VERSION34 to 35).--help, the docs and the completions say "wait for a connection and pause before the first line of code".What changes for a client.
Debugger.setPauseOnDebuggerStatements. Its reason isPauseOnNextStatement(wasDebuggerStatement), also when a breakpoint is at the same place.--inspect-wait(0 lines differ).// @bun,// @bun @bun-cjs, an empty file, and abun build --compileexecutable started withBUN_INSPECT=<url>?break=1, with and without bytecode. The--inspect-brkflag does not reach the VM of an executable until Forward --inspect and --dns-result-order to the VM in compiled executables #40703.bun testpauses in the first test file only.The debug adapter of the VS Code extension (
packages/bun-debug-adapter-protocol, driven the way VS Code drives it,"stopOnEntry": true, the "Debugger Statements" box not checked). Stops shown to the user:entry.ts, noneentry.ts, line 1entry.ts, line 3entry.cjs, line 3With the box checked, 1.4.3-canary.1 shows the entry stop of a CommonJS entry on line 2 (the injected line), and hides the entry stop of an ES module that has a breakpoint. The adapter puts a breakpoint of its own at
0:0of a file that has breakpoints and resumes when it pauses, and a pause on the injected statement had that breakpoint's reason.Measurements. x86-64 Linux. Release builds (LTO) of
mainbbdc5a519e0and of this PR unless a line says otherwise.Zig::GlobalObject::moduleLoaderEvaluate112,EvalGlobalObject::moduleLoaderEvaluate162,evaluateCommonJSModuleOnce1,319, the same in both). Transpile path:transpile_source_code_inner2,848 to 2,831 andTranspilerJob::run_from_worker_thread1,638 to 1,618 (the atomic exchange is gone). Counted inobjdump -d.reload_entry_point643 to 666 instructions.Debugger__schedulePauseAtEntry,Debugger__cancelPauseAtEntry). Calls over a program of 500 modules: plain 0,--inspect-wait0,--inspect-brk1 (gdb hit counts, debug build).--inspect-wait), 1 (--inspect-brk).Debugger.setPauseOnDebuggerStatements). An entry that starts with a class pauses at 1:1 with the class not initialised.--inspect-wait: 0. Extra start pauses after the first entry load: 0 (two test files, a--hotreload, a--hotreload that fails to parse and is fixed).numberOfDFGCompiles(fib)with no start pause delivered (breakpoints inactive, not set, noDebugger.enable): 1. Under--inspect-wait: 1.size).bloatyis not installed here. Transpiler cache entries rewritten once: 20 of 20, then 0. WebKit prebuilts of the preview build: 42 of 42.Self-review. The 15 concerns:
Module.runMainthat loads the entry later lost the pause. Fixed: the request is not withdrawn when the load returns. Test.registerCodeBlocksaved more registers on the path with no request. Fixed: the slow path is out of line (+2 instructions).Fixes #32591needed the qualification above.BUN_INSPECT=<url>?break=1(Forward --inspect and --dns-result-order to the VM in compiled executables #40703).Debugger.setBreakpointsActive.Differences from the plan of the design review. No check of the loader of the entry: a
.json,.toml,.yamlor text entry does not run, and an.htmlor.wasmentry has no JavaScript at its URL, so its request stays unused until the next entry load. The tests are in a new file, because no file had tests for this flag.Found on the way, not in this PR.
awaitfailsASSERT(m_pauseForAwaitInGenerator)inDebugger::willAwaitin a debug build, and a release build does not stop on the next line. It happens under--inspect-waitfrom adebuggerstatement too. No open PR found.?wait=1: the user's breakpoint and the adapter's own0:0breakpoint are one location. It is the same on 1.4.3-canary.1.test/cli/run/transpiler-cache.test.ts"--define invalidates cache" and "--drop invalidates cache" need more than 5 s in a debug build. They pass with--timeout 60000.Not run. Windows and macOS. The tests use TCP on 127.0.0.1 and the adapter's own launch path, which has a Windows branch.
Related. #24254 (a client with no start pause), #4222 (the injected statement), #32592 (the earlier attempt: the statement on line 1 beside the first statement), #43409 (the entry of the
nodeshim).no test proof · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/cli/inspect/inspect-brk.test.ts