[dart/fuzzer] DartFuzz Improvements
Included:
(1) Added more expression variants in generator.
(2) Added --isolates option in driver (enables parallel execution).
Change-Id: I4e04490acc3046fd4771dd07502ba0fcdceb7df5
Reviewed-on: https://dart-review.googlesource.com/c/77720
Reviewed-by: Alexander Thomas <athom@google.com>
Commit-Queue: Aart Bik <ajcbik@google.com>
diff --git a/runtime/tools/dartfuzz/README.md b/runtime/tools/dartfuzz/README.md
index dcf0b3f..cfd8b5e 100644
--- a/runtime/tools/dartfuzz/README.md
+++ b/runtime/tools/dartfuzz/README.md
@@ -32,6 +32,7 @@
dart dartfuzz_test.dart
run_dartfuzz_test.py [--help]
+ [--isolates ISOLATES ]
[--repeat REPEAT]
[--true_divergence]
[--mode1 MODE]
@@ -40,8 +41,9 @@
where
--help : prints help and exits
+ --isolates : number of isolates in the session (1 by default)
--repeat : number of tests to run (1000 by default)
- --show-stats : show session statistics (true by default)
+ --show-stats : show statistics during session (true by default)
--true-divergence : only report true divergences (true by default)
--dart-top : sets DART_TOP explicitly through command line
--mode1 : m1
diff --git a/runtime/tools/dartfuzz/dartfuzz.dart b/runtime/tools/dartfuzz/dartfuzz.dart
index 06790d0..9a36b2b 100644
--- a/runtime/tools/dartfuzz/dartfuzz.dart
+++ b/runtime/tools/dartfuzz/dartfuzz.dart
@@ -10,7 +10,7 @@
// Version of DartFuzz. Increase this each time changes are made
// to preserve the property that a given version of DartFuzz yields
// the same fuzzed program for a deterministic random seed.
-const String version = '1.0';
+const String version = '1.1';
// Restriction on statement and expression depths.
const int stmtDepth = 5;
@@ -58,7 +58,7 @@
// Interesting characters.
const interestingChars =
- 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#&()+-';
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#&()+- ';
// Class that represents Dart types.
class DartType {
@@ -85,6 +85,13 @@
DartType.INT_STRING_MAP
];
+// Class that represents Dart library methods.
+class DartLib {
+ final String name;
+ final List<DartType> proto;
+ const DartLib(this.name, this.proto);
+}
+
// Naming conventions.
const varName = 'var';
const paramName = 'par';
@@ -375,16 +382,12 @@
emit('-${rand.nextInt(100)}');
}
- void emitInterestingInt() {
- int i = rand.nextInt(interestingIntegers.length);
- emit('${interestingIntegers[i]}');
- }
-
void emitInt() {
switch (rand.nextInt(4)) {
// favors small positive int
case 0:
- emitInterestingInt();
+ emit(
+ '${interestingIntegers[rand.nextInt(interestingIntegers.length)]}');
break;
case 1:
emitSmallNegativeInt();
@@ -420,71 +423,52 @@
}
void emitChar() {
- emit(interestingChars[rand.nextInt(interestingChars.length)]);
+ switch (rand.nextInt(10)) {
+ // favors regular char
+ case 0:
+ emit('\\u2665');
+ break;
+ case 1:
+ emit('\\u{1f600}'); // rune
+ break;
+ default:
+ emit(interestingChars[rand.nextInt(interestingChars.length)]);
+ break;
+ }
}
void emitString() {
- switch (rand.nextInt(4)) {
- // favors non-null
- case 0:
- emit('null');
- break;
- default:
- {
- emit("'");
- int l = rand.nextInt(8);
- for (int i = 0; i < l; i++) {
- emitChar();
- }
- emit("'");
- break;
- }
+ emit("'");
+ int l = rand.nextInt(8);
+ for (int i = 0; i < l; i++) {
+ emitChar();
}
+ emit("'");
}
void emitIntList() {
- switch (rand.nextInt(4)) {
- // favors non-null
- case 0:
- emit('null');
- break;
- default:
- {
- emit('[ ');
- int l = 1 + rand.nextInt(4);
- for (int i = 0; i < l; i++) {
- emitInt();
- if (i != (l - 1)) {
- emit(', ');
- }
- }
- emit(' ]');
- break;
- }
+ emit('[ ');
+ int l = 1 + rand.nextInt(4);
+ for (int i = 0; i < l; i++) {
+ emitInt();
+ if (i != (l - 1)) {
+ emit(', ');
+ }
}
+ emit(' ]');
}
void emitIntStringMap() {
- switch (rand.nextInt(4)) {
- // favors non-null
- case 0:
- emit('null');
- break;
- default:
- {
- emit('{ ');
- int l = 1 + rand.nextInt(4);
- for (int i = 0; i < l; i++) {
- emit('$i : ');
- emitString();
- if (i != (l - 1)) {
- emit(', ');
- }
- }
- emit(' }');
- break;
- }
+ emit('{ ');
+ int l = 1 + rand.nextInt(4);
+ for (int i = 0; i < l; i++) {
+ emit('$i : ');
+ emitString();
+ if (i != (l - 1)) {
+ emit(', ');
+ }
}
+ emit(' }');
}
void emitLiteral(DartType tp) {
@@ -548,28 +532,115 @@
}
void emitExprList(int depth, List<DartType> proto) {
+ emit('(');
for (int i = 1; i < proto.length; i++) {
emitExpr(depth, proto[i]);
if (i != (proto.length - 1)) {
emit(', ');
}
}
+ emit(')');
}
+ // Emit expression with unary operator: (~(x))
+ void emitUnaryExpr(int depth, DartType tp) {
+ if (tp == DartType.BOOL || tp == DartType.INT || tp == DartType.DOUBLE) {
+ emit('(');
+ emitUnaryOp(tp);
+ emit('(');
+ emitExpr(depth + 1, tp);
+ emit('))');
+ } else {
+ emitTerminal(tp); // resort to terminal
+ }
+ }
+
+ // Emit expression with binary operator: (x + y)
+ void emitBinaryExpr(int depth, DartType tp) {
+ if (tp == DartType.BOOL) {
+ // For boolean, allow type switch with relational op.
+ if (rand.nextInt(2) == 0) {
+ DartType deeper_tp = getType();
+ emit('(');
+ emitExpr(depth + 1, deeper_tp);
+ emitRelOp(deeper_tp);
+ emitExpr(depth + 1, deeper_tp);
+ emit(')');
+ return;
+ }
+ }
+ emit('(');
+ emitExpr(depth + 1, tp);
+ emitBinaryOp(tp);
+ emitExpr(depth + 1, tp);
+ emit(')');
+ }
+
+ // Emit expression with ternary operator: (b ? x : y)
+ void emitTernaryExpr(int depth, DartType tp) {
+ emit('(');
+ emitExpr(depth + 1, DartType.BOOL);
+ emit(' ? ');
+ emitExpr(depth + 1, tp);
+ emit(' : ');
+ emitExpr(depth + 1, tp);
+ emit(')');
+ }
+
+ // Emit expression with pre/post-increment/decrement operator: (x++)
+ void emitPreOrPostExpr(int depth, DartType tp) {
+ if (tp == DartType.INT) {
+ int r = rand.nextInt(2);
+ emit('(');
+ if (r == 0) emitPreOrPostOp(tp);
+ emitScalarVar(tp);
+ if (r == 1) emitPreOrPostOp(tp);
+ emit(')');
+ } else {
+ emitTerminal(tp); // resort to terminal
+ }
+ }
+
+ // Emit library call.
+ void emitLibraryCall(int depth, DartType tp) {
+ if (tp == DartType.INT_STRING_MAP) {
+ emitTerminal(tp); // resort to terminal
+ return;
+ }
+ DartLib lib = oneOf<DartLib>(getLibrary(tp));
+ List<DartType> proto = lib.proto;
+ // Receiver.
+ if (proto[0] != null) {
+ DartType deeper_tp = proto[0];
+ emit('(');
+ emitExpr(depth + 1, deeper_tp);
+ emit(').');
+ }
+ // Call.
+ emit('${lib.name}');
+ // Parameters.
+ if (proto.length == 1) {
+ emit('()');
+ } else if (proto[1] != null) {
+ emitExprList(depth + 1, proto);
+ }
+ }
+
+ // Helper for a method call.
bool pickedCall(
int depth, DartType tp, String name, List<List<DartType>> protos, int m) {
for (int i = m - 1; i >= 0; i--) {
if (tp == protos[i][0]) {
- emit('$name$i(');
+ emit('$name$i');
emitExprList(depth + 1, protos[i]);
- emit(')');
return true;
}
}
return false;
}
- void emitCall(int depth, DartType tp) {
+ // Emit method call within the program.
+ void emitMethodCall(int depth, DartType tp) {
// Only call backward to avoid infinite recursion.
if (currentClass == null) {
// Outside a class: call backward in global methods.
@@ -591,70 +662,7 @@
emitTerminal(tp); // resort to terminal.
}
- // Emit expression with unary operator: (~(x))
- void emitUnaryExpr(int depth, DartType tp) {
- if (tp == DartType.BOOL || tp == DartType.INT || tp == DartType.DOUBLE) {
- emit('(');
- emitUnaryOp(tp);
- emit('(');
- emitExpr(depth + 1, tp);
- emit('))');
- } else {
- emitTerminal(tp); // resort to terminal
- }
- }
-
- // Emit expression with binary operator: (x + y)
- void emitBinaryExpr(int depth, DartType tp) {
- if (tp == DartType.BOOL || tp == DartType.INT || tp == DartType.DOUBLE) {
- emit('(');
- emitExpr(depth + 1, tp);
- emitBinaryOp(tp);
- emitExpr(depth + 1, tp);
- emit(')');
- } else {
- emitTerminal(tp); // resort to terminal
- }
- }
-
- // Emit expression with pre/post-increment/decrement operator: (x++)
- void emitPreOrPostExpr(int depth, DartType tp) {
- if (tp == DartType.INT) {
- int r = rand.nextInt(2);
- emit('(');
- if (r == 0) emitPreOrPostOp(tp);
- emitScalarVar(tp);
- if (r == 1) emitPreOrPostOp(tp);
- emit(')');
- } else {
- emitTerminal(tp); // resort to terminal
- }
- }
-
- // Emit type converting expression.
- void emitTypeConv(int depth, DartType tp) {
- if (tp == DartType.BOOL) {
- DartType deeper_tp = getType();
- emit('(');
- emitExpr(depth + 1, deeper_tp);
- emitRelOp(deeper_tp);
- emitExpr(depth + 1, deeper_tp);
- emit(')');
- } else if (tp == DartType.INT) {
- emit('(');
- emitExpr(depth + 1, DartType.DOUBLE);
- emit(').toInt()');
- } else if (tp == DartType.DOUBLE) {
- emit('(');
- emitExpr(depth + 1, DartType.INT);
- emit(').toDouble()');
- } else {
- emitTerminal(tp); // resort to terminal
- }
- }
-
// Emit expression.
- // TODO: add many more constructs
void emitExpr(int depth, DartType tp) {
// Continuing nested expressions becomes less likely as the depth grows.
if (rand.nextInt(depth + 1) > exprDepth) {
@@ -662,7 +670,7 @@
return;
}
// Possibly nested expression.
- switch (rand.nextInt(6)) {
+ switch (rand.nextInt(7)) {
case 0:
emitUnaryExpr(depth, tp);
break;
@@ -670,13 +678,16 @@
emitBinaryExpr(depth, tp);
break;
case 2:
- emitPreOrPostExpr(depth, tp);
+ emitTernaryExpr(depth, tp);
break;
case 3:
- emitTypeConv(depth, tp);
+ emitPreOrPostExpr(depth, tp);
break;
case 4:
- emitCall(depth, tp);
+ emitLibraryCall(depth, tp);
+ break;
+ case 5:
+ emitMethodCall(depth, tp);
break;
default:
emitTerminal(tp);
@@ -688,11 +699,6 @@
// Operators.
//
- // Emit one of the given choices.
- T oneOf<T>(List<T> choices) {
- return choices[rand.nextInt(choices.length)];
- }
-
// Emit same type in-out assignment operator.
void emitAssignOp(DartType tp) {
if (tp == DartType.INT) {
@@ -707,12 +713,14 @@
' ^= ',
' >>= ',
' <<= ',
+ ' ??= ',
' = '
]));
} else if (tp == DartType.DOUBLE) {
- emit(oneOf(const <String>[' += ', ' -= ', ' *= ', ' /= ', ' = ']));
+ emit(oneOf(
+ const <String>[' += ', ' -= ', ' *= ', ' /= ', ' ??= ', ' = ']));
} else {
- emit(' = ');
+ emit(oneOf(const <String>[' ??= ', ' = ']));
}
}
@@ -744,16 +752,19 @@
' | ',
' ^ ',
' >> ',
- ' << '
+ ' << ',
+ ' ?? '
]));
} else if (tp == DartType.DOUBLE) {
- emit(oneOf(const <String>[' + ', ' - ', ' * ', ' / ']));
+ emit(oneOf(const <String>[' + ', ' - ', ' * ', ' / ', ' ?? ']));
+ } else if (tp == DartType.STRING || tp == DartType.INT_LIST) {
+ emit(oneOf(const <String>[' + ', ' ?? ']));
} else {
- assert(false);
+ emit(' ?? ');
}
}
- // Emit increment operator.
+ // Emit same type in-out increment operator.
void emitPreOrPostOp(DartType tp) {
if (tp == DartType.INT) {
emit(oneOf(const <String>['++', '--']));
@@ -772,9 +783,105 @@
}
//
+ // Library methods.
+ //
+
+ // Get list of library methods, organized by return type.
+ // Proto list:
+ // [ receiver-type (null denotes none),
+ // param1 type (null denotes getter),
+ // param2 type,
+ // ...
+ // ]
+ List<DartLib> getLibrary(DartType tp) {
+ if (tp == DartType.BOOL) {
+ return const [
+ DartLib('isEven', [DartType.INT, null]),
+ DartLib('isOdd', [DartType.INT, null]),
+ DartLib('isEmpty', [DartType.STRING, null]),
+ DartLib('isEmpty', [DartType.INT_STRING_MAP, null]),
+ DartLib('isNotEmpty', [DartType.STRING, null]),
+ DartLib('isNotEmpty', [DartType.INT_STRING_MAP, null]),
+ DartLib('endsWith', [DartType.STRING, DartType.STRING]),
+ DartLib('remove', [DartType.INT_LIST, DartType.INT]),
+ DartLib('containsValue', [DartType.INT_STRING_MAP, DartType.STRING]),
+ DartLib('containsKey', [DartType.INT_STRING_MAP, DartType.INT]),
+ ];
+ } else if (tp == DartType.INT) {
+ return const [
+ DartLib('bitLength', [DartType.INT, null]),
+ DartLib('sign', [DartType.INT, null]),
+ DartLib('abs', [DartType.INT]),
+ DartLib('round', [DartType.INT]),
+ DartLib('round', [DartType.DOUBLE]),
+ DartLib('floor', [DartType.INT]),
+ DartLib('floor', [DartType.DOUBLE]),
+ DartLib('ceil', [DartType.INT]),
+ DartLib('ceil', [DartType.DOUBLE]),
+ DartLib('truncate', [DartType.INT]),
+ DartLib('truncate', [DartType.DOUBLE]),
+ DartLib('toInt', [DartType.DOUBLE]),
+ DartLib('toUnsigned', [DartType.INT, DartType.INT]),
+ DartLib('toSigned', [DartType.INT, DartType.INT]),
+ DartLib('modInverse', [DartType.INT, DartType.INT]),
+ DartLib('modPow', [DartType.INT, DartType.INT, DartType.INT]),
+ DartLib('length', [DartType.STRING, null]),
+ DartLib('length', [DartType.INT_LIST, null]),
+ DartLib('length', [DartType.INT_STRING_MAP, null]),
+ DartLib('codeUnitAt', [DartType.STRING, DartType.INT]),
+ DartLib('compareTo', [DartType.STRING, DartType.STRING]),
+ DartLib('removeLast', [DartType.INT_LIST]),
+ DartLib('removeAt', [DartType.INT_LIST, DartType.INT]),
+ DartLib('indexOf', [DartType.INT_LIST, DartType.INT]),
+ DartLib('lastIndexOf', [DartType.INT_LIST, DartType.INT]),
+ ];
+ } else if (tp == DartType.DOUBLE) {
+ return const [
+ DartLib('sign', [DartType.DOUBLE, null]),
+ DartLib('abs', [DartType.DOUBLE]),
+ DartLib('toDouble', [DartType.INT]),
+ DartLib('roundToDouble', [DartType.INT]),
+ DartLib('roundToDouble', [DartType.DOUBLE]),
+ DartLib('floorToDouble', [DartType.INT]),
+ DartLib('floorToDouble', [DartType.DOUBLE]),
+ DartLib('ceilToDouble', [DartType.INT]),
+ DartLib('ceilToDouble', [DartType.DOUBLE]),
+ DartLib('truncateToDouble', [DartType.INT]),
+ DartLib('truncateToDouble', [DartType.DOUBLE]),
+ DartLib('remainder', [DartType.DOUBLE, DartType.DOUBLE]),
+ ];
+ } else if (tp == DartType.STRING) {
+ return const [
+ DartLib('toString', [DartType.BOOL]),
+ DartLib('toString', [DartType.INT]),
+ DartLib('toString', [DartType.DOUBLE]),
+ DartLib('toRadixString', [DartType.INT, DartType.INT]),
+ DartLib('trim', [DartType.STRING]),
+ DartLib('trimLeft', [DartType.STRING]),
+ DartLib('trimRight', [DartType.STRING]),
+ DartLib('toLowerCase', [DartType.STRING]),
+ DartLib('toUpperCase', [DartType.STRING]),
+ DartLib('substring', [DartType.STRING, DartType.INT]),
+ DartLib('padLeft', [DartType.STRING, DartType.INT]),
+ DartLib('padRight', [DartType.STRING, DartType.INT]),
+ DartLib('replaceRange',
+ [DartType.STRING, DartType.INT, DartType.INT, DartType.STRING]),
+ DartLib('remove', [DartType.INT_STRING_MAP, DartType.INT]),
+ ];
+ } else if (tp == DartType.INT_LIST) {
+ return const [
+ DartLib('sublist', [DartType.INT_LIST, DartType.INT]),
+ ];
+ } else {
+ assert(false);
+ }
+ }
+
+ //
// Types.
//
+ // Get a random value type.
DartType getType() {
switch (rand.nextInt(6)) {
case 0:
@@ -854,6 +961,11 @@
}
}
+ // Emits one of the given choices.
+ T oneOf<T>(List<T> choices) {
+ return choices[rand.nextInt(choices.length)];
+ }
+
// Random seed used to generate program.
final int seed;
@@ -872,7 +984,7 @@
// Types of global variables.
List<DartType> globalVars;
- // Prototypes of all global functions (first element is return type).
+ // Prototypes of all global methods (first element is return type).
List<List<DartType>> globalMethods;
// Types of fields over all classes.
@@ -908,7 +1020,7 @@
new DartFuzz(seed, file).run();
file.closeSync();
} catch (e) {
- print('Usage: dart dartfuzz.dart [OPTIONS] FILENAME');
- print(parser.usage);
+ print('Usage: dart dartfuzz.dart [OPTIONS] FILENAME\n${parser.usage}\n$e');
+ exitCode = 255;
}
}
diff --git a/runtime/tools/dartfuzz/dartfuzz_test.dart b/runtime/tools/dartfuzz/dartfuzz_test.dart
index bfde9ae..87af806 100644
--- a/runtime/tools/dartfuzz/dartfuzz_test.dart
+++ b/runtime/tools/dartfuzz/dartfuzz_test.dart
@@ -4,6 +4,7 @@
import 'dart:convert';
import 'dart:io';
+import 'dart:isolate';
import 'dart:math';
import 'package:args/args.dart';
@@ -14,31 +15,14 @@
const sigkill = 9;
const timeout = 30; // in seconds
-// Supported modes.
-const List<String> modes = [
- 'jit-debug-ia32',
- 'jit-debug-x64',
- 'jit-debug-arm32',
- 'jit-debug-arm64',
- 'aot-debug-x64',
- 'aot-debug-arm64',
- 'jit-ia32',
- 'jit-x64',
- 'jit-arm32',
- 'jit-arm64',
- 'aot-x64',
- 'aot-arm64',
- 'js'
-];
-
// Exit code of running a test.
enum ResultCode { success, timeout, error }
/// Result of running a test.
class TestResult {
TestResult(this.code, this.output);
- ResultCode code;
- String output;
+ final ResultCode code;
+ final String output;
}
/// Command runner.
@@ -61,15 +45,17 @@
/// Abstraction for running one test in a particular mode.
abstract class TestRunner {
- String description();
- TestResult run(String fileName);
+ TestResult run();
+ String description;
// Factory.
static TestRunner getTestRunner(
- Map<String, String> env, String top, String mode) {
- if (mode.startsWith('jit')) return new TestRunnerJIT(env, top, mode);
- if (mode.startsWith('aot')) return new TestRunnerAOT(env, top, mode);
- if (mode.startsWith('js')) return new TestRunnerJS(env, top, mode);
+ String mode, String top, String tmp, Map<String, String> env) {
+ if (mode.startsWith('jit'))
+ return new TestRunnerJIT(getTag(mode), top, tmp, env);
+ if (mode.startsWith('aot'))
+ return new TestRunnerAOT(getTag(mode), top, tmp, env);
+ if (mode.startsWith('js')) return new TestRunnerJS(top, tmp, env);
throw ('unknown runner in mode: $mode');
}
@@ -89,116 +75,115 @@
/// Concrete test runner of Dart JIT.
class TestRunnerJIT implements TestRunner {
- TestRunnerJIT(Map<String, String> e, String top, String mode) {
- tag = TestRunner.getTag(mode);
+ TestRunnerJIT(String tag, String top, String tmp, Map<String, String> e) {
+ description = 'JIT-${tag}';
dart = '$top/out/$tag/dart';
+ fileName = '$tmp/fuzz.dart';
env = e;
}
- String description() {
- return "JIT-${tag}";
+
+ TestResult run() {
+ return runCommand([dart, fileName], env);
}
- TestResult run(String fileName) {
- return runCommand(['$dart', fileName], env);
- }
-
- String tag;
+ String description;
String dart;
+ String fileName;
Map<String, String> env;
}
/// Concrete test runner of Dart AOT.
class TestRunnerAOT implements TestRunner {
- TestRunnerAOT(Map<String, String> e, String top, String mode) {
- tag = TestRunner.getTag(mode);
+ TestRunnerAOT(String tag, String top, String tmp, Map<String, String> e) {
+ description = 'AOT-${tag}';
precompiler = '$top/pkg/vm/tool/precompiler2';
dart = '$top/pkg/vm/tool/dart_precompiled_runtime2';
+ fileName = '$tmp/fuzz.dart';
+ snapshot = '$tmp/snapshot';
env = Map<String, String>.from(e);
env['DART_CONFIGURATION'] = tag;
}
- String description() {
- return "AOT-${tag}";
- }
- TestResult run(String fileName) {
- TestResult result = runCommand(['$precompiler', fileName, 'snapshot'], env);
+ TestResult run() {
+ TestResult result = runCommand([precompiler, fileName, snapshot], env);
if (result.code != ResultCode.success) {
return result;
}
- return runCommand(['$dart', 'snapshot'], env);
+ return runCommand([dart, snapshot], env);
}
- String tag;
+ String description;
String precompiler;
String dart;
+ String fileName;
+ String snapshot;
Map<String, String> env;
}
/// Concrete test runner of Dart2JS.
class TestRunnerJS implements TestRunner {
- TestRunnerJS(Map<String, String> e, String top, String mode) {
+ TestRunnerJS(String top, String tmp, Map<String, String> e) {
+ description = 'Dart2JS';
dart2js = '$top/out/ReleaseX64/dart-sdk/bin/dart2js';
+ fileName = '$tmp/fuzz.dart';
+ js = '$tmp/out.js';
env = e;
}
- String description() {
- return "Dart2JS";
- }
- TestResult run(String fileName) {
- TestResult result = runCommand(['$dart2js', fileName], env);
+ TestResult run() {
+ TestResult result = runCommand([dart2js, fileName, '-o', js], env);
if (result.code != ResultCode.success) {
return result;
}
- return runCommand(['nodejs', 'out.js'], env);
+ return runCommand(['nodejs', js], env);
}
+ String description;
String dart2js;
+ String fileName;
+ String js;
Map<String, String> env;
}
-/// Class to run a fuzz testing session.
+/// Class to run fuzz testing.
class DartFuzzTest {
DartFuzzTest(this.env, this.repeat, this.trueDivergence, this.showStats,
this.top, this.mode1, this.mode2);
- bool runSession() {
- setupSession();
+ bool run() {
+ setup();
- print('\n**\n**** Dart Fuzz Testing\n**\n');
- print('Fuzz Version : ${version}');
- print('#Tests : ${repeat}');
- print('Exec-Mode 1 : ${runner1.description()}');
- print('Exec-Mode 2 : ${runner2.description()}');
- print('Dart Dev : ${top}');
- print('Orig Dir : ${orgDir.path}');
- print('Temp Dir : ${tmpDir.path}\n');
+ print('\nRun isolate: ${runner1.description} vs. '
+ '${runner2.description} in ${tmpDir.path}');
- showStatistics();
+ if (showStats) {
+ showStatistics();
+ }
for (int i = 0; i < repeat; i++) {
numTests++;
seed = rand.nextInt(1 << 32);
generateTest();
runTest();
- showStatistics();
+ if (showStats) {
+ showStatistics();
+ }
}
- cleanupSession();
- if (numDivergences != 0) {
- print('\n\nfailure\n');
- return false;
- }
- print('\n\nsuccess\n');
- return true;
+ print('\nDone isolate: ${runner1.description} vs. '
+ '${runner2.description} in ${tmpDir.path}');
+ showStatistics();
+ print('');
+
+ cleanup();
+ return numDivergences == 0;
}
- void setupSession() {
+ void setup() {
rand = new Random();
- orgDir = Directory.current;
tmpDir = Directory.systemTemp.createTempSync('dart_fuzz');
- Directory.current = tmpDir;
- fileName = 'fuzz.dart';
- runner1 = TestRunner.getTestRunner(env, top, mode1);
- runner2 = TestRunner.getTestRunner(env, top, mode2);
+ fileName = '${tmpDir.path}/fuzz.dart';
+ runner1 = TestRunner.getTestRunner(mode1, top, tmpDir.path, env);
+ runner2 = TestRunner.getTestRunner(mode2, top, tmpDir.path, env);
numTests = 0;
numSuccess = 0;
numNotRun = 0;
@@ -206,16 +191,13 @@
numDivergences = 0;
}
- void cleanupSession() {
- Directory.current = orgDir;
+ void cleanup() {
tmpDir.delete(recursive: true);
}
void showStatistics() {
- if (showStats) {
- stdout.write('\rTests: $numTests Success: $numSuccess Not-Run: '
- '$numNotRun: Time-Out: $numTimeOut Divergences: $numDivergences');
- }
+ stdout.write('\rTests: $numTests Success: $numSuccess Not-Run: '
+ '$numNotRun: Time-Out: $numTimeOut Divergences: $numDivergences');
}
void generateTest() {
@@ -225,8 +207,8 @@
}
void runTest() {
- TestResult result1 = runner1.run(fileName);
- TestResult result2 = runner2.run(fileName);
+ TestResult result1 = runner1.run();
+ TestResult result2 = runner2.run();
checkDivergence(result1, result2);
}
@@ -269,7 +251,8 @@
void reportDivergence(
TestResult result1, TestResult result2, bool outputDivergence) {
numDivergences++;
- print('\n\nDIVERGENCE on generated program $version:$seed\n');
+ print('\n\nDIVERGENCE $version:$seed : ${runner1.description} vs. '
+ '${runner2.description}\n');
if (outputDivergence) {
print('out1:\n${result1.output}\n\nout2:\n${result2.output}\n');
}
@@ -284,9 +267,8 @@
final String mode1;
final String mode2;
- // Session.
+ // Test.
Random rand;
- Directory orgDir;
Directory tmpDir;
String fileName;
TestRunner runner1;
@@ -301,38 +283,119 @@
int numDivergences;
}
-// Picks a mode (command line or random).
-String getMode(String mode, String other) {
- // Random when not set.
- if (mode == null || mode == '') {
- // Pick a mode at random (not JS), different from other.
- Random rand = new Random();
- do {
- mode = modes[rand.nextInt(modes.length - 1)];
- } while (mode == other);
- }
- // Verify mode.
- if (modes.contains(mode)) {
- return mode;
- }
- throw ('unknown mode: $mode');
-}
+/// Class to start fuzz testing session.
+class DartFuzzTestSession {
+ DartFuzzTestSession(this.isolates, this.repeat, this.trueDivergence,
+ this.showStats, String tp, this.mode1, this.mode2)
+ : top = getTop(tp) {}
-// Picks a top directory (command line, environment, or current).
-String getTop(String top) {
- if (top == null || top == '') {
- top = Platform.environment['DART_TOP'];
+ start() async {
+ print('\n**\n**** Dart Fuzz Testing Session\n**\n');
+ print('Fuzz Version : ${version}');
+ print('Isolates : ${isolates}');
+ print('Tests : ${repeat}');
+ print('True Divergence : ${trueDivergence}');
+ print('Show Stats : ${showStats}');
+ print('Dart Dev : ${top}');
+ // Fork.
+ List<ReceivePort> ports = new List();
+ for (int i = 0; i < isolates; i++) {
+ ReceivePort r = new ReceivePort();
+ ports.add(r);
+ port = r.sendPort;
+ await Isolate.spawn(run, this);
+ }
+ // Join.
+ bool success = true;
+ for (int i = 0; i < isolates; i++) {
+ var x = await ports[i].first;
+ success = success && x;
+ }
+ if (success) {
+ print('\nsuccess\n');
+ } else {
+ print('\nfailure\n');
+ exitCode = 1;
+ }
}
- if (top == null || top == '') {
- top = Directory.current.path;
+
+ static run(DartFuzzTestSession session) {
+ bool success = false;
+ try {
+ final m1 = getMode(session.mode1, null);
+ final m2 = getMode(session.mode2, m1);
+ final fuzz = new DartFuzzTest(Platform.environment, session.repeat,
+ session.trueDivergence, session.showStats, session.top, m1, m2);
+ success = fuzz.run();
+ } catch (e) {
+ print('Isolate: $e');
+ }
+ session.port.send(success);
}
- return top;
+
+ // Picks a top directory (command line, environment, or current).
+ static String getTop(String top) {
+ if (top == null || top == '') {
+ top = Platform.environment['DART_TOP'];
+ }
+ if (top == null || top == '') {
+ top = Directory.current.path;
+ }
+ return top;
+ }
+
+ // Picks a mode (command line or random).
+ static String getMode(String mode, String other) {
+ // Random when not set.
+ if (mode == null || mode == '') {
+ // Pick a mode at random (not JS), different from other.
+ Random rand = new Random();
+ do {
+ mode = modes[rand.nextInt(modes.length - 1)];
+ } while (mode == other);
+ }
+ // Verify mode.
+ if (modes.contains(mode)) {
+ return mode;
+ }
+ throw ('unknown mode: $mode');
+ }
+
+ // Context.
+ final int isolates;
+ final int repeat;
+ final bool trueDivergence;
+ final bool showStats;
+ final String top;
+ final String mode1;
+ final String mode2;
+
+ // Passes each port to isolate.
+ SendPort port;
+
+ // Supported modes.
+ static const List<String> modes = [
+ 'jit-debug-ia32',
+ 'jit-debug-x64',
+ 'jit-debug-arm32',
+ 'jit-debug-arm64',
+ 'aot-debug-x64',
+ 'aot-debug-arm64',
+ 'jit-ia32',
+ 'jit-x64',
+ 'jit-arm32',
+ 'jit-arm64',
+ 'aot-x64',
+ 'aot-arm64',
+ 'js'
+ ];
}
/// Main driver for a fuzz testing session.
main(List<String> arguments) {
// Set up argument parser.
final parser = new ArgParser()
+ ..addOption('isolates', help: 'number of isolates to use', defaultsTo: '1')
..addOption('repeat', help: 'number of tests to run', defaultsTo: '1000')
..addFlag('true-divergence',
negatable: true, help: 'only report true divergences', defaultsTo: true)
@@ -342,20 +405,18 @@
..addOption('mode1', help: 'execution mode 1')
..addOption('mode2', help: 'execution mode 2');
- // Start fuzz testing session.
+ // Starts fuzz testing session.
try {
final results = parser.parse(arguments);
- final repeat = int.parse(results['repeat']);
- final trueDivergence = results['true-divergence'];
- final showStats = results['show-stats'];
- final mode1 = getMode(results['mode1'], null);
- final mode2 = getMode(results['mode2'], mode1);
- final top = getTop(results['dart-top']);
- final session = new DartFuzzTest(Platform.environment, repeat,
- trueDivergence, showStats, top, mode1, mode2);
- if (!session.runSession()) {
- exitCode = 1;
- }
+ new DartFuzzTestSession(
+ int.parse(results['isolates']),
+ int.parse(results['repeat']),
+ results['true-divergence'],
+ results['show-stats'],
+ results['dart-top'],
+ results['mode1'],
+ results['mode2'])
+ .start();
} catch (e) {
print('Usage: dart dartfuzz_test.dart [OPTIONS]\n${parser.usage}\n$e');
exitCode = 255;