|
| 1 | +# Mixed native and guest stack audit |
| 2 | + |
| 3 | +Full mixed guest/native crash reporting is a readiness requirement for this RFC, |
| 4 | +not a capability established by the continuation and HTTP tests. |
| 5 | + |
| 6 | +The fixture calls Wasm `root -> middle -> leaf -> example:native.call`, then C, |
| 7 | +C++, Rust and optionally Swift. It stops inside the native provider, parks the |
| 8 | +continuation, and traps after resumption. An early-trap case checks failure |
| 9 | +before the first yield. The ordinary-call mode also accepts a WAMR AOT module. |
| 10 | +Provider functions use `noinline` so this test can assert native ABI unwind order; |
| 11 | +this is not a requirement to disable inlining or tail dispatch in the runtime. |
| 12 | + |
| 13 | +## Observed behavior |
| 14 | + |
| 15 | +On macOS arm64, optimized builds recover the native C/C++/Rust/Swift chain in |
| 16 | +LLDB and actual Apple `.ips` reports. Matching dSYM UUIDs are checked. After |
| 17 | +yield, the native backtrace contains the scheduler/driver: the guest continuation |
| 18 | +has no native stack. A physical backtrace must not invent the old frames. |
| 19 | + |
| 20 | +WAMR previously returned an empty C trap trace for continuation failures even |
| 21 | +with `WAMR_BUILD_DUMP_CALL_STACK=1`. The initial error path discarded the trace; |
| 22 | +the resumed error path also released frames before capturing them. Both now |
| 23 | +reuse the ordinary trap mechanism. Tests require all three guest function |
| 24 | +indices for initial, resumed and ordinary failures. This requires the existing |
| 25 | +opt-in call-stack feature; the shared C header has not changed. |
| 26 | + |
| 27 | +TinyWasm still returns an empty `wasm_trap_trace`. Its optimized C adapter invokes |
| 28 | +external callbacks after returning from the interpreter, so the native backtrace |
| 29 | +does not contain an active interpreter frame either. The fixture records this |
| 30 | +limitation, not a successful guest-stack qualification. |
| 31 | + |
| 32 | +WAMR ordinary AOT calls preserve native unwinding and the opt-in logical trap |
| 33 | +trace, but the tested default AOT build shows an unnamed generated-code address |
| 34 | +in LLDB. It does not provide named guest source frames. The separate experimental |
| 35 | +`WAMR_BUILD_DEBUG_AOT` compiler configuration requires LLDB development libraries, |
| 36 | +which were unavailable in the local LLVM 21 installation. Its DWARF registration |
| 37 | +path remains unqualified by this audit. |
| 38 | + |
| 39 | +The fast interpreter's reported offsets are internal compiled-bytecode offsets; |
| 40 | +module offsets may be zero. They must not be presented to a Wasm DWARF symbolizer |
| 41 | +as original Wasm instruction offsets. Function identity is useful; full source |
| 42 | +locations require a retained mapping to the exact original module. |
| 43 | + |
| 44 | +The [recorded observations](observed.json) include native backtraces, minimal |
| 45 | +Apple crash receipts, source hashes and the unchanged disabled artifacts. |
| 46 | + |
| 47 | +## Required out-of-tree diagnostic contract |
| 48 | + |
| 49 | +1. Associate each loaded module with its content hash, component/instance identity, |
| 50 | + original Wasm/debug sidecar, and native image UUID or build ID. Preserve the |
| 51 | + component linker's mapping from lowered core functions to original modules. |
| 52 | +2. Provide a bounded, versioned, read-only guest-frame snapshot at host-call and |
| 53 | + suspension boundaries, with explicit offset coordinate systems and truncation. |
| 54 | + TinyWasm needs a public capture seam; its current `guest-debug` by-index APIs |
| 55 | + are insufficient. WAMR's allocating trap-trace API is not a fatal-signal API. |
| 56 | +3. Keep async ancestry in the external scheduler: task/call generation, parent, |
| 57 | + import identity, and active/pending/completed/cancelled state. Distinguish |
| 58 | + concurrent streams and reused slots. Show an explicit async boundary rather |
| 59 | + than treating a resumed callback as a continuous native stack. |
| 60 | +4. Capture no borrowed guest/native pointers across a yield. Publish immutable |
| 61 | + or coherently versioned records for debugger/crash readers; invalidate them on |
| 62 | + cancellation and teardown. Do not allocate, acquire runtime locks, invoke |
| 63 | + callbacks, format strings or re-enter the engine from a fatal-signal handler. |
| 64 | +5. Preserve native unwind metadata and archive matching dSYMs/ELF debug files. |
| 65 | + Keep inline/tail-call semantics honest. C++ exceptions, Rust panics and Swift |
| 66 | + errors must be caught/translated before crossing the C ABI; stack inspection |
| 67 | + does not authorize exception propagation through it. |
| 68 | +6. Compile diagnostic state and capture work out of embedded builds. Use existing |
| 69 | + runtime frames rather than a second stack updated on every instruction. Bound |
| 70 | + enabled memory and measure capture cost at native-call boundaries, including |
| 71 | + high-frequency GPU/audio imports. Disabling diagnostics must retain the LTO |
| 72 | + binary/RSS/native-call baseline; don't sacrifice tail dispatch for prettier |
| 73 | + native traces. |
| 74 | + |
| 75 | +Native Apple reports alone do not automatically include interpreter/async |
| 76 | +records. An out-of-tree reporting integration must attach or reconstruct them. |
| 77 | +Apple's [CrashReportExtension](https://developer.apple.com/documentation/crashreportextension) |
| 78 | +can inspect a crashed process out of process on its supported platforms; its |
| 79 | +documented availability is iOS/iPadOS/macOS 27, not watchOS. It must not become a |
| 80 | +runtime dependency or the only diagnostic path. GDB/LLDB readers and an external |
| 81 | +crash-context attachment remain necessary for other targets. |
| 82 | + |
| 83 | +These requirements are intentionally not a new public diagnostics ABI in this |
| 84 | +patch. Named guest frames, source mappings and async ancestry remain gates before |
| 85 | +claiming a complete `Wasm -> component/module -> native provider` crash stack. |
| 86 | + |
| 87 | +## Reproduce |
| 88 | + |
| 89 | +Build the shared runtime fixture with `run.py --runtime wamr --root /path/to/wamr |
| 90 | +--build /tmp/runtime --call-stacks`, or the equivalent TinyWasm command without |
| 91 | +`--call-stacks`. Use one compiler family for native LTO objects and the final |
| 92 | +linker. For WAMR source-level runtime debugging, additionally build its archive |
| 93 | +with `RelWithDebInfo` and `-fno-omit-frame-pointer`. The default build flags stay |
| 94 | +unchanged. |
| 95 | + |
| 96 | +Run this directory's `run.py` with `--runtime`, `--include`, `--library`, |
| 97 | +`--build` and `--debugger lldb` or `--debugger gdb`. WAMR's include directory must |
| 98 | +contain its C API header under the shared name `wasm.h`, as the parent runner |
| 99 | +arranges. Add `--swift` on macOS. Without it, a C bridge connects Rust to the |
| 100 | +checkpoint; this is the Linux GDB CI configuration. |
| 101 | + |
| 102 | +`--apple-crash` deliberately traps the standalone fixture, checks a new Apple |
| 103 | +`.ips` report for the native frame order and matching dSYM UUID, and symbolicates |
| 104 | +its image addresses with `atos`. Run it only when intentionally producing a test |
| 105 | +crash. The script records a minimal receipt, not the report's machine identifiers. |
| 106 | +`--ordinary --guest /path/to/guest.aot` checks ordinary AOT calls; compile |
| 107 | +`guest.wasm` with `--enable-dump-call-stack` and matching GC/bounds-check settings. |
| 108 | + |
| 109 | +The runner writes `debugger.txt`, `guest_trace.txt`, and `receipt.json`; Apple |
| 110 | +crash tests also write `apple_symbolicated.txt`. It explicitly records |
| 111 | +`full_mixed_guest_stack_qualified: false`, even when native unwinding passes. |
| 112 | + |
| 113 | +References: [Apple symbolication](https://developer.apple.com/documentation/xcode/adding-identifiable-symbol-names-to-a-crash-report), |
| 114 | +[GDB JIT interface](https://sourceware.org/gdb/current/onlinedocs/gdb.html/JIT-Interface.html), |
| 115 | +[LLVM debugging generated code](https://llvm.org/docs/DebuggingJITedCode.html). |
0 commit comments