From dffe3fb276ea626ec58b5e367841a61d508e9b4f Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 31 Jan 2020 12:52:00 -0800 Subject: [PATCH] Isolate and move common portable ELF loading from fixtures into //flutter/testing. (#16305) Also update all known test harnesses to use this and fixes the broken shell_unittests harness. Fixes https://github.com/flutter/flutter/issues/49853 --- runtime/BUILD.gn | 1 - runtime/runtime_test.cc | 66 +-------------- runtime/runtime_test.h | 16 +--- shell/common/shell_test.cc | 24 +----- shell/common/shell_test.h | 2 + shell/platform/embedder/BUILD.gn | 1 - .../embedder/tests/embedder_test_context.cc | 64 +++------------ .../embedder/tests/embedder_test_context.h | 6 +- testing/BUILD.gn | 43 ++++++---- testing/elf_loader.cc | 82 +++++++++++++++++++ testing/elf_loader.h | 60 ++++++++++++++ 11 files changed, 187 insertions(+), 178 deletions(-) create mode 100644 testing/elf_loader.cc create mode 100644 testing/elf_loader.h diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index 7868ad742..7b9fb56b4 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -147,7 +147,6 @@ source_set("runtime_unittests_common") { "$flutter_root/shell/common", "$flutter_root/testing:dart", "$flutter_root/third_party/tonic", - "//third_party/dart/runtime/bin:elf_loader", "//third_party/skia", ] } diff --git a/runtime/runtime_test.cc b/runtime/runtime_test.cc index facbad541..4e92925f5 100644 --- a/runtime/runtime_test.cc +++ b/runtime/runtime_test.cc @@ -4,65 +4,18 @@ #include "flutter/runtime/runtime_test.h" -#include "flutter/fml/file.h" -#include "flutter/fml/native_library.h" -#include "flutter/fml/paths.h" -#include "flutter/runtime/dart_snapshot.h" #include "flutter/runtime/dart_vm.h" #include "flutter/testing/testing.h" namespace flutter { namespace testing { -static constexpr const char* kAOTAppELFFileName = "app_elf_snapshot.so"; - -static ELFAOTSymbols LoadELFIfNecessary() { - if (!DartVM::IsRunningPrecompiledCode()) { - return {}; - } - - const auto elf_path = - fml::paths::JoinPaths({GetFixturesPath(), kAOTAppELFFileName}); - - if (!fml::IsFile(elf_path)) { - FML_LOG(ERROR) << "App AOT file does not exist for this fixture. Attempts " - "to launch the Dart VM will fail."; - return {}; - } - - ELFAOTSymbols symbols; - - // Must not be freed. - const char* error = nullptr; - - auto loaded_elf = - Dart_LoadELF(elf_path.c_str(), // file path - 0, // file offset - &error, // error (out) - &symbols.vm_snapshot_data, // vm snapshot data (out) - &symbols.vm_snapshot_instrs, // vm snapshot instrs (out) - &symbols.vm_isolate_data, // vm isolate data (out) - &symbols.vm_isolate_instrs // vm isolate instr (out) - ); - - if (loaded_elf == nullptr) { - FML_LOG(ERROR) << "Could not fetch AOT symbols from loaded ELF. Attempts " - "to launch the Dart VM will fail. Error: " - << error; - return {}; - } - - symbols.loaded_elf.reset(loaded_elf); - - return symbols; -} - RuntimeTest::RuntimeTest() : native_resolver_(std::make_shared()), assets_dir_(fml::OpenDirectory(GetFixturesPath(), false, fml::FilePermission::kRead)), - aot_symbols_(LoadELFIfNecessary()) {} + aot_symbols_(LoadELFSymbolFromFixturesIfNeccessary()) {} void RuntimeTest::SetSnapshotsAndAssets(Settings& settings) { if (!assets_dir_.is_valid()) { @@ -75,22 +28,7 @@ void RuntimeTest::SetSnapshotsAndAssets(Settings& settings) { // don't need to be explicitly supplied by the embedder. In AOT, these // snapshots will be present in the application AOT dylib. if (DartVM::IsRunningPrecompiledCode()) { - settings.vm_snapshot_data = [&]() { - return std::make_unique( - aot_symbols_.vm_snapshot_data, 0u); - }; - settings.isolate_snapshot_data = [&]() { - return std::make_unique( - aot_symbols_.vm_isolate_data, 0u); - }; - settings.vm_snapshot_instr = [&]() { - return std::make_unique( - aot_symbols_.vm_snapshot_instrs, 0u); - }; - settings.isolate_snapshot_instr = [&]() { - return std::make_unique( - aot_symbols_.vm_isolate_instrs, 0u); - }; + PrepareSettingsForAOTWithSymbols(settings, aot_symbols_); } else { settings.application_kernels = [this]() { std::vector> kernel_mappings; diff --git a/runtime/runtime_test.h b/runtime/runtime_test.h index 77e3e6ec0..6a4a4fc2f 100644 --- a/runtime/runtime_test.h +++ b/runtime/runtime_test.h @@ -9,27 +9,13 @@ #include "flutter/common/settings.h" #include "flutter/fml/macros.h" +#include "flutter/testing/elf_loader.h" #include "flutter/testing/test_dart_native_resolver.h" #include "flutter/testing/thread_test.h" -#include "third_party/dart/runtime/bin/elf_loader.h" namespace flutter { namespace testing { -struct LoadedELFDeleter { - void operator()(Dart_LoadedElf* elf) { Dart_UnloadELF(elf); } -}; - -using UniqueLoadedELF = std::unique_ptr; - -struct ELFAOTSymbols { - UniqueLoadedELF loaded_elf; - const uint8_t* vm_snapshot_data = nullptr; - const uint8_t* vm_snapshot_instrs = nullptr; - const uint8_t* vm_isolate_data = nullptr; - const uint8_t* vm_isolate_instrs = nullptr; -}; - class RuntimeTest : public ThreadTest { public: RuntimeTest(); diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 2a9e82320..ab07b9b78 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -25,7 +25,8 @@ ShellTest::ShellTest() ThreadHost::Type::UI | ThreadHost::Type::GPU), assets_dir_(fml::OpenDirectory(GetFixturesPath(), false, - fml::FilePermission::kRead)) {} + fml::FilePermission::kRead)), + aot_symbols_(LoadELFSymbolFromFixturesIfNeccessary()) {} void ShellTest::SendEnginePlatformMessage( Shell* shell, @@ -52,26 +53,7 @@ void ShellTest::SetSnapshotsAndAssets(Settings& settings) { // In JIT execution, all snapshots are present within the binary itself and // don't need to be explicitly suppiled by the embedder. if (DartVM::IsRunningPrecompiledCode()) { - settings.vm_snapshot_data = [this]() { - return fml::FileMapping::CreateReadOnly(assets_dir_, "vm_snapshot_data"); - }; - - settings.isolate_snapshot_data = [this]() { - return fml::FileMapping::CreateReadOnly(assets_dir_, - "isolate_snapshot_data"); - }; - - if (DartVM::IsRunningPrecompiledCode()) { - settings.vm_snapshot_instr = [this]() { - return fml::FileMapping::CreateReadExecute(assets_dir_, - "vm_snapshot_instr"); - }; - - settings.isolate_snapshot_instr = [this]() { - return fml::FileMapping::CreateReadExecute(assets_dir_, - "isolate_snapshot_instr"); - }; - } + PrepareSettingsForAOTWithSymbols(settings, aot_symbols_); } else { settings.application_kernels = [this]() { std::vector> kernel_mappings; diff --git a/shell/common/shell_test.h b/shell/common/shell_test.h index 12b59f38a..9bd9f7fd3 100644 --- a/shell/common/shell_test.h +++ b/shell/common/shell_test.h @@ -16,6 +16,7 @@ #include "flutter/shell/common/shell.h" #include "flutter/shell/common/thread_host.h" #include "flutter/shell/common/vsync_waiters_test.h" +#include "flutter/testing/elf_loader.h" #include "flutter/testing/test_dart_native_resolver.h" #include "flutter/testing/thread_test.h" @@ -84,6 +85,7 @@ class ShellTest : public ThreadTest { std::shared_ptr native_resolver_; ThreadHost thread_host_; fml::UniqueFD assets_dir_; + ELFAOTSymbols aot_symbols_; FML_DISALLOW_COPY_AND_ASSIGN(ShellTest); }; diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index 56ad152e1..1159b5d2a 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -136,7 +136,6 @@ if (current_toolchain == host_toolchain) { "$flutter_root/testing:opengl", "$flutter_root/testing:skia", "$flutter_root/third_party/tonic", - "//third_party/dart/runtime/bin:elf_loader", "//third_party/skia", ] } diff --git a/shell/platform/embedder/tests/embedder_test_context.cc b/shell/platform/embedder/tests/embedder_test_context.cc index 4ac7c2431..f0cdd8850 100644 --- a/shell/platform/embedder/tests/embedder_test_context.cc +++ b/shell/platform/embedder/tests/embedder_test_context.cc @@ -14,10 +14,9 @@ namespace flutter { namespace testing { -static constexpr const char* kAppAOTELFFileName = "app_elf_snapshot.so"; - EmbedderTestContext::EmbedderTestContext(std::string assets_path) : assets_path_(std::move(assets_path)), + aot_symbols_(LoadELFSymbolFromFixturesIfNeccessary()), native_resolver_(std::make_shared()) { SetupAOTMappingsIfNecessary(); isolate_create_callbacks_.push_back( @@ -29,63 +28,20 @@ EmbedderTestContext::EmbedderTestContext(std::string assets_path) }); } -EmbedderTestContext::~EmbedderTestContext() { - vm_snapshot_data_.reset(); - vm_snapshot_instructions_.reset(); - isolate_snapshot_data_.reset(); - isolate_snapshot_instructions_.reset(); - if (elf_library_handle_ != nullptr) { - Dart_UnloadELF(elf_library_handle_); - } -} +EmbedderTestContext::~EmbedderTestContext() = default; void EmbedderTestContext::SetupAOTMappingsIfNecessary() { if (!DartVM::IsRunningPrecompiledCode()) { - // Not in AOT mode. Nothing to do. return; } - - auto elf_path = fml::paths::JoinPaths({assets_path_, kAppAOTELFFileName}); - - if (!fml::IsFile(elf_path.c_str())) { - FML_LOG(ERROR) << "Could not find the applications AOT ELF file in an " - "AOT environment. Engine launches attempted with this " - "context will fail. File not found: " - << elf_path; - return; - } - - const uint8_t* vm_snapshot_data = nullptr; - const uint8_t* vm_snapshot_instructions = nullptr; - const uint8_t* isolate_snapshot_data = nullptr; - const uint8_t* isolate_snapshot_instructions = nullptr; - - const char* error = nullptr; - - elf_library_handle_ = - Dart_LoadELF(elf_path.c_str(), // filename - 0u, // file offset - &error, // error (must not be freed) - &vm_snapshot_data, // VM snapshot data - &vm_snapshot_instructions, // VM snapshot instructions - &isolate_snapshot_data, // VM isolate data - &isolate_snapshot_instructions // VM isolate instructions - ); - - if (elf_library_handle_ == nullptr) { - FML_LOG(ERROR) << "Could not load snapshot in an AOT environment. Engine " - "launches attempted with this context will fail. Error: " - << error; - return; - } - - vm_snapshot_data_.reset(new fml::NonOwnedMapping(vm_snapshot_data, 0)); - vm_snapshot_instructions_.reset( - new fml::NonOwnedMapping(vm_snapshot_instructions, 0)); - isolate_snapshot_data_.reset( - new fml::NonOwnedMapping(isolate_snapshot_data, 0)); - isolate_snapshot_instructions_.reset( - new fml::NonOwnedMapping(isolate_snapshot_instructions, 0)); + vm_snapshot_data_ = + std::make_unique(aot_symbols_.vm_snapshot_data, 0u); + vm_snapshot_instructions_ = std::make_unique( + aot_symbols_.vm_snapshot_instrs, 0u); + isolate_snapshot_data_ = + std::make_unique(aot_symbols_.vm_isolate_data, 0u); + isolate_snapshot_instructions_ = std::make_unique( + aot_symbols_.vm_isolate_instrs, 0u); } const std::string& EmbedderTestContext::GetAssetsPath() const { diff --git a/shell/platform/embedder/tests/embedder_test_context.h b/shell/platform/embedder/tests/embedder_test_context.h index eb5ae42c5..fc7bf59b7 100644 --- a/shell/platform/embedder/tests/embedder_test_context.h +++ b/shell/platform/embedder/tests/embedder_test_context.h @@ -16,9 +16,9 @@ #include "flutter/fml/mapping.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/tests/embedder_test_compositor.h" +#include "flutter/testing/elf_loader.h" #include "flutter/testing/test_dart_native_resolver.h" #include "flutter/testing/test_gl_surface.h" -#include "third_party/dart/runtime/bin/elf_loader.h" #include "third_party/skia/include/core/SkImage.h" namespace flutter { @@ -74,9 +74,7 @@ class EmbedderTestContext { using NextSceneCallback = std::function image)>; std::string assets_path_; - - // Pieces of the Dart snapshot in ELF form, loaded by Dart's ELF library. - Dart_LoadedElf* elf_library_handle_ = nullptr; + ELFAOTSymbols aot_symbols_; std::unique_ptr vm_snapshot_data_; std::unique_ptr vm_snapshot_instructions_; std::unique_ptr isolate_snapshot_data_; diff --git a/testing/BUILD.gn b/testing/BUILD.gn index ad83d23e6..7d1d212fa 100644 --- a/testing/BUILD.gn +++ b/testing/BUILD.gn @@ -9,11 +9,11 @@ source_set("testing_lib") { testonly = true sources = [ - "$flutter_root/testing/assertions.h", - "$flutter_root/testing/testing.cc", - "$flutter_root/testing/testing.h", - "$flutter_root/testing/thread_test.cc", - "$flutter_root/testing/thread_test.h", + "assertions.h", + "testing.cc", + "testing.h", + "thread_test.cc", + "thread_test.h", ] public_deps = [ @@ -27,7 +27,7 @@ source_set("testing") { testonly = true sources = [ - "$flutter_root/testing/run_all_unittests.cc", + "run_all_unittests.cc", ] public_deps = [ @@ -39,14 +39,21 @@ source_set("dart") { testonly = true sources = [ - "$flutter_root/testing/test_dart_native_resolver.cc", - "$flutter_root/testing/test_dart_native_resolver.h", + "elf_loader.cc", + "elf_loader.h", + "test_dart_native_resolver.cc", + "test_dart_native_resolver.h", ] public_deps = [ ":testing", + "$flutter_root/common", + "$flutter_root/fml", + "$flutter_root/runtime", "$flutter_root/runtime:libdart", "$flutter_root/third_party/tonic", + "//third_party/dart/runtime/bin:elf_loader", + "//third_party/skia", ] } @@ -54,11 +61,11 @@ source_set("skia") { testonly = true sources = [ - "$flutter_root/testing/assertions_skia.cc", - "$flutter_root/testing/assertions_skia.h", - "$flutter_root/testing/canvas_test.h", - "$flutter_root/testing/mock_canvas.cc", - "$flutter_root/testing/mock_canvas.h", + "assertions_skia.cc", + "assertions_skia.h", + "canvas_test.h", + "mock_canvas.cc", + "mock_canvas.h", ] public_deps = [ @@ -74,8 +81,8 @@ if (current_toolchain == host_toolchain) { configs += [ "//third_party/swiftshader_flutter:swiftshader_config" ] sources = [ - "$flutter_root/testing/test_gl_surface.cc", - "$flutter_root/testing/test_gl_surface.h", + "test_gl_surface.cc", + "test_gl_surface.h", ] deps = [ @@ -95,14 +102,14 @@ if (current_toolchain == host_toolchain) { testonly = true sources = [ - "$flutter_root/testing/test_metal_surface.cc", - "$flutter_root/testing/test_metal_surface.h", + "test_metal_surface.cc", + "test_metal_surface.h", ] defines = [] if (shell_enable_metal) { - sources += [ "$flutter_root/testing/test_metal_surface_impl.mm" ] + sources += [ "test_metal_surface_impl.mm" ] defines += [ "TESTING_ENABLE_METAL" ] } diff --git a/testing/elf_loader.cc b/testing/elf_loader.cc new file mode 100644 index 000000000..4dce26b66 --- /dev/null +++ b/testing/elf_loader.cc @@ -0,0 +1,82 @@ +// 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. + +#include "flutter/testing/elf_loader.h" + +#include "flutter/fml/file.h" +#include "flutter/fml/paths.h" +#include "flutter/runtime/dart_vm.h" +#include "flutter/testing/testing.h" + +namespace flutter { +namespace testing { + +static constexpr const char* kAOTAppELFFileName = "app_elf_snapshot.so"; + +ELFAOTSymbols LoadELFSymbolFromFixturesIfNeccessary() { + if (!DartVM::IsRunningPrecompiledCode()) { + return {}; + } + + const auto elf_path = + fml::paths::JoinPaths({GetFixturesPath(), kAOTAppELFFileName}); + + if (!fml::IsFile(elf_path)) { + FML_LOG(ERROR) << "App AOT file does not exist for this fixture. Attempts " + "to launch the Dart VM with these AOT symbols will fail."; + return {}; + } + + ELFAOTSymbols symbols; + + // Must not be freed. + const char* error = nullptr; + + auto loaded_elf = + Dart_LoadELF(elf_path.c_str(), // file path + 0, // file offset + &error, // error (out) + &symbols.vm_snapshot_data, // vm snapshot data (out) + &symbols.vm_snapshot_instrs, // vm snapshot instrs (out) + &symbols.vm_isolate_data, // vm isolate data (out) + &symbols.vm_isolate_instrs // vm isolate instr (out) + ); + + if (loaded_elf == nullptr) { + FML_LOG(ERROR) + << "Could not fetch AOT symbols from loaded ELF. Attempts " + "to launch the Dart VM with these AOT symbols will fail. Error: " + << error; + return {}; + } + + symbols.loaded_elf.reset(loaded_elf); + + return symbols; +} + +bool PrepareSettingsForAOTWithSymbols(Settings& settings, + const ELFAOTSymbols& symbols) { + if (!DartVM::IsRunningPrecompiledCode()) { + return false; + } + settings.vm_snapshot_data = [&]() { + return std::make_unique(symbols.vm_snapshot_data, 0u); + }; + settings.isolate_snapshot_data = [&]() { + return std::make_unique(symbols.vm_isolate_data, 0u); + }; + settings.vm_snapshot_instr = [&]() { + return std::make_unique(symbols.vm_snapshot_instrs, + 0u); + }; + settings.isolate_snapshot_instr = [&]() { + return std::make_unique(symbols.vm_isolate_instrs, + 0u); + }; + return true; +} + +} // namespace testing +} // namespace flutter diff --git a/testing/elf_loader.h b/testing/elf_loader.h new file mode 100644 index 000000000..1d9a93de6 --- /dev/null +++ b/testing/elf_loader.h @@ -0,0 +1,60 @@ +// 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. + +#ifndef FLUTTER_TESTING_ELF_LOADER_H_ +#define FLUTTER_TESTING_ELF_LOADER_H_ + +#include + +#include "flutter/common/settings.h" +#include "flutter/fml/macros.h" +#include "third_party/dart/runtime/bin/elf_loader.h" + +namespace flutter { +namespace testing { + +struct LoadedELFDeleter { + void operator()(Dart_LoadedElf* elf) { ::Dart_UnloadELF(elf); } +}; + +using UniqueLoadedELF = std::unique_ptr; + +struct ELFAOTSymbols { + UniqueLoadedELF loaded_elf; + const uint8_t* vm_snapshot_data = nullptr; + const uint8_t* vm_snapshot_instrs = nullptr; + const uint8_t* vm_isolate_data = nullptr; + const uint8_t* vm_isolate_instrs = nullptr; +}; + +//------------------------------------------------------------------------------ +/// @brief Attempts to resolve AOT symbols from the portable ELF loader. +/// This location is automatically resolved from the fixtures +/// generator. This only returns valid symbols when the VM is +/// configured for AOT. +/// +/// @return The loaded ELF symbols. +/// +ELFAOTSymbols LoadELFSymbolFromFixturesIfNeccessary(); + +//------------------------------------------------------------------------------ +/// @brief Prepare the settings objects various AOT mappings resolvers with +/// the symbols already loaded. This method does nothing in non-AOT +/// runtime modes. +/// +/// @warning The symbols must not be collected till all shell instantiations +/// made using the settings object are collected. +/// +/// @param[in/out] settings The settings whose AOT resolvers to populate. +/// @param[in] symbols The symbols used to populate the settings object. +/// +/// @return If the settings object was correctly updated. +/// +bool PrepareSettingsForAOTWithSymbols(Settings& settings, + const ELFAOTSymbols& symbols); + +} // namespace testing +} // namespace flutter + +#endif // FLUTTER_TESTING_ELF_LOADER_H_ -- GitLab