code wiki / _hdl_build / nx_dup_source_check.nx
nx_dup_source_check.nx
buildroot/runtime/_hdl_build/nx_dup_source_check.nx
about
nx_dup_source_check.nx v3 -- ANTI-CLOBBER cross-tree source-dup detector.
🚨v3 EXISTS BECAUSE v2 WAS AN OOM BOMB THAT TOOK THE NAS DOWN (2026-07-20). Root cause, stated plainly:
v2 widened the scan from 2 hardcoded dirs to ALL discovered source trees (78 pairs) -- a good change -- but
REUSED v1's `exists()` helper UNCHANGED, and that helper did `sys_mmap(4096)` PER CALL and never unmapped.
v1 called it ~15k times (ONE pair) = ~60MB = survivable. v2 called it ~250k times = ~1GB LEAKED PER RUN, on a
cron. Result: NAS userspace starved -- kernel still SYN-ACKs but sshd gives no banner, HTTPS returns status=0,
even DSM stops answering.
★THE LESSON, so it is never repeated: CHANGING A CALLER'S FAN-OUT RE-COSTS EVERY CALLEE. A 2->78 pair change
is a 17x amplifier on an O(files) helper. Reviewing only the code you WROTE is not enough -- you must re-cost
the code you INHERITED AND AMPLIFIED. And note what the v2 selftest could NOT do: it was GREEN 4/4 because
fixture dirs hold 2-3 files, so it tested the ALGORITHM at scale and never the RESOURCE at scale. A scale-law
tooth that asserts only correctness-on-big-input is HALF a tooth; the other half is a RESOURCE bound.
v3 FIX: every path buffer is allocated ONCE PER PAIR (or once per walk) and passed down, never per file.
Allocation profile is now O(pairs + dirs), not O(files), and it is DECLARED in the output envelope so the
property is inspectable rather than assumed. Detection semantics and the emitted " DUP basename: " line are
UNCHANGED (rule 19) -- nx_favela_census counts that exact substring out of knowledge/status/dup_source.log.
expect_exit:0 when clean.
dependencies 2 imports · 0 importers
imports: nx_syscalls.nxnx_itoa_lib.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 23 | const K_MAGIC_131072: i64 = 131072 |
| 24 | const K_MAGIC_4096: i64 = 4096 |
| 28 | const K_CMP_CAP: i64 = 1048576 |
| 35 | const FP_MULT: i64 = 131 |
| 36 | const FP_MASK: i64 = 1099511627775 |
| 37 | const FP_FILECAP: i64 = 1048576 |
| 42 | const FP_PAGE_DEF: i64 = 300 |
| 43 | const FP_PAGE_MAX: i64 = 900 |
| 47 | const FP_STATBUF: i64 = 144 |
| 48 | const FP_MTIME_SLOT: i64 = 11 |
| 49 | const FP_TEST_MTIME: i64 = 1750000000 |
| 227 | const DP_NDLCAP: i64 = 512 |
functions
| 51 | func dp_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } |
| 56 | func dp_wn(v: i64) -> i64 { nxi_out(v); return 0 } |
| 58 | func ends_nx(nm: *u8) -> i64 |
| 65 | func join_path(buf: *u8, dir: *u8, name: *u8) -> i64 |
| 75 | func exists(dir: *u8, name: *u8, scratch: *u8) -> i64 |
| 82 | func is_dot(nm: *u8) -> i64 called by 1: discover |
| 90 | func str_copy(dst: *u8, src: *u8) -> i64 { var i: i64=0; while src[i]!=(0 as u8) { dst[i]=src[i]; i=i+1 } dst[i]=0 as u8; return i } called by 1: discover |
| 92 | func dp_streq(a: *u8, b: *u8) -> i64 |
| 112 | func dp_in_buildroot(dir: *u8) -> i64 called by 1: dp_is_probed |
| 127 | func dp_probe_rank(dir: *u8) -> i64 |
| 141 | func dp_deadwin(ra: i64, ha: i64, rb: i64, hb: i64) -> i64 |
| 147 | func dp_is_probed(dir: *u8) -> i64 |
| 158 | func dp_atoi(s: *u8) -> i64 called by 1: main |
| 169 | func dp_sum(buf: *u8, n: i64) -> i64 |
| 179 | func dp_slurp(path: *u8, buf: *u8, cap: i64) -> i64 |
| 196 | func dp_same(pa: *u8, pb: *u8, ba: *u8, bb: *u8, cap: i64) -> i64 |
| 230 | func dp_find_in(hay: *u8, hn: i64, ndl: *u8, nn: i64) -> i64 called by 1: dp_exports_overlap |
| 252 | func dp_find_at(hay: *u8, hn: i64, ndl: *u8, nn: i64) -> i64 called by 1: dp_sig_mismatch |
| 272 | func dp_arity(buf: *u8, n: i64, start: i64) -> i64 called by 1: dp_sig_mismatch |
| 297 | func dp_exports_overlap(pa: *u8, pb: *u8, ba: *u8, bb: *u8, cap: i64) -> i64 |
| 343 | func dp_sig_mismatch(pa: *u8, pb: *u8, ba: *u8, bb: *u8, cap: i64) -> i64 |
| 385 | func dp_has_main(path: *u8, buf: *u8, cap: i64) -> i64 |
| 416 | func dp_fresh(pa: *u8, pb: *u8, dirA: *u8, dirB: *u8, sb: *u8) -> i64 |
| 434 | func scan2(dirA: *u8, dirB: *u8, verbose: i64, stats: *i64, ca: *u8, cb: *u8, ccap: i64) -> i64 called by 3: scanselftestmain calls 19: sys_openat_rdsys_mmapsys_getdents64dirent_typedirent_nameends_nx+13 |
| 553 | func scan(dirA: *u8, dirB: *u8, verbose: i64) -> i64 |
| 563 | func wfile(path: *u8) -> i64 { let fd: i64=sys_openat_wr(path, 420); if fd>=0 { sys_write(fd, "x" as *u8, 1); sys_close(fd) } return 0 } |
| 566 | func wfile2(path: *u8, s: *u8) -> i64 |
| 573 | func discover(root: *u8, out: *u8, maxd: i64, maxdepth: i64, cap: *i64) -> i64 |
| 628 | func selftest() -> i64 |
| 836 | func dp_mtime(path: *u8, sb: *u8) -> i64 |
| 842 | func dp_fp_count(dir: *u8) -> i64 |
| 863 | func dp_fingerprint(dir: *u8, offset: i64, limit: i64) -> i64 |
| 942 | func main(argc: i64, argv: *i64) -> i64 |