Skip to content

debug: inspect parked Wasm goroutine source stacks - #2742

Merged
cpunion merged 6 commits into
xgo-dev:mainfrom
cpunion:codex/wasm-debug-runtime-20261006
Oct 14, 2026
Merged

cpunion merged 6 commits into
xgo-dev:mainfrom
cpunion:codex/wasm-debug-runtime-20261006

Conversation

@cpunion

@cpunion cpunion commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Browser debug builds retain logical Go caller frames and a versioned runtime registry, so DevTools $goroutines can show live IDs, parent IDs, state, last processor and source stacks even when a fiber is parked. llgo debug enables the opt-in tag; ordinary builds retain their existing instrumentation policy. The extension decoder uses batched memory reads, sequence guards and lazy snapshot objects that are released with their children.

Validation: compiler/debugger/schema Go tests and extension tests; ten real Chrome for Testing 152 sessions covering Memory32/64, 1/2/4 workers, embedded/external DWARF, parked recursive stacks after GC, P0 display and record removal after exit. The same browser acceptance runs in PR CI.

Rebased on main 1bbfc1f75, preserving upstream's caller-frame slot reuse and cached source-location handling. Restore the GC metadata fixture’s segmented-heap mode required by the current runtime. The macOS deadline regression that failed the previous CI head passes three consecutive runs with the upstream fix. Update the pinned QEMU image to 10.2.3 after an ARM64 sysroot tool query crashed under QEMU 7; all 52 CI workflow contract tests pass.

Users pause all worker threads in DevTools before inspecting shared state. Logical frames expose function/file/line, not parked-frame locals or automatic worker control. Advances browser runtime views in #2164; WASI guest-debug transport and physical-board qualification remain separate.

@cpunion

cpunion commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

@fennoai please review the opt-in Wasm logical goroutine registry, caller-frame instrumentation and standalone decoder, especially GC/lifetime safety, paused-worker consistency and default-build overhead. Full compiler/browser/debugger/schema tests and the actual Chrome Memory32/64 1/2/4-worker matrix pass locally, including parked recursive stacks after GC and record removal after exit. The frontend still requires pausing other worker threads before shared-memory inspection; it does not claim automatic worker control or parked-frame locals.

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: WASM debug runtime + goroutines panel

Thorough, well-engineered PR. The lock-free registry snapshot protocol (epoch at the registry level, per-node odd/even sequence guards with begin/end double-reads, re-verified in the JS reader after the walk) is clean and well-tested (Memory32/64, 1/2/4 workers, corruption cases). Build-tag partitioning across the stub / wasm-debug / other variants fully covers with no overlap. Docs (dev/browser-debugging.md, doc/debugging.md, the READMEs) and schema_v1.json accurately match the runtime behavior and consumer fields.

