nx_race_telemetry.nx source
↩ module page · 775 lines · 30302 B
1// nx_race_telemetry.nx -- SEED: substrate-native lap recorder.
2//
3// Replaces the bash bootstrap at bench/race_telemetry.sh for the hot
4// path (lap recording). The bash script also handles
5// patch/perspective/competition/tail/stats/clear -- those remain in
6// bash until substrate-native CLI dispatch + tail/stats land.
7//
8// Per user directive 2026-05-15:
9// "if we need to build our own shell and tools from the bits up
10// lets do it"
11// "i want nishi stack to do the work with you just initially
12// mapping what needs to be done till it can be self sufficient"
13//
14// SEED scope (one responsibility, per cardinal 9):
15// 1. Format one lap row matching the existing schema
16// 2. Append to .race_telemetry.tsv via sys_openat_append + sys_write
17// 3. Return bytes-written or -errno (no exceptions, no NULL)
18//
19// Existing bash schema (column-tab-separated):
20// timestamp event_type bench_name lap_n duration_ms verdict
21// thermal_c notes
22//
23// Substrate seed emits the same row layout but uses Unix-milliseconds
24// for timestamp (sortable, monotonic in single-run; bash version uses
25// ISO 8601). race_analysis.sh accepts either since it parses by
26// column index, not by timestamp content.
27//
28// Sealed-enum verdict codes (caller passes one of these as i64):
29// NX_RT_WIN = 0
30// NX_RT_TIE = 1
31// NX_RT_LOSE = 2
32// NX_RT_RECORD_ONLY = 3
33// NX_RT_UNDECIDED = 4
34// Out-of-range verdict falls back to RECORD_ONLY (defensive at
35// boundary, per cardinal 12).
36//
37// license_tier: ORIGINAL
38// genealogy_id: substrate-native -- no external derivation
39// lineage_id: nx_race_telemetry_seed_q10
40
41// nx_safety_envelope: (schema: nishi-library/seeds/safety-critical-standards.toml)
42// intended_use: "Racing-crew telemetry recorder -- lap / patch /
43// perspective / corner / competition event
44// capture + tail / clear / stats read-side.
45// Substrate-native replacement for bash
46// race_telemetry.sh."
47// sil_target: SIL1 (observability primitive; failure mode
48// is missing evidence, not direct injury;
49// BUT the FAILURE/LATENT-FAULT axis
50// wired through here IS load-bearing
51// for higher-tier safety claims)
52// asil_target: QM
53// dal_target: NONE
54// iec_62304_class: NONE
55// evidence: [no_floating_point, sealed_enum_complete_NX_RT,
56// graceful_degrade_on_io_failure_cardinal_14,
57// append_only_no_destructive_writes_cardinal_13,
58// defensive_truncation_at_every_byte_copy,
59// schema_match_with_bash_race_telemetry_sh]
60// hazard_register: [bug-tape-telemetry-loss-breaks-incident-replay,
61// bug-tape-bench-name-injection-in-log,
62// bug-tape-clock-skew-misorders-events]
63// residual_risk: "Timestamp uses sys_now_ms; clock jumps
64// backward (NTP step) can misorder events.
65// race_analysis.sh uses column-index parsing
66// so this affects ORDERING analysis only, not
67// per-event verdicts. Bench-name truncation
68// at 64 bytes is documented; out-of-bound names
69// truncate silently (cardinal 12 boundary)."
70// verdict: NOT_YET_EVALUATED
71
72import "nx_syscalls.nx"
73import "nx_fs.nx"
74
75const NX_RT_WIN: i64 = 0
76const NX_RT_TIE: i64 = 1
77const NX_RT_LOSE: i64 = 2
78const NX_RT_RECORD_ONLY: i64 = 3
79const NX_RT_UNDECIDED: i64 = 4
80
81// ----- byte helpers ------------------------------------------------
82
83func _copy_bytes(src: *u8, dst: *u8, n: i64) -> i64 {
84 var i: i64 = 0
85 while i < n {
86 dst[i] = src[i]
87 i = i + 1
88 }
89 return n
90}
91
92// Format unsigned i64 into `out` as ascii decimal. Returns byte
93// count. Caller guarantees out has >= 20 bytes (max u64 is 20
94// digits). Defensive: 0 emits "0" (one byte), not empty.
95func _u64_to_ascii(n: i64, out: *u8) -> i64 {
96 if n == 0 {
97 out[0] = 48
98 return 1
99 }
100 let buf: *u8 = sys_mmap(32)
101 var k: i64 = 0
102 var x: i64 = n
103 while x > 0 {
104 buf[k] = (x % 10 + 48) as u8
105 x = x / 10
106 k = k + 1
107 }
108 var i: i64 = 0
109 while i < k {
110 out[i] = buf[k - 1 - i]
111 i = i + 1
112 }
113 return k
114}
115
116// Signed wrapper: emits leading '-' for negatives, then digits.
117func _i64_to_ascii(n: i64, out: *u8) -> i64 {
118 if n < 0 {
119 out[0] = 45 // '-'
120 let nbytes: i64 = _u64_to_ascii(0 - n, (out as i64 + 1) as *u8)
121 return nbytes + 1
122 }
123 return _u64_to_ascii(n, out)
124}
125
126// Write a literal byte and advance.
127func _emit_byte(out: *u8, off: i64, b: i64) -> i64 {
128 out[off] = b as u8
129 return off + 1
130}
131
132// Emit verdict mnemonic into `out`, returns byte count. Defensive:
133// unknown code falls back to RECORD_ONLY (per boundary rule).
134func _verdict_emit(code: i64, out: *u8) -> i64 {
135 if code == NX_RT_WIN {
136 out[0]=87; out[1]=73; out[2]=78
137 return 3
138 }
139 if code == NX_RT_TIE {
140 out[0]=84; out[1]=73; out[2]=69
141 return 3
142 }
143 if code == NX_RT_LOSE {
144 out[0]=76; out[1]=79; out[2]=83; out[3]=69
145 return 4
146 }
147 if code == NX_RT_UNDECIDED {
148 out[0]=85; out[1]=78; out[2]=68; out[3]=69; out[4]=67
149 out[5]=73; out[6]=68; out[7]=69; out[8]=68
150 return 9
151 }
152 // Default + NX_RT_RECORD_ONLY both emit RECORD_ONLY.
153 out[0]=82; out[1]=69; out[2]=67; out[3]=79; out[4]=82; out[5]=68
154 out[6]=95; out[7]=79; out[8]=78; out[9]=76; out[10]=89
155 return 11
156}
157
158// ----- row primitives ----------------------------------------------
159//
160// Three event variants share the same prefix (unix-ms timestamp
161// + tab + event_type + tab) and the same tail (sys_openat_append +
162// sys_write + sys_close). Per cardinal DRY-through-shared-libraries
163// the rule-of-three triggers extraction.
164
165func _emit_prelude(buf: *u8, event_str: *u8, event_len: i64) -> i64 {
166 var off: i64 = 0
167 let now: i64 = sys_now_ms()
168 off = off + _u64_to_ascii(now, (buf as i64 + off) as *u8)
169 off = _emit_byte(buf, off, 9)
170 _copy_bytes(event_str, (buf as i64 + off) as *u8, event_len)
171 off = off + event_len
172 off = _emit_byte(buf, off, 9)
173 return off
174}
175
176// Append already-formatted row bytes to the log. Returns bytes
177// written or -errno. Per cardinal 14: failed telemetry capture
178// must not crash the caller.
179func _append_row(path: *u8, buf: *u8, off: i64) -> i64 {
180 let fd: i64 = sys_openat_append(path, 420)
181 if fd < 0 { return fd }
182 let n: i64 = sys_write(fd, buf, off)
183 sys_close(fd)
184 return n
185}
186
187// Emit a single "-" placeholder followed by tab. Patch + perspective
188// rows leave structural columns blank with a dash, matching the bash
189// schema so race_analysis.sh column-indexed parsing is one parser.
190func _emit_dash_tab(buf: *u8, off: i64) -> i64 {
191 buf[off] = 45 // '-'
192 buf[off + 1] = 9 // tab
193 return off + 2
194}
195
196// ----- public API: append one lap row ------------------------------
197
198// nx_race_telemetry_lap
199// path NUL-terminated TSV log path (typically
200// ".race_telemetry.tsv\0").
201// bench NOT NUL-terminated; caller passes bench_len.
202// bench_len byte length of bench name.
203// lap_n lap index (1-origin matches bash convention).
204// duration_ms elapsed wall-clock for the lap.
205// verdict NX_RT_* code. Out-of-range falls back to
206// RECORD_ONLY.
207// thermal_c10 thermal reading * 10 (Q1) or -1 if unknown.
208// Emitted as decimal-with-one-fractional or "?"
209// to match the bash schema.
210//
211// Returns:
212// >= 0 : bytes written to log on success.
213// < 0 : -errno from sys_openat_append / sys_write. Never
214// crashes the caller -- failed telemetry capture must
215// not break a bench (cardinal 14: graceful degradation).
216func nx_race_telemetry_lap(path: *u8, bench: *u8, bench_len: i64, lap_n: i64, duration_ms: i64, verdict: i64, thermal_c10: i64) -> i64 {
217 let buf: *u8 = sys_mmap(256)
218 let evt: *u8 = sys_mmap(8)
219 evt[0]=108; evt[1]=97; evt[2]=112 // "lap"
220 var off: i64 = _emit_prelude(buf, evt, 3)
221
222 var to_copy: i64 = bench_len
223 if to_copy > 64 { to_copy = 64 }
224 _copy_bytes(bench, (buf as i64 + off) as *u8, to_copy)
225 off = off + to_copy
226 off = _emit_byte(buf, off, 9)
227
228 off = off + _i64_to_ascii(lap_n, (buf as i64 + off) as *u8)
229 off = _emit_byte(buf, off, 9)
230
231 off = off + _i64_to_ascii(duration_ms, (buf as i64 + off) as *u8)
232 off = _emit_byte(buf, off, 9)
233
234 off = off + _verdict_emit(verdict, (buf as i64 + off) as *u8)
235 off = _emit_byte(buf, off, 9)
236
237 if thermal_c10 < 0 {
238 buf[off] = 63 // '?'
239 off = off + 1
240 } else {
241 let whole: i64 = thermal_c10 / 10
242 let frac: i64 = thermal_c10 % 10
243 off = off + _u64_to_ascii(whole, (buf as i64 + off) as *u8)
244 off = _emit_byte(buf, off, 46) // '.'
245 off = _emit_byte(buf, off, frac + 48)
246 }
247 off = _emit_byte(buf, off, 9)
248 off = _emit_byte(buf, off, 10)
249 return _append_row(path, buf, off)
250}
251
252// nx_race_telemetry_patch
253// Records a fix applied to a bench between runs. Matches the
254// bash schema: timestamp\tpatch\tbench\t-\t-\t-\t-\tcommit=<sha> <notes>\n
255//
256// bench bench name the patch targets
257// commit git commit SHA bytes (caller passes commit_len)
258// notes free-text trailing notes (caller passes notes_len);
259// pass 0/0 for empty
260//
261// Returns bytes written (>=0) or -errno.
262func nx_race_telemetry_patch(path: *u8, bench: *u8, bench_len: i64, commit: *u8, commit_len: i64, notes: *u8, notes_len: i64) -> i64 {
263 let buf: *u8 = sys_mmap(512)
264 let evt: *u8 = sys_mmap(8)
265 evt[0]=112; evt[1]=97; evt[2]=116; evt[3]=99; evt[4]=104 // "patch"
266 var off: i64 = _emit_prelude(buf, evt, 5)
267
268 var to_copy: i64 = bench_len
269 if to_copy > 64 { to_copy = 64 }
270 _copy_bytes(bench, (buf as i64 + off) as *u8, to_copy)
271 off = off + to_copy
272 off = _emit_byte(buf, off, 9)
273
274 off = _emit_dash_tab(buf, off) // lap_n
275 off = _emit_dash_tab(buf, off) // duration_ms
276 off = _emit_dash_tab(buf, off) // verdict
277 off = _emit_dash_tab(buf, off) // thermal_c
278
279 // notes column: "commit=<sha> <notes>"
280 buf[off]=99; buf[off+1]=111; buf[off+2]=109; buf[off+3]=109
281 buf[off+4]=105; buf[off+5]=116; buf[off+6]=61 // "commit="
282 off = off + 7
283 var c_copy: i64 = commit_len
284 if c_copy > 64 { c_copy = 64 }
285 _copy_bytes(commit, (buf as i64 + off) as *u8, c_copy)
286 off = off + c_copy
287 if notes_len > 0 {
288 off = _emit_byte(buf, off, 32) // space
289 var n_copy: i64 = notes_len
290 if n_copy > 128 { n_copy = 128 }
291 _copy_bytes(notes, (buf as i64 + off) as *u8, n_copy)
292 off = off + n_copy
293 }
294 off = _emit_byte(buf, off, 10)
295 return _append_row(path, buf, off)
296}
297
298// nx_race_telemetry_corner
299// Records a profiled code region -- function, loop, syscall etc.
300// Distinct from lap because durations are typically in
301// NANOSECONDS (corners are sub-millisecond) and don't carry
302// verdicts. Matches bash schema:
303// timestamp\tcorner\tbench\t-\t<duration_ns>\t-\t-\tregion=<r> <notes>\n
304//
305// region name of the profiled region
306// notes free-text (pass 0/0 for empty)
307//
308// Returns bytes written (>=0) or -errno.
309func nx_race_telemetry_corner(path: *u8, bench: *u8, bench_len: i64, region: *u8, region_len: i64, duration_ns: i64, notes: *u8, notes_len: i64) -> i64 {
310 let buf: *u8 = sys_mmap(512)
311 let evt: *u8 = sys_mmap(8)
312 evt[0]=99; evt[1]=111; evt[2]=114; evt[3]=110; evt[4]=101; evt[5]=114 // "corner"
313 var off: i64 = _emit_prelude(buf, evt, 6)
314
315 var to_copy: i64 = bench_len
316 if to_copy > 64 { to_copy = 64 }
317 _copy_bytes(bench, (buf as i64 + off) as *u8, to_copy)
318 off = off + to_copy
319 off = _emit_byte(buf, off, 9)
320
321 off = _emit_dash_tab(buf, off) // lap_n
322
323 off = off + _i64_to_ascii(duration_ns, (buf as i64 + off) as *u8)
324 off = _emit_byte(buf, off, 9)
325
326 off = _emit_dash_tab(buf, off) // verdict
327 off = _emit_dash_tab(buf, off) // thermal_c
328
329 // notes: region=<r> <notes>
330 buf[off]=114; buf[off+1]=101; buf[off+2]=103; buf[off+3]=105
331 buf[off+4]=111; buf[off+5]=110; buf[off+6]=61 // "region="
332 off = off + 7
333 var r_copy: i64 = region_len
334 if r_copy > 64 { r_copy = 64 }
335 _copy_bytes(region, (buf as i64 + off) as *u8, r_copy)
336 off = off + r_copy
337 if notes_len > 0 {
338 off = _emit_byte(buf, off, 32)
339 var n_copy: i64 = notes_len
340 if n_copy > 128 { n_copy = 128 }
341 _copy_bytes(notes, (buf as i64 + off) as *u8, n_copy)
342 off = off + n_copy
343 }
344 off = _emit_byte(buf, off, 10)
345 return _append_row(path, buf, off)
346}
347
348// nx_race_telemetry_competition
349// Records a head-to-head result against an external benchmark
350// (Vampire CASC, Codeforces, Kaggle, etc.). Matches bash schema:
351// timestamp\tcompetition\t<arena>/<problem>\t-\t<result_ms>\t<verdict>\t-\t<notes>\n
352//
353// arena vampire-casc | codeforces | kaggle | acm-icpc | ...
354// problem contest problem id
355// result_ms time-to-solve in ms (-1 for N/A / offline class)
356// verdict NX_RT_* code; out-of-range falls back to RECORD_ONLY
357//
358// Returns bytes written (>=0) or -errno.
359func nx_race_telemetry_competition(path: *u8, arena: *u8, arena_len: i64, problem: *u8, problem_len: i64, result_ms: i64, verdict: i64, notes: *u8, notes_len: i64) -> i64 {
360 let buf: *u8 = sys_mmap(512)
361 let evt: *u8 = sys_mmap(16)
362 evt[0]=99; evt[1]=111; evt[2]=109; evt[3]=112; evt[4]=101 // "compe"
363 evt[5]=116; evt[6]=105; evt[7]=116; evt[8]=105; evt[9]=111 // "titio"
364 evt[10]=110 // "n"
365 var off: i64 = _emit_prelude(buf, evt, 11)
366
367 // bench_name column: <arena>/<problem>
368 var a_copy: i64 = arena_len
369 if a_copy > 32 { a_copy = 32 }
370 _copy_bytes(arena, (buf as i64 + off) as *u8, a_copy)
371 off = off + a_copy
372 off = _emit_byte(buf, off, 47) // '/'
373 var p_copy: i64 = problem_len
374 if p_copy > 32 { p_copy = 32 }
375 _copy_bytes(problem, (buf as i64 + off) as *u8, p_copy)
376 off = off + p_copy
377 off = _emit_byte(buf, off, 9)
378
379 off = _emit_dash_tab(buf, off) // lap_n
380
381 if result_ms < 0 {
382 buf[off] = 78; buf[off+1] = 47; buf[off+2] = 65 // "N/A"
383 off = off + 3
384 } else {
385 off = off + _u64_to_ascii(result_ms, (buf as i64 + off) as *u8)
386 }
387 off = _emit_byte(buf, off, 9)
388
389 off = off + _verdict_emit(verdict, (buf as i64 + off) as *u8)
390 off = _emit_byte(buf, off, 9)
391
392 off = _emit_dash_tab(buf, off) // thermal_c
393
394 if notes_len > 0 {
395 var n_copy: i64 = notes_len
396 if n_copy > 128 { n_copy = 128 }
397 _copy_bytes(notes, (buf as i64 + off) as *u8, n_copy)
398 off = off + n_copy
399 }
400 off = _emit_byte(buf, off, 10)
401 return _append_row(path, buf, off)
402}
403
404// nx_race_telemetry_perspective
405// Records a substrate-discovered observation queued for future
406// analysis. Matches the bash schema:
407// timestamp\tperspective\t-\t-\t-\t-\t-\tcat=<cat> obs="<obs>"\n
408//
409// category short tag (e.g. "regalloc", "spill_count")
410// observation free-text observation
411//
412// Returns bytes written (>=0) or -errno.
413func nx_race_telemetry_perspective(path: *u8, category: *u8, cat_len: i64, observation: *u8, obs_len: i64) -> i64 {
414 let buf: *u8 = sys_mmap(512)
415 let evt: *u8 = sys_mmap(16)
416 evt[0]=112; evt[1]=101; evt[2]=114; evt[3]=115 // "pers"
417 evt[4]=112; evt[5]=101; evt[6]=99; evt[7]=116; evt[8]=105 // "pect"+"i"
418 evt[9]=118; evt[10]=101 // "ve"
419 var off: i64 = _emit_prelude(buf, evt, 11)
420
421 off = _emit_dash_tab(buf, off) // bench_name
422 off = _emit_dash_tab(buf, off) // lap_n
423 off = _emit_dash_tab(buf, off) // duration_ms
424 off = _emit_dash_tab(buf, off) // verdict
425 off = _emit_dash_tab(buf, off) // thermal_c
426
427 // notes: cat=<cat> obs="<obs>"
428 buf[off]=99; buf[off+1]=97; buf[off+2]=116; buf[off+3]=61 // "cat="
429 off = off + 4
430 var c_copy: i64 = cat_len
431 if c_copy > 32 { c_copy = 32 }
432 _copy_bytes(category, (buf as i64 + off) as *u8, c_copy)
433 off = off + c_copy
434 off = _emit_byte(buf, off, 32) // space
435 buf[off]=111; buf[off+1]=98; buf[off+2]=115; buf[off+3]=61 // "obs="
436 buf[off+4]=34 // '"'
437 off = off + 5
438 var o_copy: i64 = obs_len
439 if o_copy > 256 { o_copy = 256 }
440 _copy_bytes(observation, (buf as i64 + off) as *u8, o_copy)
441 off = off + o_copy
442 off = _emit_byte(buf, off, 34) // '"'
443 off = _emit_byte(buf, off, 10)
444 return _append_row(path, buf, off)
445}
446
447// ----- read-side: clear + tail -------------------------------------
448//
449// Closes the last 20% of the bash race_telemetry.sh dispatcher.
450// `tail` -> read the last N lines of the log into a caller buffer.
451// `clear` -> unlink the log; idempotent (NotFound is treated as
452// success, matching bash `rm -f` semantics).
453//
454// Both compose against nx_fs (fs_tail_raw, fs_unlink_raw) so the
455// substrate-native versions stay i64-return like the rest of this
456// file's public API.
457
458// nx_race_telemetry_tail
459// path NUL-terminated log path
460// n_lines how many trailing lines to read (cap at file size)
461// out_buf caller-allocated buffer for the suffix
462// out_cap buffer capacity in bytes; result is truncated to fit
463//
464// Returns bytes copied (>=0) or -errno.
465func nx_race_telemetry_tail(path: *u8, n_lines: i64, out_buf: *u8, out_cap: i64) -> i64 {
466 return fs_tail_raw(path, n_lines, out_buf, out_cap)
467}
468
469// nx_race_telemetry_clear
470// Idempotent unlink. NotFound (-ENOENT = -2) is treated as
471// success (returns 0); other errors flow through as -errno.
472// Matches bash `rm -f` semantics so the substrate substitution
473// is drop-in.
474//
475// Returns 0 on success (file removed or already absent) or -errno
476// for any other failure.
477func nx_race_telemetry_clear(path: *u8) -> i64 {
478 let rc: i64 = fs_unlink_raw(path)
479 if rc == 0 { return 0 }
480 if rc == 0 - 2 { return 0 }
481 return rc
482}
483
484// Match a substring at buf[off..off+target_len] against target.
485// Returns 1 on full match, 0 otherwise. Caller must guarantee
486// off + target_len <= buf_len.
487func _matches_at(buf: *u8, off: i64, target: *u8, target_len: i64) -> i64 {
488 var i: i64 = 0
489 while i < target_len {
490 if buf[off + i] != target[i] { return 0 }
491 i = i + 1
492 }
493 return 1
494}
495
496// nx_race_telemetry_stats
497// Walks the log line-by-line, counts entries by event_type.
498// Caller passes a *i64 array of length 6:
499// counts[0] = total non-comment rows
500// counts[1] = lap rows
501// counts[2] = patch rows
502// counts[3] = corner rows
503// counts[4] = perspective rows
504// counts[5] = competition rows
505//
506// Returns 0 on success, -errno on failure to open/read the file.
507//
508// Skips lines starting with '#' (the header line in bash logs).
509// Unknown event_type values increment counts[0] but no per-kind
510// bucket -- defensive at boundary (future bash extensions don't
511// poison the counters).
512func nx_race_telemetry_stats(path: *u8, counts: *i64) -> i64 {
513 let len_box: *i64 = sys_mmap(16) as *i64
514 *len_box = 0
515 let buf: *u8 = fs_read_all(path, len_box)
516 if buf == (0 as *u8) {
517 let fd: i64 = fs_open_rd_raw(path)
518 if fd >= 0 { sys_close(fd) }
519 return fd
520 }
521 let total: i64 = *len_box
522
523 var k: i64 = 0
524 while k < 6 { counts[k] = 0; k = k + 1 }
525
526 let lap_str: *u8 = "lap" as *u8
527 let patch_str: *u8 = "patch" as *u8
528 let corner_str: *u8 = "corner" as *u8
529 let perspective_str: *u8 = "perspective" as *u8
530 let competition_str: *u8 = "competition" as *u8
531
532 var line_start: i64 = 0
533 while line_start < total {
534 if buf[line_start] == 35 {
535 var s: i64 = line_start
536 while s < total {
537 if buf[s] == 10 { break }
538 s = s + 1
539 }
540 line_start = s + 1
541 continue
542 }
543
544 var p: i64 = line_start
545 while p < total {
546 if buf[p] == 9 { break }
547 if buf[p] == 10 { break }
548 p = p + 1
549 }
550 if p >= total { break }
551 if buf[p] != 9 {
552 line_start = p + 1
553 continue
554 }
555
556 let col2_start: i64 = p + 1
557 var q: i64 = col2_start
558 while q < total {
559 if buf[q] == 9 { break }
560 if buf[q] == 10 { break }
561 q = q + 1
562 }
563 let col2_len: i64 = q - col2_start
564
565 var matched: i64 = 0
566 if col2_len == 3 {
567 if _matches_at(buf, col2_start, lap_str, 3) {
568 counts[1] = counts[1] + 1
569 matched = 1
570 }
571 }
572 if col2_len == 5 {
573 if _matches_at(buf, col2_start, patch_str, 5) {
574 counts[2] = counts[2] + 1
575 matched = 1
576 }
577 }
578 if col2_len == 6 {
579 if _matches_at(buf, col2_start, corner_str, 6) {
580 counts[3] = counts[3] + 1
581 matched = 1
582 }
583 }
584 if col2_len == 11 {
585 if _matches_at(buf, col2_start, perspective_str, 11) {
586 counts[4] = counts[4] + 1
587 matched = 1
588 }
589 if _matches_at(buf, col2_start, competition_str, 11) {
590 counts[5] = counts[5] + 1
591 matched = 1
592 }
593 }
594 counts[0] = counts[0] + 1
595
596 var nl: i64 = q
597 while nl < total {
598 if buf[nl] == 10 { break }
599 nl = nl + 1
600 }
601 line_start = nl + 1
602 }
603 return 0
604}
605
606// ----- self-test ---------------------------------------------------
607
608func main() -> i64 {
609 // T1: byte helpers cover boundary cases.
610 let tmp: *u8 = sys_mmap(32)
611 var k: i64 = _u64_to_ascii(0, tmp)
612 if k != 1 { return __syscall(93, 1, 0, 0, 0, 0, 0) }
613 if tmp[0] != 48 { return __syscall(93, 2, 0, 0, 0, 0, 0) }
614
615 k = _u64_to_ascii(12345, tmp)
616 if k != 5 { return __syscall(93, 3, 0, 0, 0, 0, 0) }
617 if tmp[0] != 49 { return __syscall(93, 4, 0, 0, 0, 0, 0) }
618 if tmp[4] != 53 { return __syscall(93, 5, 0, 0, 0, 0, 0) }
619
620 k = _i64_to_ascii(0 - 7, tmp)
621 if k != 2 { return __syscall(93, 6, 0, 0, 0, 0, 0) }
622 if tmp[0] != 45 { return __syscall(93, 7, 0, 0, 0, 0, 0) }
623 if tmp[1] != 55 { return __syscall(93, 8, 0, 0, 0, 0, 0) }
624
625 // T2: verdict mnemonic length per code.
626 k = _verdict_emit(NX_RT_WIN, tmp); if k != 3 { return __syscall(93, 10, 0, 0, 0, 0, 0) }
627 k = _verdict_emit(NX_RT_TIE, tmp); if k != 3 { return __syscall(93, 11, 0, 0, 0, 0, 0) }
628 k = _verdict_emit(NX_RT_LOSE, tmp); if k != 4 { return __syscall(93, 12, 0, 0, 0, 0, 0) }
629 k = _verdict_emit(NX_RT_UNDECIDED, tmp); if k != 9 { return __syscall(93, 13, 0, 0, 0, 0, 0) }
630 k = _verdict_emit(NX_RT_RECORD_ONLY, tmp); if k != 11 { return __syscall(93, 14, 0, 0, 0, 0, 0) }
631 // Out-of-range falls back to RECORD_ONLY (11 bytes).
632 k = _verdict_emit(99, tmp); if k != 11 { return __syscall(93, 15, 0, 0, 0, 0, 0) }
633
634 // T2.5: start with a fresh log so the stats assertion is
635 // deterministic across re-runs (the file is append-mode).
636 let path_pre: *u8 = sys_mmap(64)
637 path_pre[0]=46; path_pre[1]=116; path_pre[2]=109; path_pre[3]=112; path_pre[4]=45
638 path_pre[5]=114; path_pre[6]=97; path_pre[7]=99; path_pre[8]=101; path_pre[9]=45
639 path_pre[10]=116;path_pre[11]=101;path_pre[12]=108;path_pre[13]=101;path_pre[14]=109
640 path_pre[15]=101;path_pre[16]=116;path_pre[17]=114;path_pre[18]=121;path_pre[19]=45
641 path_pre[20]=115;path_pre[21]=101;path_pre[22]=101;path_pre[23]=100;path_pre[24]=46
642 path_pre[25]=116;path_pre[26]=115;path_pre[27]=118;path_pre[28]=0
643 nx_race_telemetry_clear(path_pre)
644
645 // T3: full lap append. Path is in-memory NUL-terminated.
646 let path: *u8 = sys_mmap(64)
647 // ".tmp-race-telemetry-seed.tsv\0"
648 path[0]=46; path[1]=116; path[2]=109; path[3]=112; path[4]=45
649 path[5]=114; path[6]=97; path[7]=99; path[8]=101; path[9]=45
650 path[10]=116;path[11]=101;path[12]=108;path[13]=101;path[14]=109
651 path[15]=101;path[16]=116;path[17]=114;path[18]=121;path[19]=45
652 path[20]=115;path[21]=101;path[22]=101;path[23]=100;path[24]=46
653 path[25]=116;path[26]=115;path[27]=118;path[28]=0
654
655 let bench: *u8 = sys_mmap(32)
656 // "smoke_bench"
657 bench[0]=115;bench[1]=109;bench[2]=111;bench[3]=107;bench[4]=101
658 bench[5]=95; bench[6]=98; bench[7]=101;bench[8]=110;bench[9]=99
659 bench[10]=104
660
661 let n: i64 = nx_race_telemetry_lap(path, bench, 11, 1, 250, NX_RT_RECORD_ONLY, 425)
662 if n <= 0 { return __syscall(93, 20, 0, 0, 0, 0, 0) }
663
664 // T4: -1 thermal emits "?". Second call must also succeed.
665 let n2: i64 = nx_race_telemetry_lap(path, bench, 11, 2, 300, NX_RT_WIN, 0 - 1)
666 if n2 <= 0 { return __syscall(93, 21, 0, 0, 0, 0, 0) }
667
668 // T5: Out-of-range verdict falls back without erroring.
669 let n3: i64 = nx_race_telemetry_lap(path, bench, 11, 3, 280, 99, 0 - 1)
670 if n3 <= 0 { return __syscall(93, 22, 0, 0, 0, 0, 0) }
671
672 // T6: patch row with commit + notes.
673 let sha: *u8 = sys_mmap(16)
674 // "8302d014"
675 sha[0]=56; sha[1]=51; sha[2]=48; sha[3]=50
676 sha[4]=100; sha[5]=48; sha[6]=49; sha[7]=52
677
678 let notes: *u8 = sys_mmap(32)
679 // "evict policy"
680 notes[0]=101; notes[1]=118; notes[2]=105; notes[3]=99; notes[4]=116
681 notes[5]=32 // space
682 notes[6]=112; notes[7]=111; notes[8]=108; notes[9]=105
683 notes[10]=99; notes[11]=121
684
685 let n4: i64 = nx_race_telemetry_patch(path, bench, 11, sha, 8, notes, 12)
686 if n4 <= 0 { return __syscall(93, 30, 0, 0, 0, 0, 0) }
687
688 // T7: patch with empty notes (notes_len == 0).
689 let n5: i64 = nx_race_telemetry_patch(path, bench, 11, sha, 8, notes, 0)
690 if n5 <= 0 { return __syscall(93, 31, 0, 0, 0, 0, 0) }
691
692 // T8: perspective row with category + observation.
693 let cat: *u8 = sys_mmap(16)
694 // "regalloc"
695 cat[0]=114; cat[1]=101; cat[2]=103; cat[3]=97
696 cat[4]=108; cat[5]=108; cat[6]=111; cat[7]=99
697
698 let obs: *u8 = sys_mmap(64)
699 // "fpr eviction fires"
700 obs[0]=102; obs[1]=112; obs[2]=114; obs[3]=32
701 obs[4]=101; obs[5]=118; obs[6]=105; obs[7]=99; obs[8]=116; obs[9]=105; obs[10]=111; obs[11]=110
702 obs[12]=32
703 obs[13]=102; obs[14]=105; obs[15]=114; obs[16]=101; obs[17]=115
704
705 let n6: i64 = nx_race_telemetry_perspective(path, cat, 8, obs, 18)
706 if n6 <= 0 { return __syscall(93, 32, 0, 0, 0, 0, 0) }
707
708 // T9: corner row (function-level perf attribution, ns duration).
709 let region: *u8 = sys_mmap(32)
710 // "spill_furthest_active"
711 region[0]=115; region[1]=112; region[2]=105; region[3]=108; region[4]=108
712 region[5]=95; region[6]=102; region[7]=117; region[8]=114; region[9]=116
713 region[10]=104;region[11]=101;region[12]=115;region[13]=116
714 region[14]=95;
715 region[15]=97; region[16]=99; region[17]=116; region[18]=105; region[19]=118; region[20]=101
716
717 let n7: i64 = nx_race_telemetry_corner(path, bench, 11, region, 21, 12345, obs, 18)
718 if n7 <= 0 { return __syscall(93, 33, 0, 0, 0, 0, 0) }
719
720 // T10: competition row with measurable result.
721 let arena: *u8 = sys_mmap(32)
722 // "vampire-casc"
723 arena[0]=118; arena[1]=97; arena[2]=109; arena[3]=112; arena[4]=105
724 arena[5]=114; arena[6]=101; arena[7]=45; arena[8]=99; arena[9]=97
725 arena[10]=115;arena[11]=99
726
727 let prob: *u8 = sys_mmap(16)
728 // "TPT001-1"
729 prob[0]=84; prob[1]=80; prob[2]=84; prob[3]=48
730 prob[4]=48; prob[5]=49; prob[6]=45; prob[7]=49
731
732 let n8: i64 = nx_race_telemetry_competition(path, arena, 12, prob, 8, 850, NX_RT_TIE, notes, 12)
733 if n8 <= 0 { return __syscall(93, 34, 0, 0, 0, 0, 0) }
734
735 // T11: competition with result=-1 (offline class -> "N/A").
736 let n9: i64 = nx_race_telemetry_competition(path, arena, 12, prob, 8, 0 - 1, NX_RT_UNDECIDED, notes, 0)
737 if n9 <= 0 { return __syscall(93, 35, 0, 0, 0, 0, 0) }
738
739 // T12: tail returns the last 2 lines of whatever we just wrote.
740 let tail_out: *u8 = sys_mmap(1024)
741 let got: i64 = nx_race_telemetry_tail(path, 2, tail_out, 1024)
742 if got <= 0 { return __syscall(93, 40, 0, 0, 0, 0, 0) }
743 // 2 newlines expected in the suffix (one per line read).
744 var nl: i64 = 0
745 var j: i64 = 0
746 while j < got {
747 if tail_out[j] == 10 { nl = nl + 1 }
748 j = j + 1
749 }
750 if nl != 2 { return __syscall(93, 41, 0, 0, 0, 0, 0) }
751
752 // T12.5: stats reads back and counts per-kind.
753 // We wrote 3 laps + 2 patches + 1 perspective + 1 corner +
754 // 2 competitions = 9 total non-comment rows.
755 let stats_counts: *i64 = sys_mmap(64) as *i64
756 let stats_rc: i64 = nx_race_telemetry_stats(path, stats_counts)
757 if stats_rc != 0 { return __syscall(93, 45, 0, 0, 0, 0, 0) }
758 if stats_counts[0] != 9 { return __syscall(93, 46, 0, 0, 0, 0, 0) }
759 if stats_counts[1] != 3 { return __syscall(93, 47, 0, 0, 0, 0, 0) }
760 if stats_counts[2] != 2 { return __syscall(93, 48, 0, 0, 0, 0, 0) }
761 if stats_counts[3] != 1 { return __syscall(93, 49, 0, 0, 0, 0, 0) }
762 if stats_counts[4] != 1 { return __syscall(93, 53, 0, 0, 0, 0, 0) }
763 if stats_counts[5] != 2 { return __syscall(93, 54, 0, 0, 0, 0, 0) }
764
765 // T13: clear removes the file.
766 let c1: i64 = nx_race_telemetry_clear(path)
767 if c1 != 0 { return __syscall(93, 50, 0, 0, 0, 0, 0) }
768 if fs_exists(path) != 0 { return __syscall(93, 51, 0, 0, 0, 0, 0) }
769
770 // T14: clear is idempotent -- re-clear returns 0, not -ENOENT.
771 let c2: i64 = nx_race_telemetry_clear(path)
772 if c2 != 0 { return __syscall(93, 52, 0, 0, 0, 0, 0) }
773
774 return 0
775}