Version 2.15.0-179.0.dev

Merge commit '8062c4cc6719f2f3f75b2b8ca4f6bcfb7b3679f0' into 'dev'
diff --git a/runtime/bin/security_context.cc b/runtime/bin/security_context.cc
index d2cd927..ee431c8 100644
--- a/runtime/bin/security_context.cc
+++ b/runtime/bin/security_context.cc
@@ -807,7 +807,7 @@
   SSLFilter::InitializeLibrary();
   SSL_CTX* ctx = SSL_CTX_new(TLS_method());
   SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLCertContext::CertificateCallback);
-  SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
+  SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
   SSL_CTX_set_cipher_list(ctx, "HIGH:MEDIUM");
   SSLCertContext* context = new SSLCertContext(ctx);
   Dart_Handle err = SetSecurityContext(args, context);
diff --git a/runtime/docs/gc.md b/runtime/docs/gc.md
index dc35cb5..7c2fd9a 100644
--- a/runtime/docs/gc.md
+++ b/runtime/docs/gc.md
@@ -154,7 +154,7 @@
 
 The concurrent marker starts with an acquire-release operation, so all writes by the mutator up to the time that marking starts are visible to the marker.
 
-For old-space objects created before marking started, in each slot the marker can see either its value at the time marking started or any subsequent value sorted in the slot. Any slot that contained a pointer continues to continue a valid pointer for the object's lifetime, so no matter which value the marker sees, it won't interpret a non-pointer as a pointer. (The one interesting case here is array truncation, where some slot in the array will become the header of a filler object. We ensure this is safe for concurrent marking by ensuring the header for the filler object looks like a Smi.) If the marker sees an old value, we may lose some precision and retain a dead object, but we remain correct because the new value has been marked by the mutator.
+For old-space objects created before marking started, in each slot the marker can see either its value at the time marking started or any subsequent value sorted in the slot. Any slot that contained a pointer continues to contain a valid pointer for the object's lifetime, so no matter which value the marker sees, it won't interpret a non-pointer as a pointer. (The one interesting case here is array truncation, where some slot in the array will become the header of a filler object. We ensure this is safe for concurrent marking by ensuring the header for the filler object looks like a Smi.) If the marker sees an old value, we may lose some precision and retain a dead object, but we remain correct because the new value has been marked by the mutator.
 
 For old-space objects created after marking started, the marker may see uninitialized values because operations on slots are not synchronized. To prevent this, during marking we allocate old-space objects [black (marked)](https://en.wikipedia.org/wiki/Tracing_garbage_collection#TRI-COLOR) so the marker will not visit them.
 
diff --git a/runtime/tests/concurrency/run_stress_test_shards.dart b/runtime/tests/concurrency/run_stress_test_shards.dart
index 2d31d15..87eb9ab 100644
--- a/runtime/tests/concurrency/run_stress_test_shards.dart
+++ b/runtime/tests/concurrency/run_stress_test_shards.dart
@@ -12,6 +12,8 @@
 
 import '../vm/dart/snapshot_test_helper.dart';
 
+int crashCounter = 0;
+
 void forwardStream(Stream<List<int>> input, IOSink output) {
   // Print the information line-by-line.
   input
@@ -22,14 +24,25 @@
   });
 }
 
