aboutsummaryrefslogtreecommitdiff
path: root/test/fuzz/test_runner.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/fuzz/test_runner.py')
-rwxr-xr-xtest/fuzz/test_runner.py76
1 files changed, 52 insertions, 24 deletions
diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py
index bb93060739..e2454c4237 100755
--- a/test/fuzz/test_runner.py
+++ b/test/fuzz/test_runner.py
@@ -1,16 +1,17 @@
#!/usr/bin/env python3
-# Copyright (c) 2019 The Bitcoin Core developers
+# Copyright (c) 2019-2020 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Run fuzz test targets.
"""
+from concurrent.futures import ThreadPoolExecutor, as_completed
import argparse
import configparser
+import logging
import os
-import sys
import subprocess
-import logging
+import sys
def main():
@@ -36,6 +37,12 @@ def main():
help="A comma-separated list of targets to exclude",
)
parser.add_argument(
+ '--par',
+ type=int,
+ default=4,
+ help='How many targets to merge or execute in parallel.',
+ )
+ parser.add_argument(
'seed_dir',
help='The seed corpus to run on (must contain subfolders for each fuzz target).',
)
@@ -124,40 +131,53 @@ def main():
logging.error("subprocess timed out: Currently only libFuzzer is supported")
sys.exit(1)
- if args.m_dir:
- merge_inputs(
+ with ThreadPoolExecutor(max_workers=args.par) as fuzz_pool:
+ if args.m_dir:
+ merge_inputs(
+ fuzz_pool=fuzz_pool,
+ corpus=args.seed_dir,
+ test_list=test_list_selection,
+ build_dir=config["environment"]["BUILDDIR"],
+ merge_dir=args.m_dir,
+ )
+ return
+
+ run_once(
+ fuzz_pool=fuzz_pool,
corpus=args.seed_dir,
test_list=test_list_selection,
build_dir=config["environment"]["BUILDDIR"],
- merge_dir=args.m_dir,
+ use_valgrind=args.valgrind,
)
- return
-
- run_once(
- corpus=args.seed_dir,
- test_list=test_list_selection,
- build_dir=config["environment"]["BUILDDIR"],
- use_valgrind=args.valgrind,
- )
-def merge_inputs(*, corpus, test_list, build_dir, merge_dir):
+def merge_inputs(*, fuzz_pool, corpus, test_list, build_dir, merge_dir):
logging.info("Merge the inputs in the passed dir into the seed_dir. Passed dir {}".format(merge_dir))
+ jobs = []
for t in test_list:
args = [
os.path.join(build_dir, 'src', 'test', 'fuzz', t),
'-merge=1',
+ '-use_value_profile=1', # Also done by oss-fuzz https://github.com/google/oss-fuzz/issues/1406#issuecomment-387790487
os.path.join(corpus, t),
os.path.join(merge_dir, t),
]
os.makedirs(os.path.join(corpus, t), exist_ok=True)
os.makedirs(os.path.join(merge_dir, t), exist_ok=True)
- logging.debug('Run {} with args {}'.format(t, args))
- output = subprocess.run(args, check=True, stderr=subprocess.PIPE, universal_newlines=True).stderr
- logging.debug('Output: {}'.format(output))
+ def job(t, args):
+ output = 'Run {} with args {}\n'.format(t, " ".join(args))
+ output += subprocess.run(args, check=True, stderr=subprocess.PIPE, universal_newlines=True).stderr
+ logging.debug(output)
+
+ jobs.append(fuzz_pool.submit(job, t, args))
+
+ for future in as_completed(jobs):
+ future.result()
-def run_once(*, corpus, test_list, build_dir, use_valgrind):
+
+def run_once(*, fuzz_pool, corpus, test_list, build_dir, use_valgrind):
+ jobs = []
for t in test_list:
corpus_path = os.path.join(corpus, t)
os.makedirs(corpus_path, exist_ok=True)
@@ -168,10 +188,18 @@ def run_once(*, corpus, test_list, build_dir, use_valgrind):
]
if use_valgrind:
args = ['valgrind', '--quiet', '--error-exitcode=1'] + args
- logging.debug('Run {} with args {}'.format(t, args))
- result = subprocess.run(args, stderr=subprocess.PIPE, universal_newlines=True)
- output = result.stderr
- logging.debug('Output: {}'.format(output))
+
+ def job(t, args):
+ output = 'Run {} with args {}'.format(t, args)
+ result = subprocess.run(args, stderr=subprocess.PIPE, universal_newlines=True)
+ output += result.stderr
+ return output, result
+
+ jobs.append(fuzz_pool.submit(job, t, args))
+
+ for future in as_completed(jobs):
+ output, result = future.result()
+ logging.debug(output)
try:
result.check_returncode()
except subprocess.CalledProcessError as e:
@@ -179,7 +207,7 @@ def run_once(*, corpus, test_list, build_dir, use_valgrind):
logging.info(e.stdout)
if e.stderr:
logging.info(e.stderr)
- logging.info("Target \"{}\" failed with exit code {}: {}".format(t, e.returncode, " ".join(args)))
+ logging.info("Target \"{}\" failed with exit code {}".format(" ".join(result.args), e.returncode))
sys.exit(1)