Commit a06a00a
crash handler: walk frame pointers seeded from the fault context (#31163)
## Problem
Native crash traces had regressed to `libc::backtrace()` /
`RtlCaptureStackBackTrace`, which depend on unwind tables. Two things
make that fail in shipped builds:
- Release strips the unwind tables those APIs need on POSIX
(`-fno-asynchronous-unwind-tables`, `--no-eh-frame-hdr`).
- The POSIX signal handler runs on an `SA_ONSTACK` altstack, whose frame
chain is disjoint from the faulting thread's stack.
So native crash captures collapsed to the crash handler's *own* frames
(or nothing at all on Linux), and every native crash got reported — and
grouped — under `Bun__captureStackTrace`, with no sign of the code that
actually faulted.
## Fix
This restores the Zig build's mechanism — frame-pointer walking on
POSIX, native `.pdata` on Windows — with the trace seeded from the fault
register context.
**POSIX:** walk frame pointers (force-enabled in every build via
`-Cforce-frame-pointers=yes` / `-fno-omit-frame-pointer`, so this works
with or without `.eh_frame`). The walker (`frame_address`,
`MemoryAccessor`, `StackIterator`) lives in `bun_core::debug`; `btjs.rs`
is deduped to re-export it (−433 lines). For real faults, the signal
handler reads `(pc, fp)` from the saved `ucontext` (previously
discarded) and seeds the walk from there — frame 0 is the exact faulting
instruction and the handler/altstack frames are never in the chain.
**Windows:** keep the native `.pdata`-based `RtlCaptureStackBackTrace`
(frame-pointer walking derails once it crosses into code that doesn't
maintain `rbp` — the prebuilt JavaScriptCore, LLInt assembly). Prepend
`ExceptionAddress` as frame 0 and trim the handler/ntdll frames by
address-matching against it. `.pdata` is emitted in all configs, so this
works in debug and release.
**Panic format matches Zig:** `-Zlocation-detail=none` stays (the panic
call site is recoverable from the now-working backtrace, so embedding
~320 KB of `#[track_caller]` `Location` structs is redundant — Zig had
~0 embedded source paths). The panic hook drops the `(file:line:col)`
suffix from the message and emits just the panic text, like Zig did; the
location is in the trace.
**Removed:** the dead `Bun__captureStackTrace` C-ABI export and the
`libc::backtrace`/numeric-skip paths.
## Validation
CI green on every shipped target (286/286): Linux x64/aarch64 (glibc and
musl), macOS x64/aarch64, Windows x64/aarch64. Binary size: +48–65 KB vs
main.
Manually validated traces on macOS arm64, Linux x64, Windows x64, debug
and release: frame 0 is the fault site, the trace is the real call stack
with no handler frames, and bun.report's existing fingerprint logic (top
5 symbolized frames) groups distinct crashes correctly without changes.
## Limitations
- **Windows faults at addresses without `.pdata`** (calling a
corrupted/null function pointer, JIT code): RtlCapture cannot unwind
past the fault, so the trace is just the fault PC. No worse than before
— main captures handler+ntdll noise there; neither recovers the real
callers.
- **FreeBSD / Android**: best-effort. The walker runs, but no fault
context is extracted (panics fine, signals degraded). bun.report has no
platform code for FreeBSD and folds Android into Linux, so end-to-end
reporting there is unsupported regardless. No regression vs main.
## Out of scope
Fault-register capture and the trace v3 format (#29607 / bun.report#24)
— separate follow-up.
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>1 parent 0d84cdd commit a06a00a
9 files changed
Lines changed: 559 additions & 827 deletions
File tree
- scripts/build
- src
- bun_bin
- bun_core
- crash_handler
- jsc
- runtime/api
- sys/windows
- windows_sys
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
456 | 456 | | |
457 | 457 | | |
458 | 458 | | |
459 | | - | |
460 | | - | |
461 | | - | |
462 | | - | |
463 | | - | |
464 | | - | |
465 | | - | |
466 | | - | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
467 | 468 | | |
468 | 469 | | |
469 | 470 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
17 | | - | |
| 16 | + | |
| 17 | + | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| |||
0 commit comments