Security: no actionable issues. Goroutine strings reach DevTools as structured protocol objects ({type:'string', ...}), not HTML, so no DOM-injection sink is introduced; keep them text-only downstream. Memory reads are bounded (LIMIT = 4096, cycle detection, numberAddress range checks, Chrome's bounds-checked getWasmLinearMemory).

Findings below are minor/perf — nothing blocking.

Comment thread cmd/internal/browser/extension/goroutines.js
Comment thread cmd/internal/debug/debug.go
Comment thread runtime/internal/runtime/traceback_threads_wasm_debug.go Outdated
Comment thread runtime/internal/runtime/traceback_threads_wasm_debug.go
@fennoai

fennoai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Focused review: GC/lifetime, paused-worker consistency, default-build overhead

I verified each of your three highlighted concerns directly against the code (locked range 78973102...9cae6c0). Short version: all three hold up. Details below.

✅ Default-build overhead — zero

wasmDebuggerEnabled is a compile-time const, not a variable. The stub (traceback_wasm_debug_stub.go) sets const wasmDebuggerEnabled = false under !llgo || !wasm || baremetal || !llgo.wasm.debugger, which is the exact negation of the wasm-debug file's llgo && wasm && !baremetal && llgo.wasm.debugger — full coverage, no overlap. So in every ordinary build the eight if wasmDebuggerEnabled { … } guards added to caller.go (Push/Pop/updateCurrentFrame/store-create) are dead-code eliminated, and the three stub shims (beginWasmCallerUpdate/endWasmCallerUpdate/publishWasmCallerStore) inline to nothing. The traceback_threads_other.go tag edit (wasm → wasm && !llgo.wasm.debugger) correctly carves out only the debugger-wasm case, so native/other builds are untouched. No per-frame cost in the default path.

✅ GC / lifetime safety

  • Node storage: nodes are c.Malloc'd libc memory (not GC-scanned), which is the right call — it avoids recursing into Go allocation while registering the initial G before its local context is installed (as the comment notes).
  • node.callers → *callerLocationStore dangling: safe. The store is a GC-heap object kept alive by the //llgointernal:gls per-goroutine callerLocationStoreCurrent, i.e. for the whole G lifetime. The node's lifetime is a subset of the G's (both set up in initG→registerTraceback, both torn down in destroyG/freeRuntimeContext→unregisterTraceback), so the store can't be collected while the node still references it. unregisterTraceback frees only the node, never the store — correct.
  • node.gp / g.tracebackThread: the opaque unsafe.Pointer in g.tracebackThread mirrors the pre-existing native variant exactly, so GC's conservative treatment of that field is unchanged by this PR.
  • Reuse / staleness: each context is freshly AllocRoot'd and Memset-zeroed (no pooling that could carry a stale tracebackThread), and unregisterTraceback is idempotent (nil-guarded, clears gp.tracebackThread before c.Free), so the destroyG + freeRuntimeContext double-call path is safe. This lines up with your "record removal after exit" test.

✅ Paused-worker consistency

The seqlock protocol is correct and defensively enforced in the standalone decoder (goroutines.js):

  • Registry epoch: writers bump it odd→even around list mutation; the decoder rejects an odd initial read and re-checks equality at the end → any concurrent register/unregister is caught.
  • Per-node sequence: writers bump it odd→even around in-place frame updates (begin/endWasmCallerUpdate, with updateDepth bracketing nested updates so one transition spans the whole nested update); the decoder rejects odd and re-checks per node → any concurrent frame mutation is caught.
  • Guards: LIMIT = 4096 on both node count and frame count, cycle detection (visited), nil-G rejection, and frame.size <= 0.

Net effect: if a worker is not actually paused, the decoder fails loudly with a "pause all workers" error rather than returning torn/corrupt data — which matches your stated contract that the frontend requires pausing other workers and makes no automatic-worker-control claim.

Notes (non-blocking, carried over from the diff review)

  • goroutines.js does one awaited getWasmLinearMemory per field; per node that's ~frames * 7 serialized round-trips (up to LIMIT nodes/depth). Correct, but batching a whole node/frame-struct read would cut transport calls ~10×.
  • traceback_threads_wasm_debug.go: registerTraceback's parent param is unused on wasm (parent linkage is reconstructed in JS via parentGoid); and the return after fatal(...) is dead in the targeted js && wasm build where fatal→ForceExit is noreturn. Both cosmetic.
  • cmd/internal/debug/debug.go: llgo.wasm.debugger is appended unconditionally for the browser backend, so a user who already passed -tags=llgo.wasm.debugger gets it twice (harmless; a strings.Contains guard would be tidier).

No blocking issues on any of the three areas you flagged.

@codecov

codecov Bot commented Oct 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@cpunion

cpunion commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

Current doc_verify failure is the unchanged README LLDB homepage (https://lldb.llvm.org/): lychee reports a connection failure, with 95 other links passing and no excluded links. Direct access currently returns HTTP 200 and the official documentation remains valid. I have not retried CI, added link exclusions/tolerance, or changed a valid URL to bypass the failure. The full local Chrome debugger matrix and all 23 extension tests pass; the fresh hosted runtime/debugger checks are still running.

@cpunion

cpunion commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator Author

@fennoai please check the small P0 correction in eed7d78. Runtime P IDs are zero-based; debugger records now initialize last-P to -1 and the decoder reads the signed field so P0 remains valid. A new Memory32/64 unit test covers zero and the unstarted sentinel, and the real Chrome matrix now asserts the main G is P0 and both parked Gs have valid last-P values. All 24 extension tests and the complete actual 1/2/4-worker, embedded/external matrix pass. The earlier external-link failure was left intact; this head changes runtime decoding and tests, with no URL or CI-rule changes.

@fennoai

fennoai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

P0 correction verified ✅ — zero-based processor IDs

Checked eed7d788a against the code (and the sibling 0409080d6 that also landed since my last review). The fix is correct and complete.

The P0 fix (eed7d788a) is right

  • Runtime (traceback_threads_wasm_debug.go): processorID now initializes to -1 in registerTraceback (*node = wasmDebuggerNode{gp: gp, processorID: -1}), and beginWasmCallerUpdate overwrites it with the real zero-based gp.m.p.id once the G first runs. So the sentinel is strictly -1 = never run, >= 0 = last P.
  • Decoder (goroutines.js): reads the field width-correctly as signed — BigInt.asIntN(nodeFields[spec.processor_id].size * 8, unsigned(...)) — then treats processor >= 0n as a real P (so P0 → "0") and negative as null. This is robust regardless of the DWARF signed/unsigned labelling because it keys only off the byte size. Exactly the fix for the processorID == 0 nuance from the prior review.
  • Tests: the new goroutines_test.js case asserts P0 → '0' and 0xffffffff → null across both memory widths (4 and 8); chrome_session.mjs now asserts the main G is P0, both parked Gs have non-null last-P, and (multi-worker) that they differ. Correct coverage of the zero and unstarted-sentinel edges.

Also landed since last review (0409080d6) — spot-checked, no regressions

This commit resolved all three non-blocking notes from my earlier review, and I verified it doesn't weaken the consistency guarantees:

  • Batched memory reads: the decoder now fetches each fixed struct / the frame array in one getWasmLinearMemory call and decodes scalars locally — the ~10× round-trip reduction I suggested. Crucially, the seqlock re-verification stays fresh: the trailing per-node sequence and final epoch re-checks use plugin.readNamedUnsigned (a live transport read), not the cached nodeBytes/registryBytes — so the paused-worker torn-read detection still works. The batched frame read is bounded (count <= LIMIT, memory() caps size <= LIMIT*256), well clear of realistic frame sizes.
  • Duplicate build tag: now guarded by slices.Contains(strings.Fields(strings.ReplaceAll(conf.Tags, ",", " ")), "llgo.wasm.debugger"), correctly handling comma- and space-separated tag lists.
  • Unused parent param: renamed to _ with a one-line comment.

Checks I ran

  • node --test in the extension dir: 24/24 pass.
  • gofmt -l on the changed Go files: clean.

No issues. The P0 correction does what it says, and the batching change is consistency-safe. The pre-existing external-link failure is unrelated to this head (runtime decoding + tests only, no URL/CI changes), as you noted.

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

LLGo WebAssembly build benchmarks

e35a0dc767b6 | workflow run | long-term charts

WebAssembly output sizes
Example, profile and compiler Wasm module vs base Generated JS glue vs base
cprintf/j32-emscripten/LLGo 145969 B +449 B / +0.3% (worse) 88157 B 0 B / +0.0%
cprintf/j32-goos-js/LLGo 145197 B +449 B / +0.3% (worse) 72985 B 0 B / +0.0%
cprintf/j64-emscripten-memory64/LLGo 133576 B +356 B / +0.3% (worse) 91328 B 0 B / +0.0%
cprintf/w32-goos-wasip1/LLGo 155660 B +416 B / +0.3% (worse) 0 B 0 B / 0.0%
cprintf/w32-wasi/LLGo 156338 B +416 B / +0.3% (worse) 0 B 0 B / 0.0%
fmtprintf/j32-emscripten/LLGo 2997403 B +693 B / +0.02313% (worse) 130661 B 0 B / +0.0%
fmtprintf/j32-goos-js/Go 2526852 B 0 B / +0.0% 0 B 0 B / 0.0%
fmtprintf/j32-goos-js/LLGo 3092201 B +714 B / +0.0231% (worse) 102110 B 0 B / +0.0%
fmtprintf/j64-emscripten-memory64/LLGo 2759915 B +634 B / +0.02298% (worse) 136141 B 0 B / +0.0%
fmtprintf/w32-goos-wasip1/Go 2500019 B 0 B / +0.0% 0 B 0 B / 0.0%
fmtprintf/w32-goos-wasip1/LLGo 2341822 B +656 B / +0.02802% (worse) 0 B 0 B / 0.0%
fmtprintf/w32-wasi/LLGo 2338498 B +656 B / +0.02806% (worse) 0 B 0 B / 0.0%
j32-emscripten/LLGo 145482 B +449 B / +0.3% (worse) 88157 B 0 B / +0.0%
j32-goos-js/Go 1895533 B 0 B / +0.0% 0 B 0 B / 0.0%
j32-goos-js/LLGo 144945 B +449 B / +0.3% (worse) 72985 B 0 B / +0.0%
j64-emscripten-memory64/LLGo 133130 B +357 B / +0.3% (worse) 91328 B 0 B / +0.0%
reflectcall/j32-emscripten/LLGo 1444226 B +698 B / +0.04835% (worse) 104917 B 0 B / +0.0%
reflectcall/j32-goos-js/Go 2191221 B 0 B / +0.0% 0 B 0 B / 0.0%
reflectcall/j32-goos-js/LLGo 1493964 B +699 B / +0.04681% (worse) 89743 B 0 B / +0.0%
reflectcall/j64-emscripten-memory64/LLGo 1336672 B +588 B / +0.04401% (worse) 109492 B 0 B / +0.0%
reflectcall/w32-goos-wasip1/Go 2205707 B 0 B / +0.0% 0 B 0 B / 0.0%
reflectcall/w32-goos-wasip1/LLGo 1272200 B +656 B / +0.1% (worse) 0 B 0 B / 0.0%
reflectcall/w32-wasi/LLGo 1269821 B +656 B / +0.1% (worse) 0 B 0 B / 0.0%
w32-goos-wasip1/Go 1909947 B 0 B / +0.0% 0 B 0 B / 0.0%
w32-goos-wasip1/LLGo 155334 B +416 B / +0.3% (worse) 0 B 0 B / 0.0%
w32-wasi/LLGo 156012 B +416 B / +0.3% (worse) 0 B 0 B / 0.0%
LLGo WebAssembly build measurements
Example and profile Build vs base
j32-emscripten 8.652 s -484.3 ms / -5.3% (better)
j32-goos-js 8.308 s -193.3 ms / -2.3% (better)
j64-emscripten-memory64 7.381 s -770.7 ms / -9.5% (better)
reflectcall/w32-wasi 25.863 s -1.001 s / -3.7% (better)
w32-goos-wasip1 5.175 s -528 ms / -9.3% (better)
w32-wasi 4.935 s -930.2 ms / -15.9% (better)

Compared with 1bbfc1f75db4 measured in the same runner job.

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

LLGo baseline benchmarks

314d7d0cab79 | workflow run | long-term charts

Program measurements

Platform Workload File size vs base Text size vs base Build vs base Run vs base
Linux cprintf 8568 B 0 B / +0.0% 387 B 0 B / +0.0% 837.401 ms -31.54 ms / -3.6% (better) 1.320 ms +47.58 us / +3.7% (worse)
Linux cprintf-lto 8408 B 0 B / +0.0% 368 B 0 B / +0.0% 859.216 ms +15.95 ms / +1.9% (worse) 1.312 ms +63.05 us / +5.0% (worse)
Linux fmtprintf 4900504 B +1064 B / +0.02172% (worse) 506206 B 0 B / +0.0% 5.730 s +250.7 ms / +4.6% (worse) 4.059 ms -507.1 us / -11.1% (better)
Linux fmtprintf-lto 3620864 B +256 B / +0.007071% (worse) 444290 B 0 B / +0.0% 15.489 s -947.1 ms / -5.8% (better) 4.039 ms -390.8 us / -8.8% (better)
Linux println 690344 B +872 B / +0.1% (worse) 17095 B 0 B / +0.0% 876.810 ms -79.91 ms / -8.4% (better) 1.929 ms -155.6 us / -7.5% (better)
Linux println-lto 192744 B +48 B / +0.02491% (worse) 14484 B 0 B / +0.0% 1.157 s -191.9 ms / -14.2% (better) 1.686 ms -266.7 us / -13.7% (better)
macOS cprintf 50736 B 0 B / +0.0% 4409 B 0 B / +0.0% 1.230 s -142.4 ms / -10.4% (better) 6.295 ms +1.233 ms / +24.4% (worse)
macOS cprintf-lto 50496 B 0 B / +0.0% 161 B 0 B / +0.0% 1.435 s +194.8 ms / +15.7% (worse) 2.778 ms -4.547 ms / -62.1% (better)
macOS fmtprintf 1793088 B 0 B / +0.0% 882992 B +256 B / +0.029% (worse) 4.879 s -890 ms / -15.4% (better) 61.124 ms -14.82 ms / -19.5% (better)
macOS fmtprintf-lto 1362176 B 0 B / +0.0% 767060 B +196 B / +0.02556% (worse) 11.524 s -3.494 s / -23.3% (better) 92.296 ms -16.32 ms / -15.0% (better)
macOS println 100080 B 0 B / +0.0% 24519 B 0 B / +0.0% 1.007 s -329.4 ms / -24.6% (better) 5.008 ms +505.5 us / +11.2% (worse)
macOS println-lto 84016 B 0 B / +0.0% 21760 B 0 B / +0.0% 1.367 s -322.7 ms / -19.1% (better) 4.718 ms -2.219 ms / -32.0% (better)
Windows MinGW cprintf 662016 B +1024 B / +0.2% (worse) 4550 B 0 B / +0.0% 1.553 s -50.97 ms / -3.2% (better) 3.426 ms -425.6 us / -11.1% (better)
Windows MinGW cprintf-lto 44544 B 0 B / +0.0% 4502 B 0 B / +0.0% 1.611 s -60.27 ms / -3.6% (better) 3.460 ms -256.6 us / -6.9% (better)
Windows MinGW fmtprintf 5457408 B 0 B / +0.0% 606134 B 0 B / +0.0% 5.317 s -131.3 ms / -2.4% (better) 10.599 ms -2.823 ms / -21.0% (better)
Windows MinGW fmtprintf-lto 4160000 B 0 B / +0.0% 553254 B 0 B / +0.0% 12.877 s -93.77 ms / -0.7% (better) 10.153 ms -843.9 us / -7.7% (better)
Windows MinGW println 717824 B +1024 B / +0.1% (worse) 25446 B 0 B / +0.0% 1.579 s -26.13 ms / -1.6% (better) 6.325 ms -624.9 us / -9.0% (better)
Windows MinGW println-lto 210944 B 0 B / +0.0% 22326 B 0 B / +0.0% 1.843 s -31.42 ms / -1.7% (better) 6.412 ms -422 us / -6.2% (better)
Windows MinGW 386 cprintf 610816 B +1024 B / +0.2% (worse) 5352 B 0 B / +0.0% 1.568 s -46.31 ms / -2.9% (better) 5.101 ms -77.5 us / -1.5% (better)
Windows MinGW 386 cprintf-lto 104448 B 0 B / +0.0% 5112 B 0 B / +0.0% 1.580 s -84.56 ms / -5.1% (better) 4.960 ms -475.4 us / -8.7% (better)
Windows MinGW 386 fmtprintf 4759040 B +512 B / +0.01076% (worse) 473902 B 0 B / +0.0% 5.427 s +73.58 ms / +1.4% (worse) 11.983 ms +294.2 us / +2.5% (worse)
Windows MinGW 386 fmtprintf-lto 4162048 B 0 B / +0.0% 452294 B 0 B / +0.0% 12.439 s -71.73 ms / -0.6% (better) 13.640 ms +433.2 us / +3.3% (worse)
Windows MinGW 386 println 664064 B +1024 B / +0.2% (worse) 21694 B 0 B / +0.0% 1.595 s -6.493 ms / -0.4% (better) 8.742 ms -589.3 us / -6.3% (better)
Windows MinGW 386 println-lto 260608 B 0 B / +0.0% 19514 B 0 B / +0.0% 1.883 s -10.72 ms / -0.6% (better) 8.455 ms -429.7 us / -4.8% (better)
Windows MinGW ARM64 cprintf 673280 B +1536 B / +0.2% (worse) 4436 B 0 B / +0.0% 1.949 s +10.53 ms / +0.5% (worse) 6.848 ms +45.9 us / +0.7% (worse)
Windows MinGW ARM64 cprintf-lto 44032 B 0 B / +0.0% 4388 B 0 B / +0.0% 2.015 s +61.82 ms / +3.2% (worse) 7.490 ms +975.2 us / +15.0% (worse)
Windows MinGW ARM64 fmtprintf 5368320 B +1024 B / +0.01908% (worse) 514292 B 0 B / +0.0% 5.482 s +90.73 ms / +1.7% (worse) 15.912 ms +457.2 us / +3.0% (worse)
Windows MinGW ARM64 fmtprintf-lto 4326400 B 0 B / +0.0% 480352 B -20 B / -0.004163% (better) 12.481 s +506.3 ms / +4.2% (worse) 16.673 ms +1.749 ms / +11.7% (worse)
Windows MinGW ARM64 println 724992 B +512 B / +0.1% (worse) 24160 B 0 B / +0.0% 1.980 s +37.36 ms / +1.9% (worse) 12.199 ms +257.2 us / +2.2% (worse)
Windows MinGW ARM64 println-lto 218624 B 0 B / +0.0% 21516 B 0 B / +0.0% 2.246 s +62.98 ms / +2.9% (worse) 11.670 ms -280.7 us / -2.3% (better)
Windows MSVC cprintf 905728 B +512 B / +0.1% (worse) 66003 B 0 B / +0.0% 1.482 s +3.358 ms / +0.2% (worse) 3.409 ms -99.9 us / -2.8% (better)
Windows MSVC cprintf-lto 292352 B 0 B / +0.0% 65971 B 0 B / +0.0% 1.475 s -5.425 ms / -0.4% (better) 3.365 ms +62.6 us / +1.9% (worse)
Windows MSVC fmtprintf 5757952 B +512 B / +0.008893% (worse) 701894 B 0 B / +0.0% 5.182 s -13.65 ms / -0.3% (better) 11.944 ms +467.3 us / +4.1% (worse)
Windows MSVC fmtprintf-lto 4483072 B 0 B / +0.0% 652806 B 0 B / +0.0% 12.107 s +212.4 ms / +1.8% (worse) 11.688 ms -125.9 us / -1.1% (better)
Windows MSVC println 1030656 B +1536 B / +0.1% (worse) 121334 B 0 B / +0.0% 1.463 s -134.3 us / -0.009176% (better) 6.829 ms -113.7 us / -1.6% (better)
Windows MSVC println-lto 531456 B 0 B / +0.0% 118870 B 0 B / +0.0% 1.756 s +34.01 ms / +2.0% (worse) 7.077 ms -3.449 ms / -32.8% (better)
Windows MSVC 386 cprintf 521728 B +1024 B / +0.2% (worse) 3947 B 0 B / +0.0% 1.458 s -49.36 ms / -3.3% (better) 6.238 ms +54.2 us / +0.9% (worse)
Windows MSVC 386 cprintf-lto 44544 B 0 B / +0.0% 3901 B 0 B / +0.0% 1.655 s +163.8 ms / +11.0% (worse) 6.594 ms -395.8 us / -5.7% (better)
Windows MSVC 386 fmtprintf 4492288 B +512 B / +0.0114% (worse) 457314 B 0 B / +0.0% 5.340 s +42.05 ms / +0.8% (worse) 15.263 ms +640.4 us / +4.4% (worse)
Windows MSVC 386 fmtprintf-lto 3910144 B +512 B / +0.0131% (worse) 427681 B 0 B / +0.0% 11.949 s -197.1 ms / -1.6% (better) 15.173 ms -1.7 ms / -10.1% (better)
Windows MSVC 386 println 577024 B +1024 B / +0.2% (worse) 20566 B 0 B / +0.0% 1.451 s -254.1 ms / -14.9% (better) 10.686 ms +124.4 us / +1.2% (worse)
Windows MSVC 386 println-lto 200192 B 0 B / +0.0% 18711 B 0 B / +0.0% 1.722 s -42.63 ms / -2.4% (better) 10.369 ms -549.8 us / -5.0% (better)
Windows MSVC ARM64 cprintf 672768 B 0 B / +0.0% 4240 B 0 B / +0.0% 1.470 s -46.95 ms / -3.1% (better) 5.940 ms -346.2 us / -5.5% (better)
Windows MSVC ARM64 cprintf-lto 49152 B 0 B / +0.0% 4140 B 0 B / +0.0% 1.488 s -26.31 ms / -1.7% (better) 5.979 ms -264.9 us / -4.2% (better)
Windows MSVC ARM64 fmtprintf 5365248 B +1536 B / +0.02864% (worse) 514164 B 0 B / +0.0% 4.719 s +15.44 ms / +0.3% (worse) 14.303 ms -381.1 us / -2.6% (better)
Windows MSVC ARM64 fmtprintf-lto 4333056 B 0 B / +0.0% 480960 B -16 B / -0.003327% (better) 10.595 s -285.3 ms / -2.6% (better) 14.949 ms -319.3 us / -2.1% (better)
Windows MSVC ARM64 println 728064 B +1024 B / +0.1% (worse) 24200 B 0 B / +0.0% 1.472 s -13.57 ms / -0.9% (better) 10.494 ms -332.3 us / -3.1% (better)
Windows MSVC ARM64 println-lto 223232 B 0 B / +0.0% 21688 B 0 B / +0.0% 1.696 s -20.8 ms / -1.2% (better) 11.129 ms +141.1 us / +1.3% (worse)
Core language and compiler benchmarks
Platform Benchmark ns/op vs base
Linux BenchmarkLookupPCRandom 14.750 ns/op +0.19 ns/op / +1.3% (worse)
Linux BenchmarkMergeCompilerFlags 202.200 ns/op -14.2 ns/op / -6.6% (better)
Linux BenchmarkMergeLinkerFlags 135 ns/op -9.8 ns/op / -6.8% (better)
Linux BenchmarkChannelBuffered 55.850 ns/op +0.56 ns/op / +1.0% (worse)
Linux BenchmarkChannelHandoff 13388 ns/op +63 ns/op / +0.5% (worse)
Linux BenchmarkDefer 52.200 ns/op -1.84 ns/op / -3.4% (better)
Linux BenchmarkDirectCall 1.165 ns/op -0.004 ns/op / -0.3% (better)
Linux BenchmarkGlobalRead 1.170 ns/op +0.003 ns/op / +0.3% (worse)
Linux BenchmarkGlobalWrite 7.841 ns/op +0.06 ns/op / +0.8% (worse)
Linux BenchmarkGoroutine 28709 ns/op -3121 ns/op / -9.8% (better)
Linux BenchmarkInterfaceCall 5.882 ns/op +0.025 ns/op / +0.4% (worse)
Linux BenchmarkRuntimeGetG 2.358 ns/op +0.007 ns/op / +0.3% (worse)
macOS BenchmarkLookupPCRandom 21.660 ns/op +1.71 ns/op / +8.6% (worse)
macOS BenchmarkMergeCompilerFlags 229.500 ns/op -6.7 ns/op / -2.8% (better)
macOS BenchmarkMergeLinkerFlags 145.300 ns/op +31.8 ns/op / +28.0% (worse)
macOS BenchmarkChannelBuffered 34.290 ns/op -4.79 ns/op / -12.3% (better)
macOS BenchmarkChannelHandoff 10181 ns/op -2194 ns/op / -17.7% (better)
macOS BenchmarkDefer 48.480 ns/op -0.64 ns/op / -1.3% (better)
macOS BenchmarkDirectCall 1.283 ns/op -0.156 ns/op / -10.8% (better)
macOS BenchmarkGlobalRead 1.200 ns/op -0.239 ns/op / -16.6% (better)
macOS BenchmarkGlobalWrite 1.440 ns/op -0.192 ns/op / -11.8% (better)
macOS BenchmarkGoroutine 67869 ns/op +3362 ns/op / +5.2% (worse)
macOS BenchmarkInterfaceCall 5.310 ns/op -0.258 ns/op / -4.6% (better)
macOS BenchmarkRuntimeGetG 2.923 ns/op +0.284 ns/op / +10.8% (worse)
Windows MinGW BenchmarkLookupPCRandom 12.940 ns/op +0.18 ns/op / +1.4% (worse)
Windows MinGW BenchmarkMergeCompilerFlags 623.700 ns/op +12.5 ns/op / +2.0% (worse)
Windows MinGW BenchmarkMergeLinkerFlags 545.800 ns/op +11.8 ns/op / +2.2% (worse)
Windows MinGW BenchmarkChannelBuffered 30.070 ns/op +0.18 ns/op / +0.6% (worse)
Windows MinGW BenchmarkChannelHandoff 880.800 ns/op -69.4 ns/op / -7.3% (better)
Windows MinGW BenchmarkDefer 53.650 ns/op -1.19 ns/op / -2.2% (better)
Windows MinGW BenchmarkDirectCall 1.546 ns/op -0.002 ns/op / -0.1% (better)
Windows MinGW BenchmarkGlobalRead 1.547 ns/op -0.003 ns/op / -0.2% (better)
Windows MinGW BenchmarkGlobalWrite 2.470 ns/op +0.001 ns/op / +0.0405% (worse)
Windows MinGW BenchmarkGoroutine 90542 ns/op -1146 ns/op / -1.2% (better)
Windows MinGW BenchmarkInterfaceCall 8.391 ns/op +0.014 ns/op / +0.2% (worse)
Windows MinGW BenchmarkRuntimeGetG 2.481 ns/op -0.004 ns/op / -0.2% (better)
Windows MinGW 386 BenchmarkLookupPCRandom 26.500 ns/op -0.02 ns/op / -0.1% (better)
Windows MinGW 386 BenchmarkMergeCompilerFlags 761.700 ns/op +17.4 ns/op / +2.3% (worse)
Windows MinGW 386 BenchmarkMergeLinkerFlags 695.500 ns/op +33.7 ns/op / +5.1% (worse)
Windows MinGW 386 BenchmarkChannelBuffered 39.340 ns/op +0.04 ns/op / +0.1% (worse)
Windows MinGW 386 BenchmarkChannelHandoff 759.600 ns/op -39.9 ns/op / -5.0% (better)
Windows MinGW 386 BenchmarkDefer 41.200 ns/op -0.29 ns/op / -0.7% (better)
Windows MinGW 386 BenchmarkDirectCall 1.552 ns/op +0.003 ns/op / +0.2% (worse)
Windows MinGW 386 BenchmarkGlobalRead 1.864 ns/op +0.003 ns/op / +0.2% (worse)
Windows MinGW 386 BenchmarkGlobalWrite 7.765 ns/op -0.014 ns/op / -0.2% (better)
Windows MinGW 386 BenchmarkGoroutine 108769 ns/op +5125 ns/op / +4.9% (worse)
Windows MinGW 386 BenchmarkInterfaceCall 8.147 ns/op +0.052 ns/op / +0.6% (worse)
Windows MinGW 386 BenchmarkRuntimeGetG 1.925 ns/op +0.001 ns/op / +0.1% (worse)
Windows MinGW ARM64 BenchmarkLookupPCRandom 12.180 ns/op +0.03 ns/op / +0.2% (worse)
Windows MinGW ARM64 BenchmarkMergeCompilerFlags 566 ns/op +4.5 ns/op / +0.8% (worse)
Windows MinGW ARM64 BenchmarkMergeLinkerFlags 520.700 ns/op -0.5 ns/op / -0.1% (better)
Windows MinGW ARM64 BenchmarkChannelBuffered 37.190 ns/op -0.03 ns/op / -0.1% (better)
Windows MinGW ARM64 BenchmarkChannelHandoff 4850 ns/op +892 ns/op / +22.5% (worse)
Windows MinGW ARM64 BenchmarkDefer 56.040 ns/op +0.03 ns/op / +0.1% (worse)
Windows MinGW ARM64 BenchmarkDirectCall 0.590 ns/op -0.0007 ns/op / -0.1% (better)
Windows MinGW ARM64 BenchmarkGlobalRead 0.590 ns/op -0.0005 ns/op / -0.1% (better)
Windows MinGW ARM64 BenchmarkGlobalWrite 0.737 ns/op 0 ns/op / +0.0%
Windows MinGW ARM64 BenchmarkGoroutine 60245 ns/op -775 ns/op / -1.3% (better)
Windows MinGW ARM64 BenchmarkInterfaceCall 4.147 ns/op 0 ns/op / +0.0%
Windows MinGW ARM64 BenchmarkRuntimeGetG 1.774 ns/op -0.034 ns/op / -1.9% (better)
Windows MSVC BenchmarkLookupPCRandom 13.230 ns/op +0.3 ns/op / +2.3% (worse)
Windows MSVC BenchmarkMergeCompilerFlags 627.200 ns/op +1.1 ns/op / +0.2% (worse)
Windows MSVC BenchmarkMergeLinkerFlags 543.800 ns/op -16.5 ns/op / -2.9% (better)
Windows MSVC BenchmarkChannelBuffered 29.740 ns/op 0 ns/op / +0.0%
Windows MSVC BenchmarkChannelHandoff 1184 ns/op +33 ns/op / +2.9% (worse)
Windows MSVC BenchmarkDefer 56.330 ns/op +0.83 ns/op / +1.5% (worse)
Windows MSVC BenchmarkDirectCall 1.549 ns/op 0 ns/op / +0.0%
Windows MSVC BenchmarkGlobalRead 1.547 ns/op 0 ns/op / +0.0%
Windows MSVC BenchmarkGlobalWrite 2.459 ns/op +0.006 ns/op / +0.2% (worse)
Windows MSVC BenchmarkGoroutine 90171 ns/op +1556 ns/op / +1.8% (worse)
Windows MSVC BenchmarkInterfaceCall 8.676 ns/op -0.02 ns/op / -0.2% (better)
Windows MSVC BenchmarkRuntimeGetG 1.862 ns/op -0.001 ns/op / -0.1% (better)
Windows MSVC 386 BenchmarkLookupPCRandom 69.440 ns/op -0.41 ns/op / -0.6% (better)
Windows MSVC 386 BenchmarkMergeCompilerFlags 948.300 ns/op -10.1 ns/op / -1.1% (better)
Windows MSVC 386 BenchmarkMergeLinkerFlags 875.100 ns/op -25.4 ns/op / -2.8% (better)
Windows MSVC 386 BenchmarkChannelBuffered 52.280 ns/op +0.01 ns/op / +0.01913% (worse)
Windows MSVC 386 BenchmarkChannelHandoff 3540 ns/op +408 ns/op / +13.0% (worse)
Windows MSVC 386 BenchmarkDefer 54.950 ns/op -0.78 ns/op / -1.4% (better)
Windows MSVC 386 BenchmarkDirectCall 0.866 ns/op -0.017 ns/op / -1.9% (better)
Windows MSVC 386 BenchmarkGlobalRead 0.890 ns/op +0.0241 ns/op / +2.8% (worse)
Windows MSVC 386 BenchmarkGlobalWrite 16.300 ns/op +0.06 ns/op / +0.4% (worse)
Windows MSVC 386 BenchmarkGoroutine 95212 ns/op -2795 ns/op / -2.9% (better)
Windows MSVC 386 BenchmarkInterfaceCall 6.732 ns/op +0.1 ns/op / +1.5% (worse)
Windows MSVC 386 BenchmarkRuntimeGetG 2.046 ns/op -0.034 ns/op / -1.6% (better)
Windows MSVC ARM64 BenchmarkLookupPCRandom 12.060 ns/op -0.01 ns/op / -0.1% (better)
Windows MSVC ARM64 BenchmarkMergeCompilerFlags 564.100 ns/op +9.5 ns/op / +1.7% (worse)
Windows MSVC ARM64 BenchmarkMergeLinkerFlags 520.700 ns/op +9.2 ns/op / +1.8% (worse)
Windows MSVC ARM64 BenchmarkChannelBuffered 38.660 ns/op +1.4 ns/op / +3.8% (worse)
Windows MSVC ARM64 BenchmarkChannelHandoff 3748 ns/op +973 ns/op / +35.1% (worse)
Windows MSVC ARM64 BenchmarkDefer 58.440 ns/op +0.37 ns/op / +0.6% (worse)
Windows MSVC ARM64 BenchmarkDirectCall 0.589 ns/op 0 ns/op / +0.0%
Windows MSVC ARM64 BenchmarkGlobalRead 0.590 ns/op +0.0005 ns/op / +0.1% (worse)
Windows MSVC ARM64 BenchmarkGlobalWrite 3.749 ns/op +0.003 ns/op / +0.1% (worse)
Windows MSVC ARM64 BenchmarkGoroutine 54207 ns/op +401 ns/op / +0.7% (worse)
Windows MSVC ARM64 BenchmarkInterfaceCall 4.239 ns/op +0.001 ns/op / +0.0236% (worse)
Windows MSVC ARM64 BenchmarkRuntimeGetG 1.773 ns/op -0.033 ns/op / -1.8% (better)
Timer runtime benchmarks
Platform Operation and runtime ns/op vs base
Linux AfterFuncZeroDelivery/Go 915.400 ns/op -9.4 ns/op / -1.0% (better)
Linux AfterFuncZeroDelivery/LLGo 37854 ns/op -3574 ns/op / -8.6% (better)
Linux CreateStop/Go 295 ns/op -1.7 ns/op / -0.6% (better)
Linux CreateStop/LLGo 1846 ns/op -35 ns/op / -1.9% (better)
Linux RearmStopped/Go 117.200 ns/op +1.2 ns/op / +1.0% (worse)
Linux RearmStopped/LLGo 1463 ns/op +169 ns/op / +13.1% (worse)
Linux ResetActive/Go 69.010 ns/op +0.42 ns/op / +0.6% (worse)
Linux ResetActive/LLGo 683.400 ns/op +8.4 ns/op / +1.2% (worse)
Linux ResetHeap1024/Go 67.820 ns/op +0.63 ns/op / +0.9% (worse)
Linux ResetHeap1024/LLGo 164.900 ns/op +2 ns/op / +1.2% (worse)
macOS AfterFuncZeroDelivery/Go 695.400 ns/op +16.7 ns/op / +2.5% (worse)
macOS AfterFuncZeroDelivery/LLGo 115778 ns/op -14357 ns/op / -11.0% (better)
macOS CreateStop/Go 253.200 ns/op +50 ns/op / +24.6% (worse)
macOS CreateStop/LLGo 890.200 ns/op +156.8 ns/op / +21.4% (worse)
macOS RearmStopped/Go 89.740 ns/op +7.12 ns/op / +8.6% (worse)
macOS RearmStopped/LLGo 304.900 ns/op -326.3 ns/op / -51.7% (better)
macOS ResetActive/Go 68.210 ns/op +6.32 ns/op / +10.2% (worse)
macOS ResetActive/LLGo 159.300 ns/op -71.4 ns/op / -30.9% (better)
macOS ResetHeap1024/Go 68.490 ns/op +8.8 ns/op / +14.7% (worse)
macOS ResetHeap1024/LLGo 77.820 ns/op -86.78 ns/op / -52.7% (better)
Windows MinGW AfterFuncZeroDelivery/Go 555.500 ns/op +1.8 ns/op / +0.3% (worse)
Windows MinGW AfterFuncZeroDelivery/LLGo 178370 ns/op -327 ns/op / -0.2% (better)
Windows MinGW CreateStop/Go 116.400 ns/op -3.5 ns/op / -2.9% (better)
Windows MinGW CreateStop/LLGo 461.700 ns/op -25.5 ns/op / -5.2% (better)
Windows MinGW RearmStopped/Go 31.460 ns/op 0 ns/op / +0.0%
Windows MinGW RearmStopped/LLGo 245.500 ns/op -9.5 ns/op / -3.7% (better)
Windows MinGW ResetActive/Go 20.220 ns/op -0.14 ns/op / -0.7% (better)
Windows MinGW ResetActive/LLGo 168.700 ns/op +16 ns/op / +10.5% (worse)
Windows MinGW ResetHeap1024/Go 20.520 ns/op -0.02 ns/op / -0.1% (better)
Windows MinGW ResetHeap1024/LLGo 119.400 ns/op +1.6 ns/op / +1.4% (worse)
Windows MinGW 386 AfterFuncZeroDelivery/Go 963.900 ns/op +15.8 ns/op / +1.7% (worse)
Windows MinGW 386 AfterFuncZeroDelivery/LLGo 198972 ns/op -2097 ns/op / -1.0% (better)
Windows MinGW 386 CreateStop/Go 193.900 ns/op +3.1 ns/op / +1.6% (worse)
Windows MinGW 386 CreateStop/LLGo 480.100 ns/op -17.4 ns/op / -3.5% (better)
Windows MinGW 386 RearmStopped/Go 63.100 ns/op -0.47 ns/op / -0.7% (better)
Windows MinGW 386 RearmStopped/LLGo 311.500 ns/op -16.8 ns/op / -5.1% (better)
Windows MinGW 386 ResetActive/Go 39.010 ns/op -0.03 ns/op / -0.1% (better)
Windows MinGW 386 ResetActive/LLGo 936.400 ns/op -24.6 ns/op / -2.6% (better)
Windows MinGW 386 ResetHeap1024/Go 39.330 ns/op -0.25 ns/op / -0.6% (better)
Windows MinGW 386 ResetHeap1024/LLGo 177.200 ns/op +1.9 ns/op / +1.1% (worse)
Windows MinGW ARM64 AfterFuncZeroDelivery/Go 659.900 ns/op -20.1 ns/op / -3.0% (better)
Windows MinGW ARM64 AfterFuncZeroDelivery/LLGo 152478 ns/op +2679 ns/op / +1.8% (worse)
Windows MinGW ARM64 CreateStop/Go 197.700 ns/op -1.8 ns/op / -0.9% (better)
Windows MinGW ARM64 CreateStop/LLGo 350.100 ns/op +21.1 ns/op / +6.4% (worse)
Windows MinGW ARM64 RearmStopped/Go 70.640 ns/op +0.21 ns/op / +0.3% (worse)
Windows MinGW ARM64 RearmStopped/LLGo 213.900 ns/op +2.7 ns/op / +1.3% (worse)
Windows MinGW ARM64 ResetActive/Go 31.240 ns/op +0.08 ns/op / +0.3% (worse)
Windows MinGW ARM64 ResetActive/LLGo 100.100 ns/op -6 ns/op / -5.7% (better)
Windows MinGW ARM64 ResetHeap1024/Go 31.180 ns/op +0.19 ns/op / +0.6% (worse)
Windows MinGW ARM64 ResetHeap1024/LLGo 112.200 ns/op +0.7 ns/op / +0.6% (worse)
Windows MSVC AfterFuncZeroDelivery/Go 550.700 ns/op -3.9 ns/op / -0.7% (better)
Windows MSVC AfterFuncZeroDelivery/LLGo 177313 ns/op +110 ns/op / +0.1% (worse)
Windows MSVC CreateStop/Go 117.400 ns/op +1.2 ns/op / +1.0% (worse)
Windows MSVC CreateStop/LLGo 417.400 ns/op +13 ns/op / +3.2% (worse)
Windows MSVC RearmStopped/Go 31.420 ns/op -0.03 ns/op / -0.1% (better)
Windows MSVC RearmStopped/LLGo 222.200 ns/op -1.6 ns/op / -0.7% (better)
Windows MSVC ResetActive/Go 20.090 ns/op +0.07 ns/op / +0.3% (worse)
Windows MSVC ResetActive/LLGo 157 ns/op +7.2 ns/op / +4.8% (worse)
Windows MSVC ResetHeap1024/Go 20.470 ns/op +0.14 ns/op / +0.7% (worse)
Windows MSVC ResetHeap1024/LLGo 118.200 ns/op +0.2 ns/op / +0.2% (worse)
Windows MSVC 386 AfterFuncZeroDelivery/Go 1135 ns/op +2 ns/op / +0.2% (worse)
Windows MSVC 386 AfterFuncZeroDelivery/LLGo 211661 ns/op +2299 ns/op / +1.1% (worse)
Windows MSVC 386 CreateStop/Go 284.200 ns/op +5.6 ns/op / +2.0% (worse)
Windows MSVC 386 CreateStop/LLGo 772.800 ns/op -21.6 ns/op / -2.7% (better)
Windows MSVC 386 RearmStopped/Go 93.850 ns/op +0.52 ns/op / +0.6% (worse)
Windows MSVC 386 RearmStopped/LLGo 387.100 ns/op -35.5 ns/op / -8.4% (better)
Windows MSVC 386 ResetActive/Go 47.130 ns/op +0.69 ns/op / +1.5% (worse)
Windows MSVC 386 ResetActive/LLGo 309.300 ns/op +21.5 ns/op / +7.5% (worse)
Windows MSVC 386 ResetHeap1024/Go 46.710 ns/op -1.23 ns/op / -2.6% (better)
Windows MSVC 386 ResetHeap1024/LLGo 184.600 ns/op +15.2 ns/op / +9.0% (worse)
Windows MSVC ARM64 AfterFuncZeroDelivery/Go 678 ns/op +7.2 ns/op / +1.1% (worse)
Windows MSVC ARM64 AfterFuncZeroDelivery/LLGo 130663 ns/op -2028 ns/op / -1.5% (better)
Windows MSVC ARM64 CreateStop/Go 198.200 ns/op +3.2 ns/op / +1.6% (worse)
Windows MSVC ARM64 CreateStop/LLGo 335.400 ns/op -3.5 ns/op / -1.0% (better)
Windows MSVC ARM64 RearmStopped/Go 70.700 ns/op +0.13 ns/op / +0.2% (worse)
Windows MSVC ARM64 RearmStopped/LLGo 222.100 ns/op +2.8 ns/op / +1.3% (worse)
Windows MSVC ARM64 ResetActive/Go 30.900 ns/op +0.06 ns/op / +0.2% (worse)
Windows MSVC ARM64 ResetActive/LLGo 110.200 ns/op -3.5 ns/op / -3.1% (better)
Windows MSVC ARM64 ResetHeap1024/Go 30.920 ns/op -0.06 ns/op / -0.2% (better)
Windows MSVC ARM64 ResetHeap1024/LLGo 116.200 ns/op +0.1 ns/op / +0.1% (worse)

Compared with 1bbfc1f75db4 measured in the same runner job.

@cpunion
cpunion force-pushed the codex/wasm-debug-runtime-20261006 branch 2 times, most recently from ede6941 to 49441f3 Compare October 6, 2026 09:38
@cpunion
cpunion force-pushed the codex/wasm-debug-runtime-20261006 branch from 49441f3 to e2b9d68 Compare October 13, 2026 11:34

@visualfc visualfc left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve. The lock-free registry snapshot, opt-in logical frames, and $goroutines decoder look correct. One non-blocking follow-up on empty Go strings.

Comment thread cmd/internal/browser/extension/goroutines.js
@cpunion
cpunion merged commit 0831561 into xgo-dev:main Oct 14, 2026
109 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants