code wiki / _hdl_build / nx_apm_collect.nx
nx_apm_collect.nx
buildroot/runtime/_hdl_build/nx_apm_collect.nx
about
nx_apm_collect.nx -- SOVEREIGN NAS-side INTERNAL-HTTP concurrency collector (the hardware-up APM layer).
WHY (the gap nx_apm_sweep cannot fill): nx_apm_sweep probes nishifamily.com over the FULL public TLS
chain from the workstation. That is the right "what a browser sees" view, but each probe pays an ~800ms
Nishi TLS handshake whose cost is CPU-bound on this 21-workstream-contended workstation, so conc>1
latency is handshake-noise, not server truth -> it cannot cleanly measure a daemon's CONCURRENCY health.
This organ runs ON THE NAS (where the daemons live) and probes their plaintext loopback ports DIRECTLY
over HTTP/1.1 with "Connection: close" -- no TLS, no contention -> the latency it measures IS the daemon.
It fires `conc` SIMULTANEOUS cold probes per target (= conc browser tabs), with a per-probe SO_RCVTIMEO
backstop so a truly-hung socket can never wedge the collector forever (rule #14, graceful degradation).
It distinguishes GREEN / AMBER(stall>2s) / RED(timeout|err) / DOWN(connection refused) -- the last is the
liar-kill: a daemon that is ABSENT must never read as healthy. Targets are DATA-DRIVEN (rule #11) from
knowledge/status/apm_targets.tsv; results append to a durable time-series knowledge/status/apm_collect.tsv.
Single responsibility (rule #9): HTTP latency/concurrency only. Per-daemon CPU/mem is nx_perf_sentinel's
job already; the dashboard rung joins the two time-series. Proven by nx_apm_collect_gate (mock daemons).
usage: nx_apm_collect (reads knowledge/status/apm_targets.tsv)
license_tier: ORIGINAL
dependencies 2 imports · 1 importers
imports: nx_syscalls.nxnx_connect.nx
imported by: nx_apm_collect_gate.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 23 | const AC_MAGIC_65536: i64 = 65536 |
| 24 | const AC_MAGIC_1024: i64 = 1024 |
| 26 | const AC_STALL_MS: i64 = 2000 |
| 27 | const AC_TIMEOUT_S: i64 = 22 // > the measured ~20s :8791 wedge, so we still capture it |
| 28 | const AC_TARGETS: *u8 = "knowledge/status/apm_targets.tsv" |
| 29 | const AC_METRICS: *u8 = "knowledge/status/apm_collect.tsv" |
| 32 | const AC_V_OK: i64 = 0 |
| 33 | const AC_V_SOCK: i64 = 1 |
| 34 | const AC_V_DOWN: i64 = 2 // connect refused -> daemon absent |
| 35 | const AC_V_SEND: i64 = 3 |
| 36 | const AC_V_TIMEOUT: i64 = 4 // no bytes before SO_RCVTIMEO |
| 39 | const AC_R_GREEN: i64 = 0 |
| 40 | const AC_R_AMBER: i64 = 1 |
| 41 | const AC_R_RED: i64 = 2 |
| 42 | const AC_R_DOWN: i64 = 3 |
functions
| 45 | func ac_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 46 | func ac_w(s: *u8) -> i64 { return sys_write(1, s, ac_slen(s)) } |
| 47 | func ac_atoi(s: *u8) -> i64 called by 1: ac_migrate_targets |
| 53 | func ac_n(buf: *u8, off: i64, v: i64) -> i64 |
| 63 | func ac_s(buf: *u8, off: i64, s: *u8) -> i64 { var o: i64 = off; var i: i64 = 0; while s[i] != (0 as u8) { buf[o] = s[i]; o = o + 1; i = i + 1 } return o } |
| 64 | func ac_wn(v: i64) -> i64 { let b: *u8 = sys_mmap(24); let e: i64 = ac_n(b, 0, v); return sys_write(1, b, e) } |
| 65 | func ac_wpad(v: i64, w: i64) -> i64 |
| 70 | func ac_sort(a: *i64, n: i64) -> i64 called by 1: ac_burst |
| 80 | func ac_addr(out: *u8, a: i64, b: i64, c: i64, d: i64, port: i64) -> i64 called by 1: ac_probe |
| 88 | func ac_build_get(req: *u8, path: *u8) -> i64 |
| 95 | func ac_read_small(path: *u8, buf: *u8, cap: i64) -> i64 |
| 107 | func ac_field(buf: *u8, n: i64, pos: i64, out: *u8, outcap: i64, stop: *i64) -> i64 called by 1: ac_migrate_targets |
| 126 | func ac_probe(port: i64, path: *u8, timeout_secs: i64, ip: i64, out: *i64) -> i64 called by 1: ac_burst calls 10: sys_now_mssys_socketsys_set_socket_timeoutsys_mmapac_addrnx_connect_bounded+4 |
| 156 | func ac_line_eq(buf: *u8, start: i64, end: i64, label: *u8) -> i64 called by 1: ac_seg_index |
| 162 | func ac_seg_index(label: *u8) -> i64 |
| 171 | func ac_burst(port: i64, path: *u8, label: *u8, conc: i64, timeout_secs: i64, epoch: i64, ip: i64, out_stats: *i64) -> i64 |
| 254 | func ac_parse_ip(s: *u8) -> i64 |
| 266 | func ac_starts2(buf: *u8, ls: i64, end: i64, key: *u8) -> i64 { var m: i64=0; while key[m]!=(0 as u8) { if ls+m>=end { return 0 } if buf[ls+m]!=key[m] { return 0 } m=m+1 } return m } |
| 267 | func ac_kv_int(buf: *u8, n: i64, key: *u8) -> i64 |
| 274 | func ac_kv_str(buf: *u8, n: i64, key: *u8, out: *u8, outcap: i64) -> i64 |
| 282 | func ac_migrate_targets() -> i64 |
| 317 | func main(argc: i64, argv: *i64) -> i64 |