[test] Add option to test harness to run VM tests under rr and save traces from crashes.
E.g., ./tools/test.py --rr --repeat=100 Change-Id: I2776b38daa6b14d1ca59800969a0ecfaeb71a0ee Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/167241 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
98a21f44d5
commit
79db8e0055
@@ -10,6 +10,7 @@ import 'dart:io' as io;
|
||||
import 'command_output.dart';
|
||||
import 'configuration.dart';
|
||||
import 'path.dart';
|
||||
import 'test_case.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
/// A command executed as a step in a test case.
|
||||
@@ -531,6 +532,75 @@ class VMBatchCommand extends ProcessCommand implements VMCommand {
|
||||
}
|
||||
}
|
||||
|
||||
// Run a VM test under RR, and copy the trace if it crashes. Using a helper
|
||||
// script like the precompiler does not work because the RR traces are large
|
||||
// and we must diligently erase them for non-crashes even if the test times
|
||||
// out and would be killed by the harness, so the copying and cleanup logic
|
||||
// must be in the harness.
|
||||
class RRCommand extends Command {
|
||||
VMCommand originalCommand;
|
||||
VMCommand wrappedCommand;
|
||||
io.Directory recordingDir;
|
||||
io.Directory savedDir;
|
||||
|
||||
RRCommand(this.originalCommand)
|
||||
: super._("rr", index: originalCommand.index) {
|
||||
final suffix = "/rr-trace-" + originalCommand.hashCode.toString();
|
||||
recordingDir = io.Directory(io.Directory.systemTemp.path + suffix);
|
||||
savedDir = io.Directory("out" + suffix);
|
||||
final executable = "rr";
|
||||
final arguments = <String>[
|
||||
"record",
|
||||
"--chaos",
|
||||
"--output-trace-dir=" + recordingDir.path,
|
||||
];
|
||||
arguments.add(originalCommand.executable);
|
||||
arguments.addAll(originalCommand.arguments);
|
||||
wrappedCommand = VMCommand(
|
||||
executable, arguments, originalCommand.environmentOverrides,
|
||||
index: originalCommand.index);
|
||||
}
|
||||
|
||||
RRCommand indexedCopy(int index) =>
|
||||
RRCommand(originalCommand.indexedCopy(index));
|
||||
|
||||
Future<CommandOutput> run(int timeout) async {
|
||||
// rr will fail if the output trace directory already exists. Delete any
|
||||
// that might be leftover from interrupting the harness.
|
||||
if (await recordingDir.exists()) {
|
||||
await recordingDir.delete(recursive: true);
|
||||
}
|
||||
final output = await RunningProcess(wrappedCommand, timeout).run();
|
||||
if (output.hasCrashed) {
|
||||
if (await savedDir.exists()) {
|
||||
await savedDir.delete(recursive: true);
|
||||
}
|
||||
await recordingDir.rename(savedDir.path);
|
||||
await io.File(savedDir.path + "/command.txt")
|
||||
.writeAsString(wrappedCommand.reproductionCommand);
|
||||
await io.File(savedDir.path + "/stdout.txt").writeAsBytes(output.stdout);
|
||||
await io.File(savedDir.path + "/stderr.txt").writeAsBytes(output.stderr);
|
||||
} else {
|
||||
await recordingDir.delete(recursive: true);
|
||||
}
|
||||
|
||||
return VMCommandOutput(this, output.exitCode, output.hasTimedOut,
|
||||
output.stdout, output.stderr, output.time, output.pid);
|
||||
}
|
||||
|
||||
String get reproductionCommand =>
|
||||
wrappedCommand.reproductionCommand + " (rr replay " + savedDir.path + ")";
|
||||
|
||||
void _buildHashCode(HashCodeBuilder builder) {
|
||||
originalCommand._buildHashCode(builder);
|
||||
builder.add(42);
|
||||
}
|
||||
|
||||
bool _equal(RRCommand other) =>
|
||||
hashCode == other.hashCode &&
|
||||
originalCommand._equal(other.originalCommand);
|
||||
}
|
||||
|
||||
abstract class AdbCommand {
|
||||
String get buildPath;
|
||||
List<String> get extraLibraries;
|
||||
|
||||
@@ -32,6 +32,7 @@ class TestConfiguration {
|
||||
this.batch,
|
||||
this.batchDart2JS,
|
||||
this.copyCoreDumps,
|
||||
this.rr,
|
||||
this.isVerbose,
|
||||
this.listTests,
|
||||
this.listStatusFiles,
|
||||
@@ -82,6 +83,7 @@ class TestConfiguration {
|
||||
final bool batch;
|
||||
final bool batchDart2JS;
|
||||
final bool copyCoreDumps;
|
||||
final bool rr;
|
||||
final bool fastTestsOnly;
|
||||
final bool isVerbose;
|
||||
final bool listTests;
|
||||
|
||||
@@ -293,6 +293,8 @@ settings.''',
|
||||
'''If we see a crash that we did not expect, copy the core dumps to
|
||||
"/tmp".''',
|
||||
hide: true),
|
||||
_Option.bool('rr', '''Run VM tests under rr and save traces from crashes''',
|
||||
hide: true),
|
||||
_Option(
|
||||
'local_ip',
|
||||
'''IP address the HTTP servers should listen on. This address is also
|
||||
@@ -735,6 +737,7 @@ compiler.''',
|
||||
batch: !(data["noBatch"] as bool),
|
||||
batchDart2JS: data["dart2js_batch"] as bool,
|
||||
copyCoreDumps: data["copy_coredumps"] as bool,
|
||||
rr: data["rr"] as bool,
|
||||
isVerbose: data["verbose"] as bool,
|
||||
listTests: data["list"] as bool,
|
||||
listStatusFiles: data["list_status_files"] as bool,
|
||||
|
||||
@@ -614,6 +614,8 @@ class CommandExecutorImpl implements CommandExecutor {
|
||||
return RunningProcess(command, timeout,
|
||||
configuration: globalConfiguration)
|
||||
.run();
|
||||
} else if (command is RRCommand) {
|
||||
return command.run(timeout);
|
||||
} else {
|
||||
throw ArgumentError("Unknown command type ${command.runtimeType}.");
|
||||
}
|
||||
|
||||
@@ -263,6 +263,9 @@ class DartVmRuntimeConfiguration extends RuntimeConfiguration {
|
||||
if (_configuration.sanitizer != Sanitizer.none) {
|
||||
multiplier *= 2;
|
||||
}
|
||||
if (_configuration.rr) {
|
||||
multiplier *= 2;
|
||||
}
|
||||
return multiplier;
|
||||
}
|
||||
}
|
||||
@@ -297,7 +300,11 @@ class StandaloneDartRuntimeConfiguration extends DartVmRuntimeConfiguration {
|
||||
arguments.insertAll(0, config.arguments);
|
||||
executable = config.executable;
|
||||
}
|
||||
return [VMCommand(executable, arguments, environmentOverrides)];
|
||||
var command = VMCommand(executable, arguments, environmentOverrides);
|
||||
if (_configuration.rr && !isCrashExpected) {
|
||||
return [RRCommand(command)];
|
||||
}
|
||||
return [command];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,7 +333,11 @@ class DartPrecompiledRuntimeConfiguration extends DartVmRuntimeConfiguration {
|
||||
executable = config.executable;
|
||||
}
|
||||
|
||||
return [VMCommand(executable, arguments, environmentOverrides)];
|
||||
var command = VMCommand(executable, arguments, environmentOverrides);
|
||||
if (_configuration.rr && !isCrashExpected) {
|
||||
return [RRCommand(command)];
|
||||
}
|
||||
return [command];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user