7This script minimizes a program generated by dartfuzz.dart.
11import multiprocessing
as mp
16STATEMENT_MASK_LINE = 0
17STATEMENT_NUMBER_LINE = 1
18EXPRESSION_MASK_LINE = 2
19EXPRESSION_NUMBER_LINE = 3
24 Generates bitpatterns of <nbits> bits which indicate which
25 statements or expressions should be masked when passed
as the
26 --smask
or --emask parameter to dartfuzz.dart.
27 The patterns are generated
in the following manner:
28 11111111111111111111111111111111
29 11111111111111110000000000000000
30 00000000000000001111111111111111
31 11111111111111111111111100000000
32 11111111111111110000000011111111
33 11111111000000001111111111111111
34 00000000111111111111111111111111
36 If the error persists
with a given a pattern it
is stored
in the
48 self.
max = (1 << (nbits + 1)) - 1
77 new_mask = (self.
mask | new_mask) & self.
max
106 while not self.
stop():
108 new_mask = (self.
mask |
115 if new_mask != self.
mask and \
116 new_mask
not in self.
tested:
123 raise StopIteration()
132 cmds = shlex.split(dartfuzz_cmd) + [dart_test] + \
133 [
'--mini',
'--smask',
'%d' % smask,
'--emask',
'%d' % mask]
134 p = subprocess.Popen(cmds, stdout=subprocess.PIPE)
137 cmds = shlex.split(dartfuzz_cmd) + [dart_test] + \
138 [
'--mini',
'--smask',
'%d' % mask]
139 p = subprocess.Popen(cmds, stdout=subprocess.PIPE)
140 p_stdout, p_stderr = p.communicate()
141 if p.returncode != 0:
142 raise 'Invalid return code on generate %d' % p.returncode
145 mask_new =
int(p_stdout.decode().splitlines()[EXPRESSION_MASK_LINE])
147 mask_new =
int(p_stdout.decode().splitlines()[STATEMENT_MASK_LINE])
152def run_dart(dart_cmd, dart_test, error_match_p, timeout):
153 cmd = [
'timeout', str(timeout)] + shlex.split(dart_cmd) + [dart_test]
154 p = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
155 p_stdout, p_stderr = p.communicate()
156 if error_match_p.search(p_stdout.decode())
or \
157 error_match_p.search(p_stderr.decode()):
159 if p.returncode != 0:
160 print(
"Warning: error return code %d" % p.returncode)
166def run_dart_mp(dart_cmd, dart_test, error_match_p, tries, nthreads, timeout):
168 return run_dart(dart_cmd, dart_test, error_match_p, timeout)
169 pool = mp.Pool(nthreads)
171 pool.apply_async(run_dart,
172 (dart_cmd, dart_test, error_match_p, timeout))
173 for i
in range(tries)
175 worker_done = [
False for i
in range(tries)]
178 for i, result
in enumerate(results):
182 r = result.get(timeout=0.5)
183 worker_done[i] =
True
189 except mp.TimeoutError:
220 p = subprocess.Popen(
221 dartfuzz_cmd +
" --mini " + dart_test,
223 stdout=subprocess.PIPE)
224 p_stdout, p_stderr = p.communicate()
227 nstmts =
int(p_stdout.decode().splitlines()[EXPRESSION_NUMBER_LINE])
228 mask_gen =
MaskGen(nstmts, emask)
231 nstmts =
int(p_stdout.decode().splitlines()[STATEMENT_NUMBER_LINE])
232 mask_gen =
MaskGen(nstmts, smask)
243 error_match_p1 = re.compile(error_match1)
244 error_match_p2 =
None
245 if error_match2
is not None:
246 error_match_p2 = re.compile(error_match2)
247 for mask
in mask_gen:
249 print(
"Mask: %x" % mask)
251 if cntr % print_every == 0:
253 print(
"Best I could do so far is mask %d/%d" \
254 % (max_bits, nstmts))
256 print(dartfuzz_cmd +
" " + dart_test +
257 " --mini --smask 0x%x --emask 0x%x" % (smask, min_mask))
259 print(dartfuzz_cmd +
" " + dart_test +
260 " --mini --smask 0x%x --emask 0" % (min_mask))
265 mask_new =
generate_dart(dartfuzz_cmd, dart_test, smask, mask, do_expr)
267 err =
run_dart_mp(dart_cmd, dart_test, error_match_p1, tries, threads,
270 print(
"Matched error 1 " + error_match1)
272 if (dart_cmd_ref
is not None)
and (error_match_p2
is not None):
273 err_ref =
run_dart_mp(dart_cmd_ref, dart_test, error_match_p2,
274 tries_ref, threads, timeout)
275 if err_ref
and verbose:
276 print(
"Matched error 2 " + error_match2)
280 mask_gen.update_mask(mask)
283 mask_gen.update_mask(mask_new)
284 max_bits = mask_gen.count_bits()
285 min_mask = mask_gen.mask
288 invMaskNew = mask_gen.mask | (mask_gen.max & ~mask_new)
289 if invMaskNew != mask_gen.mask
and \
290 invMaskNew
not in mask_gen.tested:
292 print(
"Mask: %x (i)" % invMaskNew)
295 err =
run_dart_mp(dart_cmd, dart_test, error_match_p1, tries,
298 print(
"Matched error 1 " + error_match1)
300 if (dart_cmd_ref
is not None)
and (error_match_p2
is not None):
302 error_match_p2, tries_ref, threads,
304 if err_ref
and verbose:
305 print(
"Matched error 2 " + error_match2)
307 mask_gen.update_mask(invMaskNew)
308 mask_gen.update_mask(mask_new)
309 max_bits = mask_gen.count_bits()
310 min_mask = mask_gen.mask
311 last_err = err
and err_ref
313 mask_gen.update_tested(mask_new)
315 print(
"Best I could do is %d/%d" \
319 print(dartfuzz_cmd +
" " + dart_test +
320 " --mini --smask 0x%x --emask 0x%x" % (smask, min_mask))
322 print(dartfuzz_cmd +
" " + dart_test +
323 " --mini --smask 0x%x --emask 0" % (min_mask))
329 parser = argparse.ArgumentParser(
330 description=
'Minimize a generated Dart program ' +
331 ' while maintaining an identified crash or divergence. ' +
332 'A second Dart program can be specified for divergence testing.')
336 help=
"dartfuzz command string "
337 "e.g. dart dartfuzz.dart --no-ffi --no-fp --seed 243123600")
340 help=
"Dart command string "
341 "e.g. ./sdk/out/ReleaseX64/dart",
346 help=
"Dart command string for reference build " +
347 "(in case of divergence testing) " +
"e.g. ./sdk/out/ReleaseX64/dart",
352 help=
"output filename for program generated by "
353 "dartfuzz command and passed to Dart command "
357 help=
"string indicating an error for Dart cmd (no multiline matches)",
362 help=
"string matching the diverging output for the Dart " +
363 "reference command (no multiline matches)",
366 '--smask', help=
"hexadecimal statements mask", default=
"0")
368 '--emask', help=
"hexadecimal expressions mask", default=
"0")
371 choices=[
"s",
"e",
"se"],
373 help=
"minimize statements (s) or expressions (e) or both (se)")
378 help=
'number of retries per run for Dart cmd')
384 help=
'number of retries per run for Dart reference cmd')
389 help=
'number of threads to use for retries')
391 '--timeout', type=int, default=60, help=
'timeout for Dart command')
393 '--verbose', help=
"print intermediate results", action=
"store_true")
394 args = parser.parse_args()
395 timeout = args.timeout
396 do_stmt =
"s" in args.typ
397 do_expr =
"e" in args.typ
398 smask =
int(args.smask, 16)
400 print(
"Minimizing Statements")
401 smask =
minimize(args.dartfuzz, args.dart, args.dart_ref,
402 args.testfile, args.err, args.err_ref, smask,
403 int(args.emask, 16),
False, args.verbose, args.tries,
404 args.tries_ref, args.threads, timeout)
406 print(
"Minimizing Expressions")
407 minimize(args.dartfuzz, args.dart, args.dart_ref,
408 args.testfile, args.err, args.err_ref, smask,
409 int(args.emask, 16),
True, args.verbose, args.tries,
410 args.tries_ref, args.threads, timeout)
413if __name__ ==
"__main__":
def __init__(self, nbits, mask)
def update_mask(self, new_mask)
def update_tested(self, new_mask)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not set
def run_dart(dart_cmd, dart_test, error_match_p, timeout)
def run_dart_mp(dart_cmd, dart_test, error_match_p, tries, nthreads, timeout)
def generate_dart(dartfuzz_cmd, dart_test, smask, mask, do_expr)
def minimize(dartfuzz_cmd, dart_cmd, dart_cmd_ref, dart_test, error_match1, error_match2, smask, emask, do_expr, verbose, tries, tries_ref, threads, timeout, print_every=32)
def print(*args, **kwargs)