#!/usr/bin/env python3 """GPU verification of graceful and hard-kill OpProf accounting.""" from __future__ import annotations import json import os import shlex import signal import subprocess import time import urllib.request from pathlib import Path from typing import Any WORKDIR = Path("/home/admin/cpfs/wjh/opprof-phase3-dash0-20260712") RUN_ROOT = WORKDIR / "runs/e-b-sidecar-verification" SOURCE = Path( "/home/admin/cpfs/wjh/opprof-phase2-dash0-20260711/vllm-v0.24.0" ) VENV = Path("/tmp/wjh-opprof-phase2-dash0-20260711/.venv") MODEL = Path("/home/admin/cpfs/wjh/models/Qwen/Qwen3-30B-A3B") CLIENT = WORKDIR / "scripts/opprof_phase3_client.py" MANIFEST = Path("/home/admin/cpfs/wjh/opprof-phase3-private/manifests/P01.jsonl") FLUSH_INTERVAL_SECONDS = 1.0 CHECKPOINT_TOLERANCE_SECONDS = 0.1 def atomic_json(path: Path, value: Any) -> None: path.parent.mkdir(parents=True, exist_ok=True) temporary = path.with_name(f"{path.name}.tmp-{os.getpid()}") with temporary.open("w", encoding="utf-8") as output: json.dump(value, output, indent=2, sort_keys=True) output.write("\n") output.flush() os.fsync(output.fileno()) os.replace(temporary, path) def run_text(command: list[str], check: bool = True) -> str: result = subprocess.run( command, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) if check and result.returncode: raise RuntimeError( f"command failed ({result.returncode}): {shlex.join(command)}\n" f"{result.stdout}" ) return result.stdout def gpu_rows() -> list[dict[str, int]]: output = run_text( [ "nvidia-smi", "--query-gpu=index,memory.used,utilization.gpu", "--format=csv,noheader,nounits", ] ) rows = [] for line in output.strip().splitlines(): index, memory, utilization = (int(value.strip()) for value in line.split(",")) rows.append( { "index": index, "memory_mib": memory, "utilization_pct": utilization, } ) return rows def compute_apps() -> str: return run_text( [ "nvidia-smi", "--query-compute-apps=gpu_uuid,pid,process_name,used_memory", "--format=csv,noheader,nounits", ], check=False, ).strip() def assert_idle() -> list[dict[str, int]]: rows = gpu_rows() if any(row["memory_mib"] or row["utilization_pct"] for row in rows): raise RuntimeError(f"dash0 is not GPU-idle: {rows}") applications = compute_apps() if applications: raise RuntimeError(f"compute applications present: {applications}") return rows def wait_ready(process: subprocess.Popen[Any], port: int) -> None: deadline = time.monotonic() + 300 while time.monotonic() < deadline: if process.poll() is not None: raise RuntimeError("server exited before readiness") try: with urllib.request.urlopen( f"http://127.0.0.1:{port}/health", timeout=1 ) as response: if response.status == 200: return except Exception: pass time.sleep(1) raise TimeoutError("server readiness timeout") def wait_zero() -> tuple[list[list[dict[str, int]]], float]: samples: list[list[dict[str, int]]] = [] consecutive = 0 deadline = time.monotonic() + 180 while time.monotonic() < deadline and consecutive < 3: rows = gpu_rows() samples.append(rows) zero = all(row["memory_mib"] == 0 for row in rows) and not compute_apps() consecutive = consecutive + 1 if zero else 0 if consecutive < 3: time.sleep(2) if consecutive < 3: raise RuntimeError("GPUs did not reach three stable zero samples") return samples, time.time() def wait_fresh_sidecar(run_dir: Path) -> dict[str, Any]: deadline = time.monotonic() + 10 while time.monotonic() < deadline: files = sorted((run_dir / "opprof").glob("*.jsonl.footer.json")) if len(files) == 1: sidecar = json.loads(files[0].read_text()) age = time.time_ns() - sidecar["checkpoint_wall_ns"] if 0 <= age <= 250_000_000: return sidecar time.sleep(0.02) raise TimeoutError("could not observe a fresh OpProf sidecar") def validate_accounting( run_dir: Path, mode: str, termination_wall_ns: int ) -> dict[str, Any]: streams = sorted((run_dir / "opprof").glob("*.jsonl")) sidecars = sorted((run_dir / "opprof").glob("*.jsonl.footer.json")) if len(streams) != 1 or len(sidecars) != 1: raise RuntimeError( f"expected one stream/sidecar, got {len(streams)}/{len(sidecars)}" ) raw = streams[0].read_bytes() if not raw.endswith(b"\n"): raise RuntimeError("partial final JSONL line") decoded = [json.loads(line) for line in raw.splitlines()] footers = [row for row in decoded if row.get("record_type") == "footer"] records = [row for row in decoded if row.get("record_type") != "footer"] sidecar = json.loads(sidecars[0].read_text()) indices = [row["step_index"] for row in records] checkpoint_age = ( termination_wall_ns - sidecar["checkpoint_wall_ns"] ) / 1e9 common = { "all_schema_1": all(row.get("schema") == 1 for row in decoded) and sidecar.get("schema") == 1, "steps_contiguous": indices == list(range(len(indices))), "written_matches_records": sidecar["written_records"] == len(records), "encoded_balanced": sidecar["encoded_records"] == sidecar["written_records"] + sidecar["dropped_records"], "last_step_matches": bool(records) and sidecar["last_step_index"] == records[-1]["step_index"], "zero_drops": sidecar["dropped_records"] == 0 and all(row["dropped_records_before"] == 0 for row in records), } if mode == "graceful": footer_ok = len(footers) == 1 and decoded[-1] is footers[0] agreement = footer_ok and all( footers[0][counter] == sidecar[counter] for counter in ("encoded_records", "written_records", "dropped_records") ) specific = { "one_footer_last": footer_ok, "final_sidecar": sidecar["final"] is True, "footer_sidecar_agree": agreement, } else: specific = { "no_in_stream_footer": not footers, "checkpoint_sidecar": sidecar["final"] is False, "checkpoint_within_bound": checkpoint_age <= FLUSH_INTERVAL_SECONDS + CHECKPOINT_TOLERANCE_SECONDS, } invariants = {**common, **specific} if not all(invariants.values()): raise RuntimeError( f"{mode} accounting invalid: {invariants}; sidecar={sidecar}" ) return { "stream": str(streams[0]), "sidecar": str(sidecars[0]), "bytes": len(raw), "records": len(records), "footer_count": len(footers), "checkpoint_age_seconds": checkpoint_age, "counters": { key: sidecar[key] for key in ("encoded_records", "written_records", "dropped_records") }, "last_step_index": sidecar["last_step_index"], "invariants": invariants, } def run_trial(mode: str, port: int) -> dict[str, Any]: run_dir = RUN_ROOT / mode if run_dir.exists(): raise RuntimeError(f"refusing to overwrite {run_dir}") run_dir.mkdir(parents=True) before = assert_idle() atomic_json(run_dir / "gpu-before.json", before) (run_dir / "clocks-before.txt").write_text( run_text(["nvidia-smi", "-q", "-d", "CLOCK"]) ) server_command = [ "taskset", "-c", "0-19", str(VENV / "bin/vllm"), "serve", str(MODEL), "--host", "127.0.0.1", "--port", str(port), "--tensor-parallel-size", "1", "--enable-chunked-prefill", "--enable-prefix-caching", "--shutdown-timeout", "120", ] client_command = [ "taskset", "-c", "0-19", str(VENV / "bin/python"), str(CLIENT), "run", "--manifest", str(MANIFEST), "--base-url", f"http://127.0.0.1:{port}", "--model", str(MODEL), "--load-point", "saturation", "--request-rate", "inf", "--max-concurrency", "256", "--ignore-eos", "--temperature", "0", "--warmup-seconds", "20", "--clean-segment-seconds", "40", "--num-clean-segments", "3", "--drain-timeout-seconds", "120", "--workload-seed", "20260712", "--result-dir", str(run_dir / "client"), ] with (run_dir / "commands.log").open("w", encoding="utf-8") as output: output.write( f"GPU_COMMAND {mode} server: {shlex.join(server_command)}; " "expected=60-180s startup + 140-180s load\n" ) output.write( f"GPU_COMMAND {mode} client: {shlex.join(client_command)}; " "expected=20s warmup + 120s clean + drain\n" ) environment = os.environ.copy() environment.update( { "CUDA_VISIBLE_DEVICES": "0", "VLLM_OPPROF_DIR": str(run_dir / "opprof"), "HF_HUB_OFFLINE": "1", "TRANSFORMERS_OFFLINE": "1", "PYTHONUNBUFFERED": "1", } ) started = time.time() server_log = (run_dir / "server.log").open("ab", buffering=0) server: subprocess.Popen[Any] | None = None try: print( f"GPU_COMMAND sidecar-{mode}: P01/C00 GPU0, 20s warmup + " "120s clean, expected 4-6 wall-min", flush=True, ) server = subprocess.Popen( server_command, cwd=SOURCE, env=environment, stdout=server_log, stderr=subprocess.STDOUT, start_new_session=True, ) atomic_json( run_dir / "state.json", {"status": "server_starting", "server_pid": server.pid}, ) wait_ready(server, port) with (run_dir / "client.log").open("ab", buffering=0) as client_log: client = subprocess.run( client_command, cwd=WORKDIR, stdout=client_log, stderr=subprocess.STDOUT, ) if client.returncode: raise RuntimeError(f"client exited {client.returncode}") if mode == "graceful": termination_wall_ns = time.time_ns() os.kill(server.pid, signal.SIGINT) server.wait(timeout=150) log_text = (run_dir / "server.log").read_text(errors="replace") if "mode=drain timeout=120s" not in log_text: raise RuntimeError("official drain-mode log not observed") else: wait_fresh_sidecar(run_dir) termination_wall_ns = time.time_ns() os.killpg(server.pid, signal.SIGKILL) server.wait(timeout=30) zero_samples, zero_at = wait_zero() accounting = validate_accounting(run_dir, mode, termination_wall_ns) client_result = json.loads((run_dir / "client/result.json").read_text()) result = { "schema": 1, "status": "pass", "mode": mode, "server_returncode": server.returncode, "termination_wall_ns": termination_wall_ns, "accounting": accounting, "clean": client_result["clean"], "failed_records": client_result["failed_records"], "gpu_zero_samples": zero_samples, "gpu_seconds": zero_at - started, } atomic_json(run_dir / "result.json", result) atomic_json(run_dir / "state.json", {"status": "complete"}) return result except Exception as error: atomic_json( run_dir / "state.json", {"status": "failed", "failure": repr(error)}, ) if server is not None and server.poll() is None: try: os.killpg(server.pid, signal.SIGKILL) except ProcessLookupError: pass wait_zero() raise finally: server_log.close() def main() -> None: if RUN_ROOT.exists(): raise RuntimeError(f"refusing to overwrite {RUN_ROOT}") RUN_ROOT.mkdir(parents=True) state: dict[str, Any] = {"schema": 1, "status": "running", "results": {}} atomic_json(RUN_ROOT / "state.json", state) for offset, mode in enumerate(("graceful", "hard-kill")): result = run_trial(mode, 8010 + offset) state["results"][mode] = result atomic_json(RUN_ROOT / "state.json", state) state["status"] = "complete" state["gpu_seconds"] = sum( result["gpu_seconds"] for result in state["results"].values() ) atomic_json(RUN_ROOT / "state.json", state) print(json.dumps(state, sort_keys=True), flush=True) if __name__ == "__main__": main()