[ VM ] Add --mark-main-isolate-as-system-isolate flag

Allows for tools like the Dart test runner to hide the main isolate by
marking it as a system isolate, only showing the isolates users are
interested in.

TEST=pkg/vm_service/mark_main_isolate_as_system_isolate_test.dart

Change-Id: I24d0f20614e89076a05499c206d576c355489a13
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/207203
Reviewed-by: Jacob Richman <jacobr@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
diff --git a/pkg/vm_service/test/mark_main_isolate_as_system_isolate_test.dart b/pkg/vm_service/test/mark_main_isolate_as_system_isolate_test.dart
new file mode 100644
index 0000000..c69323d
--- /dev/null
+++ b/pkg/vm_service/test/mark_main_isolate_as_system_isolate_test.dart
@@ -0,0 +1,39 @@
+// 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.
+
+import 'dart:isolate';
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service.dart' as service;
+
+import 'common/service_test_common.dart';
+import 'common/test_helper.dart';
+
+foo(void _) async {
+  print('non system isolate started');
+  while (true) {}
+}
+
+testMain() async {
+  await Isolate.spawn<void>(foo, null);
+  print('started system isolate main');
+  while (true) {}
+}
+
+var tests = <VMTest>[
+  (service.VmService service) async {
+    final vm = await service.getVM();
+    expect(vm.isolates!.length, 1);
+    expect(vm.isolates!.first.name, 'foo');
+    expect(vm.systemIsolates!.length, greaterThanOrEqualTo(1));
+    expect(vm.systemIsolates!.where((e) => e.name == 'main').isNotEmpty, true);
+  }
+];
+
+main([args = const <String>[]]) => runVMTests(
+      args,
+      tests,
+      'mark_main_isolate_as_system_isolate_test.dart',
+      testeeConcurrent: testMain,
+      extraArgs: ['--mark-main-isolate-as-system-isolate'],
+    );
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index ba743e7..a288a79 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -934,6 +934,7 @@
   int exit_code = 0;
   Dart_IsolateFlags flags;
   Dart_IsolateFlagsInitialize(&flags);
+  flags.is_system_isolate = Options::mark_main_isolate_as_system_isolate();
 
   Dart_Isolate isolate = CreateIsolateGroupAndSetupHelper(
       /* is_main_isolate */ true, script_name, "main",
diff --git a/runtime/bin/main_options.h b/runtime/bin/main_options.h
index 8f9736d..66e6fde 100644
--- a/runtime/bin/main_options.h
+++ b/runtime/bin/main_options.h
@@ -46,7 +46,8 @@
   V(disable_dart_dev, disable_dart_dev)                                        \
   V(long_ssl_cert_evaluation, long_ssl_cert_evaluation)                        \
   V(bypass_trusting_system_roots, bypass_trusting_system_roots)                \
-  V(delayed_filewatch_callback, delayed_filewatch_callback)
+  V(delayed_filewatch_callback, delayed_filewatch_callback)                    \
+  V(mark_main_isolate_as_system_isolate, mark_main_isolate_as_system_isolate)
 
 // Boolean flags that have a short form.
 #define SHORT_BOOL_OPTIONS_LIST(V)                                             \
diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc
index 18789db..0ca25ee 100644
--- a/runtime/lib/isolate.cc
+++ b/runtime/lib/isolate.cc
@@ -634,6 +634,7 @@
 
     // Make a copy of the state's isolate flags and hand it to the callback.
     Dart_IsolateFlags api_flags = *(state_->isolate_flags());
+    api_flags.is_system_isolate = false;
     Dart_Isolate isolate = (create_group_callback)(
         state_->script_url(), name, nullptr, state_->package_config(),
         &api_flags, parent_isolate_->init_callback_data(), &error);