code wiki / _hdl_build / nx_engineer_verify.nx

nx_engineer_verify.nx source

↩ module page · 141 lines · 7089 B

1// nx_engineer_verify.nx -- the NISHI ENGINEER's one-step batch sovereign VERIFIER / TEST gate. 2// RACI CORRECTION (operator: "the doctor doesnt test the engineer does, the doctor fixes"): TESTING / 3// VERIFICATION is the ENGINEER's responsibility -- this gate was originally MISNAMED "doctor". The 4// DOCTOR's job is to HEAL a failed build (nx_doctor_build::doc_rebuild); the ENGINEER runs the gate. 5// Clean pipeline: Builder AUTHORS -> ENGINEER VERIFIES (this) -> on fail DOCTOR HEALS -> re-verify -> 6// Council ADMITS. ONE invocation builds + runs + reports MANY modules through the pure-sovereign path 7// (nx_cc -> nxasm_x86 -> run, NO gcc/.sh/shell-loop). Each module: compile (recompile-retry), assemble, 8// run under the <10s watchdog, capture the real exit via sys_wait4. Exits 0 iff all passed. Usage: 9// nx_engineer_verify <module1> <module2> ... 10// license_tier: ORIGINAL Reuses the sovereign spine from nx_sov_build_run. 11import "nx_syscalls.nx" 12import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 13const DSV_MAGIC_4096: i64 = 4096 14 15const DSV_MIN_ASM_BYTES: i64 = 128 16const DSV_MAX_RETRIES: i64 = 12 17const DSV_RUN_TIMEOUT_MS: i64 = 8000 // the <10s law: a hung module is KILLED + reported, never waited on 18 19func dsv_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 20// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 21// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 22// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 23// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 24func dsv_putn(v: i64) -> i64 { nxi_out(v); return 0 } 25func dsv_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i } 26 27// fork + stdout/stderr redirect + execve; parent waits; returns child's WEXITSTATUS. 28func dsv_run(path: *u8, argv: *i64, envp: *i64, redir_out: i64, redir_err: i64) -> i64 { 29 let pid: i64 = sys_fork() 30 if pid == 0 { 31 if redir_out >= 0 { sys_dup3(redir_out, 1, 0) } 32 if redir_err >= 0 { sys_dup3(redir_err, 2, 0) } 33 sys_execve(path, argv, envp) 34 sys_exit(127) 35 } 36 let st: *i64 = sys_mmap(16) as *i64 37 sys_wait4(pid, st, 0) 38 return (st[0] >> 8) & 0xff 39} 40 41func dsv_filesize(path: *u8) -> i64 { 42 let fd: i64 = sys_openat_rd(path) 43 if fd < 0 { return 0 } 44 let buf: *u8 = sys_mmap(DSV_MAGIC_4096) 45 var total: i64 = 0 46 var n: i64 = sys_read(fd, buf, DSV_MAGIC_4096) 47 while n > 0 { total = total + n; n = sys_read(fd, buf, DSV_MAGIC_4096) } 48 sys_close(fd) 49 return total 50} 51 52// build ONE module through the sovereign path; return its run-exit, or a negative code on build failure. 53// -3 = compile-fail (empty .s after retries) ; -4 = nxasm-fail (unencoded instruction) 54func dsv_build_run_one(name: *u8, compiler: *u8, asm_tool: *u8, envp: *i64, devnull: i64) -> i64 { 55 let src: *u8 = sys_mmap(512); var o: i64 = 0 56 o = dsv_cat(src, o, "runtime/_hdl_build/" as *u8); o = dsv_cat(src, o, name); o = dsv_cat(src, o, ".nx" as *u8); src[o] = 0 as u8 57 let chk: i64 = sys_openat_rd(src) 58 if chk < 0 { o = 0; o = dsv_cat(src, o, "runtime/" as *u8); o = dsv_cat(src, o, name); o = dsv_cat(src, o, ".nx" as *u8); src[o] = 0 as u8 } 59 if chk >= 0 { sys_close(chk) } 60 let spath: *u8 = sys_mmap(256); o = 0 61 o = dsv_cat(spath, o, "/tmp/" as *u8); o = dsv_cat(spath, o, name); o = dsv_cat(spath, o, ".s" as *u8); spath[o] = 0 as u8 62 let elfpath: *u8 = sys_mmap(256); o = 0 63 o = dsv_cat(elfpath, o, "/tmp/" as *u8); o = dsv_cat(elfpath, o, name); o = dsv_cat(elfpath, o, ".sov.elf" as *u8); elfpath[o] = 0 as u8 64 65 var asmbytes: i64 = 0 66 var tries: i64 = 0 67 while tries < DSV_MAX_RETRIES { 68 let sfd: i64 = sys_openat_wr(spath, 0x1a4) 69 let cc: *i64 = sys_mmap(8*4) as *i64 70 cc[0] = compiler as i64; cc[1] = src as i64; cc[2] = 0 71 dsv_run(compiler, cc, envp, sfd, devnull) 72 sys_close(sfd) 73 asmbytes = dsv_filesize(spath) 74 if asmbytes > DSV_MIN_ASM_BYTES { tries = DSV_MAX_RETRIES } 75 if tries != DSV_MAX_RETRIES { tries = tries + 1 } 76 } 77 if asmbytes <= DSV_MIN_ASM_BYTES { return 0 - 3 } 78 79 let aa: *i64 = sys_mmap(8*4) as *i64 80 aa[0] = asm_tool as i64; aa[1] = spath as i64; aa[2] = elfpath as i64; aa[3] = 0 81 let rc_a: i64 = dsv_run(asm_tool, aa, envp, 0 - 1, devnull) 82 if rc_a != 0 { return 0 - 4 } 83 84 let rr: *i64 = sys_mmap(8*4) as *i64 85 rr[0] = elfpath as i64; rr[1] = 0 86 return dsv_run_watchdog(elfpath, rr, envp, DSV_RUN_TIMEOUT_MS) // stdout passes through; hung = killed 87} 88 89// run under a SOVEREIGN WATCHDOG: a timer child sleeps then kills the test pid. A module that 90// hangs (the 7-minute jam-check spin) is killed at the deadline and reported as -9, instead of 91// the Doctor inheriting the hang. wait4 on both children -- no zombies. 92func dsv_run_watchdog(path: *u8, argv: *i64, envp: *i64, timeout_ms: i64) -> i64 { 93 let pid: i64 = sys_fork() 94 if pid == 0 { 95 sys_execve(path, argv, envp) 96 sys_exit(127) 97 } 98 let wd: i64 = sys_fork() 99 if wd == 0 { 100 sys_sleep_ms(timeout_ms) 101 nx_kill(pid, 9) 102 sys_exit(0) 103 } 104 let st: *i64 = sys_mmap(16) as *i64 105 sys_wait4(pid, st, 0) 106 nx_kill(wd, 9) 107 let st2: *i64 = sys_mmap(16) as *i64 108 sys_wait4(wd, st2, 0) 109 if (st[0] % 256) != 0 { return 0 - 9 } // signal-killed (timeout watchdog or crash) 110 return (st[0] >> 8) & 0xff 111} 112 113func main(argc: i64, argv: *i64) -> i64 { 114 if argc < 2 { dsv_puts("usage: nx_doctor_sov_verify <module1> <module2> ...\n" as *u8); sys_exit(2); return 2 } 115 let compiler: *u8 = "_offc/nx_cc_known_good.elf" as *u8 116 let asm_tool: *u8 = "_offc/nxasm_x86_main.elf" as *u8 117 let envp: *i64 = sys_mmap(8*4) as *i64 118 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64; envp[1] = 0 119 let devnull: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 120 121 dsv_puts("=== NISHI ENGINEER sovereign batch VERIFY/TEST (nx_cc->nxasm_x86->run, no gcc/sh) ===\n" as *u8) 122 var pass: i64 = 0 123 var total: i64 = 0 124 var i: i64 = 1 125 while i < argc { 126 let name: *u8 = argv[i] as *u8 127 let rc: i64 = dsv_build_run_one(name, compiler, asm_tool, envp, devnull) 128 total = total + 1 129 dsv_puts(" ENGINEER module=" as *u8); dsv_puts(name); dsv_puts(" exit=" as *u8); dsv_putn(rc) 130 if rc == 0 { dsv_puts(" PASS\n" as *u8); pass = pass + 1 } 131 if rc == 0 - 3 { dsv_puts(" BUILD-FAIL(compile)\n" as *u8) } 132 if rc == 0 - 4 { dsv_puts(" BUILD-FAIL(nxasm-unencoded)\n" as *u8) } 133 if rc == 0 - 9 { dsv_puts(" TIMEOUT/SIGNAL(watchdog-killed)\n" as *u8) } 134 if rc > 0 { dsv_puts(" FAIL(runtime exit)\n" as *u8) } 135 i = i + 1 136 } 137 dsv_puts("ENGINEER SUMMARY pass=" as *u8); dsv_putn(pass); dsv_puts("/" as *u8); dsv_putn(total); dsv_puts("\n" as *u8) 138 if pass == total { sys_exit(0) } 139 sys_exit(1) 140 return 1 141}