code wiki / _hdl_build / nx_memvel.nx

nx_memvel.nx source

↩ module page · 206 lines · 9647 B

1// nx_memvel.nx -- WHICH process is EATING memory, ranked by SUSTAINED growth (seq1365 / seq1376). 2// 3// WHY IT EXISTS: nx_resmon ranks by MAGNITUDE, so a stable 3 GB tenant outranks a 50 MB process 4// doubling every minute and the real leaker hides. Measured 2026-07-30: swap 525->609 permil in 51 min 5// (~2.03 GB, ~40 MB/min) while the worst_committed process grew ~1 MB. LAW: a census that reports a 6// LEVEL cannot find a LEAK. 7// 8// ★★WHY IT IS N-SAMPLE (this organ's own v1 was WRONG -- seq1376): the FIRST version took ONE 5-second 9// window and I published its top row as a unified root cause. The very next run of the same organ put a 10// DIFFERENT process on top (3560 kB/s vs the previous winner's 1597) and demoted the old winner to #3. 11// The top slot ROTATES. A one-shot ranking of a noisy signal is a coin flip dressed as a finding -- the 12// exact ONE-WINDOW-IS-NOT-A-RATE defect (seq1340) committed by the author of that law, using the tool 13// built to prevent it. Knowing a discipline is not obeying it, so the discipline is now STRUCTURAL: 14// this organ CANNOT report a single-window ranking, because it no longer takes one. 15// 16// ★THE DECIDING COLUMN IS grew_in=k/N, NOT the rate. A real leak grows in EVERY window (k=N). A process 17// that tops one window and vanishes from the next is NOISE, and now says so on its own line. Rate alone 18// cannot distinguish them, which is precisely how v1 misled. 19// Rule 15: top-k, pid-join and rate arithmetic imported from nx_ctxtop_lib / nx_procchurn_lib. 20// Exit 0 = sampled; 3 = UNMEASURED (fail-closed). 21// license_tier: ORIGINAL Read-only. No hw writes (Rule 26). expect_exit: 0 22import "nx_ctxtop_lib.nx" 23import "nx_proc_ctl.nx" 24 25const MV_MAXP: i64 = 2048 26const MV_TABLE_BYTES: i64 = 16384 27const MV_DIRBUF: i64 = 65536 28const MV_STATBUF: i64 = 4096 29const MV_TOPK: i64 = 8 30const MV_TOPK_BYTES: i64 = 128 31const MV_ROUNDS: i64 = 5 32const MV_WINDOW_MS: i64 = 1200 33const MV_US_PER_MS: i64 = 1000 34const MV_NAMEOFF: i64 = 6 35const MV_EXIT_UNMEASURED: i64 = 3 36const MV_EXIT_REFUSED: i64 = 4 37const MV_ADMIT_DEFAULT: i64 = 800 38const MV_CONFBUF: i64 = 4096 39const MV_LOADBUF: i64 = 128 40 41// committed = RSS + Swap. Judging on COMMITTED (never VmSize/VmData) is inherited from nx_resmon's own 42// correction: VmData counts reservations never faulted in and once reported 40.6 GiB on a 36.9 GB box. 43func mv_committed(sbuf: *u8, sn: i64) -> i64 { 44 let r: i64 = rm_field(sbuf, sn, "VmRSS:" as *u8) 45 let w: i64 = rm_field(sbuf, sn, "VmSwap:" as *u8) 46 if r < 0 { return 0 - 1 } 47 var t: i64 = r 48 if w > 0 { t = t + w } 49 return t 50} 51 52func mv_scan(pids: *i64, kbs: *i64, dbuf: *u8, path: *u8, sbuf: *u8) -> i64 { 53 var cnt: i64 = 0 54 let fd: i64 = sys_openat_rd("/proc" as *u8) 55 if fd < 0 { return 0 } 56 var run: i64 = 1 57 while run == 1 { 58 let n: i64 = sys_getdents64(fd, dbuf, MV_DIRBUF) 59 if n <= 0 { run = 0 } else { 60 var off: i64 = 0 61 while off < n { 62 let rec: *u8 = ((dbuf as i64) + off) as *u8 63 let reclen: i64 = dirent_reclen(rec) 64 if reclen <= 0 { off = n } else { 65 let nm: *u8 = dirent_name(rec) 66 if nm[0] >= (48 as u8) { if nm[0] <= (57 as u8) { if cnt < MV_MAXP { 67 var p: i64 = 0 68 let pre: *u8 = "/proc/" as *u8 69 var a: i64 = 0 70 while pre[a] != (0 as u8) { path[p] = pre[a]; p = p + 1; a = a + 1 } 71 var pidv: i64 = 0 72 a = 0 73 while nm[a] != (0 as u8) { path[p] = nm[a]; pidv = pidv * 10 + ((nm[a] as i64) - 48); p = p + 1; a = a + 1 } 74 let suf: *u8 = "/status" as *u8 75 a = 0 76 while suf[a] != (0 as u8) { path[p] = suf[a]; p = p + 1; a = a + 1 } 77 path[p] = 0 as u8 78 let sn: i64 = rm_read(path, sbuf, MV_STATBUF) 79 if sn > 0 { 80 let c: i64 = mv_committed(sbuf, sn) 81 if c >= 0 { pids[cnt] = pidv; kbs[cnt] = c; cnt = cnt + 1 } 82 } 83 } } } 84 off = off + reclen 85 } 86 } 87 } 88 } 89 sys_close(fd) 90 return cnt 91} 92 93func mv_putname(pid: i64, path: *u8, sbuf: *u8) -> i64 { 94 var p: i64 = 0 95 let pre: *u8 = "/proc/" as *u8 96 var a: i64 = 0 97 while pre[a] != (0 as u8) { path[p] = pre[a]; p = p + 1; a = a + 1 } 98 p = pc_catn(path, p, pid) 99 let suf: *u8 = "/status" as *u8 100 a = 0 101 while suf[a] != (0 as u8) { path[p] = suf[a]; p = p + 1; a = a + 1 } 102 path[p] = 0 as u8 103 let sn: i64 = rm_read(path, sbuf, MV_STATBUF) 104 if sn <= 0 { rm_puts("<gone>" as *u8); return 0 } 105 var z: i64 = MV_NAMEOFF 106 let out: *u8 = sys_mmap(64) 107 var w: i64 = 0 108 while z < sn { if sbuf[z] == (10 as u8) { z = sn } else { if w < 62 { out[w] = sbuf[z]; w = w + 1 } z = z + 1 } } 109 out[w] = 0 as u8 110 rm_puts(out) 111 sys_munmap(out, 64) 112 return 0 113} 114 115func main() -> i64 { 116 // ADMISSION CONTROL (seq341 / seq1389): 5 rounds x ~450 procs = ~2700 openat+read+close per run. 117 // That is a real load, and this organ exists to measure a box already under pressure -- exactly when 118 // adding to it is worst. ★LAW: A DIAGNOSTIC THAT CANNOT REFUSE TO RUN IS A LOAD GENERATOR WITH GOOD 119 // INTENTIONS. Fail-CLOSED: an unreadable loadavg REFUSES (ct_admit), because not knowing the load is 120 // not permission to add to it. 121 let cbuf: *u8 = sys_mmap(MV_CONFBUF) 122 let cn: i64 = rm_read("knowledge/status/procchurn.conf" as *u8, cbuf, MV_CONFBUF) 123 let maxload: i64 = rm_conf(cbuf, cn, "admit-max-load-centi" as *u8, MV_ADMIT_DEFAULT) 124 let lbuf: *u8 = sys_mmap(MV_LOADBUF) 125 let ln: i64 = rm_read("/proc/loadavg" as *u8, lbuf, MV_LOADBUF) 126 let loadc: i64 = ct_load_centi(lbuf, ln) 127 if ct_admit(loadc, maxload) == 0 { 128 rm_puts("NX-MEMVEL verdict=REFUSED load_centi=" as *u8); rm_num(loadc) 129 rm_puts(" max=" as *u8); rm_num(maxload) 130 rm_puts(" why=host already saturated; a /proc-walking diagnostic must not add to it (seq341)\n" as *u8) 131 return MV_EXIT_REFUSED 132 } 133 let pa: *i64 = sys_mmap(MV_TABLE_BYTES) as *i64 // previous scan pids 134 let ka: *i64 = sys_mmap(MV_TABLE_BYTES) as *i64 // previous scan committed kB 135 let pb: *i64 = sys_mmap(MV_TABLE_BYTES) as *i64 // current scan pids 136 let kb: *i64 = sys_mmap(MV_TABLE_BYTES) as *i64 // current scan committed kB 137 let ap: *i64 = sys_mmap(MV_TABLE_BYTES) as *i64 // accumulator pids 138 let asum: *i64 = sys_mmap(MV_TABLE_BYTES) as *i64 // accumulator summed positive delta (kB) 139 let acnt: *i64 = sys_mmap(MV_TABLE_BYTES) as *i64 // accumulator: in how many windows it GREW 140 let dbuf: *u8 = sys_mmap(MV_DIRBUF) 141 let path: *u8 = sys_mmap(256) 142 let sbuf: *u8 = sys_mmap(MV_STATBUF) 143 144 let t0: i64 = sys_now_us() 145 var na: i64 = mv_scan(pa, ka, dbuf, path, sbuf) 146 if na <= 0 { rm_puts("NX-MEMVEL verdict=UNMEASURED why=/proc walk empty\n" as *u8); return MV_EXIT_UNMEASURED } 147 var nacc: i64 = 0 148 var grew_total: i64 = 0 149 var r: i64 = 0 150 var nb: i64 = 0 151 while r < MV_ROUNDS { 152 sys_sleep_ms(MV_WINDOW_MS) 153 nb = mv_scan(pb, kb, dbuf, path, sbuf) 154 var i: i64 = 0 155 while i < nb { 156 let j: i64 = ct_find(pa, na, pb[i]) 157 if j >= 0 { 158 let d: i64 = kb[i] - ka[j] 159 if d > 0 { 160 grew_total = grew_total + d 161 var s: i64 = ct_find(ap, nacc, pb[i]) 162 if s < 0 { if nacc < MV_MAXP { ap[nacc] = pb[i]; asum[nacc] = 0; acnt[nacc] = 0; s = nacc; nacc = nacc + 1 } } 163 if s >= 0 { asum[s] = asum[s] + d; acnt[s] = acnt[s] + 1 } 164 } 165 } 166 i = i + 1 167 } 168 // current becomes previous (copy, so the next round diffs against THIS scan) 169 var c: i64 = 0 170 while c < nb { pa[c] = pb[c]; ka[c] = kb[c]; c = c + 1 } 171 na = nb 172 r = r + 1 173 } 174 let el: i64 = (sys_now_us() - t0) / MV_US_PER_MS 175 if el <= 0 { rm_puts("NX-MEMVEL verdict=UNMEASURED why=zero-length observation\n" as *u8); return MV_EXIT_UNMEASURED } 176 177 let tp: *i64 = sys_mmap(MV_TOPK_BYTES) as *i64 178 let tr: *i64 = sys_mmap(MV_TOPK_BYTES) as *i64 179 var used: i64 = 0 180 var q: i64 = 0 181 while q < nacc { used = ct_topk_insert(tp, tr, MV_TOPK, used, ap[q], asum[q]); q = q + 1 } 182 183 rm_puts("NX-MEMVEL rounds=" as *u8); rm_num(MV_ROUNDS) 184 rm_puts(" total_ms=" as *u8); rm_num(el) 185 rm_puts(" procs=" as *u8); rm_num(nb) 186 rm_puts(" grew_kb=" as *u8); rm_num(grew_total) 187 rm_puts(" growers=" as *u8); rm_num(nacc); rm_puts("\n" as *u8) 188 var k: i64 = 0 189 while k < used { 190 let s2: i64 = ct_find(ap, nacc, tp[k]) 191 rm_puts(" #" as *u8); rm_num(k + 1) 192 rm_puts(" pid=" as *u8); rm_num(tp[k]) 193 rm_puts(" kb_per_sec=" as *u8); rm_num(pc_rate(tr[k], el)) 194 rm_puts(" grew_in=" as *u8) 195 if s2 >= 0 { rm_num(acnt[s2]) } else { rm_num(0) } 196 rm_puts("/" as *u8); rm_num(MV_ROUNDS) 197 rm_puts(" name=" as *u8) 198 mv_putname(tp[k], path, sbuf) 199 rm_puts("\n" as *u8) 200 k = k + 1 201 } 202 rm_puts("READ grew_in FIRST: k/" as *u8); rm_num(MV_ROUNDS) 203 rm_puts(" = SUSTAINED leak; anything less is a rotating top slot = NOISE, not a finding (seq1376)\n" as *u8) 204 if nb >= MV_MAXP { rm_puts(" WARNING scan CAPPED -- coverage INCOMPLETE, totals are a floor\n" as *u8) } 205 return 0 206}