code wiki / (root) / nx_arena_probe.nx

nx_arena_probe.nx source

↩ module page · 85 lines · 3615 B

1// nx_arena_probe.nx -- A/B METER for small-allocation cost in nx_syscalls.sys_mmap. 2// 3// WHY: sys_mmap is a raw anonymous-mmap wrapper, so EVERY call -- including sys_mmap(16) for a wait4 4// status word -- costs a whole 4096-byte page AND a separate kernel VMA. Measured 2026-08-06 by 5// nx_mmapbal deep: 17,157 functions / 43,498 sites allocate memory they never return. That is a 6// commit problem (Committed_AS 179GB vs CommitLimit 42.7GB) and, less obviously, a HARD CEILING: one 7// VMA per call runs into vm.max_map_count (65530 by default), after which mmap returns -ENOMEM and 8// callers write through the failed pointer -- the dmesg-proven nx_hostctl SEGFAULT at 9// 0xfffffffffffffff4 that sys_munmap's own header describes. 10// 11// This probe exists so the arena change is judged by a NUMBER TAKEN BEFORE IT, not by argument. 12// Run it, record VmSize/VmRSS, change sys_mmap, run it again. Same binary source both times. 13// 14// TEETH, not just a meter -- both must hold or the arena is unsafe at any size: 15// distinct=1 consecutive allocations never alias 16// zeroed=1 every returned region is zero-filled. A bump arena that ever RECYCLES bytes breaks 17// this silently, and the corpus relies on it (nx_mmapbal: "mmap zeroes, so an untouched 18// slot reads empty with no init loop"). This is exactly why the arena must never hand 19// the same bytes out twice, and why LIFO give-back on munmap was rejected. 20// 21// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 22import "nx_syscalls.nx" 23import "nx_itoa_lib.nx" 24 25const AP_N: i64 = 20000 26const AP_SZ: i64 = 32 27const AP_BUF: i64 = 8192 28 29func 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 } 30 31// Dump /proc/self/status so VmSize / VmRSS / VmPTE are read from the KERNEL, not inferred from the 32// allocation count. The buffer is deliberately larger than any arena threshold so this call itself 33// takes the real-mmap path in BOTH arms and cannot skew the comparison. 34func ap_status() -> i64 { 35 let fd: i64 = sys_openat_rd("/proc/self/status" as *u8) 36 if fd < 0 { ap_puts("status=UNREADABLE\n" as *u8); return 1 } 37 let b: *u8 = sys_mmap(AP_BUF) 38 var n: i64 = 0 39 var r: i64 = sys_read(fd, b, AP_BUF - 1) 40 while r > 0 { 41 n = n + r 42 if n >= AP_BUF - 1 { r = 0 } else { r = sys_read(fd, ((b as i64) + n) as *u8, AP_BUF - 1 - n) } 43 } 44 sys_close(fd) 45 sys_write(1, b, n) 46 return 0 47} 48 49func main(argc: i64, argv: *i64) -> i64 { 50 var distinct: i64 = 1 51 var zeroed: i64 = 1 52 var failed_at: i64 = 0 - 1 53 var prev: i64 = 0 54 var acc: i64 = 0 55 var i: i64 = 0 56 while i < AP_N { 57 let p: *u8 = sys_mmap(AP_SZ) 58 let pa: i64 = p as i64 59 if pa <= 0 { 60 failed_at = i 61 i = AP_N 62 } else { 63 if pa == prev { distinct = 0 } 64 var k: i64 = 0 65 while k < AP_SZ { 66 if p[k] != (0 as u8) { zeroed = 0 } 67 k = k + 1 68 } 69 // dirty one byte so the page is actually faulted in and VmRSS reflects real cost 70 p[0] = 65 as u8 71 acc = acc + (p[0] as i64) 72 prev = pa 73 i = i + 1 74 } 75 } 76 ap_puts("ARENA-PROBE n=" as *u8); nxi_out(AP_N) 77 ap_puts(" size=" as *u8); nxi_out(AP_SZ) 78 ap_puts(" distinct=" as *u8); nxi_out(distinct) 79 ap_puts(" zeroed=" as *u8); nxi_out(zeroed) 80 ap_puts(" failed_at=" as *u8); nxi_out(failed_at) 81 ap_puts(" acc=" as *u8); nxi_out(acc) 82 ap_puts("\n" as *u8) 83 ap_status() 84 return 0 85}