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