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}