mirror of https://github.com/google/oss-fuzz.git
[infra] helper.py: support "profile" command for a single fuzz target with arbitrary corpus location. (#1535)
* [infra] helper.py: support "profile" command for a single fuzz target with arbitrary corpus location. * Address review feedback. * Update code_coverage.md page.
This commit is contained in:
parent
d48b11f529
commit
9874e09f53
|
@ -40,6 +40,8 @@ and try again. Once `gsutil` works, you can run the report generation.
|
|||
|
||||
## Generating code coverage reports
|
||||
|
||||
### Full project report
|
||||
|
||||
To generate code coverage report using the corpus aggregated on OSS-Fuzz, run:
|
||||
|
||||
```bash
|
||||
|
@ -54,6 +56,22 @@ each fuzz target, then run:
|
|||
python infra/helper.py profile --no-corpus-download $project_name
|
||||
```
|
||||
|
||||
### Single fuzz target
|
||||
|
||||
You can generate a code coverage report for a particular fuzz target with
|
||||
`--fuzz-target` argument:
|
||||
|
||||
```bash
|
||||
python infra/helper.py profile --fuzz-target=<fuzz_target_name> $project_name
|
||||
```
|
||||
|
||||
In this mode, you can specify an arbitrary corpus location for the fuzz target
|
||||
via `--corpus-dir` to be used instead of the corpus downloaded from OSS-Fuzz:
|
||||
|
||||
```bash
|
||||
python infra/helper.py profile --fuzz-target=<fuzz_target_name> --corpus-dir=<my_local_corpus_dir> $project_name
|
||||
```
|
||||
|
||||
|
||||
[Clang Source-based Code Coverage]: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
|
||||
[gsutil tool]: https://cloud.google.com/storage/docs/gsutil_install
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
################################################################################
|
||||
cd $OUT
|
||||
|
||||
if (( $# > 0 )); then
|
||||
FUZZ_TARGETS="$@"
|
||||
else
|
||||
FUZZ_TARGETS="$(find . -maxdepth 1 -type f -executable)"
|
||||
fi
|
||||
|
||||
LOGS_DIR="$OUT/logs"
|
||||
mkdir -p $LOGS_DIR
|
||||
|
||||
|
@ -39,7 +45,7 @@ function run_fuzz_target {
|
|||
}
|
||||
|
||||
# Run each fuzz target, generate raw coverage dumps.
|
||||
for fuzz_target in $(find . -maxdepth 1 -type f -executable); do
|
||||
for fuzz_target in $FUZZ_TARGETS; do
|
||||
echo "Running $fuzz_target"
|
||||
run_fuzz_target $fuzz_target &
|
||||
objects="$objects -object=$fuzz_target"
|
||||
|
|
|
@ -128,6 +128,10 @@ def main():
|
|||
'use corpus located in build/corpus/<project>/<fuzz_target>/')
|
||||
profile_parser.add_argument('--port', default='8008', help='specify port for '
|
||||
'a local HTTP server rendering coverage report')
|
||||
profile_parser.add_argument('--fuzz-target', help='specify name of a fuzz '
|
||||
'target to be run for generating coverage report')
|
||||
profile_parser.add_argument('--corpus-dir', help='specify location of corpus '
|
||||
'to be used (requires --fuzz-target argument)')
|
||||
|
||||
reproduce_parser = subparsers.add_parser(
|
||||
'reproduce', help='Reproduce a crash.')
|
||||
|
@ -530,7 +534,7 @@ def _get_latest_corpus(project_name, fuzz_target, base_corpus_dir):
|
|||
subprocess.check_call(command)
|
||||
|
||||
|
||||
def download_corpus(project_name):
|
||||
def download_corpus(args):
|
||||
"""Download most recent corpus from GCS for the given project."""
|
||||
try:
|
||||
with open(os.devnull, 'w') as stdout:
|
||||
|
@ -541,32 +545,43 @@ def download_corpus(project_name):
|
|||
file=sys.stderr)
|
||||
return False
|
||||
|
||||
fuzz_targets = _get_fuzz_targets(project_name)
|
||||
corpus_dir = _get_corpus_dir(project_name)
|
||||
if args.fuzz_target:
|
||||
fuzz_targets = [args.fuzz_target]
|
||||
else:
|
||||
fuzz_targets = _get_fuzz_targets(args.project_name)
|
||||
|
||||
corpus_dir = _get_corpus_dir(args.project_name)
|
||||
if not os.path.exists(corpus_dir):
|
||||
os.makedirs(corpus_dir)
|
||||
|
||||
def _download_for_single_target(fuzz_target):
|
||||
try:
|
||||
_get_latest_corpus(project_name, fuzz_target, corpus_dir)
|
||||
_get_latest_corpus(args.project_name, fuzz_target, corpus_dir)
|
||||
return True
|
||||
except Exception as e:
|
||||
print('ERROR: corpus download for %s failed: %s' % (fuzz_target, str(e)),
|
||||
file=sys.stderr)
|
||||
return False
|
||||
|
||||
print('Downloading corpus for %s project' % project_name)
|
||||
print('Downloading corpus for %s project' % args.project_name)
|
||||
thread_pool = ThreadPool(multiprocessing.cpu_count())
|
||||
return all(thread_pool.map(_download_for_single_target, fuzz_targets))
|
||||
|
||||
|
||||
def profile(args):
|
||||
"""Generate code coverage using clang source based code coverage."""
|
||||
if args.corpus_dir and not args.fuzz_target:
|
||||
print('ERROR: --corpus-dir requires specifying a particular fuzz target '
|
||||
'using --fuzz-target',
|
||||
file=sys.stderr)
|
||||
return 1
|
||||
|
||||
if not _check_project_exists(args.project_name):
|
||||
return 1
|
||||
if not args.no_corpus_download:
|
||||
if not download_corpus(args.project_name):
|
||||
return 1
|
||||
|
||||
if not args.no_corpus_download and not args.corpus_dir:
|
||||
if not download_corpus(args):
|
||||
return 1
|
||||
|
||||
env = [
|
||||
'FUZZING_ENGINE=libfuzzer',
|
||||
|
@ -575,14 +590,27 @@ def profile(args):
|
|||
'HTTP_PORT=%s' % args.port,
|
||||
]
|
||||
|
||||
run_args = _env_to_docker_args(env) + [
|
||||
run_args = _env_to_docker_args(env)
|
||||
|
||||
if args.corpus_dir:
|
||||
if not os.path.exists(args.corpus_dir):
|
||||
print('ERROR: the path provided in --corpus-dir argument does not exist',
|
||||
file=sys.stderr)
|
||||
return 1
|
||||
corpus_dir = os.path.realpath(args.corpus_dir)
|
||||
run_args.extend(['-v', '%s:/corpus/%s' % (corpus_dir, args.fuzz_target)])
|
||||
else:
|
||||
run_args.extend(['-v', '%s:/corpus' % _get_corpus_dir(args.project_name)])
|
||||
|
||||
run_args.extend([
|
||||
'-v', '%s:/out' % _get_output_dir(args.project_name),
|
||||
'-v', '%s:/corpus' % _get_corpus_dir(args.project_name),
|
||||
'-p', '%s:%s' % (args.port, args.port),
|
||||
'-t', 'gcr.io/oss-fuzz-base/base-runner',
|
||||
]
|
||||
])
|
||||
|
||||
run_args.append('coverage')
|
||||
if args.fuzz_target:
|
||||
run_args.append(args.fuzz_target)
|
||||
|
||||
exit_code = docker_run(run_args)
|
||||
if exit_code == 0:
|
||||
|
|
Loading…
Reference in New Issue