1*08b48e0bSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*08b48e0bSAndroid Build Coastguard Worker# Part of the aflplusplus project, requires Python 3.8+. 3*08b48e0bSAndroid Build Coastguard Worker# Author: Chris Ball <[email protected]>, ported from Marc "van Hauser" Heuse's "benchmark.sh". 4*08b48e0bSAndroid Build Coastguard Workerimport argparse, asyncio, json, multiprocessing, os, platform, re, shutil, sys 5*08b48e0bSAndroid Build Coastguard Workerfrom dataclasses import asdict, dataclass 6*08b48e0bSAndroid Build Coastguard Workerfrom decimal import Decimal 7*08b48e0bSAndroid Build Coastguard Workerfrom enum import Enum, auto 8*08b48e0bSAndroid Build Coastguard Workerfrom pathlib import Path 9*08b48e0bSAndroid Build Coastguard Workerfrom typing import Dict, List, Optional, Tuple 10*08b48e0bSAndroid Build Coastguard Worker 11*08b48e0bSAndroid Build Coastguard Workerblue = lambda text: f"\033[1;94m{text}\033[0m"; gray = lambda text: f"\033[1;90m{text}\033[0m" 12*08b48e0bSAndroid Build Coastguard Workergreen = lambda text: f"\033[0;32m{text}\033[0m"; red = lambda text: f"\033[0;31m{text}\033[0m" 13*08b48e0bSAndroid Build Coastguard Workeryellow = lambda text: f"\033[0;33m{text}\033[0m" 14*08b48e0bSAndroid Build Coastguard Worker 15*08b48e0bSAndroid Build Coastguard Workerclass Mode(Enum): 16*08b48e0bSAndroid Build Coastguard Worker multicore = auto() 17*08b48e0bSAndroid Build Coastguard Worker singlecore = auto() 18*08b48e0bSAndroid Build Coastguard Worker 19*08b48e0bSAndroid Build Coastguard Worker@dataclass 20*08b48e0bSAndroid Build Coastguard Workerclass Target: 21*08b48e0bSAndroid Build Coastguard Worker source: Path 22*08b48e0bSAndroid Build Coastguard Worker binary: Path 23*08b48e0bSAndroid Build Coastguard Worker 24*08b48e0bSAndroid Build Coastguard Worker@dataclass 25*08b48e0bSAndroid Build Coastguard Workerclass Run: 26*08b48e0bSAndroid Build Coastguard Worker execs_per_sec: float 27*08b48e0bSAndroid Build Coastguard Worker execs_total: float 28*08b48e0bSAndroid Build Coastguard Worker fuzzers_used: int 29*08b48e0bSAndroid Build Coastguard Worker 30*08b48e0bSAndroid Build Coastguard Worker@dataclass 31*08b48e0bSAndroid Build Coastguard Workerclass Config: 32*08b48e0bSAndroid Build Coastguard Worker afl_persistent_config: bool 33*08b48e0bSAndroid Build Coastguard Worker afl_system_config: bool 34*08b48e0bSAndroid Build Coastguard Worker afl_version: Optional[str] 35*08b48e0bSAndroid Build Coastguard Worker comment: str 36*08b48e0bSAndroid Build Coastguard Worker compiler: str 37*08b48e0bSAndroid Build Coastguard Worker target_arch: str 38*08b48e0bSAndroid Build Coastguard Worker 39*08b48e0bSAndroid Build Coastguard Worker@dataclass 40*08b48e0bSAndroid Build Coastguard Workerclass Hardware: 41*08b48e0bSAndroid Build Coastguard Worker cpu_fastest_core_mhz: float 42*08b48e0bSAndroid Build Coastguard Worker cpu_model: str 43*08b48e0bSAndroid Build Coastguard Worker cpu_threads: int 44*08b48e0bSAndroid Build Coastguard Worker 45*08b48e0bSAndroid Build Coastguard Worker@dataclass 46*08b48e0bSAndroid Build Coastguard Workerclass Results: 47*08b48e0bSAndroid Build Coastguard Worker config: Optional[Config] 48*08b48e0bSAndroid Build Coastguard Worker hardware: Optional[Hardware] 49*08b48e0bSAndroid Build Coastguard Worker targets: Dict[str, Dict[str, Optional[Run]]] 50*08b48e0bSAndroid Build Coastguard Worker 51*08b48e0bSAndroid Build Coastguard Workerall_modes = [Mode.singlecore, Mode.multicore] 52*08b48e0bSAndroid Build Coastguard Workerall_targets = [ 53*08b48e0bSAndroid Build Coastguard Worker Target(source=Path("../utils/persistent_mode/test-instr.c").resolve(), binary=Path("test-instr-persist-shmem")), 54*08b48e0bSAndroid Build Coastguard Worker Target(source=Path("../test-instr.c").resolve(), binary=Path("test-instr")) 55*08b48e0bSAndroid Build Coastguard Worker] 56*08b48e0bSAndroid Build Coastguard Workermodes = [mode.name for mode in all_modes] 57*08b48e0bSAndroid Build Coastguard Workertargets = [str(target.binary) for target in all_targets] 58*08b48e0bSAndroid Build Coastguard Workercpu_count = multiprocessing.cpu_count() 59*08b48e0bSAndroid Build Coastguard Workerenv_vars = { 60*08b48e0bSAndroid Build Coastguard Worker "AFL_DISABLE_TRIM": "1", "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES": "1", "AFL_FAST_CAL": "1", 61*08b48e0bSAndroid Build Coastguard Worker "AFL_NO_UI": "1", "AFL_TRY_AFFINITY": "1", "PATH": f'{str(Path("../").resolve())}:{os.environ["PATH"]}', 62*08b48e0bSAndroid Build Coastguard Worker} 63*08b48e0bSAndroid Build Coastguard Worker 64*08b48e0bSAndroid Build Coastguard Workerparser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) 65*08b48e0bSAndroid Build Coastguard Workerparser.add_argument("-b", "--basedir", help="directory to use for temp files", type=str, default="/tmp/aflpp-benchmark") 66*08b48e0bSAndroid Build Coastguard Workerparser.add_argument("-d", "--debug", help="show verbose debugging output", action="store_true") 67*08b48e0bSAndroid Build Coastguard Workerparser.add_argument("-r", "--runs", help="how many runs to average results over", type=int, default=3) 68*08b48e0bSAndroid Build Coastguard Workerparser.add_argument("-f", "--fuzzers", help="how many afl-fuzz workers to use", type=int, default=cpu_count) 69*08b48e0bSAndroid Build Coastguard Workerparser.add_argument("-m", "--mode", help="pick modes", action="append", default=modes, choices=modes) 70*08b48e0bSAndroid Build Coastguard Workerparser.add_argument("-c", "--comment", help="add a comment about your setup", type=str, default="") 71*08b48e0bSAndroid Build Coastguard Workerparser.add_argument("--cpu", help="override the detected CPU model name", type=str, default="") 72*08b48e0bSAndroid Build Coastguard Workerparser.add_argument("--mhz", help="override the detected CPU MHz", type=str, default="") 73*08b48e0bSAndroid Build Coastguard Workerparser.add_argument( 74*08b48e0bSAndroid Build Coastguard Worker "-t", "--target", help="pick targets", action="append", default=["test-instr-persist-shmem"], choices=targets 75*08b48e0bSAndroid Build Coastguard Worker) 76*08b48e0bSAndroid Build Coastguard Workerargs = parser.parse_args() 77*08b48e0bSAndroid Build Coastguard Worker# Really unsatisfying argparse behavior: we want a default and to allow multiple choices, but if there's a manual choice 78*08b48e0bSAndroid Build Coastguard Worker# it should override the default. Seems like we have to remove the default to get that and have correct help text? 79*08b48e0bSAndroid Build Coastguard Workerif len(args.target) > 1: 80*08b48e0bSAndroid Build Coastguard Worker args.target = args.target[1:] 81*08b48e0bSAndroid Build Coastguard Workerif len(args.mode) > 2: 82*08b48e0bSAndroid Build Coastguard Worker args.mode = args.mode[2:] 83*08b48e0bSAndroid Build Coastguard Worker 84*08b48e0bSAndroid Build Coastguard Workerchosen_modes = [mode for mode in all_modes if mode.name in args.mode] 85*08b48e0bSAndroid Build Coastguard Workerchosen_targets = [target for target in all_targets if str(target.binary) in args.target] 86*08b48e0bSAndroid Build Coastguard Workerresults = Results(config=None, hardware=None, targets={ 87*08b48e0bSAndroid Build Coastguard Worker str(t.binary): {m.name: None for m in chosen_modes} for t in chosen_targets} 88*08b48e0bSAndroid Build Coastguard Worker) 89*08b48e0bSAndroid Build Coastguard Workerdebug = lambda text: args.debug and print(blue(text)) 90*08b48e0bSAndroid Build Coastguard Worker 91*08b48e0bSAndroid Build Coastguard Workerasync def clean_up_tempfiles() -> None: 92*08b48e0bSAndroid Build Coastguard Worker shutil.rmtree(f"{args.basedir}/in") 93*08b48e0bSAndroid Build Coastguard Worker for target in chosen_targets: 94*08b48e0bSAndroid Build Coastguard Worker target.binary.unlink() 95*08b48e0bSAndroid Build Coastguard Worker for mode in chosen_modes: 96*08b48e0bSAndroid Build Coastguard Worker shutil.rmtree(f"{args.basedir}/out-{mode.name}-{str(target.binary)}") 97*08b48e0bSAndroid Build Coastguard Worker 98*08b48e0bSAndroid Build Coastguard Workerasync def check_afl_persistent() -> bool: 99*08b48e0bSAndroid Build Coastguard Worker with open("/proc/cmdline", "r") as cmdline: 100*08b48e0bSAndroid Build Coastguard Worker return "mitigations=off" in cmdline.read().strip().split(" ") 101*08b48e0bSAndroid Build Coastguard Worker 102*08b48e0bSAndroid Build Coastguard Workerasync def check_afl_system() -> bool: 103*08b48e0bSAndroid Build Coastguard Worker sysctl = next((s for s in ["sysctl", "/sbin/sysctl"] if shutil.which(s)), None) 104*08b48e0bSAndroid Build Coastguard Worker if sysctl: 105*08b48e0bSAndroid Build Coastguard Worker (returncode, stdout, _) = await run_command([sysctl, "kernel.randomize_va_space"]) 106*08b48e0bSAndroid Build Coastguard Worker return returncode == 0 and stdout.decode().rstrip().split(" = ")[1] == "0" 107*08b48e0bSAndroid Build Coastguard Worker return False 108*08b48e0bSAndroid Build Coastguard Worker 109*08b48e0bSAndroid Build Coastguard Workerasync def prep_env() -> None: 110*08b48e0bSAndroid Build Coastguard Worker Path(f"{args.basedir}/in").mkdir(exist_ok=True, parents=True) 111*08b48e0bSAndroid Build Coastguard Worker with open(f"{args.basedir}/in/in.txt", "wb") as seed: 112*08b48e0bSAndroid Build Coastguard Worker seed.write(b"\x00" * 10240) 113*08b48e0bSAndroid Build Coastguard Worker 114*08b48e0bSAndroid Build Coastguard Workerasync def compile_target(source: Path, binary: Path) -> None: 115*08b48e0bSAndroid Build Coastguard Worker print(f" [*] Compiling the {binary} fuzzing harness for the benchmark to use.") 116*08b48e0bSAndroid Build Coastguard Worker (returncode, stdout, stderr) = await run_command( 117*08b48e0bSAndroid Build Coastguard Worker [str(Path("../afl-clang-lto").resolve()), "-o", str(Path(binary.resolve())), str(Path(source).resolve())] 118*08b48e0bSAndroid Build Coastguard Worker ) 119*08b48e0bSAndroid Build Coastguard Worker if returncode == 0: 120*08b48e0bSAndroid Build Coastguard Worker return 121*08b48e0bSAndroid Build Coastguard Worker print(yellow(f" [*] afl-clang-lto was unable to compile; falling back to afl-cc.")) 122*08b48e0bSAndroid Build Coastguard Worker (returncode, stdout, stderr) = await run_command( 123*08b48e0bSAndroid Build Coastguard Worker [str(Path("../afl-cc").resolve()), "-o", str(Path(binary.resolve())), str(Path(source).resolve())] 124*08b48e0bSAndroid Build Coastguard Worker ) 125*08b48e0bSAndroid Build Coastguard Worker if returncode != 0: 126*08b48e0bSAndroid Build Coastguard Worker sys.exit(red(f" [*] Error: afl-cc is unable to compile: {stderr.decode()} {stdout.decode()}")) 127*08b48e0bSAndroid Build Coastguard Worker 128*08b48e0bSAndroid Build Coastguard Workerasync def run_command(cmd: List[str]) -> Tuple[Optional[int], bytes, bytes]: 129*08b48e0bSAndroid Build Coastguard Worker debug(f"Launching command: {cmd} with env {env_vars}") 130*08b48e0bSAndroid Build Coastguard Worker p = await asyncio.create_subprocess_exec( 131*08b48e0bSAndroid Build Coastguard Worker *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, env=env_vars 132*08b48e0bSAndroid Build Coastguard Worker ) 133*08b48e0bSAndroid Build Coastguard Worker stdout, stderr = await p.communicate() 134*08b48e0bSAndroid Build Coastguard Worker debug(f"Output: {stdout.decode()} {stderr.decode()}") 135*08b48e0bSAndroid Build Coastguard Worker return (p.returncode, stdout, stderr) 136*08b48e0bSAndroid Build Coastguard Worker 137*08b48e0bSAndroid Build Coastguard Workerasync def check_deps() -> None: 138*08b48e0bSAndroid Build Coastguard Worker if not (plat := platform.system()) == "Linux": sys.exit(red(f" [*] {plat} is not supported by this script yet.")) 139*08b48e0bSAndroid Build Coastguard Worker if not os.access(Path("../afl-fuzz").resolve(), os.X_OK) and os.access(Path("../afl-cc").resolve(), os.X_OK) and ( 140*08b48e0bSAndroid Build Coastguard Worker os.path.exists(Path("../SanitizerCoveragePCGUARD.so").resolve())): 141*08b48e0bSAndroid Build Coastguard Worker sys.exit(red(" [*] Compile AFL++: we need afl-fuzz, afl-clang-fast and SanitizerCoveragePCGUARD.so built.")) 142*08b48e0bSAndroid Build Coastguard Worker 143*08b48e0bSAndroid Build Coastguard Worker (returncode, stdout, stderr) = await run_command([str(Path("../afl-cc").resolve()), "-v"]) 144*08b48e0bSAndroid Build Coastguard Worker if returncode != 0: 145*08b48e0bSAndroid Build Coastguard Worker sys.exit(red(f" [*] Error: afl-cc -v returned: {stderr.decode()} {stdout.decode()}")) 146*08b48e0bSAndroid Build Coastguard Worker compiler = "" 147*08b48e0bSAndroid Build Coastguard Worker target_arch = "" 148*08b48e0bSAndroid Build Coastguard Worker for line in stderr.decode().split("\n"): 149*08b48e0bSAndroid Build Coastguard Worker if "clang version" in line: 150*08b48e0bSAndroid Build Coastguard Worker compiler = line 151*08b48e0bSAndroid Build Coastguard Worker elif m := re.match(r"^Target: (.*)", line): 152*08b48e0bSAndroid Build Coastguard Worker target_arch = m.group(1) 153*08b48e0bSAndroid Build Coastguard Worker 154*08b48e0bSAndroid Build Coastguard Worker # Pick some sample settings from afl-{persistent,system}-config to try to see whether they were run. 155*08b48e0bSAndroid Build Coastguard Worker afl_pc = await check_afl_persistent() 156*08b48e0bSAndroid Build Coastguard Worker afl_sc = await check_afl_system() 157*08b48e0bSAndroid Build Coastguard Worker if not afl_pc: 158*08b48e0bSAndroid Build Coastguard Worker print(yellow(f" [*] afl-persistent-config did not run; run it to improve performance (and decrease security).")) 159*08b48e0bSAndroid Build Coastguard Worker if not afl_sc: 160*08b48e0bSAndroid Build Coastguard Worker print(yellow(f" [*] afl-system-config did not run; run it to improve performance (and decrease security).")) 161*08b48e0bSAndroid Build Coastguard Worker results.config = Config(afl_persistent_config=afl_pc, afl_system_config=afl_sc, afl_version="", 162*08b48e0bSAndroid Build Coastguard Worker comment=args.comment, compiler=compiler, target_arch=target_arch) 163*08b48e0bSAndroid Build Coastguard Worker 164*08b48e0bSAndroid Build Coastguard Workerasync def colon_values(filename: str, searchKey: str) -> List[str]: 165*08b48e0bSAndroid Build Coastguard Worker """Return a colon-separated value given a key in a file, e.g. 'cpu MHz : 4976.109')""" 166*08b48e0bSAndroid Build Coastguard Worker with open(filename, "r") as fh: 167*08b48e0bSAndroid Build Coastguard Worker kv_pairs = (line.split(": ", 1) for line in fh if ": " in line) 168*08b48e0bSAndroid Build Coastguard Worker v_list = [v.rstrip() for k, v in kv_pairs if k.rstrip() == searchKey] 169*08b48e0bSAndroid Build Coastguard Worker return v_list 170*08b48e0bSAndroid Build Coastguard Worker 171*08b48e0bSAndroid Build Coastguard Workerasync def describe_afl_config() -> str: 172*08b48e0bSAndroid Build Coastguard Worker if results.config is None: 173*08b48e0bSAndroid Build Coastguard Worker return "unknown" 174*08b48e0bSAndroid Build Coastguard Worker elif results.config.afl_persistent_config and results.config.afl_system_config: 175*08b48e0bSAndroid Build Coastguard Worker return "both" 176*08b48e0bSAndroid Build Coastguard Worker elif results.config.afl_persistent_config: 177*08b48e0bSAndroid Build Coastguard Worker return "persistent" 178*08b48e0bSAndroid Build Coastguard Worker elif results.config.afl_system_config: 179*08b48e0bSAndroid Build Coastguard Worker return "system" 180*08b48e0bSAndroid Build Coastguard Worker else: 181*08b48e0bSAndroid Build Coastguard Worker return "none" 182*08b48e0bSAndroid Build Coastguard Worker 183*08b48e0bSAndroid Build Coastguard Workerasync def save_benchmark_results() -> None: 184*08b48e0bSAndroid Build Coastguard Worker """Append a single row to the benchmark results in JSON Lines format (which is simple to write and diff).""" 185*08b48e0bSAndroid Build Coastguard Worker with open("benchmark-results.jsonl", "a") as jsonfile: 186*08b48e0bSAndroid Build Coastguard Worker json.dump(asdict(results), jsonfile, sort_keys=True) 187*08b48e0bSAndroid Build Coastguard Worker jsonfile.write("\n") 188*08b48e0bSAndroid Build Coastguard Worker print(blue(f" [*] Results have been written to the {jsonfile.name} file.")) 189*08b48e0bSAndroid Build Coastguard Worker with open("COMPARISON.md", "r+") as comparisonfile: 190*08b48e0bSAndroid Build Coastguard Worker described_config = await describe_afl_config() 191*08b48e0bSAndroid Build Coastguard Worker aflconfig = described_config.ljust(12) 192*08b48e0bSAndroid Build Coastguard Worker if results.hardware is None: 193*08b48e0bSAndroid Build Coastguard Worker return 194*08b48e0bSAndroid Build Coastguard Worker cpu_model = results.hardware.cpu_model.ljust(51) 195*08b48e0bSAndroid Build Coastguard Worker if cpu_model in comparisonfile.read(): 196*08b48e0bSAndroid Build Coastguard Worker print(blue(f" [*] Results have not been written to the COMPARISON.md file; this CPU is already present.")) 197*08b48e0bSAndroid Build Coastguard Worker return 198*08b48e0bSAndroid Build Coastguard Worker cpu_mhz = str(round(results.hardware.cpu_fastest_core_mhz)).ljust(5) 199*08b48e0bSAndroid Build Coastguard Worker if not "test-instr-persist-shmem" in results.targets or \ 200*08b48e0bSAndroid Build Coastguard Worker not "multicore" in results.targets["test-instr-persist-shmem"] or \ 201*08b48e0bSAndroid Build Coastguard Worker not "singlecore" in results.targets["test-instr-persist-shmem"] or \ 202*08b48e0bSAndroid Build Coastguard Worker results.targets["test-instr-persist-shmem"]["singlecore"] is None or \ 203*08b48e0bSAndroid Build Coastguard Worker results.targets["test-instr-persist-shmem"]["multicore"] is None: 204*08b48e0bSAndroid Build Coastguard Worker return 205*08b48e0bSAndroid Build Coastguard Worker single = str(round(results.targets["test-instr-persist-shmem"]["singlecore"].execs_per_sec)).ljust(10) 206*08b48e0bSAndroid Build Coastguard Worker multi = str(round(results.targets["test-instr-persist-shmem"]["multicore"].execs_per_sec)).ljust(9) 207*08b48e0bSAndroid Build Coastguard Worker cores = str(args.fuzzers).ljust(7) 208*08b48e0bSAndroid Build Coastguard Worker comparisonfile.write(f"{cpu_model} | {cpu_mhz} | {cores} | {single} | {multi} | {aflconfig} |\n") 209*08b48e0bSAndroid Build Coastguard Worker print(blue(f" [*] Results have been written to the COMPARISON.md file.")) 210*08b48e0bSAndroid Build Coastguard Worker with open("COMPARISON.md", "r") as comparisonfile: 211*08b48e0bSAndroid Build Coastguard Worker print(comparisonfile.read()) 212*08b48e0bSAndroid Build Coastguard Worker 213*08b48e0bSAndroid Build Coastguard Worker 214*08b48e0bSAndroid Build Coastguard Workerasync def main() -> None: 215*08b48e0bSAndroid Build Coastguard Worker try: 216*08b48e0bSAndroid Build Coastguard Worker await clean_up_tempfiles() 217*08b48e0bSAndroid Build Coastguard Worker except FileNotFoundError: 218*08b48e0bSAndroid Build Coastguard Worker pass 219*08b48e0bSAndroid Build Coastguard Worker await check_deps() 220*08b48e0bSAndroid Build Coastguard Worker if args.mhz: 221*08b48e0bSAndroid Build Coastguard Worker cpu_mhz = float(args.mhz) 222*08b48e0bSAndroid Build Coastguard Worker else: 223*08b48e0bSAndroid Build Coastguard Worker cpu_mhz_str = await colon_values("/proc/cpuinfo", "cpu MHz") 224*08b48e0bSAndroid Build Coastguard Worker if len(cpu_mhz_str) == 0: 225*08b48e0bSAndroid Build Coastguard Worker cpu_mhz_str.append("0") 226*08b48e0bSAndroid Build Coastguard Worker cpu_mhz = max([float(c) for c in cpu_mhz_str]) # use the fastest CPU MHz for now 227*08b48e0bSAndroid Build Coastguard Worker if args.cpu: 228*08b48e0bSAndroid Build Coastguard Worker cpu_model = [args.cpu] 229*08b48e0bSAndroid Build Coastguard Worker else: 230*08b48e0bSAndroid Build Coastguard Worker cpu_model = await colon_values("/proc/cpuinfo", "model name") or [""] 231*08b48e0bSAndroid Build Coastguard Worker results.hardware = Hardware(cpu_fastest_core_mhz=cpu_mhz, cpu_model=cpu_model[0], cpu_threads=cpu_count) 232*08b48e0bSAndroid Build Coastguard Worker await prep_env() 233*08b48e0bSAndroid Build Coastguard Worker print(f" [*] Ready, starting benchmark...") 234*08b48e0bSAndroid Build Coastguard Worker for target in chosen_targets: 235*08b48e0bSAndroid Build Coastguard Worker await compile_target(target.source, target.binary) 236*08b48e0bSAndroid Build Coastguard Worker binary = str(target.binary) 237*08b48e0bSAndroid Build Coastguard Worker for mode in chosen_modes: 238*08b48e0bSAndroid Build Coastguard Worker if mode == Mode.multicore: 239*08b48e0bSAndroid Build Coastguard Worker print(blue(f" [*] Using {args.fuzzers} fuzzers for multicore fuzzing "), end="") 240*08b48e0bSAndroid Build Coastguard Worker print(blue("(use --fuzzers to override)." if args.fuzzers == cpu_count else f"(the default is {cpu_count})")) 241*08b48e0bSAndroid Build Coastguard Worker execs_per_sec, execs_total = ([] for _ in range(2)) 242*08b48e0bSAndroid Build Coastguard Worker for run_idx in range(0, args.runs): 243*08b48e0bSAndroid Build Coastguard Worker print(gray(f" [*] {mode.name} {binary} run {run_idx+1} of {args.runs}, execs/s: "), end="", flush=True) 244*08b48e0bSAndroid Build Coastguard Worker fuzzers = range(0, args.fuzzers if mode == Mode.multicore else 1) 245*08b48e0bSAndroid Build Coastguard Worker outdir = f"{args.basedir}/out-{mode.name}-{binary}" 246*08b48e0bSAndroid Build Coastguard Worker cmds = [] 247*08b48e0bSAndroid Build Coastguard Worker for fuzzer_idx, afl in enumerate(fuzzers): 248*08b48e0bSAndroid Build Coastguard Worker name = ["-o", outdir, "-M" if fuzzer_idx == 0 else "-S", str(afl)] 249*08b48e0bSAndroid Build Coastguard Worker cmds.append(["afl-fuzz", "-i", f"{args.basedir}/in"] + name + ["-s", "123", "-V10", "-D", f"./{binary}"]) 250*08b48e0bSAndroid Build Coastguard Worker # Prepare the afl-fuzz tasks, and then block while waiting for them to finish. 251*08b48e0bSAndroid Build Coastguard Worker fuzztasks = [run_command(cmds[cpu]) for cpu in fuzzers] 252*08b48e0bSAndroid Build Coastguard Worker await asyncio.gather(*fuzztasks) 253*08b48e0bSAndroid Build Coastguard Worker afl_versions = await colon_values(f"{outdir}/0/fuzzer_stats", "afl_version") 254*08b48e0bSAndroid Build Coastguard Worker if results.config: 255*08b48e0bSAndroid Build Coastguard Worker results.config.afl_version = afl_versions[0] 256*08b48e0bSAndroid Build Coastguard Worker # Our score is the sum of all execs_per_sec entries in fuzzer_stats files for the run. 257*08b48e0bSAndroid Build Coastguard Worker sectasks = [colon_values(f"{outdir}/{afl}/fuzzer_stats", "execs_per_sec") for afl in fuzzers] 258*08b48e0bSAndroid Build Coastguard Worker all_execs_per_sec = await asyncio.gather(*sectasks) 259*08b48e0bSAndroid Build Coastguard Worker execs = sum([Decimal(count[0]) for count in all_execs_per_sec]) 260*08b48e0bSAndroid Build Coastguard Worker print(green(execs)) 261*08b48e0bSAndroid Build Coastguard Worker execs_per_sec.append(execs) 262*08b48e0bSAndroid Build Coastguard Worker # Also gather execs_total and total_run_time for this run. 263*08b48e0bSAndroid Build Coastguard Worker exectasks = [colon_values(f"{outdir}/{afl}/fuzzer_stats", "execs_done") for afl in fuzzers] 264*08b48e0bSAndroid Build Coastguard Worker all_execs_total = await asyncio.gather(*exectasks) 265*08b48e0bSAndroid Build Coastguard Worker execs_total.append(sum([Decimal(count[0]) for count in all_execs_total])) 266*08b48e0bSAndroid Build Coastguard Worker 267*08b48e0bSAndroid Build Coastguard Worker # (Using float() because Decimal() is not JSON-serializable.) 268*08b48e0bSAndroid Build Coastguard Worker avg_afl_execs_per_sec = round(Decimal(sum(execs_per_sec) / len(execs_per_sec)), 2) 269*08b48e0bSAndroid Build Coastguard Worker afl_execs_total = int(sum([Decimal(execs) for execs in execs_total])) 270*08b48e0bSAndroid Build Coastguard Worker run = Run(execs_per_sec=float(avg_afl_execs_per_sec), execs_total=afl_execs_total, fuzzers_used=len(fuzzers)) 271*08b48e0bSAndroid Build Coastguard Worker results.targets[binary][mode.name] = run 272*08b48e0bSAndroid Build Coastguard Worker print(f" [*] Average execs/sec for this test across all runs was: {green(avg_afl_execs_per_sec)}") 273*08b48e0bSAndroid Build Coastguard Worker if (((max(execs_per_sec) - min(execs_per_sec)) / avg_afl_execs_per_sec) * 100) > 15: 274*08b48e0bSAndroid Build Coastguard Worker print(yellow(" [*] The difference between your slowest and fastest runs was >15%, maybe try again?")) 275*08b48e0bSAndroid Build Coastguard Worker 276*08b48e0bSAndroid Build Coastguard Worker await clean_up_tempfiles() 277*08b48e0bSAndroid Build Coastguard Worker await save_benchmark_results() 278*08b48e0bSAndroid Build Coastguard Worker 279*08b48e0bSAndroid Build Coastguard Workerif __name__ == "__main__": 280*08b48e0bSAndroid Build Coastguard Worker asyncio.run(main()) 281*08b48e0bSAndroid Build Coastguard Worker 282