| Matt Johnston | 060c71e | 2025-01-08 14:35:13 +0800 | [diff] [blame] | 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 |  | 
|  | 14 | import argparse | 
|  | 15 | import subprocess | 
|  | 16 | import sys | 
|  | 17 | from pathlib import Path | 
|  | 18 |  | 
|  | 19 |  | 
|  | 20 | def 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 | "--ignore-errors", | 
|  | 65 | "unmapped", | 
|  | 66 | "--legend", | 
|  | 67 | lcov_file, | 
|  | 68 | ], | 
|  | 69 | check=True, | 
|  | 70 | ) | 
|  | 71 |  | 
|  | 72 | html = outdir / "index.html" | 
|  | 73 | print(f"\n\nOutput is file://{html.absolute()}", file=sys.stderr) | 
|  | 74 |  | 
|  | 75 |  | 
|  | 76 | def main(): | 
|  | 77 | parser = argparse.ArgumentParser() | 
|  | 78 | parser.add_argument("corpus", type=str, help="Corpus directory") | 
|  | 79 | parser.add_argument("program", type=str, help="Target Program") | 
|  | 80 | parser.add_argument("srcdir", type=str, help="Source directory") | 
|  | 81 | parser.add_argument("builddir", type=str) | 
|  | 82 | parser.add_argument("outdir", type=str) | 
|  | 83 | args = parser.parse_args() | 
|  | 84 |  | 
|  | 85 | run(args) | 
|  | 86 |  | 
|  | 87 |  | 
|  | 88 | if __name__ == "__main__": | 
|  | 89 | main() |