| |
| from __future__ import annotations |
|
|
| import argparse |
| import hashlib |
| import json |
| import os |
| import shutil |
| import signal |
| import subprocess |
| import sys |
| import time |
| from pathlib import Path |
| from typing import Any |
|
|
|
|
| ROOT = Path(__file__).resolve().parent |
| ARTIFACT = ROOT / "add_overflow_oob_min.circle" |
| TFLITE_ARTIFACT = ROOT / "add_overflow_oob_min.tflite" |
|
|
|
|
| def sha256(path: Path) -> str: |
| return hashlib.sha256(path.read_bytes()).hexdigest() |
|
|
|
|
| def run(cmd: list[str], env: dict[str, str], timeout: float, cwd: Path) -> dict[str, Any]: |
| started = time.monotonic() |
| proc = subprocess.Popen( |
| cmd, |
| cwd=cwd, |
| env=env, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE, |
| text=True, |
| start_new_session=True, |
| ) |
| try: |
| stdout, stderr = proc.communicate(timeout=timeout) |
| timed_out = False |
| except subprocess.TimeoutExpired: |
| os.killpg(proc.pid, signal.SIGTERM) |
| try: |
| stdout, stderr = proc.communicate(timeout=1) |
| except subprocess.TimeoutExpired: |
| os.killpg(proc.pid, signal.SIGKILL) |
| stdout, stderr = proc.communicate() |
| timed_out = True |
|
|
| return { |
| "cmd": cmd, |
| "returncode": proc.returncode, |
| "timed_out": timed_out, |
| "elapsed_sec": round(time.monotonic() - started, 3), |
| "stdout_tail": stdout[-2000:], |
| "stderr_tail": stderr[-2000:], |
| } |
|
|
|
|
| def build_env(one_root: Path) -> dict[str, str]: |
| env = os.environ.copy() |
| env["PATH"] = f"{one_root / 'bin'}:{env.get('PATH', '')}" |
| env["LD_LIBRARY_PATH"] = f"{one_root / 'lib'}:{env.get('LD_LIBRARY_PATH', '')}" |
| env["PYTHONNOUSERSITE"] = "1" |
| return env |
|
|
|
|
| def main() -> int: |
| parser = argparse.ArgumentParser() |
| parser.add_argument( |
| "--one-root", |
| type=Path, |
| default=Path(os.environ.get("ONE_ROOT", "/workspace/circle/repos/onecc-1.30.1")), |
| help="Path to an unpacked Samsung ONE onecc release.", |
| ) |
| parser.add_argument( |
| "--out-dir", |
| type=Path, |
| default=ROOT / "verify_outputs", |
| help="Directory for generated verification outputs.", |
| ) |
| parser.add_argument("--modelscan", action="store_true", help="Run ModelScan if available.") |
| args = parser.parse_args() |
|
|
| args.out_dir.mkdir(parents=True, exist_ok=True) |
| env = build_env(args.one_root) |
| cwd = ROOT |
|
|
| results: dict[str, Any] = { |
| "artifacts": { |
| ARTIFACT.name: {"sha256": sha256(ARTIFACT), "size": ARTIFACT.stat().st_size}, |
| TFLITE_ARTIFACT.name: { |
| "sha256": sha256(TFLITE_ARTIFACT), |
| "size": TFLITE_ARTIFACT.stat().st_size, |
| }, |
| }, |
| "commands": {}, |
| } |
|
|
| results["commands"]["one_version"] = run(["one-version"], env, timeout=5, cwd=cwd) |
| results["commands"]["circle2circle_default"] = run( |
| ["circle2circle", str(ARTIFACT), str(args.out_dir / "default.circle")], |
| env, |
| timeout=5, |
| cwd=cwd, |
| ) |
| results["commands"]["circle2circle_expand_broadcast_const"] = run( |
| [ |
| "circle2circle", |
| "--expand_broadcast_const", |
| str(ARTIFACT), |
| str(args.out_dir / "expand_broadcast_const.circle"), |
| ], |
| env, |
| timeout=5, |
| cwd=cwd, |
| ) |
|
|
| one_import = args.one_root / "bin" / "one-import-tflite" |
| if one_import.exists(): |
| py = sys.executable or shutil.which("python3") or "python3" |
| imported = args.out_dir / "imported_from_tflite.circle" |
| results["commands"]["one_import_tflite"] = run( |
| [py, str(one_import), "-i", str(TFLITE_ARTIFACT), "-o", str(imported)], |
| env, |
| timeout=20, |
| cwd=cwd, |
| ) |
| if imported.exists(): |
| results["artifacts"][str(imported)] = { |
| "sha256": sha256(imported), |
| "size": imported.stat().st_size, |
| } |
| results["commands"]["imported_circle2circle_expand_broadcast_const"] = run( |
| [ |
| "circle2circle", |
| "--expand_broadcast_const", |
| str(imported), |
| str(args.out_dir / "imported_expand_broadcast_const.circle"), |
| ], |
| env, |
| timeout=5, |
| cwd=cwd, |
| ) |
|
|
| if args.modelscan: |
| modelscan = shutil.which("modelscan") or str(Path(sys.executable).parent / "modelscan") |
| if modelscan and not Path(modelscan).exists(): |
| modelscan = None |
| if modelscan: |
| results["commands"]["modelscan"] = run( |
| [modelscan, "scan", "-p", str(ARTIFACT), "-r", "json", "--show-skipped"], |
| env, |
| timeout=60, |
| cwd=cwd, |
| ) |
| else: |
| results["commands"]["modelscan"] = {"skipped": "modelscan not on PATH"} |
|
|
| default_ok = results["commands"]["circle2circle_default"]["returncode"] == 0 |
| crash_rc = results["commands"]["circle2circle_expand_broadcast_const"]["returncode"] |
| crash_ok = crash_rc in {-11, 139} |
| imported_result = results["commands"].get("imported_circle2circle_expand_broadcast_const") |
| imported_ok = not imported_result or imported_result.get("returncode") in {-11, 139} |
|
|
| results["verdict"] = { |
| "default_path_ok": default_ok, |
| "expand_broadcast_const_sigsegv": crash_ok, |
| "imported_tflite_preserves_crash": imported_ok, |
| "passed": default_ok and crash_ok and imported_ok, |
| } |
|
|
| print(json.dumps(results, indent=2)) |
| return 0 if results["verdict"]["passed"] else 1 |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit(main()) |
|
|