diff options
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/harness/value_demos/run_all.py')
| -rw-r--r-- | debug/risc-v-gsp-rm-vm/harness/value_demos/run_all.py | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/harness/value_demos/run_all.py b/debug/risc-v-gsp-rm-vm/harness/value_demos/run_all.py new file mode 100644 index 0000000..808c227 --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/harness/value_demos/run_all.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""Run every value-validation demo and emit one machine-readable results JSON. + +Usage (from the tool root, ``open-iov/debug/risc-v-gsp-rm-vm``): + + python3 harness/value_demos/run_all.py + python3 harness/value_demos/run_all.py --only s01,s03,s05,s09 + VALUE_DEMO_BACKEND=qemu python3 harness/value_demos/run_all.py + +Each demo runs in its own subprocess with a hard timeout, so one wedged demo +cannot take the suite down. The aggregate lands in +``harness/value_demos/artifacts/results.json``. +""" +from __future__ import annotations + +import argparse +import json +import os +import subprocess +import sys +import time + +HERE = os.path.dirname(os.path.abspath(__file__)) +TOOL_ROOT = os.path.dirname(os.path.dirname(HERE)) +sys.path.insert(0, HERE) + +from _common import ARTIFACT_ROOT, SUITE_BUDGET_S, write_json # noqa: E402 + +DEMOS = [ + ("s01", "s01_reply_consumer.py"), + ("s02", "s02_counter_publication.py"), + ("s03", "s03_unknown_command.py"), + ("s04", "s04_run_until_function.py"), + ("s05", "s05_snapshot_diff.py"), + ("s06", "s06_inject_service.py"), + ("s07", "s07_version_diff.py"), + ("s08", "s08_mutation_triage.py"), + ("s09", "s09_grabbing_a_ghidra_address.py"), + ("s10", "s10_full_session_evidence.py"), +] + + +def _run_one(key: str, script: str, timeout: float) -> dict: + path = os.path.join(HERE, script) + start = time.monotonic() + try: + proc = subprocess.run( + [sys.executable, path], + cwd=TOOL_ROOT, + capture_output=True, + text=True, + timeout=timeout, + ) + stdout, stderr, returncode = proc.stdout, proc.stderr, proc.returncode + except subprocess.TimeoutExpired as exc: + stdout = (exc.stdout or b"").decode("utf-8", "replace") if isinstance(exc.stdout, bytes) else (exc.stdout or "") + stderr = (exc.stderr or b"").decode("utf-8", "replace") if isinstance(exc.stderr, bytes) else (exc.stderr or "") + returncode = -9 + elapsed = time.monotonic() - start + result = None + for line in reversed(stdout.splitlines()): + if line.startswith("RESULT_JSON: "): + try: + result = json.loads(line[len("RESULT_JSON: ") :]) + except json.JSONDecodeError: + result = None + break + if result is None: + result = { + "success": False, + "failure": f"no RESULT_JSON (exit {returncode})", + "stderr_tail": stderr[-800:], + } + result["demo_key"] = key + result["runner_elapsed_s"] = round(elapsed, 4) + result["exit_code"] = returncode + return result + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--only", default="", help="comma-separated demo keys (e.g. s01,s03)") + parser.add_argument("--timeout", type=float, default=45.0, help="per-demo timeout seconds") + parser.add_argument("--out", default=os.path.join(ARTIFACT_ROOT, "results.json")) + args = parser.parse_args() + + selected = [item for item in DEMOS if not args.only or item[0] in args.only.split(",")] + if args.only and not selected: + print(f"no demos match --only {args.only!r}", file=sys.stderr) + return 2 + + started = time.monotonic() + results = [] + for key, script in selected: + print(f"--- running {script} ---", flush=True) + result = _run_one(key, script, args.timeout) + results.append(result) + status = "PASS" if result.get("success") else "FAIL" + print(f"--- {key}: {status} ({result['runner_elapsed_s']:.2f}s) ---", flush=True) + if time.monotonic() - started > SUITE_BUDGET_S: + print(f"[run_all] suite budget {SUITE_BUDGET_S:.0f}s exceeded; continuing", flush=True) + + total = time.monotonic() - started + passed = [r for r in results if r.get("success")] + payload = { + "format": "greeneye-value-demos-results/1", + "tool": "harness/value_demos/run_all.py", + "backend_env": os.environ.get("VALUE_DEMO_BACKEND", "native (default)"), + "demos_selected": [k for k, _ in selected], + "demos_passed": [r["demo_key"] for r in passed], + "demos_failed": [r["demo_key"] for r in results if not r.get("success")], + "total_elapsed_s": round(total, 3), + "measured_elapsed_s": round(sum(r["runner_elapsed_s"] for r in results), 3), + "results": results, + } + write_json(args.out, payload) + print(f"\n{len(passed)}/{len(results)} demos passed in {total:.1f}s; results: {args.out}") + print(f"RESULT_JSON: {json.dumps({k: payload[k] for k in ('format', 'demos_passed', 'demos_failed', 'total_elapsed_s')}, sort_keys=True)}") + return 0 if len(passed) == len(results) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) |
