File size: 21,291 Bytes
0693474 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 | #!/usr/bin/env python3
"""CPU-only parsers and owned-process supervisor for coordinator window scripts.
No remote access occurs except in explicit `supervise`, used only by --execute.
Raw evidence stays in the private campaign directory. Summaries contain aggregates.
"""
from __future__ import annotations
import argparse
import csv
import hashlib
import json
import math
import os
from pathlib import Path
import re
import selectors
import signal
import statistics
import subprocess
import sys
import time
from typing import Any, IO, cast
MEM_FLOOR_KB = 5_242_880
PAIR = ('GPU-c4e7a371-d8a8-ebf0-918c-da3e38c87a93',
'GPU-7744b44b-8a13-b06d-a67e-f958fd74b8fc')
REQUIRED: dict[str, set[str]] = {
'preflight': {'exclusive_window', 'production_drained', 'rollback_saved', 'd2_paused',
'source_pins', 'image_base_hashes', 'owned_processes'},
'idle': {'experiment_idle', 'no_foreign_requests'},
'runtime': {'both_rank_hashes', 'model_manifest', 'worker_identity', 'hello_identity',
'rank_env_equal', 'capacity_async_process', 'proxy_torch_absent', 'proxy_mlock',
'local_drafter_absent', 'engram_required', 'kv_layout', 'no_runtime_errors'},
'clean': {'both_rank_logs', 'counters_before_after', 'no_fallback', 'no_watchdog',
'no_nan', 'no_provenance_stops', 'no_swap_growth', 'healthy'},
'worker': {'pair_owner_coord', 'owned_transient_only', 'both_gpu_locks', 'source_hashes',
'arm_identity', 'ready_pid', 'four_slots', 'max_length', 'no_emulation', 'no_dumps'},
'pair-results': {'fresh_results', 'both_arm_soaks', 'dump_identity', 'v1_classified',
'rx_gates', 'pair_timing', 'both_card_peaks', 'runtime_identity'},
'recalibrate': {'pair_source_matches', 'verify_only_round_ms', 'overlap_audited',
'fit_heldout', 'config_staged', 'both_rank_costs'},
'up1-prereq': {'k1_qualified', 's18_reviewed'},
'up1-p0': {'control_k1_equivalent', 'cc_noise',
'short_tokens_identical', 'request_accounting', 'round_within_one_percent'},
'split-bitwise': {'rank0_rows_33_42_43_63', 'rank1_rows_33_42_43_63',
'per_row_oracle_43_63', 'dispatch_controls_32_64', 'no_host_count_sync'},
'cpu-pin': {'actual_thread_affinities', 'allowed_cpuset', 'poller_verified', 'proxy_engram_tails'},
'replay': {'successor_gt_128_both_sites', 'remainders_1_127_128_129', 'partial_budget_le_128',
'retained_l20_kv', 'actual_suffix', 'partial_need_draft_zero', 'full_draft_window',
'cancel_reuse', 'preempt_prefix', 'reconnect_timeout_resync', 'ragged_reject',
'mm_exclusion', 'two_prompt_logprobs', 'final_logits_needles', 'fused_torch_equal',
'zero_provenance_strings', 'zero_internal_error_increment'},
'k5': {'five_proposals', 'twentyfour_target_rows', 'accepted_round_stats', 'prose_review_tool'},
'dual-gid': {'rank0_both_ipv4_rocev2_gid3', 'rank1_both_ipv4_rocev2_gid3'},
'dual-ring': {'collective_byte_identity', 'both_functions_traffic', 'no_nccl_fallback'},
'vor': {'valid_lens_index_bitwise', 'padded_invalid_replay_300k', 'stock_exclusions',
'no_exception_disable', 'eligible_once_per_step', 'copy_on_change', 'no_request_jit'},
'rp1-preflight': {'k1_qualified', 'base20_both_images', 'checkpoint_manifest_both',
'cpu_topology_allowed', 'odirect', 'disk_budget_8g', 'cpu_bytes_256m'},
'rp1-p0': {'no_connector_cli', 'no_rp1_switches', 'same_k1_pool_groups', 'same_fixed_outputs'},
'rp1-rank': {'independent_preload_payloads', 'both_ranks_store', 'valid_ranges_recorded',
'rank_sha_crc_equal', 'gloo_init', 'cap_29568', 'cpu_bytes_256m'},
'rp1-restart': {'stores_complete', 'old_engine_recorded', 'old_processes_gone',
'unused_shm_only', 'disk_preserved'},
'rp1-empty': {'new_engine_empty', 'no_intervening_salts', 'new_engine_id'},
'rp1-stale': {'new_rank1_owned_mmap', 'bounded_64k_sentinel', 'rank0_untouched',
'mn_verify_off', 'sync_before_gpu', 'selected_payload_equal', 'codes_logits_pass'},
'rp1-crc': {'stopped_owned_namespace', 'aligned_odirect_flip', 'exact_crc_mismatch',
'block_sidecar_removed', 'no_invalid_completion', 'recompute_pass'},
'rp1-sidecar': {'stopped_owned_namespace', 'recorded_sidecar_only', 'miss_removed', 'recompute_pass'},
'rp1-namespace': {'model_and_layout_mismatch', 'old_namespace_unread', 'cached_zero',
'true_identity_restored', 'no_false_namespace_writes'},
'rp1-concurrency': {'four_distinct_150k_saved_salts', 'empty_restart', 'four_reload_lt_300s',
'each_load_le_29568', 'codes_six_of_six', 'no_foreign_codes',
'no_pending_loop', 'explicit_million_clamped', 'file_gloo_counters'},
'rp1-reload-evidence': {'cold_fresh_cached_zero', 'reload_positive_le_cap',
'file_reads_and_gloo_increase', 'all_compare_pass', 'no_ambiguous_logits'},
'depth': {'sp2_configs', 'matching_target_worker', 'max_depth_five', 'same_sampling_budget',
'costs_measured', 'overlap_audited', 'heldout_groups', 'decisions_logged'},
}
def require(condition: bool, message: str) -> None:
if not condition:
raise ValueError(message)
def load(path: Path) -> Any:
return json.loads(path.read_text())
def kv(text: str, expected: int) -> int:
values = [int(x.replace(',', '')) for x in re.findall(r'GPU KV cache size: ([\d,]+)', text)]
require(expected > 0 and bool(values), 'missing GPU KV cache size or invalid expectation')
require(all(x == expected for x in values), f'KV token mismatch: {values}, expected {expected}')
return values[0]
def memory(text: str) -> tuple[int, int]:
fields = dict(re.findall(r'^(MemAvailable|SwapFree):\s*(\d+)\s+kB\s*$', text, re.M))
require(set(fields) == {'MemAvailable', 'SwapFree'}, 'missing/malformed memory fields')
available, swap = int(fields['MemAvailable']), int(fields['SwapFree'])
require(available >= MEM_FLOOR_KB, f'MemAvailable {available} < {MEM_FLOOR_KB} kB')
return available, swap
def dual(text: str, rank: int) -> None:
prefix = f'EXL3 RDMA all-reduce: rank {rank} of 2 on rocep1s0f1,roceP2p1s0f1'
require(any(line.startswith(prefix) and re.match(r'(?:,|$)', line[len(prefix):])
for line in text.splitlines()), f'missing exact dual-ring rank {rank} log prefix')
require(not re.search(r'RDMA all-reduce off.*NCCL stays', text), 'NCCL-only fallback')
def clean_logs(text: str) -> None:
for stop in ('invalid replay tap provenance', 'replay skip would remove taps',
'RDMA all-reduce off', 'CUDA error', 'illegal memory access', 'Traceback (most recent call last)'):
require(stop not in text, f'log stop: {stop}')
def counter_delta(before: dict[str, Any], after: dict[str, Any]) -> None:
# Accept absent sparse zero counters, but refuse missing counter maps or a restarted/reset session.
require(isinstance(before.get('counters'), dict) and isinstance(after.get('counters'), dict),
'missing counters map')
require(after.get('uptime_s', -1) >= before.get('uptime_s', 0), 'stats reset during arm')
for key in set(before['counters']) | set(after['counters']):
a, b = before['counters'].get(key, 0), after['counters'].get(key, 0)
require(isinstance(a, (int, float)) and isinstance(b, (int, float)) and b >= a,
f'counter reset/malformed: {key}')
if any(s in key for s in ('fallback', 'failed', 'error', 'watchdog', 'preempted', 'skipped')):
require(b == a, f'new failure counter: {key}: {a}->{b}')
for stats in (before, after):
if 'async_watchdog' in stats:
require(stats['async_watchdog'].get('fires') == 0, 'async watchdog fired')
def receipt(path: Path, gate: str, arm: str, root: Path) -> None:
require(gate in REQUIRED, f'unknown gate {gate}')
data = load(path)
require(data.get('schema') == 'wr1-gate-v1', 'wrong receipt schema')
require(data.get('gate') == gate and data.get('arm') == arm, 'receipt gate/arm mismatch')
require(data.get('run_dir') == str(root.resolve()), 'receipt belongs to another run')
checks = data.get('checks', {})
require(isinstance(checks, dict) and all(value is True for value in checks.values()), 'receipt contains a nonpassing check')
require(all(checks.get(key) is True for key in REQUIRED[gate]),
f'missing/failed {gate} checks: {sorted(key for key in REQUIRED[gate] if checks.get(key) is not True)}')
require(bool(data.get('evidence')), 'receipt requires hashed local source evidence')
for row in data['evidence']:
source = Path(row['path']).resolve()
require(source.is_relative_to(root.resolve()) and source != path.resolve(), 'evidence outside run or self-reference')
require(source.is_file() and source.stat().st_mtime >= (root / '.run-start').stat().st_mtime - 2,
f'missing/stale evidence {source}')
require(hashlib.sha256(source.read_bytes()).hexdigest() == row['sha256'], 'evidence SHA256 mismatch')
print(f'PASS {gate} {arm}: {len(REQUIRED[gate])} checks, source evidence hashes verified')
def timing(path: Path) -> None:
data = load(path)
require(data.get('classification') == 'MEASURED', 'timing must be MEASURED from pair + verify-only evidence')
require(data.get('arm') == 'native-hybrid-kx2', 'wrong timing arm')
require(data.get('overlap_audited') is True and data.get('verify_only') is True, 'unverified cost semantics')
draft, rounds = data['draft_ms'], data['round_ms']
require(isinstance(draft, (int, float)) and math.isfinite(draft) and draft >= 0, 'invalid draft_ms')
require(isinstance(rounds, list) and len(rounds) == 6, 'round_ms must contain depths 0..5')
require(all(isinstance(x, (int, float)) and math.isfinite(x) and x > 0 for x in rounds), 'invalid round_ms')
require(all(a <= b for a, b in zip(rounds, rounds[1:])), 'nonmonotonic verify-only costs')
for row in data['sources']:
source = Path(row['path'])
require(hashlib.sha256(source.read_bytes()).hexdigest() == row['sha256'], 'timing source hash mismatch')
require(bool(data['sources']), 'timing source evidence missing')
print(f'MEASURED timing from {path}: DRAFT_MS={draft}, ROUND_MS={",".join(map(str, rounds))}')
def bench(path: Path) -> list[dict[str, Any]]:
records = [json.loads(line) for line in path.read_text().splitlines() if line.strip()]
runs = [r for r in records if r.get('type') == 'run']
requests = [r for r in records if r.get('type') == 'request']
require(bool(runs) and bool(requests), 'missing run/request measurements')
for row in records:
require(not row.get('error'), f'benchmark error in {path}')
result: list[dict[str, Any]] = []
for run in runs:
rows = [r for r in requests if r.get('run_id') == run['run_id']]
require(len(rows) == run['request_count'] and bool(rows), 'request accounting mismatch')
spec = run['spec']
require(spec.get('rounds', 0) > 0 and spec.get('mean_accepted_per_round') is not None, 'missing accepted/round')
for row in rows:
require(row.get('ttft_s') is not None and row.get('step_gap_ms', {}).get('p50') is not None,
'missing TTFT/step p50')
require(not run.get('telemetry_errors'), 'benchmark telemetry error')
result.append({'classification': 'MEASURED', 'source': str(path), 'label': run['config_label'],
'ctx': run['target_ctx'], 'concurrency': run['concurrency'], 'mixed': run['mixed'],
'cache': run['cache_state'], 'rep': run['rep'], 'delivered_tps': run['aggregate_tps'],
'accepted_per_round': spec['mean_accepted_per_round'],
'step_p50_ms': statistics.median(r['step_gap_ms']['p50'] for r in rows),
'ttft_s': statistics.median(r['ttft_s'] for r in rows),
'first_visible_s': [r.get('first_visible_s') for r in rows]})
return result
def exit_status(proc: subprocess.Popen[Any]) -> int | None:
# WNOWAIT reserves the leader PID even when it exited, so cleanup cannot signal a reused group.
status = os.waitid(os.P_PID, proc.pid, os.WEXITED | os.WNOHANG | os.WNOWAIT)
if status is None:
return None
return status.si_status if status.si_code == os.CLD_EXITED else -status.si_status
def stop_owned(proc: subprocess.Popen[Any]) -> None:
# The direct leader has not been reaped: its new session/group cannot belong to an unrelated process.
if proc.returncode is not None:
return
for sig in (signal.SIGTERM, signal.SIGKILL):
try:
os.killpg(proc.pid, sig)
except ProcessLookupError:
pass # Already gone. Still reap this directly owned child below.
if sig == signal.SIGTERM:
time.sleep(0.1)
proc.wait(timeout=3)
def supervise(args: argparse.Namespace) -> int:
root, name = args.out, args.name
require(not (root / 'STOP').exists(), 'STOP file exists')
select = selectors.DefaultSelector()
monitors: list[subprocess.Popen[str]] = []
outputs: list[IO[str]] = []
command: subprocess.Popen[Any] | None = None
baseline_path = root / 'swap-baseline.json'
initial: dict[str, int] = load(baseline_path) if baseline_path.exists() else {}
last: dict[str, float] = {}
deadline = time.monotonic() + 20
def interrupted(signum: int, _frame: Any) -> None:
raise RuntimeError(f'supervisor interrupted by signal {signum}')
old_handlers = {s: signal.signal(s, interrupted) for s in (signal.SIGTERM, signal.SIGINT, signal.SIGHUP)}
try:
ssh = ['ssh', '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=5', '-o', 'ServerAliveInterval=5', '-o', 'ServerAliveCountMax=2']
memscript = "import time; from pathlib import Path\nwhile True:\n print(';'.join(x for x in Path('/proc/meminfo').read_text().splitlines() if x.startswith(('MemAvailable:', 'SwapFree:'))), flush=True); time.sleep(1)"
import shlex
memcmd = 'exec python3 -u -c ' + shlex.quote(memscript)
specs = [(host, memcmd, 'mem') for host in args.hosts]
specs.append((args.x86host, f'nvidia-smi -i {",".join(PAIR)} --query-gpu=timestamp,uuid,memory.used,power.draw,temperature.gpu --format=csv,noheader -l 1', 'gpu'))
for host, script, kind in specs:
monitor_key = f'{kind}-{host}'
log = (root / f'{name}.{monitor_key}.log').open('x')
err = (root / f'{name}.{monitor_key}.stderr').open('x')
outputs.extend([log, err])
proc = subprocess.Popen([*ssh, host, script], stdout=subprocess.PIPE, stderr=err,
text=True, start_new_session=True, bufsize=1)
monitors.append(proc)
assert proc.stdout is not None
os.set_blocking(proc.stdout.fileno(), False)
select.register(proc.stdout, selectors.EVENT_READ, (kind, host, log))
pending = {key.fileobj: '' for key in select.get_map().values()}
while True:
now = time.monotonic()
if (root / 'STOP').exists():
raise RuntimeError('coordinator STOP file present')
for key, _mask in select.select(timeout=0.1):
stream = cast(IO[str], key.fileobj)
chunk = os.read(stream.fileno(), 65536).decode()
require(bool(chunk), 'telemetry stream closed')
pending[stream] += chunk
while '\n' in pending[stream]:
line, pending[stream] = pending[stream].split('\n', 1)
kind, host, log = key.data
log.write(f'{time.time():.3f} {line}\n'); log.flush()
if kind == 'mem':
available, swap = memory(line.replace(';', '\n'))
initial.setdefault(host, swap)
baseline_path.write_text(json.dumps(initial) + '\n')
require(swap >= initial[host], f'swap growth on {host}')
last[f'{kind}-{host}'] = now
else:
columns = next(csv.reader([line], skipinitialspace=True))
require(len(columns) == 5 and columns[1] in PAIR, 'invalid GPU telemetry/UUID')
require(float(columns[2]) >= 0, 'invalid GPU memory')
last[columns[1]] = now
require(all(exit_status(p) is None for p in monitors), 'telemetry process exited')
expected = [f'mem-{h}' for h in args.hosts] + list(PAIR)
if command is None:
require(now < deadline, 'no initial telemetry within 20 s')
if all(k in last for k in expected):
cmd = args.command[1:] if args.command[0] == '--' else args.command
command = subprocess.Popen(cmd, start_new_session=True)
deadline = now + args.timeout
else:
require(now < deadline, f'step exceeded {args.timeout}s')
require(all(now - last[k] < 15 for k in expected), 'telemetry stale >15s')
rc = exit_status(command)
if rc is not None:
require(rc == 0, f'command exited rc={rc}')
return 0
except (Exception, KeyboardInterrupt) as exc:
(root / 'STOP').write_text(f'{name}: {exc}\n')
print(f'STOP {name}: {exc}', file=sys.stderr)
return 1
finally:
if command is not None:
stop_owned(command)
for proc in monitors:
stop_owned(proc)
select.close()
for output in outputs:
output.close()
for sig, handler in old_handlers.items():
signal.signal(sig, handler)
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
sub = parser.add_subparsers(dest='mode', required=True)
p = sub.add_parser('kv'); p.add_argument('path', type=Path); p.add_argument('expected', type=int)
p = sub.add_parser('dual'); p.add_argument('path', type=Path); p.add_argument('rank', type=int, choices=[0, 1])
p = sub.add_parser('logs'); p.add_argument('path', type=Path)
p = sub.add_parser('counters'); p.add_argument('before', type=Path); p.add_argument('after', type=Path)
p = sub.add_parser('receipt'); p.add_argument('path', type=Path); p.add_argument('gate'); p.add_argument('arm'); p.add_argument('root', type=Path)
p = sub.add_parser('timing'); p.add_argument('path', type=Path)
p = sub.add_parser('rollback'); p.add_argument('path', type=Path)
p = sub.add_parser('bench'); p.add_argument('path', type=Path)
p = sub.add_parser('summary'); p.add_argument('root', type=Path); p.add_argument('--pair', action='store_true')
p = sub.add_parser('supervise'); p.add_argument('--out', type=Path, required=True); p.add_argument('--name', required=True)
p.add_argument('--hosts', nargs=2, required=True); p.add_argument('--x86host', required=True)
p.add_argument('--timeout', type=float, default=10800); p.add_argument('command', nargs=argparse.REMAINDER)
args = parser.parse_args()
if args.mode == 'kv': print(f'MEASURED {args.path}: KV tokens {kv(args.path.read_text(), args.expected)}')
elif args.mode == 'dual': dual(args.path.read_text(), args.rank)
elif args.mode == 'logs': clean_logs(args.path.read_text())
elif args.mode == 'counters': counter_delta(load(args.before), load(args.after))
elif args.mode == 'receipt': receipt(args.path, args.gate, args.arm, args.root)
elif args.mode == 'timing': timing(args.path)
elif args.mode == 'rollback':
text = args.path.read_text()
require(not args.path.is_symlink() and text.startswith('#!/usr/bin/env bash\n'), 'saved coordinator-next.sh must be a local bash script')
require(all(fragment in text for fragment in ('prod_window.sh close', 'PRODUCTION HOLD OK', './start.sh',
'window.sh close')), 'incomplete saved coordinator restoration commands')
subprocess.run(['bash', '-n', str(args.path)], check=True)
print(text, end='')
elif args.mode == 'bench': print(json.dumps(bench(args.path), allow_nan=False))
elif args.mode == 'summary':
data = [r for path in sorted(args.root.glob('*-cold.jsonl')) + sorted(args.root.glob('*-warm.jsonl')) for r in bench(path)]
require(bool(data) or args.pair, 'no benchmark summaries')
(args.root / 'summary.json').write_text(json.dumps(data, indent=2, allow_nan=False) + '\n')
print(f'MEASURED: {len(data)} cells; sources in {args.root}/summary.json. Coordinator applies plan pass lines; no automatic promotion.')
elif args.mode == 'supervise': return supervise(args)
return 0
if __name__ == '__main__':
try:
sys.exit(main())
except (ValueError, KeyError, OSError, TypeError) as error:
print(f'STOP: {error}', file=sys.stderr)
sys.exit(1)
|