"""Check a local P1 trace against the probe's known work; never qualify a Wii run.""" import argparse import hashlib import json from pathlib import Path from analyze_p1 import analyze, read_relevant_events def sha256(path): digest = hashlib.sha256() with path.open("rb") as source: for chunk in iter(lambda: source.read(1024 * 1024), b""): digest.update(chunk) return digest.hexdigest().upper() def qualify(directory, events_path=None, xperf_summary_path=None): manifest = json.loads((directory / "manifest.json").read_text(encoding="utf-8-sig")) decoder = json.loads((directory / "decoder-summary.json").read_text(encoding="utf-8-sig")) source = events_path or directory / "events.jsonl" report = analyze(read_relevant_events(source, manifest["ProbePID"]), manifest["ProbePID"]) log = (directory / "probe.stdout.log").read_text(encoding="utf-8-sig") events = read_relevant_events(source, manifest["ProbePID"]) xperf_path = xperf_summary_path or directory / "xperf-summary.json" xperf = json.loads(xperf_path.read_text(encoding="utf-8-sig")) if xperf_path.exists() else {} counters = {(c["tid"], c["counter"]): c["value"] for c in report["last_counters_by_thread"]} # The local probe has one worker thread. Values are cumulative and exact. worker = next(e["tid"] for e in events if e["native"] and e["event"] == "Marker" and e["data"].get("Name") == "ProbeBegin") checks = { "probe_exit_success": manifest.get("ProbeExitCode") == 0, "probe_self_checks": "P1Probe exact=OK" in log and "journal-pages=128 nat-arp-replies=32" in log, "trace_requested_and_enabled": "trace-requested=1 trace-enabled=1" in log, "probe_hash_unchanged": sha256(Path(manifest["Probe"])) == manifest["ProbeSHA256"], "probe_pdb_present_and_unchanged": bool(manifest.get("ProbePDBSHA256")) and sha256(Path(manifest["ProbePDB"])) == manifest["ProbePDBSHA256"], "etl_decoded_without_loss": decoder.get("status") == 0 and decoder.get("events_lost") == 0 and decoder.get("buffers_lost") == 0 and decoder.get("native_decode_errors") == 0, "qpc_frequency_agrees": decoder.get("qpc_frequency") == report["qpc_frequency"], "arm_jit_mapping": any(e["native"] and e["event"] == "CodeMap" and e["data"].get("Domain") == "ARM" and e["data"].get("Kind") == "thumb" for e in events), "nested_scope_consistency": report["scope_samples"] >= 128 and report["accounting_errors"] == 0, "no_counter_or_snapshot_regression": not report["counter_regressions"] and not report["snapshot_regressions"], "reset_distinct_generation": len(report["core_generations"]) >= 2, "journal_exact": counters.get((worker, 0)) == 128 and counters.get((worker, 1)) == 128 * (12 + 4 + 0x840 + 20) and counters.get((worker, 2)) == 0, "local_nat_exact": counters.get((worker, 3)) == 32 and counters.get((worker, 5)) == 32 and counters.get((worker, 7)) == 0 and counters.get((worker, 8)) == 32 and counters.get((worker, 10)) == 0 and report["usb_queue_events"] >= 64, } system_checks = { "arm_cpu_sample": report["cpu_leaf_samples"].get("ARM", 0) > 0, "known_sleep_classified_waiting": bool(report["known_sleep"] and report["known_sleep"]["wait_evidence_present"]), "xperf_clock_and_target_events": xperf.get("anchors", 0) >= 2 and xperf.get("max_anchor_error_qpc", float("inf")) <= report["qpc_frequency"] / 100_000 and xperf.get("xperf_invalid_target_events") == 0, } return { "schema": 1, "local_provider_passed": all(checks.values()), "full_collector_passed": all(checks.values()) and all(system_checks.values()), "native_update_ready": False, "provider_checks": checks, "system_checks": system_checks, "remaining": ["Qualify real PPC JIT workload and native C++ stacks", "Quantify full collector overhead on alternating fixed-work runs", "Validate CPU domain coverage and critical path on a native trace"], "analysis": report, } if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("capture", type=Path) parser.add_argument("--events", type=Path, help="Combined native and xperf JSONL; defaults to capture/events.jsonl") parser.add_argument("--xperf-summary", type=Path, help="Summary from xperf_to_p1.py for the same combined events") parser.add_argument("--output", type=Path, help="New qualification JSON path; defaults to capture/qualification.json") args = parser.parse_args() result = qualify(args.capture, args.events, args.xperf_summary) output = args.output or args.capture / "qualification.json" with output.open("x", encoding="utf-8") as target: json.dump(result, target, indent=2) print(json.dumps({"local_provider_passed": result["local_provider_passed"], "full_collector_passed": result["full_collector_passed"], "provider_checks": result["provider_checks"], "system_checks": result["system_checks"], "output": str(output)}, indent=2)) raise SystemExit(0 if result["local_provider_passed"] else 1)