1*abe9b37fSMatt Johnston#!/usr/bin/env python3 2*abe9b37fSMatt Johnston# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later 3*abe9b37fSMatt Johnston 4*abe9b37fSMatt Johnston# usage: fuzz-coverage.py [-h] corpus program srcdir builddir outdir 5*abe9b37fSMatt Johnston 6*abe9b37fSMatt Johnston# Runs corpus (directory of testcases) against a program 7*abe9b37fSMatt Johnston# built with coverage, and produces a html report. 8*abe9b37fSMatt Johnston 9*abe9b37fSMatt Johnston# The program should be built with --coverage -fprofile-abs-path 10*abe9b37fSMatt Johnston# -O0 may make the html report more legible? 11*abe9b37fSMatt Johnston 12*abe9b37fSMatt Johnston# Requires lcov and https://github.com/mozilla/grcov 13*abe9b37fSMatt Johnston 14*abe9b37fSMatt Johnstonimport argparse 15*abe9b37fSMatt Johnstonimport subprocess 16*abe9b37fSMatt Johnstonimport sys 17*abe9b37fSMatt Johnstonfrom pathlib import Path 18*abe9b37fSMatt Johnston 19*abe9b37fSMatt Johnston 20*abe9b37fSMatt Johnstondef run(args): 21*abe9b37fSMatt Johnston corpus = Path(args.corpus) 22*abe9b37fSMatt Johnston outdir = Path(args.outdir) 23*abe9b37fSMatt Johnston 24*abe9b37fSMatt Johnston for c in Path(args.builddir).glob("**/*.gcda"): 25*abe9b37fSMatt Johnston print(f"Removed old coverage {c}", file=sys.stderr) 26*abe9b37fSMatt Johnston c.unlink() 27*abe9b37fSMatt Johnston 28*abe9b37fSMatt Johnston print("Running corpus", file=sys.stderr) 29*abe9b37fSMatt Johnston for c in corpus.glob("*"): 30*abe9b37fSMatt Johnston c = c.open("rb").read() 31*abe9b37fSMatt Johnston subprocess.run([args.program], input=c) 32*abe9b37fSMatt Johnston 33*abe9b37fSMatt Johnston print("Running grcov", file=sys.stderr) 34*abe9b37fSMatt Johnston outdir.mkdir(parents=True, exist_ok=True) 35*abe9b37fSMatt Johnston coverage_paths = [args.builddir] 36*abe9b37fSMatt Johnston lcov_file = outdir / "lcov.info" 37*abe9b37fSMatt Johnston 38*abe9b37fSMatt Johnston subprocess.run( 39*abe9b37fSMatt Johnston [ 40*abe9b37fSMatt Johnston "grcov", 41*abe9b37fSMatt Johnston "-b", 42*abe9b37fSMatt Johnston args.program, 43*abe9b37fSMatt Johnston "-o", 44*abe9b37fSMatt Johnston lcov_file, 45*abe9b37fSMatt Johnston "-t", 46*abe9b37fSMatt Johnston "lcov", 47*abe9b37fSMatt Johnston "-s", 48*abe9b37fSMatt Johnston args.srcdir, 49*abe9b37fSMatt Johnston ] 50*abe9b37fSMatt Johnston + coverage_paths, 51*abe9b37fSMatt Johnston check=True, 52*abe9b37fSMatt Johnston ) 53*abe9b37fSMatt Johnston 54*abe9b37fSMatt Johnston print("Running genhtml", file=sys.stderr) 55*abe9b37fSMatt Johnston subprocess.run( 56*abe9b37fSMatt Johnston [ 57*abe9b37fSMatt Johnston "genhtml", 58*abe9b37fSMatt Johnston "-o", 59*abe9b37fSMatt Johnston outdir, 60*abe9b37fSMatt Johnston "--show-details", 61*abe9b37fSMatt Johnston "--highlight", 62*abe9b37fSMatt Johnston "--ignore-errors", 63*abe9b37fSMatt Johnston "source", 64*abe9b37fSMatt Johnston "--legend", 65*abe9b37fSMatt Johnston lcov_file, 66*abe9b37fSMatt Johnston ], 67*abe9b37fSMatt Johnston check=True, 68*abe9b37fSMatt Johnston ) 69*abe9b37fSMatt Johnston 70*abe9b37fSMatt Johnston html = outdir / "index.html" 71*abe9b37fSMatt Johnston print(f"\n\nOutput is file://{html.absolute()}", file=sys.stderr) 72*abe9b37fSMatt Johnston 73*abe9b37fSMatt Johnston 74*abe9b37fSMatt Johnstondef main(): 75*abe9b37fSMatt Johnston parser = argparse.ArgumentParser() 76*abe9b37fSMatt Johnston parser.add_argument("corpus", type=str, help="Corpus directory") 77*abe9b37fSMatt Johnston parser.add_argument("program", type=str, help="Target Program") 78*abe9b37fSMatt Johnston parser.add_argument("srcdir", type=str, help="Source directory") 79*abe9b37fSMatt Johnston parser.add_argument("builddir", type=str) 80*abe9b37fSMatt Johnston parser.add_argument("outdir", type=str) 81*abe9b37fSMatt Johnston args = parser.parse_args() 82*abe9b37fSMatt Johnston 83*abe9b37fSMatt Johnston run(args) 84*abe9b37fSMatt Johnston 85*abe9b37fSMatt Johnston 86*abe9b37fSMatt Johnstonif __name__ == "__main__": 87*abe9b37fSMatt Johnston main() 88