Skip to content

Commit

Permalink
Build a specialized snapshot for launching the service isolate in pro…
Browse files Browse the repository at this point in the history
…file mode on Android

Fixes flutter/flutter#91382
  • Loading branch information
jason-simmons committed Oct 21, 2021
1 parent e898106 commit c36f067
Show file tree
Hide file tree
Showing 19 changed files with 191 additions and 17 deletions.
10 changes: 9 additions & 1 deletion build/dart/rules.gni
Original file line number Diff line number Diff line change
Expand Up @@ -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.")
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions common/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ struct Settings {
// case the primary path to the library can not be loaded.
std::vector<std::string> application_library_path;

// Path to a library containing compiled Dart code usable for launching
// the VM service isolate.
std::vector<std::string> vmservice_snapshot_library_path;

std::string application_kernel_asset; // deprecated
std::string application_kernel_list_asset; // deprecated
MappingsCallback application_kernels;
Expand Down
17 changes: 8 additions & 9 deletions runtime/dart_isolate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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<DartIsolate> 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<DartIsolate> service_isolate = weak_service_isolate.lock();
if (!service_isolate) {
Expand Down
19 changes: 19 additions & 0 deletions runtime/dart_snapshot.cc
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,25 @@ fml::RefPtr<DartSnapshot> DartSnapshot::IsolateSnapshotFromMappings(
return nullptr;
}

fml::RefPtr<DartSnapshot> 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<const fml::Mapping> snapshot_data =
SearchMapping(nullptr, "", settings.vmservice_snapshot_library_path,
DartSnapshot::kIsolateDataSymbol, false);
std::shared_ptr<const fml::Mapping> 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<const fml::Mapping> data,
std::shared_ptr<const fml::Mapping> instructions)
: data_(std::move(data)), instructions_(std::move(instructions)) {}
Expand Down
9 changes: 9 additions & 0 deletions runtime/dart_snapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,15 @@ class DartSnapshot : public fml::RefCountedThreadSafe<DartSnapshot> {
std::shared_ptr<const fml::Mapping> snapshot_data,
std::shared_ptr<const fml::Mapping> 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<DartSnapshot> VMServiceIsolateSnapshotFromSettings(
const Settings& settings);

//----------------------------------------------------------------------------
/// @brief Determines if this snapshot contains a heap component. Since
/// the instructions component is optional, the method does not
Expand Down
35 changes: 30 additions & 5 deletions runtime/dart_vm_data.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,25 @@ std::shared_ptr<const DartVMData> DartVMData::Create(
}
}

fml::RefPtr<const DartSnapshot> service_isolate_snapshot =
DartSnapshot::VMServiceIsolateSnapshotFromSettings(settings);

return std::shared_ptr<const DartVMData>(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<const DartSnapshot> vm_snapshot,
fml::RefPtr<const DartSnapshot> isolate_snapshot)
fml::RefPtr<const DartSnapshot> isolate_snapshot,
fml::RefPtr<const DartSnapshot> 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;

Expand All @@ -60,4 +66,23 @@ fml::RefPtr<const DartSnapshot> DartVMData::GetIsolateSnapshot() const {
return isolate_snapshot_;
}

fml::RefPtr<const DartSnapshot> 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
20 changes: 19 additions & 1 deletion runtime/dart_vm_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,32 @@ class DartVMData {
///
fml::RefPtr<const DartSnapshot> GetIsolateSnapshot() const;

//----------------------------------------------------------------------------
/// @brief Get the isolate snapshot used to launch the service isolate
/// in the Dart VM.
///
/// @return The service isolate snapshot.
///
fml::RefPtr<const DartSnapshot> 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<const DartSnapshot> vm_snapshot_;
const fml::RefPtr<const DartSnapshot> isolate_snapshot_;
const fml::RefPtr<const DartSnapshot> service_isolate_snapshot_;

DartVMData(Settings settings,
fml::RefPtr<const DartSnapshot> vm_snapshot,
fml::RefPtr<const DartSnapshot> isolate_snapshot);
fml::RefPtr<const DartSnapshot> isolate_snapshot,
fml::RefPtr<const DartSnapshot> service_isolate_snapshot);

FML_DISALLOW_COPY_AND_ASSIGN(DartVMData);
};
Expand Down
7 changes: 7 additions & 0 deletions shell/common/switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,13 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
std::vector<std::string_view> aot_shared_library_name =
command_line.GetOptionValues(FlagForSwitch(Switch::AotSharedLibraryName));

std::vector<std::string_view> 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);
Expand Down
4 changes: 4 additions & 0 deletions shell/common/switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 "
Expand Down
11 changes: 11 additions & 0 deletions shell/platform/android/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,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") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;

Expand Down Expand Up @@ -240,6 +242,13 @@ public void ensureInitializationComplete(
+ 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);
Expand Down
12 changes: 12 additions & 0 deletions shell/vmservice/BUILD.gn
Original file line number Diff line number Diff line change
@@ -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"
}
6 changes: 6 additions & 0 deletions shell/vmservice/empty.dart
Original file line number Diff line number Diff line change
@@ -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<String> args) {}
8 changes: 8 additions & 0 deletions shell/vmservice/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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'
16 changes: 16 additions & 0 deletions testing/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -103,6 +115,10 @@ source_set("fixture_test") {
"//flutter/common",
"//flutter/runtime",
]

if (flutter_runtime_mode == "profile") {
public_deps += [ ":vmservice_snapshot" ]
}
}

if (enable_unittests) {
Expand Down
5 changes: 5 additions & 0 deletions testing/dart_fixture.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

#include "flutter/testing/dart_fixture.h"
#include "flutter/fml/paths.h"

namespace flutter::testing {

Expand Down Expand Up @@ -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<std::unique_ptr<const fml::Mapping>> kernel_mappings;
Expand Down
8 changes: 7 additions & 1 deletion testing/testing.gni
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions testing/testing.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()`.
Expand Down
1 change: 1 addition & 0 deletions tools/pub_get_offline.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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"),
Expand Down

0 comments on commit c36f067

Please sign in to comment.