AtlatestRepositoryfolio
1
;;; Per-spawn cost of the bounded worker, for whichever shape it is pointed at.2
;;;3
;;; WHAT THIS MEASURES AND WHY IT NEEDS A BASELINE4
;;;5
;;; `run-bounded` spends its wall clock on four things: the `sh -c` wrapper that6
;;; applies `ulimit`, the child's process start, the child runtime's boot, and7
;;; the parse itself. For a small note the parse is sub-millisecond, so the8
;;; figure is essentially START + BOOT -- but "essentially" is a claim, not a9
;;; measurement, and F3 is going to design against this number.10
;;;11
;;; So the harness measures THREE things, and the third is what turns a number12
;;; into an attributable one:13
;;;14
;;; worker a real inspection of a small manuscript15
;;; trivial `sh -c true` through the SAME `run-bounded` path -- the cost of16
;;; the harness itself: fork, the ulimit shell, the poll loop, the17
;;; temp files, the reap18
;;; parse a much larger manuscript, so the parse term is visible rather19
;;; than inferred to be small20
;;;21
;;; `worker - trivial` is then the runtime's own boot, separated from this22
;;; module's overhead, instead of a single figure that silently contains both.23
;;;24
;;; REPORTS MEDIAN AND SPREAD, NOT A MEAN. Process start times have a long right25
;;; tail under any load at all, and a mean over a handful of samples reports the26
;;; tail rather than the typical case. Min is printed too: on a quiet machine it27
;;; is the closest thing to the cost with no interference.28
(import (sigil core)29
(sigil string)30
(sigil list)31
(sigil process)32
(sigil path)33
(sigil math)34
(folio doc bounded))36
(define (args-after-script rest)37
(cond38
((null? rest) '())39
((string-ends-with? (car rest) ".sgl") (cdr rest))40
(else (args-after-script (cdr rest)))))42
(define argv (args-after-script (command-line)))44
;; argv: <worker-argv-json-ish> is awkward to pass; instead take45
;; <n> <small-note> <big-note> <worker-program> [worker-args...]46
(when (< (length argv) 4)47
(display "usage: <n> <small-note> <big-note> <worker-program> [args...]\n")48
(exit 2))50
;; ---------------------------------------------------------------------------51
;; THE MACHINE'S LOAD IS AN INPUT, AND IT MUST BE ABLE TO REFUSE THE RUN.52
;;53
;; This harness already checks the repo fingerprint and every sample's outcome.54
;; The third input it was trusting silently was the machine itself: a spawn cost55
;; taken against an unknown competing workload is exactly the unreproducible56
;; number this work exists to avoid, and whoever reads it will design against it.57
;;58
;; Learned the expensive way, 2026-08-23: a measurement window was granted, a59
;; heavy application started partway through it, and a memory ladder ran for two60
;; minutes under 6.7 load before anyone noticed. Nothing in the harness said so.61
;; The spawn-cost run itself turned out to be clean -- it finished 5m23s before62
;; the workload started -- but that was established afterwards from FILE63
;; TIMESTAMPS, which is archaeology, not an instrument.64
;;65
;; So: REFUSE, do not warn. A warning next to a number is read as a number.66
;; This is the same shape as the empty-fingerprint fix in `sabotage-f2b.sh` --67
;; the degenerate case must be SETUP-FAILED, not silently tolerated.68
;;69
;; HONEST LIMITATION, stated because it bounds what this check can do: the70
;; 1-minute load average LAGS. A workload that starts thirty seconds before this71
;; check will not have moved it much, and one that starts mid-run will not be72
;; caught until the post-run sample. That is why BOTH ends are sampled and both73
;; are printed: the post-run figure is what catches contamination that arrived74
;; after the go-ahead. It is a floor on confidence, not a guarantee.75
(define load-ceiling76
(let ((v (getenv "F2B_LOAD_CEILING")))77
(if (and v (string->number v)) (string->number v) 2.0)))79
;; `read-file-string` CANNOT READ /proc. Measured: `/proc/loadavg` stats as size80
;; 0 (procfs synthesises content on read), and the reader allocates by stat size,81
;; so it returns "" -- not an error, an EMPTY STRING.82
;;83
;; That is the dangerous shape, and it bit immediately: the load check appeared to84
;; fire, refusing the run with a plausible message, while actually reporting that85
;; it could not read the file at all. A check that always refuses is as useless86
;; as one that never does, and it LOOKS like it is working. It was caught by the87
;; positive control -- raising the ceiling and requiring the run to proceed --88
;; not by the refusal itself firing ([[topics/red-gate-positive-control]]).89
;;90
;; So the value comes through a subprocess, which reads procfs the way the kernel91
;; intends. This runs once before and once after the samples, never inside the92
;; measured path.93
(define (load-average-1min)94
(let* ((raw (guard (e (#t #f)) (process-output->string "cat" "/proc/loadavg")))95
(field (and (string? raw) (> (string-length raw) 0)96
(car (string-split raw " ")))))97
(and field (string->number field))))99
(define (check-load label)100
(let ((la (load-average-1min)))101
(cond102
((not la)103
;; Cannot read the load: that is SETUP-FAILED, not a pass. An absent104
;; measurement must never be treated as a favourable one.105
(display "SETUP-FAILED\tcould not read /proc/loadavg; the machine's state is an input and it is unavailable, so this run cannot certify itself\n")106
(exit 3))107
((> la load-ceiling)108
(display (format "SETUP-FAILED\tload average ~a at ~a exceeds the ceiling ~a; REFUSING to measure. Timing taken under competing load is unreproducible.~%"109
la label load-ceiling))110
(exit 3))111
(else112
(display (format "LOAD\t~a\t~a\t(ceiling ~a)~%" label la load-ceiling))113
la))))115
(define n (string->number (car argv)))116
(define small-note (cadr argv))117
(define big-note (caddr argv))118
(define worker-argv (cdddr argv))120
;; Generous limits: this measures COST, not containment, and a cap that fired121
;; would silently turn a timing sample into a kill.122
(define limits (make-limits 4194304 120000))124
(define (sample-once argv-with-arg)125
(let ((o (run-bounded argv-with-arg limits)))126
(list (outcome-kind o) (outcome-reason o) (outcome-elapsed-ms o))))128
;; Every sample's OUTCOME is checked, not just its clock. A run that crashed and129
;; a run that succeeded both produce a number, and averaging them would report130
;; the cost of failing.131
(define (collect label argv-with-arg expected-kind expected-reason)132
(let loop ((i 0) (times '()) (bad 0))133
(if (>= i n)134
(begin135
(unless (= bad 0)136
(display (format "WARNING\t~a\t~a of ~a samples had the wrong outcome; the figures below are NOT clean~%"137
label bad n)))138
(report label (list-sort < times) bad))139
(let ((s (sample-once argv-with-arg)))140
(loop (+ i 1)141
(cons (caddr s) times)142
(if (and (eq? (car s) expected-kind)143
(or (not expected-reason) (eq? (cadr s) expected-reason)))144
bad (+ bad 1)))))))146
(define (median sorted)147
(let ((len (length sorted)))148
(if (= len 0) 0 (list-ref sorted (quotient len 2)))))150
(define (report label sorted bad)151
(display (format "SAMPLE\t~a\tn=~a\tmin=~a\tmedian=~a\tmax=~a\tbad=~a~%"152
label (length sorted)153
(if (null? sorted) 0 (car sorted))154
(median sorted)155
(if (null? sorted) 0 (list-ref sorted (- (length sorted) 1)))156
bad))157
(median sorted))159
(check-load "pre-run")160
(display (format "SPAWN-COST n=~a worker=~a~%" n (car worker-argv)))162
;; WARMUP, discarded. The first spawn of a 31 MB bundle pays cold page cache,163
;; and F3's question is the STEADY-STATE cost across thousands of notes, not the164
;; cost of the first one. Reporting a cold first sample as if it were typical165
;; would overstate the figure F3 designs against. Three, then thrown away.166
(let loop ((i 0))167
(when (< i 3)168
(run-bounded (append worker-argv (list small-note)) limits)169
(loop (+ i 1))))170
(display "WARMUP done (3 spawns, discarded)\n")172
;; A trivial child through the same path: the harness's own floor.173
(define trivial (collect "trivial-sh" (list "sh" "-c" "true") (quote unindexed) (quote worker-crashed)))174
(define worker (collect "worker-small" (append worker-argv (list small-note)) (quote indexed) #f))175
(define bigp (collect "worker-big" (append worker-argv (list big-note)) (quote indexed) #f))177
(display (format "DERIVED\truntime-boot-and-start=~a ms (worker-small median minus trivial-sh median)~%"178
(- worker trivial)))179
(display (format "DERIVED\tparse-term=~a ms (worker-big median minus worker-small median)~%"180
(- bigp worker)))181
;; POST-RUN: contamination that arrived after the go-ahead is caught here, and182
;; nowhere else. This is the sample that would have flagged the aborted window.183
(check-load "post-run")184
(display "SPAWN-COST-DONE\n")