
Bug: 384605107 Change-Id: I088a70984c80f8bea8c5fa58b7c08c86425b552f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6098016 Reviewed-by: Titouan Rigoudy <titouan@chromium.org> Commit-Queue: Adrian Taylor <adetaylor@chromium.org> Cr-Commit-Position: refs/heads/main@{#1399168}
143 lines
4.6 KiB
Python
Executable File
143 lines
4.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright 2024 The Chromium Authors
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
"""Provides a local HTML report of the ClusterFuzz explorations
|
|
by a given fuzzer.
|
|
|
|
* Example usage: view_fuzz_coverage.py --fuzzer my_fuzzer_binary
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import pathlib
|
|
|
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
|
chromium_src_dir = os.path.dirname(os.path.dirname(script_dir))
|
|
|
|
# These may evolve over time, so if this script doesn't work, you may
|
|
# need to adjust these. In an ideal world we'd look these up from LUCI
|
|
# infrastructure but we're intentionally making a local script somewhat
|
|
# equivalentt to LUCI infrastructure, so for now let's not rely on that.
|
|
gn_args = """
|
|
dcheck_always_on = false
|
|
enable_mojom_fuzzer = true
|
|
ffmpeg_branding = "ChromeOS"
|
|
is_component_build = false
|
|
is_debug = false
|
|
pdf_enable_xfa = true
|
|
proprietary_codecs = true
|
|
use_clang_coverage = true
|
|
use_libfuzzer = true
|
|
use_remoteexec = true
|
|
symbol_level = 2
|
|
"""
|
|
|
|
|
|
def _ParseCommandArguments():
|
|
"""Adds and parses relevant arguments for tool comands.
|
|
|
|
Returns:
|
|
A dictionary representing the arguments.
|
|
"""
|
|
arg_parser = argparse.ArgumentParser()
|
|
arg_parser.usage = __doc__
|
|
|
|
arg_parser.add_argument('--fuzzer',
|
|
required=True,
|
|
type=str,
|
|
help='Fuzzer binary name.')
|
|
arg_parser.add_argument('--build-dir',
|
|
default=os.path.join(chromium_src_dir, 'out',
|
|
'coverage'),
|
|
help='Where to build fuzzers.')
|
|
arg_parser.add_argument('--html-dir',
|
|
default=os.path.join(chromium_src_dir, 'out',
|
|
'coverage-html'),
|
|
help='Where to put HTML report.')
|
|
arg_parser.add_argument(
|
|
'--retain-build-dir',
|
|
action='store_true',
|
|
help=
|
|
'Avoid cleaning the build dir (may result in multiple fuzzers being analyzed).'
|
|
)
|
|
args = arg_parser.parse_args()
|
|
return args
|
|
|
|
|
|
def step(name):
|
|
"""Print a banner for the upcoming task.."""
|
|
print("==== " + name + " ====:")
|
|
|
|
|
|
def check_call(args, *, cwd=None, shell=False):
|
|
"""Equivalent to subprocess.check_call but logs command."""
|
|
print(" ".join(args))
|
|
subprocess.check_call(args, cwd=cwd, shell=shell)
|
|
|
|
|
|
def Main():
|
|
args = _ParseCommandArguments()
|
|
|
|
os.makedirs(args.build_dir, exist_ok=True)
|
|
os.makedirs(args.html_dir, exist_ok=True)
|
|
|
|
step("Writing gn args")
|
|
gn_args_file = os.path.join(args.build_dir, "args.gn")
|
|
with open(gn_args_file, "w") as f:
|
|
f.write(gn_args)
|
|
|
|
if not args.retain_build_dir:
|
|
step("gn clean")
|
|
check_call(["gn", "clean", args.build_dir], cwd=chromium_src_dir)
|
|
step("gn gen")
|
|
check_call(["gn", "gen", args.build_dir], cwd=chromium_src_dir)
|
|
step("autoninja")
|
|
check_call(["autoninja", "-C", args.build_dir, args.fuzzer])
|
|
corpora_dir = tempfile.TemporaryDirectory()
|
|
step("Download corpora")
|
|
check_call([
|
|
sys.executable,
|
|
os.path.join(script_dir, "download_fuzz_corpora.py"), "--download-dir",
|
|
corpora_dir.name, "--build-dir", args.build_dir
|
|
])
|
|
individual_profdata_dir = tempfile.TemporaryDirectory()
|
|
step(
|
|
"Running fuzzers (can take a while - NB you might need a valid DISPLAY set for some fuzzers)"
|
|
)
|
|
check_call([
|
|
sys.executable,
|
|
os.path.join(script_dir, "run_all_fuzzers.py"), "--fuzzer-binaries-dir",
|
|
args.build_dir, "--fuzzer-corpora-dir", corpora_dir.name,
|
|
"--profdata-outdir", individual_profdata_dir.name
|
|
])
|
|
step("Merging profdata")
|
|
merged_profdata_dir = tempfile.TemporaryDirectory()
|
|
merged_profdata_file = os.path.join(merged_profdata_dir.name, "out.profdata")
|
|
llvm_dir = os.path.join(chromium_src_dir, "third_party", "llvm-build",
|
|
"Release+Asserts", "bin")
|
|
check_call([
|
|
sys.executable,
|
|
os.path.join(script_dir, "merge_all_profdata.py"), "--profdata-dir",
|
|
individual_profdata_dir.name, "--outfile", merged_profdata_file,
|
|
"--llvm-profdata",
|
|
os.path.join(llvm_dir, "llvm-profdata")
|
|
])
|
|
step("Generating HTML")
|
|
check_call([
|
|
os.path.join(llvm_dir, "llvm-cov"), "show", args.fuzzer, "-format=html",
|
|
"-instr-profile", merged_profdata_file, "-output-dir", args.html_dir
|
|
],
|
|
cwd=args.build_dir)
|
|
uri = pathlib.Path(os.path.join(args.html_dir, "index.html")).as_uri()
|
|
print("Report URI " + uri)
|
|
step("Opening HTML in Chrome")
|
|
check_call(["google-chrome-stable", uri], shell=True)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(Main())
|