nx_arena_probe.nx
buildroot/runtime/nx_arena_probe.nx
about
nx_arena_probe.nx -- A/B METER for small-allocation cost in nx_syscalls.sys_mmap.
WHY: sys_mmap is a raw anonymous-mmap wrapper, so EVERY call -- including sys_mmap(16) for a wait4
status word -- costs a whole 4096-byte page AND a separate kernel VMA. Measured 2026-08-06 by
nx_mmapbal deep: 17,157 functions / 43,498 sites allocate memory they never return. That is a
commit problem (Committed_AS 179GB vs CommitLimit 42.7GB) and, less obviously, a HARD CEILING: one
VMA per call runs into vm.max_map_count (65530 by default), after which mmap returns -ENOMEM and
callers write through the failed pointer -- the dmesg-proven nx_hostctl SEGFAULT at
0xfffffffffffffff4 that sys_munmap's own header describes.
This probe exists so the arena change is judged by a NUMBER TAKEN BEFORE IT, not by argument.
Run it, record VmSize/VmRSS, change sys_mmap, run it again. Same binary source both times.
TEETH, not just a meter -- both must hold or the arena is unsafe at any size:
distinct=1 consecutive allocations never alias
zeroed=1 every returned region is zero-filled. A bump arena that ever RECYCLES bytes breaks
this silently, and the corpus relies on it (nx_mmapbal: "mmap zeroes, so an untouched
slot reads empty with no init loop"). This is exactly why the arena must never hand
the same bytes out twice, and why LIFO give-back on munmap was rejected.
license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
dependencies 2 imports · 0 importers
imports: nx_syscalls.nxnx_itoa_lib.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 25 | const AP_N: i64 = 20000 |
| 26 | const AP_SZ: i64 = 32 |
| 27 | const AP_BUF: i64 = 8192 |
functions
| 29 | func ap_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } |
| 34 | func ap_status() -> i64 |
| 49 | func main(argc: i64, argv: *i64) -> i64 |