diff --git a/engine/src/flutter/build/dart/rules.gni b/engine/src/flutter/build/dart/rules.gni index 704ac9c1a3c..8a595bfcdeb 100644 --- a/engine/src/flutter/build/dart/rules.gni +++ b/engine/src/flutter/build/dart/rules.gni @@ -31,6 +31,9 @@ frontend_server_files += # # Invoker must supply dart_main and package_config. Invoker may optionally # supply aot as a boolean and product as a boolean. +# +# On Android, the invoker may provide output_aot_lib as a string to override +# the default filename for the aot-elf snapshot. template("flutter_snapshot") { assert(!is_fuchsia) assert(defined(invoker.main_dart), "main_dart is a required parameter.") @@ -134,7 +137,12 @@ template("flutter_snapshot") { "--assembly=" + rebase_path(snapshot_assembly), ] } else if (is_android) { - libapp = "$target_gen_dir/android/libs/$android_app_abi/libapp.so" + if (defined(invoker.output_aot_lib)) { + output_aot_lib = invoker.output_aot_lib + } else { + output_aot_lib = "libapp.so" + } + libapp = "$target_gen_dir/android/libs/$android_app_abi/$output_aot_lib" outputs += [ libapp ] args += [ "--snapshot_kind=app-aot-elf", diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 386588cae5c..974f4da819b 100755 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -1794,6 +1794,7 @@ FILE: ../../../flutter/shell/profiling/sampling_profiler.h FILE: ../../../flutter/shell/profiling/sampling_profiler_unittest.cc FILE: ../../../flutter/shell/version/version.cc FILE: ../../../flutter/shell/version/version.h +FILE: ../../../flutter/shell/vmservice/empty.dart FILE: ../../../flutter/sky/tools/roll/patches/chromium/android_build.patch FILE: ../../../flutter/third_party/accessibility/base/color_utils.h FILE: ../../../flutter/third_party/accessibility/base/compiler_specific.h diff --git a/engine/src/flutter/common/settings.h b/engine/src/flutter/common/settings.h index da4b7a1ec43..4854c9b45f3 100644 --- a/engine/src/flutter/common/settings.h +++ b/engine/src/flutter/common/settings.h @@ -116,6 +116,10 @@ struct Settings { // case the primary path to the library can not be loaded. std::vector application_library_path; + // Path to a library containing compiled Dart code usable for launching + // the VM service isolate. + std::vector vmservice_snapshot_library_path; + std::string application_kernel_asset; // deprecated std::string application_kernel_list_asset; // deprecated MappingsCallback application_kernels; diff --git a/engine/src/flutter/runtime/dart_isolate.cc b/engine/src/flutter/runtime/dart_isolate.cc index 3126402859c..748e430c7f5 100644 --- a/engine/src/flutter/runtime/dart_isolate.cc +++ b/engine/src/flutter/runtime/dart_isolate.cc @@ -838,8 +838,7 @@ Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate( // TODO(68663): The service isolate in debug mode is always launched without // sound null safety. Fix after the isolate snapshot data is created with the // right flags. - flags->null_safety = - vm_data->GetIsolateSnapshot()->IsNullSafetyEnabled(nullptr); + flags->null_safety = vm_data->GetServiceIsolateSnapshotNullSafety(); #endif UIDartState::Context context( @@ -848,13 +847,13 @@ Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate( context.advisory_script_uri = DART_VM_SERVICE_ISOLATE_NAME; context.advisory_script_entrypoint = DART_VM_SERVICE_ISOLATE_NAME; std::weak_ptr weak_service_isolate = - DartIsolate::CreateRootIsolate(vm_data->GetSettings(), // - vm_data->GetIsolateSnapshot(), // - nullptr, // - DartIsolate::Flags{flags}, // - nullptr, // - nullptr, // - context); // + DartIsolate::CreateRootIsolate(vm_data->GetSettings(), // + vm_data->GetServiceIsolateSnapshot(), // + nullptr, // + DartIsolate::Flags{flags}, // + nullptr, // + nullptr, // + context); // std::shared_ptr service_isolate = weak_service_isolate.lock(); if (!service_isolate) { diff --git a/engine/src/flutter/runtime/dart_snapshot.cc b/engine/src/flutter/runtime/dart_snapshot.cc index 0dbbab8b12f..9fada1b046c 100644 --- a/engine/src/flutter/runtime/dart_snapshot.cc +++ b/engine/src/flutter/runtime/dart_snapshot.cc @@ -192,6 +192,25 @@ fml::RefPtr DartSnapshot::IsolateSnapshotFromMappings( return nullptr; } +fml::RefPtr DartSnapshot::VMServiceIsolateSnapshotFromSettings( + const Settings& settings) { +#if DART_SNAPSHOT_STATIC_LINK + return nullptr; +#else // DART_SNAPSHOT_STATIC_LINK + if (settings.vmservice_snapshot_library_path.empty()) { + return nullptr; + } + + std::shared_ptr snapshot_data = + SearchMapping(nullptr, "", settings.vmservice_snapshot_library_path, + DartSnapshot::kIsolateDataSymbol, false); + std::shared_ptr snapshot_instructions = + SearchMapping(nullptr, "", settings.vmservice_snapshot_library_path, + DartSnapshot::kIsolateInstructionsSymbol, true); + return IsolateSnapshotFromMappings(snapshot_data, snapshot_instructions); +#endif // DART_SNAPSHOT_STATIC_LINK +} + DartSnapshot::DartSnapshot(std::shared_ptr data, std::shared_ptr instructions) : data_(std::move(data)), instructions_(std::move(instructions)) {} diff --git a/engine/src/flutter/runtime/dart_snapshot.h b/engine/src/flutter/runtime/dart_snapshot.h index 97b5f40763e..0b64936952f 100644 --- a/engine/src/flutter/runtime/dart_snapshot.h +++ b/engine/src/flutter/runtime/dart_snapshot.h @@ -114,6 +114,15 @@ class DartSnapshot : public fml::RefCountedThreadSafe { std::shared_ptr snapshot_data, std::shared_ptr snapshot_instructions); + //---------------------------------------------------------------------------- + /// @brief Create an isolate snapshot specialized for launching the + /// service isolate. Returns nullptr if no such snapshot is + /// available. + /// + /// @return A valid isolate snapshot or nullptr. + static fml::RefPtr VMServiceIsolateSnapshotFromSettings( + const Settings& settings); + //---------------------------------------------------------------------------- /// @brief Determines if this snapshot contains a heap component. Since /// the instructions component is optional, the method does not diff --git a/engine/src/flutter/runtime/dart_vm_data.cc b/engine/src/flutter/runtime/dart_vm_data.cc index 816d428f089..a47f5d1cc48 100644 --- a/engine/src/flutter/runtime/dart_vm_data.cc +++ b/engine/src/flutter/runtime/dart_vm_data.cc @@ -32,19 +32,25 @@ std::shared_ptr DartVMData::Create( } } + fml::RefPtr service_isolate_snapshot = + DartSnapshot::VMServiceIsolateSnapshotFromSettings(settings); + return std::shared_ptr(new DartVMData( - std::move(settings), // - std::move(vm_snapshot), // - std::move(isolate_snapshot) // + std::move(settings), // + std::move(vm_snapshot), // + std::move(isolate_snapshot), // + std::move(service_isolate_snapshot) // )); } DartVMData::DartVMData(Settings settings, fml::RefPtr vm_snapshot, - fml::RefPtr isolate_snapshot) + fml::RefPtr isolate_snapshot, + fml::RefPtr service_isolate_snapshot) : settings_(settings), vm_snapshot_(vm_snapshot), - isolate_snapshot_(isolate_snapshot) {} + isolate_snapshot_(isolate_snapshot), + service_isolate_snapshot_(service_isolate_snapshot) {} DartVMData::~DartVMData() = default; @@ -60,4 +66,23 @@ fml::RefPtr DartVMData::GetIsolateSnapshot() const { return isolate_snapshot_; } +fml::RefPtr DartVMData::GetServiceIsolateSnapshot() const { + // Use the specialized snapshot for the service isolate if the embedder + // provides one. Otherwise, use the application snapshot. + return service_isolate_snapshot_ ? service_isolate_snapshot_ + : isolate_snapshot_; +} + +bool DartVMData::GetServiceIsolateSnapshotNullSafety() const { + if (service_isolate_snapshot_) { + // The specialized snapshot for the service isolate is always built + // using null safety. However, calling Dart_DetectNullSafety on + // the service isolate snapshot will not work as expected - it will + // instead return a cached value representing the app snapshot. + return true; + } else { + return isolate_snapshot_->IsNullSafetyEnabled(nullptr); + } +} + } // namespace flutter diff --git a/engine/src/flutter/runtime/dart_vm_data.h b/engine/src/flutter/runtime/dart_vm_data.h index f614a24ad0b..e0d914ea28a 100644 --- a/engine/src/flutter/runtime/dart_vm_data.h +++ b/engine/src/flutter/runtime/dart_vm_data.h @@ -71,14 +71,32 @@ class DartVMData { /// fml::RefPtr GetIsolateSnapshot() const; + //---------------------------------------------------------------------------- + /// @brief Get the isolate snapshot used to launch the service isolate + /// in the Dart VM. + /// + /// @return The service isolate snapshot. + /// + fml::RefPtr GetServiceIsolateSnapshot() const; + + //---------------------------------------------------------------------------- + /// @brief Returns whether the service isolate snapshot requires null + /// safety in the Dart_IsolateFlags used to create the isolate. + /// + /// @return True if the snapshot requires null safety. + /// + bool GetServiceIsolateSnapshotNullSafety() const; + private: const Settings settings_; const fml::RefPtr vm_snapshot_; const fml::RefPtr isolate_snapshot_; + const fml::RefPtr service_isolate_snapshot_; DartVMData(Settings settings, fml::RefPtr vm_snapshot, - fml::RefPtr isolate_snapshot); + fml::RefPtr isolate_snapshot, + fml::RefPtr service_isolate_snapshot); FML_DISALLOW_COPY_AND_ASSIGN(DartVMData); }; diff --git a/engine/src/flutter/shell/common/switches.cc b/engine/src/flutter/shell/common/switches.cc index cb5bda824e9..050452c75e8 100644 --- a/engine/src/flutter/shell/common/switches.cc +++ b/engine/src/flutter/shell/common/switches.cc @@ -334,6 +334,13 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) { std::vector aot_shared_library_name = command_line.GetOptionValues(FlagForSwitch(Switch::AotSharedLibraryName)); + std::vector vmservice_shared_library_name = + command_line.GetOptionValues( + FlagForSwitch(Switch::AotVMServiceSharedLibraryName)); + for (auto path : vmservice_shared_library_name) { + settings.vmservice_snapshot_library_path.emplace_back(path); + } + std::string snapshot_asset_path; command_line.GetOptionValue(FlagForSwitch(Switch::SnapshotAssetPath), &snapshot_asset_path); diff --git a/engine/src/flutter/shell/common/switches.h b/engine/src/flutter/shell/common/switches.h index 6226e8d7e9f..0a44e6cc587 100644 --- a/engine/src/flutter/shell/common/switches.h +++ b/engine/src/flutter/shell/common/switches.h @@ -28,6 +28,10 @@ DEF_SWITCHES_START DEF_SWITCH(AotSharedLibraryName, "aot-shared-library-name", "Name of the *.so containing AOT compiled Dart assets.") +DEF_SWITCH(AotVMServiceSharedLibraryName, + "aot-vmservice-shared-library-name", + "Name of the *.so containing AOT compiled Dart assets for " + "launching the service isolate.") DEF_SWITCH(SnapshotAssetPath, "snapshot-asset-path", "Path to the directory containing the four files specified by " diff --git a/engine/src/flutter/shell/platform/android/BUILD.gn b/engine/src/flutter/shell/platform/android/BUILD.gn index 5b1c649694f..cb8e78de538 100644 --- a/engine/src/flutter/shell/platform/android/BUILD.gn +++ b/engine/src/flutter/shell/platform/android/BUILD.gn @@ -444,6 +444,17 @@ action("android_jar") { ":pom_embedding", ":pom_libflutter", ] + + if (flutter_runtime_mode == "profile") { + deps += [ "//flutter/shell/vmservice:vmservice_snapshot" ] + args += [ + "--native_lib", + rebase_path( + "$root_gen_dir/flutter/shell/vmservice/android/libs/$android_app_abi/libvmservice_snapshot.so", + root_build_dir, + root_build_dir), + ] + } } action("pom_libflutter") { diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java index 190324abc91..dad1a05d099 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java @@ -42,6 +42,7 @@ public class FlutterLoader { // Must match values in flutter::switches static final String AOT_SHARED_LIBRARY_NAME = "aot-shared-library-name"; + static final String AOT_VMSERVICE_SHARED_LIBRARY_NAME = "aot-vmservice-shared-library-name"; static final String SNAPSHOT_ASSET_PATH_KEY = "snapshot-asset-path"; static final String VM_SNAPSHOT_DATA_KEY = "vm-snapshot-data"; static final String ISOLATE_SNAPSHOT_DATA_KEY = "isolate-snapshot-data"; @@ -51,6 +52,7 @@ public class FlutterLoader { // Resource names used for components of the precompiled snapshot. private static final String DEFAULT_LIBRARY = "libflutter.so"; private static final String DEFAULT_KERNEL_BLOB = "kernel_blob.bin"; + private static final String VMSERVICE_SNAPSHOT_LIBRARY = "libvmservice_snapshot.so"; private static FlutterLoader instance; @@ -240,6 +242,13 @@ public class FlutterLoader { + flutterApplicationInfo.nativeLibraryDir + File.separator + flutterApplicationInfo.aotSharedLibraryName); + + // In profile mode, provide a separate library containing a snapshot for + // launching the Dart VM service isolate. + if (BuildConfig.PROFILE) { + shellArgs.add( + "--" + AOT_VMSERVICE_SHARED_LIBRARY_NAME + "=" + VMSERVICE_SNAPSHOT_LIBRARY); + } } shellArgs.add("--cache-dir-path=" + result.engineCachesPath); diff --git a/engine/src/flutter/shell/vmservice/BUILD.gn b/engine/src/flutter/shell/vmservice/BUILD.gn new file mode 100644 index 00000000000..becc886597f --- /dev/null +++ b/engine/src/flutter/shell/vmservice/BUILD.gn @@ -0,0 +1,12 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//flutter/build/dart/rules.gni") + +# Build a minimal snapshot that can be used to launch the VM service isolate. +flutter_snapshot("vmservice_snapshot") { + main_dart = "empty.dart" + package_config = ".dart_tool/package_config.json" + output_aot_lib = "libvmservice_snapshot.so" +} diff --git a/engine/src/flutter/shell/vmservice/empty.dart b/engine/src/flutter/shell/vmservice/empty.dart new file mode 100644 index 00000000000..120fa0bae20 --- /dev/null +++ b/engine/src/flutter/shell/vmservice/empty.dart @@ -0,0 +1,6 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is used to build an empty snapshot that can be used to start the VM service isolate. +void main(List args) {} diff --git a/engine/src/flutter/shell/vmservice/pubspec.yaml b/engine/src/flutter/shell/vmservice/pubspec.yaml new file mode 100644 index 00000000000..7c1f613b340 --- /dev/null +++ b/engine/src/flutter/shell/vmservice/pubspec.yaml @@ -0,0 +1,8 @@ +# Copyright 2013 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +name: vmservice_snapshot +publish_to: none +environment: + sdk: '>=2.12.0 <3.0.0' diff --git a/engine/src/flutter/testing/BUILD.gn b/engine/src/flutter/testing/BUILD.gn index e77968c536b..b5033acafd8 100644 --- a/engine/src/flutter/testing/BUILD.gn +++ b/engine/src/flutter/testing/BUILD.gn @@ -88,6 +88,18 @@ source_set("skia") { ] } +dart_snapshot_kernel("vmservice_kernel") { + dart_main = + rebase_path("//flutter/shell/vmservice/empty.dart", root_build_dir) + dart_kernel = "$target_gen_dir/assets/vmservice_kernel.bin" +} + +dart_snapshot_aot("vmservice_snapshot") { + dart_kernel = "$target_gen_dir/assets/vmservice_kernel.bin" + dart_elf_filename = "libvmservice_snapshot.so" + deps = [ ":vmservice_kernel" ] +} + source_set("fixture_test") { testonly = true @@ -103,6 +115,10 @@ source_set("fixture_test") { "//flutter/common", "//flutter/runtime", ] + + if (flutter_runtime_mode == "profile") { + public_deps += [ ":vmservice_snapshot" ] + } } if (enable_unittests) { diff --git a/engine/src/flutter/testing/dart_fixture.cc b/engine/src/flutter/testing/dart_fixture.cc index 4e86a33e33c..2852f4165fb 100644 --- a/engine/src/flutter/testing/dart_fixture.cc +++ b/engine/src/flutter/testing/dart_fixture.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "flutter/testing/dart_fixture.h" +#include "flutter/fml/paths.h" namespace flutter::testing { @@ -48,6 +49,10 @@ void DartFixture::SetSnapshotsAndAssets(Settings& settings) { // snapshots will be present in the application AOT dylib. if (DartVM::IsRunningPrecompiledCode()) { FML_CHECK(PrepareSettingsForAOTWithSymbols(settings, aot_symbols_)); +#if OS_LINUX + settings.vmservice_snapshot_library_path.emplace_back(fml::paths::JoinPaths( + {GetTestingAssetsPath(), "libvmservice_snapshot.so"})); +#endif // OS_LINUX } else { settings.application_kernels = [this]() -> Mappings { std::vector> kernel_mappings; diff --git a/engine/src/flutter/testing/testing.gni b/engine/src/flutter/testing/testing.gni index e457b0ffb77..5c67b8ceeea 100644 --- a/engine/src/flutter/testing/testing.gni +++ b/engine/src/flutter/testing/testing.gni @@ -26,10 +26,16 @@ template("fixtures_location") { assert(defined(invoker.assets_dir), "The assets directory.") location_path = rebase_path(invoker.assets_dir) + testing_assets_path = rebase_path("$root_out_dir/gen/flutter/testing/assets") # Array of source lines. We use a list to ensure a trailing newline is # emitted by write_file() to comply with -Wnewline-eof. - location_source = [ "namespace flutter {namespace testing {const char* GetFixturesPath() {return \"$location_path\";}}}" ] + location_source = [ + "namespace flutter { namespace testing { ", + "const char* GetFixturesPath() {return \"$location_path\";} ", + "const char* GetTestingAssetsPath() {return \"$testing_assets_path\";} ", + "}}", + ] location_source_path = "$target_gen_dir/_fl_$target_name.cc" write_file(location_source_path, location_source) diff --git a/engine/src/flutter/testing/testing.h b/engine/src/flutter/testing/testing.h index 99a8e1bbe7e..386763a0b27 100644 --- a/engine/src/flutter/testing/testing.h +++ b/engine/src/flutter/testing/testing.h @@ -27,6 +27,13 @@ namespace testing { /// const char* GetFixturesPath(); +//------------------------------------------------------------------------------ +/// @brief Returns the directory containing assets shared across all tests. +/// +/// @return The testing assets path. +/// +const char* GetTestingAssetsPath(); + //------------------------------------------------------------------------------ /// @brief Returns the default path to kernel_blob.bin. This file is within /// the directory returned by `GetFixturesPath()`. diff --git a/engine/src/flutter/tools/pub_get_offline.py b/engine/src/flutter/tools/pub_get_offline.py index f8a1d2f7b32..c978e8ddac5 100644 --- a/engine/src/flutter/tools/pub_get_offline.py +++ b/engine/src/flutter/tools/pub_get_offline.py @@ -17,6 +17,7 @@ import sys ALL_PACKAGES = [ os.path.join("src", "flutter", "ci"), os.path.join("src", "flutter", "flutter_frontend_server"), + os.path.join("src", "flutter", "shell", "vmservice"), os.path.join("src", "flutter", "testing", "benchmark"), os.path.join("src", "flutter", "testing", "dart"), os.path.join("src", "flutter", "testing", "litetest"),