code wiki / _hdl_build / nx_gatequality.nx

nx_gatequality.nx source

↩ module page · 322 lines · 14479 B

1// nx_gatequality.nx -- HOW MUCH OF AN ORGAN ITS GATE ACTUALLY EXERCISES. 2// 3// ===== WHY THIS EXISTS ============================================ 4// 5// nx_gatescan answers "does anything test this file". That question has a 6// blind spot large enough to have hidden a real defect in this tree: a gate 7// touching four of an organ's twelve functions counts as a gate, so the organ 8// reads as covered. 9// 10// That is exactly what happened in the food-chemistry lane. A neighbouring 11// gate imported the ingredient catalog and exercised si_name, 12// si_density_mg_per_ml, si_capsule_fill_mg and si_cost_is_indicative -- the 13// packaging and cost helpers. si_class and si_is_lawful_dietary, the two 14// functions deciding whether a substance may LAWFULLY BE SOLD, were 15// exercised by nothing at all, and the scan reported the file as gated. 16// 17// ★COVERAGE ACCUMULATES AROUND WHATEVER A NEIGHBOUR HAPPENED TO NEED, so the 18// most consequential function in a file is frequently the least checked. 19// Existence is the wrong question. This organ asks the other one. 20// 21// ===== THE MEASUREMENT ============================================ 22// 23// 1. From each organ, extract its top-level `func` definitions. 24// 2. Find the gates attributed to it (same rule as nx_gatescan: the organ 25// stem followed by an underscore, ending _test.nx or _gate.nx). 26// 3. In each gate, look for a real reference to each function name. 27// 4. Coverage = referenced / defined, unioned across all of that organ's 28// gates -- an organ tested by three files is covered by their union. 29// 30// ★★COMMENTS ARE STRIPPED BEFORE MATCHING, AND THAT IS NOT A DETAIL. Gates 31// in this tree carry long explanatory headers that NAME the functions they 32// discuss. Matching raw text would count a function as exercised because 33// its own gate mentioned it in prose -- and the worse the coverage, the more 34// likely someone wrote a comment about it. The measurement would have been 35// anti-correlated with the truth. 36// 37// ★MATCHING IS TOKEN-EXACT. A substring match would count si_class as 38// covered by a reference to si_class_of, which is how a coverage tool starts 39// reporting numbers that are reassuring and false. 40// 41// ===== WHAT THIS DOES NOT MEASURE ================================= 42// 43// Whether the assertions are any good. A gate that calls every function and 44// asserts nothing meaningful scores 1000 here. This measures REACH, which is 45// a floor on quality and not quality itself -- stated so the number is not 46// read as more than it is. 47// 48// nx_gatequality <root-dir> [max-coverage-permil] [topk] 49// default root runtime, threshold 1000 (report everything), topk 30 50// 51// license_tier: ORIGINAL 52import "nx_syscalls.nx" 53import "nx_eco_graph.nx" 54import "nx_gatelib.nx" 55const GQ_MAGIC_4096: i64 = 4096 56 57const GQ_MAXNODE: i64 = 24000 58const GQ_MAXEDGE: i64 = 8 59const GQ_ARENA: i64 = 4194304 60const GQ_HASH: i64 = 65536 61const GQ_FILECAP: i64 = 262144 62const GQ_PATHARENA: i64 = 4194304 63 64// Functions per organ are tracked in a 64-bit mask, so the 65th function of 65// an organ cannot be represented. DECLARED, and reported in the envelope 66// rather than silently truncating a coverage figure (scale law). 67const GQ_MAXFUNCS: i64 = 64 68 69func gw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 70func gn(v: i64) -> i64 { let b: *u8 = sys_mmap(24); var m: i64 = v; if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } let t: *u8 = sys_mmap(24); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 } while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } var i: i64 = 0; while i < k { b[i] = t[k - 1 - i]; i = i + 1 } sys_write(1, b, k); return 0 } 71 72// Identifier character: letters, digits, underscore. Token boundaries are 73// what keep si_class from being "covered" by si_class_of. 74 75// Walk: intern every .nx basename and remember where the file lives, so the 76// second pass can re-open organs and gates by node id. 77 78// Extract top-level `func <name>` definitions into a flat name table. 79// Returns the count. Only column-0 `func` counts, which is what makes these 80// the organ's own definitions rather than anything nested or quoted. 81 82// Copy src into dst with // comments removed (to end of line). Without this 83// a gate's own explanatory header would mark functions as exercised. 84 85// Is `name` present in `hay` as a whole token? 86 87// Attribute a gate to the most specific organ it covers (same rule as 88// nx_gatescan: stem followed by an underscore, longest match first). 89 90 91// ★★DIRECT REACH IS NOT THE RIGHT QUESTION EITHER, AND VERIFYING A ROW 92// SHOWED WHY. The first run ranked nx_mgmt_api at 15 permil. Reading its 93// gate: it drives the API OVER HTTP and calls only ma_handle() directly, so 94// every other function is exercised THROUGH the dispatcher. Scoring that as 95// 15 permil is not a finding, it is a measurement artefact -- and it would 96// have been the loudest row in the report. 97// 98// So reach is computed TRANSITIVELY: seed with the functions the gate names, 99// then follow the organ's own internal calls to a fixpoint. A dispatcher 100// entry point now correctly credits everything it can reach. 101// 102// This makes the number a genuine upper bound on reach and a floor on 103// quality: reachable does not mean asserted about. 104func gq_close_reach(src: *u8, n: i64, names: *u8, offs: *i64, lens: *i64, spans: *i64, fc: i64, seed: i64) -> i64 { 105 var reach: i64 = seed 106 var changed: i64 = 1 107 var rounds: i64 = 0 108 while changed == 1 { 109 changed = 0 110 rounds = rounds + 1 111 if rounds > 64 { changed = 0 } else { 112 var i: i64 = 0 113 while i < fc { 114 if (reach & (1 << i)) != 0 { 115 var e: i64 = n 116 if i + 1 < fc { e = spans[i + 1] } 117 let bs: i64 = spans[i] 118 if e > bs { 119 let body: *u8 = ((src as i64) + bs) as *u8 120 var j: i64 = 0 121 while j < fc { 122 if (reach & (1 << j)) == 0 { 123 let nmj: *u8 = ((names as i64) + offs[j]) as *u8 124 if gl_has_token(body, e - bs, nmj, lens[j]) == 1 { 125 reach = reach | (1 << j) 126 changed = 1 127 } 128 } 129 j = j + 1 130 } 131 } 132 } 133 i = i + 1 134 } 135 } 136 } 137 return reach 138} 139 140func main(argc: i64, argv: *i64) -> i64 { 141 var root: *u8 = "runtime" as *u8 142 var thresh: i64 = 1000 143 var topk: i64 = 30 144 if argc >= 2 { root = argv[1] as *u8 } 145 if argc >= 3 { 146 let s: *u8 = argv[2] as *u8 147 var v: i64 = 0 148 var i: i64 = 0 149 while s[i] != (0 as u8) { let d: i64 = s[i] as i64; if d >= 48 { if d <= 57 { v = v * 10 + (d - 48) } } i = i + 1 } 150 thresh = v 151 } 152 if argc >= 4 { 153 let s2: *u8 = argv[3] as *u8 154 var v2: i64 = 0 155 var i2: i64 = 0 156 while s2[i2] != (0 as u8) { let d2: i64 = s2[i2] as i64; if d2 >= 48 { if d2 <= 57 { v2 = v2 * 10 + (d2 - 48) } } i2 = i2 + 1 } 157 if v2 > 0 { topk = v2 } 158 } 159 160 let g: *EcoGraph = eg_new(GQ_MAXNODE, GQ_MAXEDGE, GQ_ARENA, GQ_HASH) 161 let patharena: *u8 = sys_mmap(GQ_PATHARENA) 162 let pathoff: *i64 = sys_mmap(GQ_MAXNODE * 8) as *i64 163 let used: *i64 = sys_mmap(16) 164 let st: *i64 = sys_mmap(16) 165 used[0] = 0 166 st[0] = 0 167 var z: i64 = 0 168 while z < GQ_MAXNODE { pathoff[z] = 0; z = z + 1 } 169 let path: *u8 = sys_mmap(GQ_MAGIC_4096) 170 var rn: i64 = 0 171 while root[rn] != (0 as u8) { path[rn] = root[rn]; rn = rn + 1 } 172 gl_walk(g, path, rn, 0 as *u8, 0 as *i64, 0 as *i64, st, 0, patharena, pathoff, used, GQ_PATHARENA) 173 174 gw("=== NISHI GATEQUALITY -- how much of each organ its gates exercise ===\n" as *u8) 175 gw("root=" as *u8); gw(root) 176 gw(" files=" as *u8); gn(st[0]) 177 gw(" nodes=" as *u8); gn(g.node_count) 178 gw(" function cap per organ=" as *u8); gn(GQ_MAXFUNCS) 179 gw(" (DECLARED)\n" as *u8) 180 181 // Per-organ accumulators. A 64-bit mask of which functions any of that 182 // organ's gates reference, unioned across gates. 183 let mask: *i64 = sys_mmap(GQ_MAXNODE * 8) as *i64 184 let ftotal: *i64 = sys_mmap(GQ_MAXNODE * 8) as *i64 185 let seen: *u8 = sys_mmap(GQ_MAXNODE + 8) 186 var y: i64 = 0 187 while y < GQ_MAXNODE { mask[y] = 0; ftotal[y] = 0; seen[y] = 0 as u8; y = y + 1 } 188 189 let obuf: *u8 = sys_mmap(GQ_FILECAP) 190 let gbuf: *u8 = sys_mmap(GQ_FILECAP) 191 let sbuf: *u8 = sys_mmap(GQ_FILECAP) 192 let names: *u8 = sys_mmap(GQ_MAXFUNCS * 96) 193 let offs: *i64 = sys_mmap(GQ_MAXFUNCS * 8) as *i64 194 let lens: *i64 = sys_mmap(GQ_MAXFUNCS * 8) as *i64 195 let spans: *i64 = sys_mmap(GQ_MAXFUNCS * 8) as *i64 196 let nbuf: *u8 = sys_mmap(512) 197 198 var gates_processed: i64 = 0 199 var v: i64 = 0 200 while v < g.node_count { 201 let nm: *u8 = ((g.arena as i64) + g.node_off[v]) as *u8 202 let nl: i64 = gl_len(nm) 203 if gl_is_gate_name(nm, nl) == 1 { 204 let organ: i64 = gl_attribute(g, nm, nl, nbuf) 205 if organ >= 0 { 206 if pathoff[organ] > 0 { 207 if pathoff[v] > 0 { 208 let opath: *u8 = ((patharena as i64) + pathoff[organ] - 1) as *u8 209 let gpath: *u8 = ((patharena as i64) + pathoff[v] - 1) as *u8 210 let on: i64 = gl_read(opath, obuf, GQ_FILECAP) 211 let gnn: i64 = gl_read(gpath, gbuf, GQ_FILECAP) 212 if on > 0 { 213 if gnn > 0 { 214 let fc: i64 = gl_extract_funcs(obuf, on, names, offs, lens, spans, GQ_MAXFUNCS) 215 if fc > 0 { 216 ftotal[organ] = fc 217 seen[organ] = 1 as u8 218 let sn: i64 = gl_strip_comments(gbuf, gnn, sbuf) 219 var direct: i64 = 0 220 var f: i64 = 0 221 while f < fc { 222 let fname: *u8 = ((names as i64) + offs[f]) as *u8 223 if gl_has_token(sbuf, sn, fname, lens[f]) == 1 { 224 direct = direct | (1 << f) 225 } 226 f = f + 1 227 } 228 // Credit everything reachable from what the 229 // gate names, through the organ's own calls. 230 mask[organ] = mask[organ] | gq_close_reach(obuf, on, names, offs, lens, spans, fc, direct) 231 gates_processed = gates_processed + 1 232 } 233 } 234 } 235 } 236 } 237 } 238 } 239 v = v + 1 240 } 241 242 // Rank by RISK: uncovered functions, not coverage ratio. An organ with 243 // twenty untested functions matters more than one with two, even if the 244 // second has a worse percentage. 245 let bk_idx: *i64 = sys_mmap((topk + 2) * 8) 246 let bk_unc: *i64 = sys_mmap((topk + 2) * 8) 247 let bk_cov: *i64 = sys_mmap((topk + 2) * 8) 248 var bn: i64 = 0 249 var organs_measured: i64 = 0 250 var fully: i64 = 0 251 var sum_cov: i64 = 0 252 253 var o: i64 = 0 254 while o < g.node_count { 255 if seen[o] == (1 as u8) { 256 let tot: i64 = ftotal[o] 257 var cov: i64 = 0 258 var b: i64 = 0 259 while b < tot { 260 if b < GQ_MAXFUNCS { if (mask[o] & (1 << b)) != 0 { cov = cov + 1 } } 261 b = b + 1 262 } 263 let permil: i64 = cov * 1000 / tot 264 organs_measured = organs_measured + 1 265 sum_cov = sum_cov + permil 266 if permil >= 1000 { fully = fully + 1 } 267 if permil <= thresh { 268 let unc: i64 = tot - cov 269 if unc > 0 { 270 var start: i64 = 0 - 1 271 if bn < topk { 272 start = bn 273 bk_unc[bn] = unc; bk_idx[bn] = o; bk_cov[bn] = permil 274 bn = bn + 1 275 } 276 if bn >= topk { 277 if start < 0 { 278 if unc > bk_unc[topk - 1] { 279 start = topk - 1 280 bk_unc[start] = unc; bk_idx[start] = o; bk_cov[start] = permil 281 } 282 } 283 } 284 var q: i64 = start 285 while q > 0 { 286 if bk_unc[q - 1] < bk_unc[q] { 287 let t1: i64 = bk_unc[q-1]; bk_unc[q-1] = bk_unc[q]; bk_unc[q] = t1 288 let t2: i64 = bk_idx[q-1]; bk_idx[q-1] = bk_idx[q]; bk_idx[q] = t2 289 let t3: i64 = bk_cov[q-1]; bk_cov[q-1] = bk_cov[q]; bk_cov[q] = t3 290 q = q - 1 291 } else { q = 0 } 292 } 293 } 294 } 295 } 296 o = o + 1 297 } 298 299 gw("gates_processed=" as *u8); gn(gates_processed) 300 gw(" organs_measured=" as *u8); gn(organs_measured) 301 gw(" fully_exercised=" as *u8); gn(fully) 302 if organs_measured > 0 { 303 gw(" mean_coverage=" as *u8); gn(sum_cov / organs_measured); gw(" permil" as *u8) 304 } 305 gw("\n-- ranked by UNCOVERED FUNCTION COUNT (top " as *u8); gn(bn) 306 gw("; bound DECLARED) --\n" as *u8) 307 var r: i64 = 0 308 while r < bn { 309 let nm2: *u8 = ((g.arena as i64) + g.node_off[bk_idx[r]]) as *u8 310 gw(" uncovered=" as *u8); gn(bk_unc[r]) 311 gw(" coverage=" as *u8); gn(bk_cov[r]) 312 gw("permil " as *u8); gw(nm2) 313 gw("\n" as *u8) 314 r = r + 1 315 } 316 gw("\nNOTE: this measures REACH -- which functions a gate touches at all.\n" as *u8) 317 gw(" A gate that calls everything and asserts nothing scores 1000.\n" as *u8) 318 gw(" Comments are stripped before matching, so a gate cannot cover a\n" as *u8) 319 gw(" function by discussing it. verdict=MEASURED\n" as *u8) 320 sys_exit(0) 321 return 0 322}