Add standalone fork-instrument crash reproducer#700
Closed
mho22 wants to merge 1 commit into
Closed
Conversation
## Summary - add `benchmarks/fork/chain.c`, a recursive walker shaped after Zend's `zend_compile_short_circuiting` so wasm-fork-instrument's per-frame cost is measurable on a binary apart from PHP itself - add `benchmarks/fork/build.sh` to produce baseline and fork- instrumented variants from one chain.c source - add `benchmarks/fork/run.mjs` to binary-search the maximum recursion depth that survives V8's call-stack budget on each variant - exit non-zero from `run.mjs` when forkinstr's max depth is not strictly less than baseline's — that invariant is what the benchmark exists to verify ## Why PR #669 asks for a standalone test case apart from PHP that reproduces the V8 `RangeError: Maximum call stack size exceeded` on deep recursion through fork-instrumented frames. Iterating on candidate fork-instrument changes via the full PHP build+run cycle takes ~45 minutes; this benchmark drops that to ~10 seconds. The PHP crash composes two causes: unbounded recursion in `zend_compile_short_circuiting` (fixed PHP-side by #669) and ~8 extra wasm locals per instrumented frame from wasm-fork-instrument's switch-dispatch state machine. `chain.c` bounds recursion by its `depth` argument so it reproduces only the kandelo-side cause in isolation; any candidate reduction of the per-frame cost can be validated by watching the forkinstr depth move. ## Testing - `bash scripts/dev-shell.sh bash benchmarks/fork/build.sh` produces identical wasm sizes (3882 / 4587 bytes) and walk-locals counts (4 / 12) across runs - `node --experimental-wasm-exnref benchmarks/fork/run.mjs` is deterministic at baseline 9 972 / forkinstr 6 648 across 4 runs on macOS arm64 / Node v24.13.0; exits 0 - with the wasms manually swapped so forkinstr > baseline, the same command exits 1 with `INVARIANT FAILED: ...` — confirms the self-test fires on regression - `bash scripts/check-abi-version.sh` exits 0 (benchmark touches no kernel/host/programs/ABI) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Closing — reopening against the correctly named branch |
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.
Summary
benchmarks/fork/chain.c, a recursive walker shaped after Zend'szend_compile_short_circuitingso wasm-fork-instrument's per-frame cost is measurable on a binary apart from PHP itselfbenchmarks/fork/build.shto produce baseline and fork-instrumented variants from one chain.c sourcebenchmarks/fork/run.mjsto binary-search the maximum recursion depth that survives V8's call-stack budget on each variantrun.mjswhen forkinstr's max depth is not strictly less than baseline's — that invariant is what the benchmark exists to verifyWhy
PR #669 asks for a standalone test case apart from PHP that reproduces the V8
RangeError: Maximum call stack size exceededon deep recursion through fork-instrumented frames. Iterating on candidate fork-instrument changes via the full PHP build+run cycle takes ~45 minutes; this benchmark drops that to ~10 seconds.The PHP crash composes two causes: unbounded recursion in
zend_compile_short_circuiting(fixed PHP-side by #669) and ~8 extra wasm locals per instrumented frame from wasm-fork-instrument's switch-dispatch state machine. `chain.c` bounds recursion by its `depth` argument so it reproduces only the kandelo-side cause in isolation; any candidate reduction of the per-frame cost can be validated by watching the forkinstr depth move.Notes
ABI impact: none. Adds a benchmark harness only; no kernel, host, or program changes.
Empirical numbers on macOS arm64 / Node v24.13.0, deterministic across 4 runs:
Differential = 3 324 frames lost (33% drop) attributable to wasm-fork-instrument's switch-dispatch state machine adding ~8 locals per frame on the recursive function. The ratio (~1.50×) is the load-bearing finding; absolute numbers are platform/tier-dependent.
Testing