code wiki / _hdl_build / nx_libprobe.nx
nx_libprobe.nx source
↩ module page · 78 lines · 4608 B
1// nx_libprobe.nx -- one-shot diagnostic: Connection:close HTTP GET to <ip> <port> <path>, measuring
2// connect / time-to-FIRST-byte / time-to-CLOSE separately. The TTFB-vs-total split is the artifact test:
3// ttfb FAST + total ~20s => the body is fast and the ~20s is a keep-alive idle wait elsewhere (artifact)
4// ttfb ~20s => the server genuinely takes ~20s to produce the body (a real wedge)
5// Drains to CLOSE (never closes early) so it can't SIGPIPE-kill a SIGPIPE-naive daemon. SO_RCVTIMEO backstop.
6// usage: nx_libprobe <a.b.c.d> <port> <path>
7// license_tier: ORIGINAL
8import "nx_sovjson_lib.nx"
9import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
10import "nx_syscalls.nx"
11const K_MAGIC_65536: i64 = 65536
12const K_MAGIC_2000: i64 = 2000
13const K_MAGIC_18000: i64 = 18000
14
15func lp_slen(s: *u8) -> i64 { return sj_vlen(s) }
16func lp_w(s: *u8) -> i64 { return sys_write(1, s, lp_slen(s)) }
17func lp_n(v: i64) -> i64 {
18 let t: *u8 = sys_mmap(24); var m: i64 = v; var k: i64 = 0
19 if m == 0 { t[0] = 48 as u8; k = 1 }
20 while m > 0 { t[k] = (48 + (m - (m/10)*10)) as u8; m = m/10; k = k + 1 }
21 let o: *u8 = sys_mmap(24); var i: i64 = 0; while i < k { o[i] = t[k-1-i]; i = i + 1 }
22 return sys_write(1, o, k)
23}
24func lp_atoi(s: *u8) -> i64 { var v: i64 = 0; var i: i64 = 0; while s[i] != (0 as u8) { let c: i64 = s[i] as i64; if c >= 48 { if c <= 57 { v = v*10 + (c-48) } } i = i + 1 } return v }
25// parse "a.b.c.d" into oct[0..4]
26func lp_parse_ip(s: *u8, oct: *i64) -> i64 {
27 var idx: i64 = 0; var v: i64 = 0; var i: i64 = 0
28 while idx < 4 {
29 let c: i64 = s[i] as i64
30 if c == 46 { oct[idx] = v; idx = idx + 1; v = 0; i = i + 1 }
31 else { if c >= 48 { if c <= 57 { v = v*10 + (c-48); i = i + 1 } else { oct[idx] = v; idx = 4 } } else { oct[idx] = v; idx = 4 } }
32 }
33 oct[3] = v
34 return 0
35}
36func lp_addr(out: *u8, a: i64, b: i64, c: i64, d: i64, port: i64) -> i64 {
37 out[0] = 2 as u8; out[1] = 0 as u8
38 out[2] = ((port >> 8) & 0xff) as u8; out[3] = (port & 0xff) as u8
39 out[4] = a as u8; out[5] = b as u8; out[6] = c as u8; out[7] = d as u8
40 var i: i64 = 8; while i < 16 { out[i] = 0 as u8; i = i + 1 }
41 return 16
42}
43
44func main(argc: i64, argv: *i64) -> i64 {
45 if argc < 3 { lp_w("usage: nx_libprobe <a.b.c.d> <port> <path>\n" as *u8); return 2 }
46 let oct: *i64 = sys_mmap(40) as *i64; lp_parse_ip(argv[1] as *u8, oct)
47 let port: i64 = lp_atoi(argv[2] as *u8)
48 var path: *u8 = "/" as *u8; if argc >= 4 { path = argv[3] as *u8 }
49
50 lp_w("probe " as *u8); lp_w(argv[1] as *u8); lp_w(":" as *u8); lp_n(port); lp_w(path); lp_w(" (Connection: close)\n" as *u8)
51 let t0: i64 = sys_now_ms()
52 let fd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0)
53 if fd < 0 { lp_w(" socket FAIL\n" as *u8); return 1 }
54 sys_set_socket_timeout(fd, 25)
55 let sa: *u8 = sys_mmap(16); lp_addr(sa, oct[0], oct[1], oct[2], oct[3], port)
56 if nx_connect_bounded(fd, sa, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(fd); lp_w(" connect REFUSED/UNREACHABLE (not LAN-reachable here -> needs on-NAS probe)\n" as *u8); return 1 }
57 let tc: i64 = sys_now_ms()
58 // build "GET <path> HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n"
59 let req: *u8 = sys_mmap(512); var p: i64 = 0
60 let g: *u8 = "GET " as *u8; var gi: i64 = 0; while g[gi] != (0 as u8) { req[p]=g[gi]; p=p+1; gi=gi+1 }
61 var pi: i64 = 0; while path[pi] != (0 as u8) { req[p]=path[pi]; p=p+1; pi=pi+1 }
62 let h: *u8 = " HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n" as *u8; var hi: i64 = 0; while h[hi] != (0 as u8) { req[p]=h[hi]; p=p+1; hi=hi+1 }
63 sys_write(fd, req, p)
64 let buf: *u8 = sys_mmap(K_MAGIC_65536)
65 var total: i64 = 0; var ttfb: i64 = 0 - 1; var keep: i64 = 1
66 while keep == 1 {
67 let r: i64 = sys_read(fd, buf, K_MAGIC_65536) // reuse buffer, drain to close (SIGPIPE-safe)
68 if r <= 0 { keep = 0 } else { if ttfb < 0 { ttfb = sys_now_ms() - t0 } total = total + r }
69 }
70 sys_close(fd)
71 let tend: i64 = sys_now_ms() - t0
72 lp_w(" connect=" as *u8); lp_n(tc - t0); lp_w("ms ttfb=" as *u8)
73 if ttfb < 0 { lp_w("(none)" as *u8) } else { lp_n(ttfb) }
74 lp_w("ms total=" as *u8); lp_n(tend); lp_w("ms bytes=" as *u8); lp_n(total); lp_w("\n" as *u8)
75 if ttfb >= 0 { if ttfb < K_MAGIC_2000 { if tend >= K_MAGIC_18000 { lp_w(" => ARTIFACT pattern: body FAST, ~20s is idle-wait (NOT a reader wedge)\n" as *u8) } } }
76 if ttfb >= K_MAGIC_18000 { lp_w(" => REAL WEDGE: server took ~20s to produce the first byte\n" as *u8) }
77 return 0
78}