code wiki / _hdl_build / nx_ad_core.nx

nx_ad_core.nx source

↩ module page · 174 lines · 7697 B

1// nx_ad_core.nx -- AUTORUN BEAT CORE (mainless; X-Q-007 "beat list as DATA"). 2// The 6h slow beat's TARGETS come from conf rows, not code: 3// T <basename> one durable-lane target per row (order preserved, cap 8) 4// No T rows -> the historical default (nx_math_rung_run) so the existing 5// conf keeps working unchanged. adc_beat runs each target sequentially via 6// _offc/nx_sov_build_run.elf (rebuild-from-source = the evidence renewal), 7// appends one AUTORUN-TGT row per target and ONE AUTORUN-BEAT row keeping 8// the historical rung_rc= field (= worst rc) plus targets=<n>. 9// license_tier: ORIGINAL 10 11import "nx_syscalls.nx" 12import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 13const ADC_MAGIC_8192: i64 = 8192 14 15const ADC_MAXTGT: i64 = 8 16const ADC_NAMECAP: i64 = 64 17 18// ATOMIC single-instance guard -- shared so the daemon AND its dup-guard GATE use the 19// SAME mechanism (DRY). flock(LOCK_EX|LOCK_NB) on a /tmp ext4 lockfile: atomic (no 20// TOCTOU) + kernel-released-on-process-death (no stale-lock problem). Returns the held 21// fd (>=0; caller keeps it open for life) or -1 if another holder has it. Replaces the 22// racy read-pidfile+/proc-cmdline check whose fork->pidfile-write window let two daemons 23// coexist (the bug hit 2026-06-13). /tmp (ext4) because flock on /mnt/c drvfs is unreliable. 24// Atomic single-instance guard via O_CREAT|O_EXCL openat: only ONE concurrent creator 25// wins (the rest get EEXIST) -- this closes the TOCTOU race the old pidfile+/proc check had. 26// NOTE: flock is NOT usable here -- rv64 32 (flock) is MISSING from the compiler's 27// rv64->x86-64 syscall table, so it silently emits as x86-64 dup2 (no lock); nx_flock.nx 28// is latent-broken for the same reason (verdict NOT_YET_EVALUATED). openat IS in the table. 29// Caller keeps the returned fd open for life; staleness (crash-leftover file) is reclaimed 30// by the daemon via its /proc-liveness check + unlink. 31func ad_acquire_lock(path: *u8) -> i64 { 32 let fd: i64 = __syscall(SYS_OPENAT, AT_FDCWD, path as i64, 193, 420, 0, 0) // O_CREAT(64)|O_EXCL(128)|O_WRONLY(1), 0644 33 if fd < 0 { return 0 - 1 } // EEXIST or error -> not acquired (another holder owns it) 34 return fd 35} 36 37func adc_w(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 } 38// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 39// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 40// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 41// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 42func adc_wn(fd: i64, v: i64) -> i64 { nxi_fd(fd, v); return 0 } 43 44func adc_read(path: *u8, buf: *u8, cap: i64) -> i64 { 45 let fd: i64 = sys_openat_rd(path) 46 if fd < 0 { return 0 } 47 var n: i64 = 0 48 var r: i64 = sys_read(fd, buf, cap - 1) 49 while r > 0 { n = n + r; r = sys_read(fd, buf + n, cap - 1 - n) } 50 sys_close(fd) 51 return n 52} 53 54// parse T rows into arena (ADC_NAMECAP bytes per slot); returns count 55// (0 conf rows -> 1 default target). Target charset [a-z0-9_] (boundary law). 56func adc_targets(confpath: *u8, arena: *u8) -> i64 { 57 let buf: *u8 = sys_mmap(ADC_MAGIC_8192) 58 let n: i64 = adc_read(confpath, buf, ADC_MAGIC_8192) 59 var cnt: i64 = 0 60 var i: i64 = 0 61 while i < n { 62 var le: i64 = i 63 var scan: i64 = 1 64 while scan == 1 { 65 if le >= n { scan = 0 } else { 66 if buf[le] == (10 as u8) { scan = 0 } else { le = le + 1 } 67 } 68 } 69 if buf[i] == (84 as u8) { 70 if i + 1 < le { 71 if buf[i + 1] == (32 as u8) { 72 if cnt < ADC_MAXTGT { 73 let dst: *u8 = (arena as i64 + cnt * ADC_NAMECAP) as *u8 74 var k: i64 = 0 75 var q: i64 = i + 2 76 var ok: i64 = 1 77 while q < le { 78 let ch: i64 = buf[q] as i64 79 var okc: i64 = 0 80 if ch >= 97 { if ch <= 122 { okc = 1 } } 81 if ch >= 48 { if ch <= 57 { okc = 1 } } 82 if ch == 95 { okc = 1 } 83 if okc == 0 { ok = 0; q = le } else { 84 if k < ADC_NAMECAP - 1 { dst[k] = ch as u8; k = k + 1 } 85 q = q + 1 86 } 87 } 88 dst[k] = 0 as u8 89 if ok == 1 { if k > 0 { cnt = cnt + 1 } } 90 } 91 } 92 } 93 } 94 i = le + 1 95 } 96 if cnt == 0 { 97 let d0: *u8 = arena 98 var j: i64 = 0 99 let dflt: *u8 = "nx_math_rung_run" as *u8 100 while dflt[j] != (0 as u8) { d0[j] = dflt[j]; j = j + 1 } 101 d0[j] = 0 as u8 102 cnt = 1 103 } 104 return cnt 105} 106 107// run one durable-lane target, stdout/err -> outpath (append); rc/128+sig 108func adc_run_one(name: *u8, outpath: *u8) -> i64 { 109 let pid: i64 = sys_fork() 110 if pid == 0 { 111 let runner: *u8 = "_offc/nx_sov_build_run.elf" as *u8 112 let argv: *i64 = sys_mmap(32) as *i64 113 argv[0] = runner as i64 114 argv[1] = name as i64 115 argv[2] = 0 116 let envp: *i64 = sys_mmap(16) as *i64 117 envp[0] = 0 118 let ofd: i64 = sys_openat_append(outpath, 0x1a4) 119 if ofd >= 0 { sys_dup3(ofd, 1, 0); sys_dup3(ofd, 2, 0) } 120 sys_execve(runner, argv, envp) 121 sys_exit(127) 122 } 123 let st: *i64 = sys_mmap(16) as *i64 124 sys_wait4(pid, st, 0) 125 let sig: i64 = st[0] & 0x7f 126 if sig != 0 { return 128 + sig } 127 return (st[0] >> 8) & 0xff 128} 129 130// one full beat: every conf target through the lane; TGT rows + BEAT row. 131// returns worst rc (0 = all green). 132func adc_beat(confpath: *u8, outpath: *u8, logpath: *u8) -> i64 { 133 let arena: *u8 = sys_mmap(ADC_MAXTGT * ADC_NAMECAP) 134 let cnt: i64 = adc_targets(confpath, arena) 135 // DAEMON-SIL-R1 self-measurement (you cannot improve what you do not measure): 136 // time the beat + count regressions. improvements_landed/driven are emitted 137 // as 0 here BY DESIGN -- this daemon only RE-PROVES (rank-D); they become real 138 // when DAEMON-SIL-R3 wires the drive loop. Emitting them now makes the honest 139 // D baseline visible + trackable. All additive: existing fields/flow unchanged. 140 let t_start: i64 = sys_now_realtime_sec() 141 let lfd: i64 = sys_openat_append(logpath, 0x1a4) 142 var worst: i64 = 0 143 var regressions: i64 = 0 144 var i: i64 = 0 145 while i < cnt { 146 let nm: *u8 = (arena as i64 + i * ADC_NAMECAP) as *u8 147 let rc: i64 = adc_run_one(nm, outpath) 148 if rc > worst { worst = rc } 149 if rc != 0 { regressions = regressions + 1 } 150 if lfd >= 0 { 151 adc_w(lfd, "AUTORUN-TGT name=" as *u8) 152 adc_w(lfd, nm) 153 adc_w(lfd, " rc=" as *u8) 154 adc_wn(lfd, rc) 155 adc_w(lfd, "\n" as *u8) 156 } 157 i = i + 1 158 } 159 if lfd >= 0 { 160 adc_w(lfd, "AUTORUN-BEAT epoch=" as *u8) 161 adc_wn(lfd, sys_now_realtime_sec()) 162 adc_w(lfd, " rung_rc=" as *u8) 163 adc_wn(lfd, worst) 164 adc_w(lfd, " targets=" as *u8) 165 adc_wn(lfd, cnt) 166 adc_w(lfd, " improvements_landed=0 driven=0 regressions=" as *u8) 167 adc_wn(lfd, regressions) 168 adc_w(lfd, " beat_cost_s=" as *u8) 169 adc_wn(lfd, sys_now_realtime_sec() - t_start) 170 if worst == 0 { adc_w(lfd, " verdict=GREEN\n" as *u8) } else { adc_w(lfd, " verdict=ATTENTION\n" as *u8) } 171 sys_close(lfd) 172 } 173 return worst 174}