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}