@@ -12,6 +12,8 @@ import 'package:path/path.dart' as path;
12
12
13
13
import '../vm/dart/snapshot_test_helper.dart' ;
14
14
15
+ int crashCounter = 0 ;
16
+
15
17
void forwardStream (Stream <List <int >> input, IOSink output) {
16
18
// Print the information line-by-line.
17
19
input
@@ -22,22 +24,33 @@ void forwardStream(Stream<List<int>> input, IOSink output) {
22
24
});
23
25
}
24
26
25
- Future <bool > run (String executable, List <String > args) async {
27
+ class PotentialCrash {
28
+ final String test;
29
+ final int pid;
30
+ final List <String > binaries;
31
+ PotentialCrash (this .test, this .pid, this .binaries);
32
+ }
33
+
34
+ Future <bool > run (
35
+ String executable, List <String > args, List <PotentialCrash > crashes) async {
26
36
print ('Running "$executable ${args .join (' ' )}"' );
27
37
final Process process = await Process .start (executable, args);
28
38
forwardStream (process.stdout, stdout);
29
39
forwardStream (process.stderr, stderr);
30
40
final int exitCode = await process.exitCode;
31
41
if (exitCode != 0 ) {
42
+ final crashNr = crashCounter++ ;
32
43
print ('=> Running "$executable ${args .join (' ' )}" failed with $exitCode ' );
44
+ print ('=> Possible crash $crashNr (pid: ${process .pid })' );
45
+ crashes.add (PotentialCrash ('crash-$crashNr ' , process.pid, [executable]));
33
46
io.exitCode = 255 ; // Make this shard fail.
34
47
return false ;
35
48
}
36
49
return true ;
37
50
}
38
51
39
52
abstract class TestRunner {
40
- Future runTest ();
53
+ Future runTest (List < PotentialCrash > crashes );
41
54
}
42
55
43
56
class JitTestRunner extends TestRunner {
@@ -46,8 +59,8 @@ class JitTestRunner extends TestRunner {
46
59
47
60
JitTestRunner (this .buildDir, this .arguments);
48
61
49
- Future runTest () async {
50
- await run ('$buildDir /dart' , arguments);
62
+ Future runTest (List < PotentialCrash > crashes ) async {
63
+ await run ('$buildDir /dart' , arguments, crashes );
51
64
}
52
65
}
53
66
@@ -58,19 +71,62 @@ class AotTestRunner extends TestRunner {
58
71
59
72
AotTestRunner (this .buildDir, this .arguments, this .aotArguments);
60
73
61
- Future runTest () async {
74
+ Future runTest (List < PotentialCrash > crashes ) async {
62
75
await withTempDir ((String dir) async {
63
76
final elfFile = path.join (dir, 'app.elf' );
64
77
65
- if (await run ('$buildDir /gen_snapshot' ,
66
- ['--snapshot-kind=app-aot-elf' , '--elf=$elfFile ' , ...arguments])) {
67
- await run (
68
- '$buildDir /dart_precompiled_runtime' , [...aotArguments, elfFile]);
78
+ if (await run (
79
+ '$buildDir /gen_snapshot' ,
80
+ ['--snapshot-kind=app-aot-elf' , '--elf=$elfFile ' , ...arguments],
81
+ crashes)) {
82
+ await run ('$buildDir /dart_precompiled_runtime' ,
83
+ [...aotArguments, elfFile], crashes);
69
84
}
70
85
});
71
86
}
72
87
}
73
88
89
+ // Produces a name that tools/utils.py:BaseCoredumpArchiver supports.
90
+ String getArchiveName (String binary) {
91
+ final parts = binary.split (Platform .pathSeparator);
92
+ late String mode;
93
+ late String arch;
94
+ final buildDir = parts[1 ];
95
+ for (final prefix in ['Release' , 'Debug' , 'Product' ]) {
96
+ if (buildDir.startsWith (prefix)) {
97
+ mode = prefix.toLowerCase ();
98
+ arch = buildDir.substring (prefix.length);
99
+ }
100
+ }
101
+ final name = parts.skip (2 ).join ('__' );
102
+ return 'binary.${mode }_${arch }_${name }' ;
103
+ }
104
+
105
+ void writeUnexpectedCrashesFile (List <PotentialCrash > crashes) {
106
+ // The format of this file is:
107
+ //
108
+ // test-name,pid,binary-file1,binary-file2,...
109
+ //
110
+ const unexpectedCrashesFile = 'unexpected-crashes' ;
111
+
112
+ final buffer = StringBuffer ();
113
+ final Set <String > archivedBinaries = {};
114
+ for (final crash in crashes) {
115
+ buffer.write ('${crash .test },${crash .pid }' );
116
+ for (final binary in crash.binaries) {
117
+ final archivedName = getArchiveName (binary);
118
+ buffer.write (',$archivedName ' );
119
+ if (! archivedBinaries.contains (archivedName)) {
120
+ File (binary).copySync (archivedName);
121
+ archivedBinaries.add (archivedName);
122
+ }
123
+ }
124
+ buffer.writeln ();
125
+ }
126
+
127
+ File (unexpectedCrashesFile).writeAsStringSync (buffer.toString ());
128
+ }
129
+
74
130
const int tsanShards = 200 ;
75
131
76
132
final configurations = < TestRunner > [
@@ -120,11 +176,14 @@ main(List<String> arguments) async {
120
176
..addOption ('shards' , help: 'number of shards used' , defaultsTo: '1' )
121
177
..addOption ('shard' , help: 'shard id' , defaultsTo: '1' )
122
178
..addOption ('output-directory' ,
123
- help: 'unused parameter to make sharding infra work' , defaultsTo: '' );
179
+ help: 'unused parameter to make sharding infra work' , defaultsTo: '' )
180
+ ..addFlag ('copy-coredumps' ,
181
+ help: 'whether to copy binaries for coredumps' , defaultsTo: false );
124
182
125
183
final options = parser.parse (arguments);
126
184
final shards = int .parse (options['shards' ]);
127
185
final shard = int .parse (options['shard' ]) - 1 ;
186
+ final copyCoredumps = options['copy-coredumps' ] as bool ;
128
187
129
188
// Tasks will eventually be killed if they do not have any output for some
130
189
// time. So we'll explicitly print something every 4 minutes.
@@ -140,8 +199,12 @@ main(List<String> arguments) async {
140
199
thisShardsConfigurations.add (configurations[i]);
141
200
}
142
201
}
202
+ final crashes = < PotentialCrash > [];
143
203
for (final config in thisShardsConfigurations) {
144
- await config.runTest ();
204
+ await config.runTest (crashes);
205
+ }
206
+ if (! crashes.isEmpty && copyCoredumps) {
207
+ writeUnexpectedCrashesFile (crashes);
145
208
}
146
209
} finally {
147
210
timer.cancel ();
0 commit comments