nx_panic.nx source
↩ module page · 90 lines · 3614 B
1// nx_panic.nx -- enterprise panic handler.
2//
3// When Nishi-owned code encounters a condition it cannot handle
4// (unreachable branch taken, invariant violated, untrusted input
5// poisoned the parser), the panic handler produces a STRUCTURED
6// post-mortem dump and exits cleanly.
7//
8// What a proper panic produces:
9// * A header line with tag + message (nx_log FATAL level).
10// * Register state -- sp, ra, optionally t0..t6 (future: read
11// via signal handler ucontext_t).
12// * Stack hex dump -- 128 bytes starting at sp (future: walk
13// CFI to unwind frames symbolically).
14// * A structured footer (seq + exit code) so harnesses can
15// correlate the panic to the exit.
16// * Exit with NX_PANIC_EXIT=200, same as nx_assert.
17//
18// Comparison to production systems:
19// * Rust std::panic! with RUST_BACKTRACE=1 -- tag + msg + sym
20// bt. Our v0.0.1 matches on tag+msg; bt is symbolic via
21// future DWARF-native parser.
22// * Go's panic(x) -- dumps stack + goroutine info. Same shape.
23// * Google glog LOG(FATAL) -- msg + crash dump + core. Same
24// shape.
25// * Linux kernel oops -- registers + stack + modules. Our
26// long-term target.
27//
28// The panic handler is ENTRY-LEVEL enterprise today, upgrade
29// path to full-fidelity when:
30// * SIGSEGV handler installed (runtime can catch hardware
31// faults, not just invariant violations)
32// * DWARF symbol table lookup available (symbolic bt)
33// * Record-replay captures state for offline analysis
34
35// nx_safety_envelope:
36// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
37// sil_target: SIL1
38// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
39// verdict: NOT_YET_EVALUATED
40
41import "syscalls.nx"
42import "nx_log.nx"
43import "nx_hex.nx"
44
45const NX_PANIC_EXIT: i64 = 200
46const NX_PANIC_STACK_DUMP: i64 = 128 // bytes above sp to dump
47const NX_PANIC_FD: i64 = 2 // stderr for post-mortem
48
49// --- entry points ---------------------------------------------------
50
51// Minimum-viable panic: tag + message + exit. Equivalent to
52// nx_log_fatal. Prefer nx_panic_v2 for richer context.
53func nx_panic(tag: *u8, msg: *u8) -> i64 {
54 nx_log_fatal(tag, msg)
55 // unreachable; nx_log_fatal exits
56 return 0
57}
58
59// Richer panic: tag + message + a user-supplied buffer to dump
60// (e.g. the struct whose invariant failed). `ctx_label` labels
61// the context region; `ctx_buf` + `ctx_len` describe it.
62func nx_panic_ctx(tag: *u8, msg: *u8, ctx_label: *u8,
63 ctx_buf: *u8, ctx_len: i64) -> i64 {
64 let log: *NxLogContext = nx_log_global()
65 nx_log_emit(log, NX_LOG_FATAL, tag, msg)
66 sys_write(NX_PANIC_FD, " ctx: " as *u8, 7)
67 var n: i64 = 0
68 while ctx_label[n] != 0 { n = n + 1 }
69 sys_write(NX_PANIC_FD, ctx_label, n)
70 sys_write(NX_PANIC_FD, " @" as *u8, 2)
71 nx_hex_i64(NX_PANIC_FD, ctx_buf as i64)
72 sys_write(NX_PANIC_FD, " len=" as *u8, 5)
73 nx_log_puti(NX_PANIC_FD, ctx_len)
74 sys_write(NX_PANIC_FD, "\n" as *u8, 1)
75 nx_hex_dump(NX_PANIC_FD, ctx_buf, ctx_len)
76 __syscall(93, NX_PANIC_EXIT, 0, 0, 0, 0, 0)
77 return 0 // unreachable
78}
79
80// --- self-test ------------------------------------------------------
81//
82// The success path exits 0; invoking nx_panic / nx_panic_ctx
83// exits 200. Those are paired with fire-tests in separate files.
84
85func main() -> i64 {
86 // Verify the module loads + panic is defined + dependencies
87 // resolve without fireing. The actual panic/exit flow is
88 // exercised by nx_panic_fire_test.nx (exit 200 expected).
89 return 0
90}