mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
Record parent and child process IDs for diagnostics, persist interruption before cleanup, and ignore repeated termination signals during cleanup. Close unexpected failures with a terminal result and retain the child's exit status without treating namespace-local PID absence as completion. Require a successful terminal run_flow result when checking simulation logs with an associated result file. Cover repeated cancellation and reject running, failed or inconsistent terminal records despite PASS text. This supports the TINY-R2-P7 campaign. Interrupted and timed-out runs remain non-acceptance evidence; UART output alone cannot qualify a Tiny simulation.
73 lines
2.6 KiB
Python
73 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
from scripts.setup_helpers import atomic_write # noqa: E402
|
|
|
|
|
|
DEFAULT_SUCCESS = "SIM_TEST_PASS"
|
|
DEFAULT_FAILURE = re.compile(
|
|
r"(?:\bFAILED?\b|\bFATAL\b|assertion failed|%Error|SIM_TEST_FAIL|SIM_TEST_TIMEOUT)",
|
|
re.IGNORECASE,
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Validate a retroSoC simulation log")
|
|
parser.add_argument("--log", type=Path, required=True)
|
|
parser.add_argument("--result", type=Path, required=True)
|
|
parser.add_argument("--require", action="append", default=[])
|
|
parser.add_argument("--reject", action="append", default=[])
|
|
args = parser.parse_args()
|
|
|
|
if not args.log.is_file():
|
|
raise SystemExit(f"simulation log not found: {args.log}")
|
|
content = args.log.read_text(encoding="utf-8", errors="replace")
|
|
required = args.require or [DEFAULT_SUCCESS]
|
|
missing = [marker for marker in required if marker not in content]
|
|
rejected = [pattern for pattern in args.reject if re.search(pattern, content)]
|
|
default_failure = DEFAULT_FAILURE.search(content)
|
|
if default_failure:
|
|
rejected.append(default_failure.group(0))
|
|
|
|
flow_result_path = args.result.with_name("result-sim.json")
|
|
flow_status = None
|
|
if flow_result_path.is_file():
|
|
try:
|
|
flow_result = json.loads(flow_result_path.read_text(encoding="utf-8"))
|
|
except (OSError, json.JSONDecodeError):
|
|
flow_result = {}
|
|
flow_status = flow_result.get("status", "invalid")
|
|
# PIDs are namespace-local and can be reused. A durable terminal
|
|
# verdict, not a liveness guess from this process, establishes success.
|
|
if flow_status != "passed" or flow_result.get("exit_code") != 0:
|
|
missing.append("successful terminal run_flow result")
|
|
|
|
passed = not missing and not rejected
|
|
report = {
|
|
"schema_version": 1,
|
|
"status": "passed" if passed else "failed",
|
|
"log": str(args.log.resolve()),
|
|
"required_markers": required,
|
|
"missing_markers": missing,
|
|
"rejected_matches": sorted(set(rejected)),
|
|
}
|
|
if flow_status is not None:
|
|
report["flow_status"] = flow_status
|
|
atomic_write(args.result, json.dumps(report, indent=2, sort_keys=True) + "\n")
|
|
print(f"simulation check: {report['status']} ({args.result.resolve()})")
|
|
return int(not passed)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|