Skip to content

Commit fa66280

Browse files
author
MarcoFalke
committed
fuzz: Run in parallel
1 parent c54295c commit fa66280

File tree

1 file changed

+50
-23
lines changed

1 file changed

+50
-23
lines changed

test/fuzz/test_runner.py

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
"""Run fuzz test targets.
66
"""
77

8+
from concurrent.futures import ThreadPoolExecutor, as_completed
89
import argparse
910
import configparser
11+
import logging
1012
import os
11-
import sys
1213
import subprocess
13-
import logging
14+
import sys
1415

1516

1617
def main():
@@ -35,6 +36,12 @@ def main():
3536
'--exclude',
3637
help="A comma-separated list of targets to exclude",
3738
)
39+
parser.add_argument(
40+
'--par',
41+
type=int,
42+
default=4,
43+
help='How many targets to merge or execute in parallel.',
44+
)
3845
parser.add_argument(
3946
'seed_dir',
4047
help='The seed corpus to run on (must contain subfolders for each fuzz target).',
@@ -124,25 +131,29 @@ def main():
124131
logging.error("subprocess timed out: Currently only libFuzzer is supported")
125132
sys.exit(1)
126133

127-
if args.m_dir:
128-
merge_inputs(
134+
with ThreadPoolExecutor(max_workers=args.par) as fuzz_pool:
135+
if args.m_dir:
136+
merge_inputs(
137+
fuzz_pool=fuzz_pool,
138+
corpus=args.seed_dir,
139+
test_list=test_list_selection,
140+
build_dir=config["environment"]["BUILDDIR"],
141+
merge_dir=args.m_dir,
142+
)
143+
return
144+
145+
run_once(
146+
fuzz_pool=fuzz_pool,
129147
corpus=args.seed_dir,
130148
test_list=test_list_selection,
131149
build_dir=config["environment"]["BUILDDIR"],
132-
merge_dir=args.m_dir,
150+
use_valgrind=args.valgrind,
133151
)
134-
return
135-
136-
run_once(
137-
corpus=args.seed_dir,
138-
test_list=test_list_selection,
139-
build_dir=config["environment"]["BUILDDIR"],
140-
use_valgrind=args.valgrind,
141-
)
142152

143153

144-
def merge_inputs(*, corpus, test_list, build_dir, merge_dir):
154+
def merge_inputs(*, fuzz_pool, corpus, test_list, build_dir, merge_dir):
145155
logging.info("Merge the inputs in the passed dir into the seed_dir. Passed dir {}".format(merge_dir))
156+
jobs = []
146157
for t in test_list:
147158
args = [
148159
os.path.join(build_dir, 'src', 'test', 'fuzz', t),
@@ -153,12 +164,20 @@ def merge_inputs(*, corpus, test_list, build_dir, merge_dir):
153164
]
154165
os.makedirs(os.path.join(corpus, t), exist_ok=True)
155166
os.makedirs(os.path.join(merge_dir, t), exist_ok=True)
156-
logging.debug('Run {} with args {}'.format(t, args))
157-
output = subprocess.run(args, check=True, stderr=subprocess.PIPE, universal_newlines=True).stderr
158-
logging.debug('Output: {}'.format(output))
159167

168+
def job(t, args):
169+
output = 'Run {} with args {}\n'.format(t, " ".join(args))
170+
output += subprocess.run(args, check=True, stderr=subprocess.PIPE, universal_newlines=True).stderr
171+
logging.debug(output)
172+
173+
jobs.append(fuzz_pool.submit(job, t, args))
174+
175+
for future in as_completed(jobs):
176+
future.result()
160177

161-
def run_once(*, corpus, test_list, build_dir, use_valgrind):
178+
179+
def run_once(*, fuzz_pool, corpus, test_list, build_dir, use_valgrind):
180+
jobs = []
162181
for t in test_list:
163182
corpus_path = os.path.join(corpus, t)
164183
os.makedirs(corpus_path, exist_ok=True)
@@ -169,18 +188,26 @@ def run_once(*, corpus, test_list, build_dir, use_valgrind):
169188
]
170189
if use_valgrind:
171190
args = ['valgrind', '--quiet', '--error-exitcode=1'] + args
172-
logging.debug('Run {} with args {}'.format(t, args))
173-
result = subprocess.run(args, stderr=subprocess.PIPE, universal_newlines=True)
174-
output = result.stderr
175-
logging.debug('Output: {}'.format(output))
191+
192+
def job(t, args):
193+
output = 'Run {} with args {}'.format(t, args)
194+
result = subprocess.run(args, stderr=subprocess.PIPE, universal_newlines=True)
195+
output += result.stderr
196+
return output, result
197+
198+
jobs.append(fuzz_pool.submit(job, t, args))
199+
200+
for future in as_completed(jobs):
201+
output, result = future.result()
202+
logging.debug(output)
176203
try:
177204
result.check_returncode()
178205
except subprocess.CalledProcessError as e:
179206
if e.stdout:
180207
logging.info(e.stdout)
181208
if e.stderr:
182209
logging.info(e.stderr)
183-
logging.info("Target \"{}\" failed with exit code {}: {}".format(t, e.returncode, " ".join(args)))
210+
logging.info("Target \"{}\" failed with exit code {}".format(" ".join(result.args), e.returncode))
184211
sys.exit(1)
185212

186213

0 commit comments

Comments
 (0)