78 lines
3.9 KiB
Markdown
78 lines
3.9 KiB
Markdown
# spikes/wasm-phase4 — production mechanics kill-test spike
|
||
|
||
Phase 4 validates the operational mechanics needed before a WASM worker can be
|
||
trusted in production. This spike is intentionally smaller than the future UCE
|
||
worker integration: it uses tiny WAT modules instead of generated UCE units, but
|
||
it exercises the Wasmtime controls and workspace cleanup paths the worker will
|
||
use. It is also deliberately the first spike on the Wasmtime-specific C++ API
|
||
(`wasmtime.hh`) — fuel, epochs, store limiters, and snapshots do not exist in
|
||
the portable `wasm.h` surface, so runtime-agnosticism ends here by design.
|
||
|
||
Run on `k-uce`:
|
||
|
||
```bash
|
||
bash spikes/wasm-phase4/build_runner.sh
|
||
/tmp/uce/wasm-phase4/runner
|
||
```
|
||
|
||
Expected final line:
|
||
|
||
```text
|
||
PHASE4 EXIT CRITERION: PASS
|
||
```
|
||
|
||
What this proves:
|
||
|
||
- **Reusable compiled artifact / snapshot proxy:** modules are compiled once and
|
||
then instantiated in fresh stores. Not OS CoW yet, but it validates the shape
|
||
of "shared core artifact + per-request workspace birth".
|
||
- **Unharmed worker:** after all six kill cases, the same engine serves a
|
||
healthy request that completes with the expected value. This — not merely
|
||
"didn't crash" — is the worker half of the Phase 4 exit criterion.
|
||
- **CPU limits, both mechanisms:** fuel (deterministic, per-instruction cost)
|
||
and epoch interruption (near-zero overhead, ticker thread, traps as
|
||
`interrupt`). Production default is **epoch** per the Phase 0 findings; fuel
|
||
is validated as the deterministic fallback. Note: the engine epoch only
|
||
advances, so every store must set its own deadline.
|
||
- **Memory limit, actually load-bearing:** the OOM fixture grows by 10 pages —
|
||
within its declared max (100) — so only the store limiter (2 pages) can deny
|
||
the growth. The denial is observed by the guest (`memory.grow` → -1) and
|
||
converted to a trap.
|
||
- **Trap-to-error-page data path:** every kill case's gate asserts the captured
|
||
message contains a `wasm backtrace` and the expected cause. Fixture frames
|
||
show `<unknown>` — readable production traces require units to keep their
|
||
name section (or a symbolication side-file in the artifact cache).
|
||
- **Trace summarizer (`src/lib/wasm_trace.h`):** trap messages are rendered
|
||
through the production collapse facility (repeated frames → `×N` lines,
|
||
mangled symbols demangled, cause/detail split out). The stack-exhaustion
|
||
case gates it on live trap output here; `site/tests/core.uce` gates the
|
||
parsing on canned messages in the native suite.
|
||
- **Handle cleanup with falsifiable checks:** closers must run exactly once per
|
||
handle and *while the store is still alive* (production closers may flush
|
||
guest-resident state); destructor-only cleanup fails the ordering check.
|
||
|
||
Kill fixtures (all genuinely trap):
|
||
|
||
- `unreachable`: `__builtin_trap` analog.
|
||
- `oob-access`: wild pointer; load at 128 KiB from a 64 KiB memory.
|
||
- `stack-exhaustion`: runaway recursion → `call stack exhausted`.
|
||
- `infinite-loop-fuel` / `infinite-loop-epoch`: same loop, both CPU limits.
|
||
- `oom-limiter`: limiter-denied `memory.grow` → guest converts -1 to a trap.
|
||
|
||
**A literal C++ null-pointer dereference is deliberately absent:** address 0 is
|
||
valid wasm linear memory, so `*(int*)nullptr` does not trap — it silently
|
||
writes inside the workspace, which is then dropped at request end (still
|
||
strictly better than a native SIGSEGV). See WASM-PROPOSAL §10 for the recorded
|
||
risk/policy.
|
||
|
||
Still deferred to production Phase 4:
|
||
|
||
- real core snapshot + OS-level CoW birth;
|
||
- wiring `wasm_trace.h` summaries into the UCE error-page UI (the summarizer
|
||
itself is done and gated); name-section policy for unit artifacts;
|
||
- wiring these traps into `linux_fastcgi.cpp` / the WASM worker backend;
|
||
- real runtime handle-table cleanup for sqlite/mysql/sockets/tasks;
|
||
- artifact ABI versioning end-to-end;
|
||
- memory-limit policy for guest allocators that return failure instead of
|
||
trapping (bump allocator vs dlmalloc behavior under the limiter).
|