debug: XS stack-overflow trace instrumentation (ymax0 v320)#2
Draft
kriscendobot wants to merge 3 commits into
Draft
debug: XS stack-overflow trace instrumentation (ymax0 v320)#2kriscendobot wants to merge 3 commits into
kriscendobot wants to merge 3 commits into
Conversation
Instrument the XS_STACK_OVERFLOW_EXIT arm of fxAbort() in xsnapPlatform.c so a
stack-overflow abort dumps, on inherited stderr:
- the value-stack fill level (slots used of stackCount, and free),
- the XS frame chain innermost-first, each frame's value-stack slot span and
source line (capped at 600 frames), and
- an allocation-free native C backtrace (backtrace_symbols_fd, requires the
<execinfo.h> include added near the top; -rdynamic is already in the worker
LINK_OPTIONS).
This was developed live during the ymax0 v320 investigation (garden issue agoric-labs#9) to
distinguish deep JS recursion from wide value-stack exhaustion. The conclusion:
the real ymax0 importBundle overflows at only ~9 frames with the value stack at
4096/4096 slots (one anonymous activation ~2,588 slots, its callee ~1,238) — wide
value-stack exhaustion, not deep recursion. XS fixes stackCount at 4096 in
xsnap-worker.c with no CLI override.
Output goes straight to stderr via fprintf because the worker compiles out the
fxReport/console path (mxNoConsole), so the usual report channel is unavailable
at this point. Debug-only; not intended to merge.
This was referenced Jun 28, 2026
Evolve the XS_STACK_OVERFLOW_EXIT instrumentation (garden issue agoric-labs#9) so the overflow trace names the responsible JavaScript calls and shows where the JS stack crosses into native C, instead of leaving the widest frame "(anonymous)". Per-frame, the walk now prints: - kind: J (JS bytecode fn, runs inside fxRunID) vs C (host/native builtin, whose name resolves to a C symbol via dladdr) — the C rows are the literal JS<->C interleave points; - jsname: the qualified function name via fxBufferFrameName, the same routine the engine uses for Error.stack, so names corroborate with sources; - source: defining module path : current line. The per-wide-frame (>200 slot) breakdown is likewise named, so slot composition is attributable to a responsible call. The native C backtrace header now explains the bytecode-VM interleave model (one fxRunID activation per JS frame; the stacks meet at kind=C frames). This commit also lands the value-stack kind histogram + per-wide-frame slot breakdown from the prior investigation round, which had only ever lived in a scratch worker and was never committed back to this branch. Validated on-host: real ymax0 import overflows and the 2,588-slot wide frame that was previously "(anonymous)" now names Array.prototype.flatMap (kind=C) called from the SES compartmentImportNow/execute path; a trivial bundle still imports clean (no spurious overflow). Allocation-free and stderr-only (mxNoConsole), so still safe at exhaustion. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The XS_STACK_OVERFLOW_EXIT frame walk printed env->value.environment.line raw. XS keeps the line in 16 bits, so any line past 32767 (routine in a large non-minified esbuild bundle — the ymax0 portfolio.contract bundle is ~43886 lines) read back as a negative txInteger, e.g. -27787 for line 37749. Unwrap negatives by +65536 in both the per-frame walk and the wide-frame breakdown so each overflow frame resolves to its real 1-based source line (e.g. .../portfolio.contract.bundle.js:37749 = hex.js decodings flatMap). Minified bundles stay small and are unaffected.
kriskowal
reviewed
Jul 9, 2026
| #include "xsAll.h" | ||
| #include "xsScript.h" | ||
| #include "xsSnapshot.h" | ||
| #include <execinfo.h> /* GARDEN INSTRUMENTATION (issue #9): native C backtrace at overflow */ |
kriskowal
reviewed
Jul 9, 2026
| source the defining module path : current line for that frame. */ | ||
| fprintf(stderr, "depth\tslots\tkind\tjsname @ source:line\n"); | ||
| while (aFrame) { | ||
| if (aDepth < 600) { |
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.
DRAFT — artifact preservation, not a merge candidate.
Captures the XS stack-overflow debug instrumentation developed live during the
ymax0 v320 value-stack-exhaustion investigation (garden issue agoric-labs#9), so the patch
never has to be re-derived from the issue thread again (it has been wiped by
redeploys twice).
What it instruments
The
XS_STACK_OVERFLOW_EXITarm offxAbort()inxsnap/sources/xsnapPlatform.c(plus#include <execinfo.h>). On astack-overflow abort it dumps, to inherited stderr:
stackCount, free remaining);source line, capped at 600 frames;
backtrace_symbols_fd;-rdynamicisalready in the worker
LINK_OPTIONS).Why direct
fprintf(stderr, …)The xsnap worker compiles out the
fxReport/console path viamxNoConsole, sothe usual report channel is unavailable at the abort point. The block is
allocation-free and therefore safe to run at exhaustion.
Key calibration result (from the investigation)
The real ymax0
importBundleoverflows at only ~9 frames with the value stackat 4096/4096 slots (one anonymous activation ~2,588 slots, its callee ~1,238) —
wide value-stack exhaustion, not deep recursion. XS fixes
stackCountat4096 in
xsnap-worker.cwith no CLI override. A deep-recursion control(
f(100000)) hits ~394 frames at 4093/4096 slots.Base is this fork's own
Agoricbranch (notagoric-labs). Companion build/runmethodology doc lives in
kriscendobot/agoric-sdk.Ref: garden issue agoric-labs#9.