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}