Files
ppsspp/Tools/cpu_fuzz_parallel.py

72 lines
2.6 KiB
Python
Executable File

#!/usr/bin/env python3
# Runs the CPUEquivalence fuzzer in several processes, each on its own range of seeds, and sums
# up the results. A case depends only on its seed, so this finds the same differences as one run.
#
# Usage: cpu_fuzz_parallel.py UNITTEST_EXE [--count N] [--seed S] [-j JOBS]
# The PPSSPP_CPUFUZZ_* environment variables other than COUNT and SEED pass through, so
# PPSSPP_CPUFUZZ_OPS=dot cpu_fuzz_parallel.py build/PPSSPPUnitTest
# fuzzes the dot products.
import argparse
import os
import re
import subprocess
import sys
from collections import Counter
def main():
parser = argparse.ArgumentParser()
parser.add_argument("exe")
parser.add_argument("--count", type=int, default=2000)
parser.add_argument("--seed", type=int, default=1)
parser.add_argument("-j", "--jobs", type=int, default=os.cpu_count())
args = parser.parse_args()
jobs = max(1, min(args.jobs, args.count))
procs = []
start = args.seed
for j in range(jobs):
n = args.count // jobs + (1 if j < args.count % jobs else 0)
env = dict(os.environ, PPSSPP_CPUFUZZ_SEED=str(start), PPSSPP_CPUFUZZ_COUNT=str(n))
procs.append((start, subprocess.Popen([args.exe, "CPUEquivalence"], env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)))
start += n
differ = Counter()
culprits = Counter()
failed = False
for first, p in procs:
out, _ = p.communicate()
if p.returncode != 0:
failed = True
print("Seeds from %d: exit code %d" % (first, p.returncode))
in_culprits = False
for line in out.splitlines():
m = re.match(r"(.+): (\d+) of \d+ cases differ", line)
if m:
differ[m.group(1)] += int(m.group(2))
in_culprits = False
continue
if line.startswith("Reported differences by last instruction"):
in_culprits = True
continue
m = re.match(r"\s+(\d+)\s+(.+)$", line)
if in_culprits and m:
culprits[m.group(2)] += int(m.group(1))
continue
# The minimized reports.
if line.startswith(("Seed ", " ")):
print(line)
if culprits:
print("Reported differences by last instruction:")
for name, n in culprits.most_common():
print("%6d %s" % (n, name))
for core, n in differ.items():
print("%s: %d of %d cases differ from the interpreter" % (core, n, args.count))
return 1 if failed else 0
if __name__ == "__main__":
sys.exit(main())