code wiki / (root) / nx_race_telemetry.nx

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}