code wiki / (root) / nx_enginelab_analysis_gate.nx

nx_enginelab_analysis_gate.nx source

↩ module page · 209 lines · 12673 B

1// nx_enginelab_analysis_gate.nx -- proves EL2 replay, EL4 shader binding, EL5 sampling, EL6 call tree. 2// 3// A SEPARATE GATE ON PURPOSE. nx_enginelab_gate belongs to the sibling lane that owns EL1/EL3/EL7 and 4// is the single writer of nx_enginelab_lib. Adding my teeth there would mean two lanes editing one 5// file, which is the collision we already paid for once today -- caught by the LINKER rather than by 6// either lane looking first, and only because we happened to pick identical names. One lane, one gate, 7// one file: the boundary is cheaper than the coordination. 8import "nx_syscalls.nx" 9import "nx_gate_verdict.nx" 10import "nx_enginelab_lib.nx" 11import "nx_enginelab_analysis.nx" 12 13const AG_CAP: i64 = 96 14const AG_BUF: i64 = 4096 15const AG_SMALL_BUF: i64 = 8 // deliberately too small for even one folded line 16 17// The corrupt-bookkeeping fixture, named for PURPOSE rather than value. The expected self time is 18// DERIVED from the two inputs rather than typed as a third literal: if either input changes the 19// expectation follows, so the tooth cannot silently start asserting the wrong number. 20const AG_CORRUPT_ZONE_US: i64 = 1000 // the zone's own duration 21const AG_IMPOSSIBLE_CHILD_US: i64 = 1500 // children recorded as costing MORE than the zone contains 22const AG_CORRUPT_SELF_US: i64 = AG_CORRUPT_ZONE_US - AG_IMPOSSIBLE_CHILD_US 23 24func ag_buf() -> *i64 { return sys_mmap(32 * EL_I64) as *i64 } 25func ag_eq(a: i64, b: i64) -> i64 { if a == b { return 1 } return 0 } 26func ag_pos(a: i64) -> i64 { if a > 0 { return 1 } return 0 } 27func ag_zero(a: i64) -> i64 { if a == 0 { return 1 } return 0 } 28func ag_eq2(a: i64, b: i64, c: i64, d: i64) -> i64 { if a == b { if c == d { return 1 } } return 0 } 29func ag_eq3(a: i64, b: i64, c: i64, d: i64, e: i64, f: i64) -> i64 { 30 if a == b { if c == d { if e == f { return 1 } } } return 0 31} 32 33// three draws; last_verts lets a caller build a DIVERGENT twin differing at exactly ordinal 2 34func ag_capture(last_verts: i64) -> *i64 { 35 let st: *i64 = el_new(AG_CAP) 36 el_draw(st, 10, 1, 500, 1, 10, 100) 37 el_draw(st, 20, 1, 501, 2, 20, 200) 38 el_draw(st, 30, 1, 502, 3, last_verts, 300) 39 return st 40} 41 42// a properly nested zone tree whose arithmetic is known by hand: 43// A 0..1000 name 100, containing B 100..400 name 200 and C 500..900 name 300 44// A: inclusive 1000, children 700, SELF 300. B: self 300. C: self 400. 45// sum of all self = 1000 == sum of root inclusive = 1000. That identity is EL6's whole tooth. 46func ag_tree() -> *i64 { 47 let st: *i64 = el_new(AG_CAP) 48 el_zone_begin(st, 0, 1, 100) 49 el_zone_begin(st, 100, 1, 200) 50 el_zone_end(st, 400) 51 el_zone_begin(st, 500, 1, 300) 52 el_zone_end(st, 900) 53 el_zone_end(st, 1000) 54 return st 55} 56 57func main() -> i64 { 58 let ctr: *i64 = gv_ctr() 59 gv_head("nx_enginelab_analysis_gate -- EL2 replay, EL4 shader binding, EL5 sampling, EL6 call tree" as *u8) 60 61 // ---- EL2: DETERMINISTIC REPLAY --------------------------------------------------------------- 62 let ca: *i64 = ag_capture(30) 63 let cb: *i64 = ag_capture(30) 64 let cc: *i64 = ag_capture(999) 65 let ra: *i64 = ag_buf() 66 let rb: *i64 = ag_buf() 67 gv_subjects("draws in the replay control" as *u8, el_draw_count(ca), ctr) 68 let rca: i64 = el_replay(ca, ra) 69 let rcb: i64 = el_replay(cb, rb) 70 gv_check("T1 two captures of the same frame replay to the SAME digest over the same draw count" as *u8, 71 ag_eq3(rca, EL_DIFF_SAME, ra[EL_RP_DIGEST], rb[EL_RP_DIGEST], ra[EL_RP_DRAWS], 3), ctr) 72 73 // A REPLAY OF NOTHING IS NOT A SUCCESSFUL REPLAY. Without this, two empty captures would agree on 74 // a digest of zero and a harness that captured nothing would report a clean replay forever. 75 let cempty: *i64 = el_new(AG_CAP) 76 let re: *i64 = ag_buf() 77 gv_check("neg-control-replaying-ZERO-draws-is-UNMEASURABLE-never-a-matching-digest" as *u8, 78 ag_eq(el_replay(cempty, re), EL_DIFF_UNMEASURABLE), ctr) 79 80 let dv: *i64 = ag_buf() 81 let same: *i64 = ag_buf() 82 let dvr: i64 = el_replay_diverge(ca, cc, dv) 83 let samer: i64 = el_replay_diverge(ca, cb, same) 84 gv_check("T3 a capture differing at draw 2 diverges AT ORDINAL 2 -- the ordinal is the product, not the mismatch" as *u8, 85 ag_eq2(dvr, EL_DIFF_DIVERGENT, dv[0], 2), ctr) 86 gv_check("T4 identical captures report SAME and name no divergence ordinal at all" as *u8, 87 ag_eq2(samer, EL_DIFF_SAME, same[0], 0 - 1), ctr) 88 gv_bite("T5 replay divergence detection" as *u8, ag_eq(dvr, EL_DIFF_DIVERGENT), ag_eq(samer, EL_DIFF_DIVERGENT), ctr) 89 90 // the running digest must be ORDER-SENSITIVE, or a reordered frame replays as identical 91 let cr: *i64 = el_new(AG_CAP) 92 el_draw(cr, 10, 1, 501, 2, 20, 200) 93 el_draw(cr, 20, 1, 500, 1, 10, 100) 94 el_draw(cr, 30, 1, 502, 3, 30, 300) 95 let rr: *i64 = ag_buf() 96 el_replay(cr, rr) 97 var t6: i64 = 0 98 if rr[EL_RP_DIGEST] != ra[EL_RP_DIGEST] { t6 = 1 } 99 gv_check("T6 the SAME draws in a DIFFERENT ORDER replay to a different digest -- order is part of the frame" as *u8, t6, ctr) 100 101 // ---- EL4: SHADER BINDING --------------------------------------------------------------------- 102 // A draw does not carry its shader; it inherits the last bind on its own thread. Resolving that is 103 // state tracking, which is what a frame debugger does that a log does not. 104 let sh: *i64 = el_new(AG_CAP) 105 el_bind_shader(sh, 5, 1, 77, 1) 106 el_bind_shader(sh, 6, 1, 88, 2) 107 el_draw(sh, 10, 1, 500, 1, 10, 100) 108 el_bind_shader(sh, 15, 1, 99, 1) 109 el_draw(sh, 20, 1, 501, 2, 20, 200) 110 let so: *i64 = ag_buf() 111 let v0: i64 = el_shader_dump(sh, 0, 1, so) 112 let f0: i64 = el_shader_dump(sh, 0, 2, so) 113 let v1: i64 = el_shader_dump(sh, 1, 1, so) 114 gv_check("T7 each draw inherits the LAST bind before it, per stage -- draw0 vertex 77, draw1 vertex 99" as *u8, 115 ag_eq2(v0, 77, v1, 99), ctr) 116 gv_check("T8 stages do not bleed: the fragment stage still reads 88 across a vertex rebind" as *u8, 117 ag_eq(f0, 88), ctr) 118 gv_bite("T9 shader rebind detection" as *u8, ag_eq(v1, 99), ag_eq(v1, v0), ctr) 119 120 // AN UNBOUND DRAW IS A REAL AND INTERESTING STATE. Reporting it as shader 0 would invent a binding 121 // that never happened, and 0 is a plausible-looking shader id -- the fabricated-constant shape. 122 let nb: *i64 = el_new(AG_CAP) 123 el_draw(nb, 10, 1, 500, 1, 10, 100) 124 let nbo: *i64 = ag_buf() 125 gv_check("neg-control-a-draw-with-NO-prior-bind-reports-minus-one-never-shader-zero" as *u8, 126 ag_eq(el_shader_dump(nb, 0, 1, nbo), 0 - 1), ctr) 127 128 // ---- EL5: STACK SAMPLING --------------------------------------------------------------------- 129 // Samples land in the INNERMOST containing zone. One sample deliberately falls outside every zone, 130 // because samples in no zone are a real measurement about the program and dropping them would 131 // quietly inflate the attributed share of everything else. 132 let sm: *i64 = ag_tree() 133 el_sample_stack(sm, 200, 1) // inside B 134 el_sample_stack(sm, 600, 1) // inside C 135 el_sample_stack(sm, 950, 1) // inside A only 136 el_sample_stack(sm, 5000, 1) // outside everything 137 let sa: *i64 = ag_buf() 138 let inB: i64 = el_sample_attribute(sm, 200, EL_SM_INTERVAL_US_DEFAULT, sa) 139 gv_check("T11 a sample inside a nested zone is attributed to the INNERMOST zone, not the outermost" as *u8, 140 ag_eq3(inB, 1, sa[EL_SM_TOTAL], 4, sa[EL_SM_UNZONED], 1), ctr) 141 let sa2: *i64 = ag_buf() 142 let inA: i64 = el_sample_attribute(sm, 100, EL_SM_INTERVAL_US_DEFAULT, sa2) 143 gv_check("T12 the enclosing zone gets ONLY the sample that fell outside both children -- no double count" as *u8, 144 ag_eq(inA, 1), ctr) 145 // THE HORIZON IS PUBLISHED, NOT IMPLIED. A sampler at interval I cannot resolve anything briefer 146 // than I: a short zone may collect ZERO samples and is invisible, not cheap. Reporting attribution 147 // without that bound beside it is how a sampling profiler reports absence of evidence as evidence. 148 gv_check("T13 the sampling INTERVAL and its HORIZON ride in the same structure as the counts" as *u8, 149 ag_eq2(sa[EL_SM_INTERVAL], EL_SM_INTERVAL_US_DEFAULT, sa[EL_SM_HORIZON], EL_SM_INTERVAL_US_DEFAULT), ctr) 150 // A REAL CONTROL, replacing a bite whose good arm was not one. The first version passed 151 // ag_zero(total - 4), which is an ASSERTION ABOUT THE SAME FIXTURE that happens to be true -- so 152 // gv_bite correctly reported FALSE-POSITIVE: fired on the good input. A control has to be a 153 // DIFFERENT fixture in which the detector MUST stay silent. Here that is a run whose every sample 154 // lands inside a zone, so UNZONED must read zero. 155 let sm2: *i64 = ag_tree() 156 el_sample_stack(sm2, 200, 1) 157 el_sample_stack(sm2, 600, 1) 158 let sa3: *i64 = ag_buf() 159 el_sample_attribute(sm2, 200, EL_SM_INTERVAL_US_DEFAULT, sa3) 160 gv_bite("T14 unzoned-sample accounting" as *u8, ag_pos(sa[EL_SM_UNZONED]), ag_pos(sa3[EL_SM_UNZONED]), ctr) 161 162 // ---- EL6: THE CALL TREE, AND ITS RECONCILIATION ---------------------------------------------- 163 // THE TOOTH IS THE ARITHMETIC, NOT THE DRAWING. Every microsecond of CPU time must be attributed 164 // EXACTLY ONCE: sum of every zone's SELF time == sum of every ROOT zone's INCLUSIVE time. If those 165 // disagree the tree is double-counting or losing time, and rendering it prettily would not reveal 166 // that. Known by hand for this fixture: 300 + 300 + 400 == 1000. 167 let fl: *i64 = ag_buf() 168 let recon: i64 = el_flame_totals(sm, fl) 169 gv_check("T15 EVERY MICROSECOND ATTRIBUTED EXACTLY ONCE: all-self 1000 equals root-inclusive 1000" as *u8, 170 ag_eq3(recon, 1, fl[EL_FL_ALLSELF], 1000, fl[EL_FL_ROOTINCL], 1000), ctr) 171 gv_check("T16 the tree counts three nodes and exactly one root -- nesting resolved, not flattened" as *u8, 172 ag_eq3(fl[EL_FL_NODES], 3, fl[EL_FL_ROOTS], 1, fl[EL_FL_OPEN], 0), ctr) 173 174 // WHAT THE IDENTITY ACTUALLY CATCHES, corrected after this tooth went VACUOUS on its first run. 175 // My first bad fixture was two zones on one thread OVERLAPPING WITHOUT NESTING, on the assumption 176 // that a non-tree must fail the identity. IT DOES NOT, and the gate was right: neither zone 177 // contains the other, so both are legitimately ROOTS, root-inclusive is 2000, all-self is 2000, 178 // and the identity HOLDS. That is a true answer about a real arrangement, not a miss. 179 // ⇒ THE IDENTITY IS A BOOKKEEPING CHECK, NOT A TOPOLOGY CHECK. It detects a child-total that 180 // disagrees with the durations around it -- which is precisely the corruption a mis-attributing 181 // zone_end would produce -- and it says NOTHING about whether the zones form a sane call tree. 182 // Scoping that honestly is worth more than a tooth that claimed a detection it never made, and it 183 // is why the fixture below corrupts the ACCOUNTING rather than the shape: a zone whose children 184 // are recorded as costing more than the zone itself, which makes its self time negative. 185 let bad: *i64 = el_new(AG_CAP) 186 el_push(bad, EL_K_ZONE, 0, AG_CORRUPT_ZONE_US, 1, 100, 0 - 1, 0, AG_IMPOSSIBLE_CHILD_US) 187 let fb: *i64 = ag_buf() 188 let brecon: i64 = el_flame_totals(bad, fb) 189 gv_check("T17 an impossible child-total makes self time NEGATIVE, and the identity refuses to reconcile" as *u8, 190 ag_eq2(brecon, 0, fb[EL_FL_ALLSELF], AG_CORRUPT_SELF_US), ctr) 191 gv_bite("T17b the reconciliation identity detects corrupt child-total bookkeeping" as *u8, 192 ag_zero(brecon), ag_zero(recon), ctr) 193 194 let buf: *u8 = sys_mmap(AG_BUF) 195 let wrote: i64 = el_flame_emit(sm, buf, AG_BUF) 196 gv_check("T18 folded stacks emit for every closed zone and report the bytes written" as *u8, 197 ag_pos(wrote), ctr) 198 // REFUSES rather than truncates: a folded stack cut in half is a DIFFERENT tree that still parses, 199 // which is the silent class -- the reader gets a clean-looking profile of a program that never ran. 200 let small: *u8 = sys_mmap(AG_SMALL_BUF) 201 gv_check("neg-control-flame-emit-REFUSES-a-short-buffer-rather-than-writing-a-half-tree-that-still-parses" as *u8, 202 ag_eq(el_flame_emit(sm, small, AG_SMALL_BUF), 0 - 1), ctr) 203 204 let rc: i64 = gv_verdict("ENGINELAB-ANALYSIS-GATE" as *u8, ctr, 205 "replay is order-sensitive and names its divergence ordinal, shader state resolves per stage and reports unbound as unbound, sampling publishes its horizon beside its counts, and the call tree reconciles every microsecond exactly once" as *u8) 206 gv_journal("nx_enginelab_analysis_gate" as *u8, ctr[0], ctr[1], ag_zero(rc)) 207 sys_exit(rc) 208 return rc 209}