-Future<bool> run(String executable, List<String> args) async {
+class PotentialCrash {
+  final String test;
+  final int pid;
+  final List<String> binaries;
+  PotentialCrash(this.test, this.pid, this.binaries);
+}
+
+Future<bool> run(
+    String executable, List<String> args, List<PotentialCrash> crashes) async {
   print('Running "$executable ${args.join(' ')}"');
   final Process process = await Process.start(executable, args);
   forwardStream(process.stdout, stdout);
   forwardStream(process.stderr, stderr);
   final int exitCode = await process.exitCode;
   if (exitCode != 0) {
+    final crashNr = crashCounter++;
     print('=> Running "$executable ${args.join(' ')}" failed with $exitCode');
+    print('=> Possible crash $crashNr (pid: ${process.pid})');
+    crashes.add(PotentialCrash('crash-$crashNr', process.pid, [executable]));
     io.exitCode = 255; // Make this shard fail.
     return false;
   }
@@ -37,7 +50,7 @@
 }
 
 abstract class TestRunner {
-  Future runTest();
+  Future runTest(List<PotentialCrash> crashes);
 }
 
 class JitTestRunner extends TestRunner {
@@ -46,8 +59,8 @@
 
   JitTestRunner(this.buildDir, this.arguments);
 
-  Future runTest() async {
-    await run('$buildDir/dart', arguments);
+  Future runTest(List<PotentialCrash> crashes) async {
+    await run('$buildDir/dart', arguments, crashes);
   }
 }
 
@@ -58,19 +71,62 @@
 
   AotTestRunner(this.buildDir, this.arguments, this.aotArguments);
 
-  Future runTest() async {
+  Future runTest(List<PotentialCrash> crashes) async {
     await withTempDir((String dir) async {
       final elfFile = path.join(dir, 'app.elf');
 
-      if (await run('$buildDir/gen_snapshot',
-          ['--snapshot-kind=app-aot-elf', '--elf=$elfFile', ...arguments])) {
-        await run(
-            '$buildDir/dart_precompiled_runtime', [...aotArguments, elfFile]);
+      if (await run(
+          '$buildDir/gen_snapshot',
+          ['--snapshot-kind=app-aot-elf', '--elf=$elfFile', ...arguments],
+          crashes)) {
+        await run('$buildDir/dart_precompiled_runtime',
+            [...aotArguments, elfFile], crashes);
       }
     });
   }
 }
 
+// Produces a name that tools/utils.py:BaseCoredumpArchiver supports.
+String getArchiveName(String binary) {
+  final parts = binary.split(Platform.pathSeparator);
+  late String mode;
+  late String arch;
+  final buildDir = parts[1];
+  for (final prefix in ['Release', 'Debug', 'Product']) {
+    if (buildDir.startsWith(prefix)) {
+      mode = prefix.toLowerCase();
+      arch = buildDir.substring(prefix.length);
+    }
+  }
+  final name = parts.skip(2).join('__');
+  return 'binary.${mode}_${arch}_${name}';
+}
+
+void writeUnexpectedCrashesFile(List<PotentialCrash> crashes) {
+  // The format of this file is:
+  //
+  //     test-name,pid,binary-file1,binary-file2,...
+  //
+  const unexpectedCrashesFile = 'unexpected-crashes';
+
+  final buffer = StringBuffer();
+  final Set<String> archivedBinaries = {};
+  for (final crash in crashes) {
+    buffer.write('${crash.test},${crash.pid}');
+    for (final binary in crash.binaries) {
+      final archivedName = getArchiveName(binary);
+      buffer.write(',$archivedName');
+      if (!archivedBinaries.contains(archivedName)) {
+        File(binary).copySync(archivedName);
+        archivedBinaries.add(archivedName);
+      }
+    }
+    buffer.writeln();
+  }
+
+  File(unexpectedCrashesFile).writeAsStringSync(buffer.toString());
+}
+
 const int tsanShards = 200;
 
 final configurations = <TestRunner>[
@@ -120,11 +176,14 @@
     ..addOption('shards', help: 'number of shards used', defaultsTo: '1')
     ..addOption('shard', help: 'shard id', defaultsTo: '1')
     ..addOption('output-directory',
-        help: 'unused parameter to make sharding infra work', defaultsTo: '');
+        help: 'unused parameter to make sharding infra work', defaultsTo: '')
+    ..addFlag('copy-coredumps',
+        help: 'whether to copy binaries for coredumps', defaultsTo: false);
 
   final options = parser.parse(arguments);
   final shards = int.parse(options['shards']);
   final shard = int.parse(options['shard']) - 1;
+  final copyCoredumps = options['copy-coredumps'] as bool;
 
   // Tasks will eventually be killed if they do not have any output for some
   // time. So we'll explicitly print something every 4 minutes.
@@ -140,8 +199,12 @@
         thisShardsConfigurations.add(configurations[i]);
       }
     }
+    final crashes = <PotentialCrash>[];
     for (final config in thisShardsConfigurations) {
-      await config.runTest();
+      await config.runTest(crashes);
+    }
+    if (!crashes.isEmpty && copyCoredumps) {
+      writeUnexpectedCrashesFile(crashes);
     }
   } finally {
     timer.cancel();
diff --git a/tools/VERSION b/tools/VERSION
index b303eca..8d77ea2 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 178
+PRERELEASE 179
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 892e142..8bcd617 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -3912,9 +3912,11 @@
         },
         {
           "name": "Run Isolate Stress Tests",
-          "script": "out/ReleaseX64/dart",
+          "script": "tools/run_with_coredumps_enabled.py",
           "arguments": [
-            "runtime/tests/concurrency/run_stress_test_shards.dart"
+            "out/ReleaseX64/dart",
+            "runtime/tests/concurrency/run_stress_test_shards.dart",
+            "--copy-coredumps"
           ],
           "shards": 10,
           "fileset": "vm-kernel"
diff --git a/tools/run_with_coredumps_enabled.py b/tools/run_with_coredumps_enabled.py
new file mode 100755
index 0000000..e69cb94
--- /dev/null
+++ b/tools/run_with_coredumps_enabled.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+# Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+from contextlib import ExitStack
+import subprocess
+import sys
+
+import utils
+
+
+def Main():
+    args = sys.argv[1:]
+
+    with ExitStack() as stack:
+        for ctx in utils.CoreDumpArchiver(args):
+            stack.enter_context(ctx)
+        exit_code = subprocess.call(args)
+
+    utils.DiagnoseExitCode(exit_code, args)
+    return exit_code
+
+
+if __name__ == '__main__':
+    sys.exit(Main())