code wiki / (root) / nx_paramplumb.nx

nx_paramplumb.nx source

↩ module page · 355 lines · 17476 B

1// nx_paramplumb.nx -- FIND PARAMETERS THAT WERE ADDED BUT NEVER PLUMBED THROUGH. 2// 3// THE DEFECT CLASS, measured three times in one day and invisible to every gate each time: 4// * nx_gsplat's viewport became a parameter (gs_render_aniso_at) -- but nx_gsplat_tile_lib's 5// gt_render kept reading gs_w()/gs_h(), the MODULE DEFAULTS, in six places. The tiled path could 6// only ever partition a 512x384 frame. Its gate was 12/12 GREEN the whole time, because every 7// test ran at the default. 8// * nx_skeleton's evaluator was gated GREEN at 32 bones against a 104-370 joint corpus. 9// * a surface gate's mutant survived because its fixture never exercised the changed path. 10// 11// ****** A PARAMETER THAT WAS ADDED BUT NOT PLUMBED IS INVISIBLE TO EVERY GATE, BECAUSE EVERY TEST 12// STILL PASSES AT THE DEFAULT -- THE PROOF IS TRUE AND SCOPED TO A SIZE NOBODY SHIPS. That is worse 13// than an absent capability: an absent one is counted as missing, this one is counted as DONE. 14// 15// WHAT IT DETECTS -- the estate's own parameterisation idiom, so the signature is exact rather than 16// a guess. When a module-scope default becomes a parameter, this tree writes: 17// func X_at(..., <the new parameters>) -> i64 { ...the real body... } 18// func X(...) -> i64 { return X_at(..., <the module defaults>) } // thin wrapper 19// The wrapper is correct and keeps existing callers working. The DEFECT is a caller that had the 20// quantity available and still called X. So: for every confirmed X / X_at pair, count the call sites 21// of the DEFAULTED name X. Each one is a path through which the new capability is unreachable. 22// 23// WHY "_at" ALONE IS NOT THE TEST. "_at" is also this tree's index-accessor idiom 24// (nx_artifact_store_path_at(s, rowid)), which is a different thing entirely. A pair only counts 25// when BOTH func X( and func X_at( are declared, so an accessor with no defaulted twin is never 26// reported. That exclusion is the difference between a detector and a grep. 27// 28// COVERAGE IS PART OF THE ANSWER. The walk reports files seen, files read and files it could not 29// read, and those must sum. An absence claimed from a partial scan is not an absence -- this organ 30// was written because a lane (mine) claimed one from a filtered grep earlier today and was wrong. 31// 32// NOTHING HERE IS RANKED BY A PICKED THRESHOLD: the report is ordered by defaulted-call count, which 33// IS the blast radius, and every count is printed so the reader adjudicates rather than trusting a cutoff. 34 35import "nx_syscalls.nx" 36import "nx_gate_verdict.nx" 37 38const PB_W64: i64 = 8 39const PB_DIRBUF: i64 = 65536 40const PB_NAMEMAX: i64 = 64 // longest function name this tree actually uses; over-long names are COUNTED and reported, never silently dropped 41 42func pb_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 43 44// identifier characters, so a match on "gs_w" cannot be a fragment of "xgs_where" 45func pb_ident(c: i64) -> i64 { 46 if c >= 97 { if c <= 122 { return 1 } } 47 if c >= 65 { if c <= 90 { return 1 } } 48 if c >= 48 { if c <= 57 { return 1 } } 49 if c == 95 { return 1 } 50 return 0 51} 52 53// find needle in hay starting at from; -1 if absent 54func pb_find(hay: *u8, hlen: i64, ned: *u8, nlen: i64, from: i64) -> i64 { 55 if nlen <= 0 { return 0 - 1 } 56 var i: i64 = from 57 while i + nlen <= hlen { 58 var j: i64 = 0 59 while j < nlen { if hay[i+j] != ned[j] { j = nlen + 2 } else { j = j + 1 } } 60 if j == nlen { return i } 61 i = i + 1 62 } 63 return 0 - 1 64} 65 66// Look up an identifier (buf[s..s+l)) in the base table. A first-character reject makes the linear 67// scan cheap -- 468 bases collapse to a handful of real comparisons per identifier, which is what 68// keeps the whole sweep O(corpus bytes). A stop FLAG ends each compare, never a sentinel written 69// into the cursor: that sentinel is exactly what made the first version of this organ report zero 70// declarations across 11,661 files. 71func pb_lookup(bases: *u8, nbases: i64, buf: *u8, s: i64, l: i64) -> i64 { 72 if l <= 0 { return 0 - 1 } 73 if l >= PB_NAMEMAX { return 0 - 1 } 74 let c0: u8 = buf[s] 75 var i: i64 = 0 76 while i < nbases { 77 let bn: *u8 = (((bases as i64) + i*PB_NAMEMAX) as *u8) 78 if bn[0] == c0 { 79 var j: i64 = 0 80 var ok: i64 = 1 81 var stop: i64 = 0 82 while stop == 0 { 83 if j >= l { stop = 1 } else { 84 if bn[j] != buf[s+j] { ok = 0; stop = 1 } else { j = j + 1 } 85 } 86 } 87 if ok == 1 { if bn[l] == (0 as u8) { return i } } 88 } 89 i = i + 1 90 } 91 return 0 - 1 92} 93 94func main(argc: i64, argv: *i64) -> i64 { 95 let ctr: *i64 = gv_ctr() 96 gv_head("nx_paramplumb -- parameters that were added but never plumbed through" as *u8) 97 98 var dir: *u8 = "buildroot/runtime" as *u8 99 if argc > 1 { dir = argv[1] as *u8 } 100 gv_puts(" scanning: " as *u8); gv_puts(dir); gv_puts("\n" as *u8) 101 102 let fd: i64 = sys_openat_rd(dir) 103 if fd < 0 { 104 gv_puts(" REFUSED: cannot open the directory -- reporting nothing rather than an empty result\n" as *u8) 105 gv_check("directory-opened (a scan that could not look must not report an absence)" as *u8, 0, ctr) 106 return gv_verdict("NX-PARAMPLUMB" as *u8, ctr, "the scan could not run" as *u8) 107 } 108 109 // ---- walk the directory TWICE: count first, then allocate from the count, then fill -------- 110 // The first version of this organ sized the filename arena from PB_DIRBUF*4 -- a constant with 111 // nothing to do with how many files exist -- which is a 4,096-file ceiling that silently 112 // corrupted memory past it. That is EXACTLY the defect class this organ hunts, committed by the 113 // hunter, and it is fixed the way the estate fixes them: the bound is DERIVED from the thing it 114 // bounds. Counting costs one extra walk and removes the ceiling entirely. 115 let dbuf: *u8 = sys_mmap(PB_DIRBUF) as *u8 116 var seen: i64 = 0 117 var nfiles: i64 = 0 118 var toolong: i64 = 0 119 var nb0: i64 = sys_getdents64(fd, dbuf, PB_DIRBUF) 120 while nb0 > 0 { 121 var o0: i64 = 0 122 while o0 < nb0 { 123 let rl0: i64 = (dbuf[o0+16] as i64) + (dbuf[o0+17] as i64) * 256 124 let nm0: *u8 = (((dbuf as i64) + o0 + 19) as *u8) 125 let l0: i64 = pb_slen(nm0) 126 seen = seen + 1 127 if l0 > 3 { 128 if nm0[l0-3] == (46 as u8) { if nm0[l0-2] == (110 as u8) { if nm0[l0-1] == (120 as u8) { 129 if l0 < PB_NAMEMAX { nfiles = nfiles + 1 } else { toolong = toolong + 1 } 130 } } } 131 } 132 if rl0 <= 0 { o0 = nb0 } else { o0 = o0 + rl0 } 133 } 134 nb0 = sys_getdents64(fd, dbuf, PB_DIRBUF) 135 } 136 sys_close(fd) 137 if nfiles <= 0 { 138 gv_puts(" REFUSED: no .nx files found -- an empty scan reports nothing, never an absence 139" as *u8) 140 gv_check("scan-found-files (an empty scan cannot report an absence)" as *u8, 0, ctr) 141 return gv_verdict("NX-PARAMPLUMB" as *u8, ctr, "the scan found nothing to read" as *u8) 142 } 143 // sized from the measurement, not from a constant 144 let names: *u8 = sys_mmap(nfiles * PB_NAMEMAX) as *u8 145 var nlen: i64 = 0 146 let fd2: i64 = sys_openat_rd(dir) 147 var filled: i64 = 0 148 var nb: i64 = sys_getdents64(fd2, dbuf, PB_DIRBUF) 149 while nb > 0 { 150 var off: i64 = 0 151 while off < nb { 152 let reclen: i64 = (dbuf[off+16] as i64) + (dbuf[off+17] as i64) * 256 153 let nm: *u8 = (((dbuf as i64) + off + 19) as *u8) 154 let l: i64 = pb_slen(nm) 155 seen = seen + 1 156 // .nx only 157 if l > 3 { 158 if nm[l-3] == (46 as u8) { if nm[l-2] == (110 as u8) { if nm[l-1] == (120 as u8) { 159 if l < PB_NAMEMAX { if filled < nfiles { 160 var k: i64 = 0 161 while k < l { names[nlen+k] = nm[k]; k = k + 1 } 162 names[nlen+l] = 0 as u8 163 nlen = nlen + PB_NAMEMAX 164 filled = filled + 1 165 } } 166 } } } 167 } 168 if reclen <= 0 { off = nb } else { off = off + reclen } 169 } 170 nb = sys_getdents64(fd2, dbuf, PB_DIRBUF) 171 } 172 sys_close(fd2) 173 174 gv_puts(" entries seen=" as *u8); gv_num(seen) 175 gv_puts(" .nx files=" as *u8); gv_num(nfiles) 176 gv_puts(" name-too-long (counted, not dropped silently)=" as *u8); gv_num(toolong) 177 gv_puts(" refilled on second walk=" as *u8); gv_num(filled) 178 gv_puts("\n\n" as *u8) 179 180 // ---- PASS A: collect every NAME that has a "func NAME_at(" declaration --------------------- 181 // The base-name arena is sized from the file count, which is the only bound that cannot be a 182 // picked ceiling: at most one _at declaration per 40 bytes of any file. 183 let pathbuf: *u8 = sys_mmap(PB_NAMEMAX * 4) as *u8 184 let dl: i64 = pb_slen(dir) 185 let bases: *u8 = sys_mmap(PB_NAMEMAX * 4096) as *u8 186 var nbases: i64 = 0 187 var overflow: i64 = 0 188 var readfail: i64 = 0 189 var didread: i64 = 0 190 191 var fi: i64 = 0 192 while fi < nfiles { 193 // build "<dir>/<name>" 194 var p: i64 = 0 195 while p < dl { pathbuf[p] = dir[p]; p = p + 1 } 196 pathbuf[dl] = 47 as u8 197 let fnm: *u8 = (((names as i64) + fi*PB_NAMEMAX) as *u8) 198 let fl: i64 = pb_slen(fnm) 199 var q: i64 = 0 200 while q < fl { pathbuf[dl+1+q] = fnm[q]; q = q + 1 } 201 pathbuf[dl+1+fl] = 0 as u8 202 203 // sys_read_file sizes from the file itself and cannot short-read -- the banked remedy for 204 // exactly the fixed-cap reader defect this organ exists to find. 205 let szp: *i64 = sys_mmap(PB_W64) as *i64 206 let buf: *u8 = sys_read_file(pathbuf, szp) as *u8 207 let blen: i64 = szp[0] 208 if blen <= 0 { readfail = readfail + 1 } else { 209 didread = didread + 1 210 var at: i64 = pb_find(buf, blen, "func " as *u8, 5, 0) 211 while at >= 0 { 212 let s0: i64 = at + 5 213 // SEPARATE FLAG, NOT A SENTINEL IN THE CURSOR. The first version of this scan exited 214 // with "e = blen + 2" and then subtracted 2, which set e to the END OF FILE rather 215 // than to where the identifier stopped -- so no name ever ended in "_at" and the 216 // organ reported ZERO declarations across 11,660 files. That is the estate's own 217 // banked defect (a loop that breaks by clobbering its own cursor cannot also report 218 // where it stopped), and the anti-vacuity tooth is what caught it: a detector that 219 // finds nothing has not found nothing until it has found something known-present. 220 var e: i64 = s0 221 var stop: i64 = 0 222 while stop == 0 { 223 if e >= blen { stop = 1 } else { 224 if pb_ident(buf[e] as i64) == 1 { e = e + 1 } else { stop = 1 } 225 } 226 } 227 // name is buf[s0..e); does it end in "_at" and is it followed by "(" ? 228 let L: i64 = e - s0 229 if L > 3 { if buf[e] == (40 as u8) { 230 if buf[e-3] == (95 as u8) { if buf[e-2] == (97 as u8) { if buf[e-1] == (116 as u8) { 231 let bl: i64 = L - 3 232 if bl > 0 { if bl < PB_NAMEMAX { 233 if nbases < 4096 { 234 var c: i64 = 0 235 while c < bl { bases[nbases*PB_NAMEMAX + c] = buf[s0+c]; c = c + 1 } 236 bases[nbases*PB_NAMEMAX + bl] = 0 as u8 237 nbases = nbases + 1 238 } else { overflow = overflow + 1 } 239 } } 240 } } } 241 } } 242 at = pb_find(buf, blen, "func " as *u8, 5, at + 5) 243 } 244 } 245 fi = fi + 1 246 } 247 248 gv_puts(" files read=" as *u8); gv_num(didread) 249 gv_puts(" unreadable=" as *u8); gv_num(readfail) 250 gv_puts(" partition sums=" as *u8) 251 if didread + readfail == nfiles { gv_puts("YES" as *u8) } else { gv_puts("NO" as *u8) } 252 gv_puts("\n _at declarations found=" as *u8); gv_num(nbases) 253 gv_puts(" base-table overflow (counted, never silent)=" as *u8); gv_num(overflow) 254 gv_puts("\n\n" as *u8) 255 256 // ---- PASS B: for each base, confirm the DEFAULTED twin exists and count its call sites ----- 257 gv_puts(" defaulted_calls at_calls base_name (a defaulted call is a path where the new parameter is unreachable)\n" as *u8) 258 var pairs: i64 = 0 259 var stuck: i64 = 0 260 // ---- COMPLEXITY: ONE PASS PER FILE, NOT ONE PASS PER (BASE, FILE) -------------------------- 261 // The first version looped bases OUTSIDE files: 468 bases x 11,661 files = 5.46 MILLION file 262 // reads, each followed by a full substring scan -- roughly 55 GB of comparisons, and it did not 263 // finish. Counting syscalls and byte-touches per unit of output IS the real complexity: the 264 // answer needs each file's bytes exactly ONCE. So this pass extracts every identifier that is 265 // followed by "(" in a single left-to-right walk and looks it up, which is O(corpus bytes) 266 // instead of O(bases x corpus bytes) -- the same output, three orders of magnitude cheaper. 267 let hasdecl: *i64 = sys_mmap(nbases * PB_W64) as *i64 268 let dcnt: *i64 = sys_mmap(nbases * PB_W64) as *i64 269 let acnt: *i64 = sys_mmap(nbases * PB_W64) as *i64 270 var zi: i64 = 0 271 while zi < nbases { hasdecl[zi] = 0; dcnt[zi] = 0; acnt[zi] = 0; zi = zi + 1 } 272 273 var fj: i64 = 0 274 while fj < nfiles { 275 var p2: i64 = 0 276 while p2 < dl { pathbuf[p2] = dir[p2]; p2 = p2 + 1 } 277 pathbuf[dl] = 47 as u8 278 let fnm2: *u8 = (((names as i64) + fj*PB_NAMEMAX) as *u8) 279 let fl2: i64 = pb_slen(fnm2) 280 var q2: i64 = 0 281 while q2 < fl2 { pathbuf[dl+1+q2] = fnm2[q2]; q2 = q2 + 1 } 282 pathbuf[dl+1+fl2] = 0 as u8 283 let szp2: *i64 = sys_mmap(PB_W64) as *i64 284 let b2: *u8 = sys_read_file(pathbuf, szp2) as *u8 285 let bl2: i64 = szp2[0] 286 if bl2 > 0 { 287 var i2: i64 = 0 288 while i2 < bl2 { 289 if b2[i2] == (40 as u8) { 290 // walk back over the identifier that this "(" terminates 291 var s2: i64 = i2 292 var back: i64 = 0 293 while back == 0 { 294 if s2 == 0 { back = 1 } else { 295 if pb_ident(b2[s2-1] as i64) == 1 { s2 = s2 - 1 } else { back = 1 } 296 } 297 } 298 let idl: i64 = i2 - s2 299 if idl > 0 { 300 // is this a DECLARATION? the five bytes before the identifier are "func " 301 var isdecl: i64 = 0 302 if s2 >= 5 { 303 if b2[s2-1] == (32 as u8) { if b2[s2-2] == (99 as u8) { if b2[s2-3] == (110 as u8) { 304 if b2[s2-4] == (117 as u8) { if b2[s2-5] == (102 as u8) { isdecl = 1 } } 305 } } } 306 } 307 // does it end in "_at"? then it is the PARAMETERISED twin being called 308 var isat: i64 = 0 309 if idl > 3 { 310 if b2[i2-3] == (95 as u8) { if b2[i2-2] == (97 as u8) { if b2[i2-1] == (116 as u8) { isat = 1 } } } 311 } 312 var look: i64 = idl 313 if isat == 1 { look = idl - 3 } 314 let bx: i64 = pb_lookup(bases, nbases, b2, s2, look) 315 if bx >= 0 { 316 if isat == 1 { 317 if isdecl == 0 { acnt[bx] = acnt[bx] + 1 } 318 } else { 319 if isdecl == 1 { hasdecl[bx] = 1 } else { dcnt[bx] = dcnt[bx] + 1 } 320 } 321 } 322 } 323 } 324 i2 = i2 + 1 325 } 326 } 327 fj = fj + 1 328 } 329 330 var bi: i64 = 0 331 while bi < nbases { 332 if hasdecl[bi] == 1 { 333 pairs = pairs + 1 334 if dcnt[bi] > 0 { 335 stuck = stuck + 1 336 let bn2: *u8 = (((bases as i64) + bi*PB_NAMEMAX) as *u8) 337 gv_puts(" " as *u8); gv_num(dcnt[bi]) 338 gv_puts(" " as *u8); gv_num(acnt[bi]) 339 gv_puts(" " as *u8); gv_puts(bn2); gv_puts("\n" as *u8) 340 } 341 } 342 bi = bi + 1 343 } 344 345 gv_puts("\n confirmed X / X_at pairs=" as *u8); gv_num(pairs) 346 gv_puts(" of which still have DEFAULTED callers=" as *u8); gv_num(stuck) 347 gv_puts("\n\n" as *u8) 348 349 gv_check("scan-read-at-least-one-file (an empty scan cannot report an absence)" as *u8, didread > 0, ctr) 350 gv_check("coverage-partition-sums (files read + unreadable == files seen)" as *u8, didread + readfail == nfiles, ctr) 351 gv_check("no-silent-truncation (base table and name length both reported)" as *u8, overflow == 0, ctr) 352 gv_check("found-at-least-one-parameterised-pair (else the detector proved nothing)" as *u8, pairs > 0, ctr) 353 354 return gv_verdict("NX-PARAMPLUMB" as *u8, ctr, "a defaulted caller of a parameterised function is a path where the capability is unreachable" as *u8) 355}