diff options
author | 2015-12-04 14:06:18 -0800 | |
---|---|---|
committer | 2015-12-23 21:14:23 -0800 | |
commit | dcdc85bbd569f0ee66c331b4219c19304a616214 (patch) | |
tree | b5ab789248e279318f6c1e3f6c511703d7294476 | |
parent | 48944c760b196188b968b7af81439466cf987a75 (diff) |
Dex2oat support for multiple oat file and image file outputs.
Multiple changes to dex2oat and the runtime to support a --multi-image
option. This generates a separate oat file and image file output for
each dex file input.
Change-Id: Ie1d6f0b8afa8aed5790065b8c2eb177990c60129
57 files changed, 2384 insertions, 1152 deletions
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk index edf107ea3e..c9af1c67a4 100644 --- a/build/Android.common_test.mk +++ b/build/Android.common_test.mk @@ -114,6 +114,9 @@ ART_TEST_RUN_TEST_ALWAYS_CLEAN ?= true # Do you want run-tests with the --debuggable flag ART_TEST_RUN_TEST_DEBUGGABLE ?= $(ART_TEST_FULL) +# Do you want to test multi-part boot-image functionality? +ART_TEST_RUN_TEST_MULTI_IMAGE ?= $(ART_TEST_FULL) + # Define the command run on test failure. $(1) is the name of the test. Executed by the shell. define ART_TEST_FAILED ( [ -f $(ART_HOST_TEST_DIR)/skipped/$(1) ] || \ diff --git a/build/Android.oat.mk b/build/Android.oat.mk index 50600ef903..884f698cd9 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -42,6 +42,7 @@ endif # $(3): 2ND_ or undefined, 2ND_ for 32-bit host builds. # $(4): wrapper, e.g., valgrind. # $(5): dex2oat suffix, e.g, valgrind requires 32 right now. +# $(6): multi-image. # NB depending on HOST_CORE_DEX_LOCATIONS so we are sure to have the dex files in frameworks for # run-test --no-image define create-core-oat-host-rules @@ -92,14 +93,25 @@ define create-core-oat-host-rules $$(error found $(2) expected pic or no-pic) endif - core_image_name := $($(3)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_IMG_SUFFIX) - core_oat_name := $($(3)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$(4)$(CORE_OAT_SUFFIX) + # If $(6) is true, generate a multi-image. + ifeq ($(6),true) + core_multi_infix := -multi + core_multi_param := --multi-image --no-inline-from=core-oj-hostdex.jar + core_multi_group := _multi + else + core_multi_infix := + core_multi_param := + core_multi_group := + endif + + core_image_name := $($(3)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_pic_infix)$$(core_multi_infix)$(4)$(CORE_IMG_SUFFIX) + core_oat_name := $($(3)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_pic_infix)$$(core_multi_infix)$(4)$(CORE_OAT_SUFFIX) # Using the bitness suffix makes it easier to add as a dependency for the run-test mk. ifeq ($(3),) - $(4)HOST_CORE_IMAGE_$(1)_$(2)_64 := $$(core_image_name) + $(4)HOST_CORE_IMAGE_$(1)_$(2)$$(core_multi_group)_64 := $$(core_image_name) else - $(4)HOST_CORE_IMAGE_$(1)_$(2)_32 := $$(core_image_name) + $(4)HOST_CORE_IMAGE_$(1)_$(2)$$(core_multi_group)_32 := $$(core_image_name) endif $(4)HOST_CORE_IMG_OUTS += $$(core_image_name) $(4)HOST_CORE_OAT_OUTS += $$(core_oat_name) @@ -111,6 +123,7 @@ define create-core-oat-host-rules $$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options) $$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name) $$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name) +$$(core_image_name): PRIVATE_CORE_MULTI_PARAM := $$(core_multi_param) $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency) @echo "host dex2oat: $$@" @mkdir -p $$(dir $$@) @@ -122,7 +135,7 @@ $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency) --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(3)ART_HOST_ARCH) \ $$(LOCAL_$(3)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \ --host --android-root=$$(HOST_OUT) --include-patch-information --generate-debug-info \ - $$(PRIVATE_CORE_COMPILE_OPTIONS) + $$(PRIVATE_CORE_MULTI_PARAM) $$(PRIVATE_CORE_COMPILE_OPTIONS) $$(core_oat_name): $$(core_image_name) @@ -138,32 +151,40 @@ endef # create-core-oat-host-rules # $(1): compiler - default, optimizing, jit, interpreter or interpreter-access-checks. # $(2): wrapper. # $(3): dex2oat suffix. +# $(4): multi-image. define create-core-oat-host-rule-combination - $(call create-core-oat-host-rules,$(1),no-pic,,$(2),$(3)) - $(call create-core-oat-host-rules,$(1),pic,,$(2),$(3)) + $(call create-core-oat-host-rules,$(1),no-pic,,$(2),$(3),$(4)) + $(call create-core-oat-host-rules,$(1),pic,,$(2),$(3),$(4)) ifneq ($(HOST_PREFER_32_BIT),true) - $(call create-core-oat-host-rules,$(1),no-pic,2ND_,$(2),$(3)) - $(call create-core-oat-host-rules,$(1),pic,2ND_,$(2),$(3)) + $(call create-core-oat-host-rules,$(1),no-pic,2ND_,$(2),$(3),$(4)) + $(call create-core-oat-host-rules,$(1),pic,2ND_,$(2),$(3),$(4)) endif endef -$(eval $(call create-core-oat-host-rule-combination,default,,)) -$(eval $(call create-core-oat-host-rule-combination,optimizing,,)) -$(eval $(call create-core-oat-host-rule-combination,interpreter,,)) -$(eval $(call create-core-oat-host-rule-combination,interp-ac,,)) -$(eval $(call create-core-oat-host-rule-combination,jit,,)) +$(eval $(call create-core-oat-host-rule-combination,default,,,false)) +$(eval $(call create-core-oat-host-rule-combination,optimizing,,,false)) +$(eval $(call create-core-oat-host-rule-combination,interpreter,,,false)) +$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,false)) +$(eval $(call create-core-oat-host-rule-combination,jit,,,false)) +$(eval $(call create-core-oat-host-rule-combination,default,,,true)) +$(eval $(call create-core-oat-host-rule-combination,optimizing,,,true)) +$(eval $(call create-core-oat-host-rule-combination,interpreter,,,true)) +$(eval $(call create-core-oat-host-rule-combination,interp-ac,,,true)) +$(eval $(call create-core-oat-host-rule-combination,jit,,,true)) valgrindHOST_CORE_IMG_OUTS := valgrindHOST_CORE_OAT_OUTS := -$(eval $(call create-core-oat-host-rule-combination,default,valgrind,32)) -$(eval $(call create-core-oat-host-rule-combination,optimizing,valgrind,32)) -$(eval $(call create-core-oat-host-rule-combination,interpreter,valgrind,32)) -$(eval $(call create-core-oat-host-rule-combination,interp-ac,valgrind,32)) -$(eval $(call create-core-oat-host-rule-combination,jit,valgrind,32)) +$(eval $(call create-core-oat-host-rule-combination,default,valgrind,32,false)) +$(eval $(call create-core-oat-host-rule-combination,optimizing,valgrind,32,false)) +$(eval $(call create-core-oat-host-rule-combination,interpreter,valgrind,32,false)) +$(eval $(call create-core-oat-host-rule-combination,interp-ac,valgrind,32,false)) +$(eval $(call create-core-oat-host-rule-combination,jit,valgrind,32,false)) valgrind-test-art-host-dex2oat-host: $(valgrindHOST_CORE_IMG_OUTS) +test-art-host-dex2oat-host: $(HOST_CORE_IMG_OUTS) + define create-core-oat-target-rules core_compile_options := core_image_name := diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h index 4aced5b455..4dcaf804de 100644 --- a/cmdline/cmdline.h +++ b/cmdline/cmdline.h @@ -80,8 +80,7 @@ static bool LocationToFilename(const std::string& location, InstructionSet isa, } } -static Runtime* StartRuntime(const char* boot_image_location, - InstructionSet instruction_set) { +static Runtime* StartRuntime(const char* boot_image_location, InstructionSet instruction_set) { CHECK(boot_image_location != nullptr); RuntimeOptions options; diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index c7c190793c..278c49017e 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -208,7 +208,8 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSe false, timer_.get(), -1, - "")); + /* profile_file */ "", + /* dex_to_oat_map */ nullptr)); // We typically don't generate an image in unit tests, disable this optimization by default. compiler_driver_->SetSupportBootImageFixup(false); } diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index f48947d537..32d751861a 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -22,6 +22,7 @@ #include "base/macros.h" #include "base/mutex-inl.h" #include "dex/compiler_ir.h" +#include "driver/compiler_driver.h" #include "thread-inl.h" #include "dex/mir_graph.h" #include "dex/quick/mir_to_lir.h" @@ -777,6 +778,17 @@ bool DexFileMethodInliner::GenSpecial(Mir2Lir* backend, uint32_t method_idx) { bool DexFileMethodInliner::GenInline(MIRGraph* mir_graph, BasicBlock* bb, MIR* invoke, uint32_t method_idx) { + // Check that we're allowed to inline. + { + CompilationUnit* cu = mir_graph->GetCurrentDexCompilationUnit()->GetCompilationUnit(); + if (!cu->compiler_driver->MayInline(dex_file_, cu->dex_file)) { + VLOG(compiler) << "Won't inline " << method_idx << " in " + << cu->dex_file->GetLocation() << " from " + << dex_file_->GetLocation(); + return false; + } + } + InlineMethod method; { ReaderMutexLock mu(Thread::Current(), lock_); diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc index 24daf2f15f..bcf20c7efa 100644 --- a/compiler/dex/quick/quick_cfi_test.cc +++ b/compiler/dex/quick/quick_cfi_test.cc @@ -58,6 +58,7 @@ class QuickCFITest : public CFITest { CompilerOptions::kDefaultNumDexMethodsThreshold, CompilerOptions::kDefaultInlineDepthLimit, CompilerOptions::kDefaultInlineMaxCodeUnits, + nullptr, false, CompilerOptions::kDefaultTopKProfileThreshold, false, @@ -74,9 +75,25 @@ class QuickCFITest : public CFITest { std::unique_ptr<const InstructionSetFeatures> isa_features; std::string error; isa_features.reset(InstructionSetFeatures::FromVariant(isa, "default", &error)); - CompilerDriver driver(&compiler_options, &verification_results, &method_inliner_map, - Compiler::kQuick, isa, isa_features.get(), - false, nullptr, nullptr, nullptr, 0, false, false, "", false, 0, -1, ""); + CompilerDriver driver(&compiler_options, + &verification_results, + &method_inliner_map, + Compiler::kQuick, + isa, + isa_features.get(), + false, + nullptr, + nullptr, + nullptr, + 0, + false, + false, + "", + false, + 0, + -1, + "", + nullptr); ClassLinker* linker = nullptr; CompilationUnit cu(&pool, isa, &driver, linker); DexFile::CodeItem code_item { 0, 0, 0, 0, 0, 0, { 0 } }; // NOLINT diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc index e977ebf722..9deabc02e9 100644 --- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc +++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc @@ -41,6 +41,7 @@ class QuickAssembleX86TestBase : public testing::Test { CompilerOptions::kDefaultNumDexMethodsThreshold, CompilerOptions::kDefaultInlineDepthLimit, CompilerOptions::kDefaultInlineMaxCodeUnits, + nullptr, false, CompilerOptions::kDefaultTopKProfileThreshold, false, @@ -72,7 +73,8 @@ class QuickAssembleX86TestBase : public testing::Test { false, 0, -1, - "")); + "", + nullptr)); cu_.reset(new CompilationUnit(pool_.get(), isa_, compiler_driver_.get(), nullptr)); DexFile::CodeItem* code_item = static_cast<DexFile::CodeItem*>( cu_->arena.Alloc(sizeof(DexFile::CodeItem), kArenaAllocMisc)); diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc index c6dbd24bf8..84fb4324b5 100644 --- a/compiler/driver/compiled_method_storage_test.cc +++ b/compiler/driver/compiled_method_storage_test.cc @@ -45,7 +45,8 @@ TEST(CompiledMethodStorage, Deduplicate) { false, nullptr, -1, - ""); + "", + nullptr); CompiledMethodStorage* storage = driver.GetCompiledMethodStorage(); ASSERT_TRUE(storage->DedupeEnabled()); // The default. diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 5630b08054..afb4b71ccf 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -334,19 +334,21 @@ class CompilerDriver::AOTCompilationStats { DISALLOW_COPY_AND_ASSIGN(AOTCompilationStats); }; -CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, - VerificationResults* verification_results, - DexFileToMethodInlinerMap* method_inliner_map, - Compiler::Kind compiler_kind, - InstructionSet instruction_set, - const InstructionSetFeatures* instruction_set_features, - bool boot_image, std::unordered_set<std::string>* image_classes, - std::unordered_set<std::string>* compiled_classes, - std::unordered_set<std::string>* compiled_methods, - size_t thread_count, bool dump_stats, bool dump_passes, - const std::string& dump_cfg_file_name, bool dump_cfg_append, - CumulativeLogger* timer, int swap_fd, - const std::string& profile_file) +CompilerDriver::CompilerDriver( + const CompilerOptions* compiler_options, + VerificationResults* verification_results, + DexFileToMethodInlinerMap* method_inliner_map, + Compiler::Kind compiler_kind, + InstructionSet instruction_set, + const InstructionSetFeatures* instruction_set_features, + bool boot_image, std::unordered_set<std::string>* image_classes, + std::unordered_set<std::string>* compiled_classes, + std::unordered_set<std::string>* compiled_methods, + size_t thread_count, bool dump_stats, bool dump_passes, + const std::string& dump_cfg_file_name, bool dump_cfg_append, + CumulativeLogger* timer, int swap_fd, + const std::string& profile_file, + const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map) : compiler_options_(compiler_options), verification_results_(verification_results), method_inliner_map_(method_inliner_map), @@ -374,6 +376,7 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, compiler_context_(nullptr), support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64), dex_files_for_oat_file_(nullptr), + dex_file_oat_filename_map_(dex_to_oat_map), compiled_method_storage_(swap_fd) { DCHECK(compiler_options_ != nullptr); DCHECK(verification_results_ != nullptr); @@ -1538,6 +1541,12 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType use_dex_cache = true; } } + if (!use_dex_cache && IsBootImage()) { + if (!AreInSameOatFile(&(const_cast<mirror::Class*>(referrer_class)->GetDexFile()), + &declaring_class->GetDexFile())) { + use_dex_cache = true; + } + } // The method is defined not within this dex file. We need a dex cache slot within the current // dex file or direct pointers. bool must_use_direct_pointers = false; @@ -1571,12 +1580,14 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType *type = sharp_type; } } else { - auto* image_space = heap->GetBootImageSpace(); bool method_in_image = false; - if (image_space != nullptr) { + const std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces(); + for (gc::space::ImageSpace* image_space : image_spaces) { const auto& method_section = image_space->GetImageHeader().GetMethodsSection(); - method_in_image = method_section.Contains( - reinterpret_cast<uint8_t*>(method) - image_space->Begin()); + if (method_section.Contains(reinterpret_cast<uint8_t*>(method) - image_space->Begin())) { + method_in_image = true; + break; + } } if (method_in_image || compiling_boot || runtime->UseJit()) { // We know we must be able to get to the method in the image, so use that pointer. @@ -2572,4 +2583,15 @@ bool CompilerDriver::IsStringInit(uint32_t method_index, const DexFile* dex_file return inliner->IsStringInitMethodIndex(method_index); } +bool CompilerDriver::MayInlineInternal(const DexFile* inlined_from, + const DexFile* inlined_into) const { + // We're not allowed to inline across dex files if we're the no-inline-from dex file. + if (inlined_from != inlined_into && + compiler_options_->GetNoInlineFromDexFile() == inlined_from) { + return false; + } + + return true; +} + } // namespace art diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index f0360ceffb..fa0cb9a412 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -97,7 +97,8 @@ class CompilerDriver { size_t thread_count, bool dump_stats, bool dump_passes, const std::string& dump_cfg_file_name, bool dump_cfg_append, CumulativeLogger* timer, int swap_fd, - const std::string& profile_file); + const std::string& profile_file, + const std::unordered_map<const DexFile*, const char*>* dex_to_oat_map); ~CompilerDriver(); @@ -113,6 +114,18 @@ class CompilerDriver { : ArrayRef<const DexFile* const>(); } + // Are the given dex files compiled into the same oat file? Should only be called after + // GetDexFilesForOatFile, as the conservative answer (when we don't have a map) is true. + bool AreInSameOatFile(const DexFile* d1, const DexFile* d2) { + if (dex_file_oat_filename_map_ == nullptr) { + // TODO: Check for this wrt/ apps and boot image calls. + return true; + } + auto it1 = dex_file_oat_filename_map_->find(d1); + auto it2 = dex_file_oat_filename_map_->find(d2); + return it1 == it2; + } + void CompileAll(jobject class_loader, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) @@ -471,6 +484,13 @@ class CompilerDriver { bool CanAssumeClassIsLoaded(mirror::Class* klass) SHARED_REQUIRES(Locks::mutator_lock_); + bool MayInline(const DexFile* inlined_from, const DexFile* inlined_into) const { + if (!kIsTargetBuild) { + return MayInlineInternal(inlined_from, inlined_into); + } + return true; + } + private: // Return whether the declaring class of `resolved_member` is // available to `referrer_class` for read or write access using two @@ -587,6 +607,8 @@ class CompilerDriver { ThreadPool* thread_pool, TimingLogger* timings) REQUIRES(!Locks::mutator_lock_); + bool MayInlineInternal(const DexFile* inlined_from, const DexFile* inlined_into) const; + const CompilerOptions* const compiler_options_; VerificationResults* const verification_results_; DexFileToMethodInlinerMap* const method_inliner_map_; @@ -621,9 +643,8 @@ class CompilerDriver { const bool boot_image_; - // If image_ is true, specifies the classes that will be included in - // the image. Note if image_classes_ is null, all classes are - // included in the image. + // If image_ is true, specifies the classes that will be included in the image. + // Note if image_classes_ is null, all classes are included in the image. std::unique_ptr<std::unordered_set<std::string>> image_classes_; // Specifies the classes that will be compiled. Note that if classes_to_compile_ is null, @@ -663,6 +684,9 @@ class CompilerDriver { // List of dex files that will be stored in the oat file. const std::vector<const DexFile*>* dex_files_for_oat_file_; + // Map from dex files to the oat file (name) they will be compiled into. + const std::unordered_map<const DexFile*, const char*>* dex_file_oat_filename_map_; + CompiledMethodStorage compiled_method_storage_; friend class CompileClassVisitor; diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index 8c38cf263e..209bb5a3c2 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -31,6 +31,7 @@ CompilerOptions::CompilerOptions() num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold), inline_depth_limit_(kUnsetInlineDepthLimit), inline_max_code_units_(kUnsetInlineMaxCodeUnits), + no_inline_from_(nullptr), include_patch_information_(kDefaultIncludePatchInformation), top_k_profile_threshold_(kDefaultTopKProfileThreshold), debuggable_(false), @@ -59,6 +60,7 @@ CompilerOptions::CompilerOptions(CompilerFilter compiler_filter, size_t num_dex_methods_threshold, size_t inline_depth_limit, size_t inline_max_code_units, + const DexFile* no_inline_from, bool include_patch_information, double top_k_profile_threshold, bool debuggable, @@ -79,6 +81,7 @@ CompilerOptions::CompilerOptions(CompilerFilter compiler_filter, num_dex_methods_threshold_(num_dex_methods_threshold), inline_depth_limit_(inline_depth_limit), inline_max_code_units_(inline_max_code_units), + no_inline_from_(no_inline_from), include_patch_information_(include_patch_information), top_k_profile_threshold_(top_k_profile_threshold), debuggable_(debuggable), diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 2b047a203c..9ad1beefec 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -72,6 +72,7 @@ class CompilerOptions FINAL { size_t num_dex_methods_threshold, size_t inline_depth_limit, size_t inline_max_code_units, + const DexFile* no_inline_from, bool include_patch_information, double top_k_profile_threshold, bool debuggable, @@ -217,6 +218,10 @@ class CompilerOptions FINAL { return abort_on_hard_verifier_failure_; } + const DexFile* GetNoInlineFromDexFile() const { + return no_inline_from_; + } + bool ParseCompilerOption(const StringPiece& option, UsageFn Usage); private: @@ -241,6 +246,10 @@ class CompilerOptions FINAL { size_t num_dex_methods_threshold_; size_t inline_depth_limit_; size_t inline_max_code_units_; + + // A dex file from which we should not inline code. + const DexFile* no_inline_from_; + bool include_patch_information_; // When using a profile file only the top K% of the profiled samples will be compiled. double top_k_profile_threshold_; diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 5afe2db27f..6859605095 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -72,11 +72,18 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { ScratchFile oat_file(OS::CreateEmptyFile(oat_filename.c_str())); const uintptr_t requested_image_base = ART_BASE_ADDRESS; + std::unordered_map<const DexFile*, const char*> dex_file_to_oat_filename_map; + std::vector<const char*> oat_filename_vector(1, oat_filename.c_str()); + for (const DexFile* dex_file : class_linker->GetBootClassPath()) { + dex_file_to_oat_filename_map.emplace(dex_file, oat_filename.c_str()); + } std::unique_ptr<ImageWriter> writer(new ImageWriter(*compiler_driver_, requested_image_base, /*compile_pic*/false, /*compile_app_image*/false, - storage_mode)); + storage_mode, + oat_filename_vector, + dex_file_to_oat_filename_map)); // TODO: compile_pic should be a test argument. { { @@ -131,12 +138,12 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { ASSERT_TRUE(dup_oat.get() != nullptr); { - bool success_image = writer->Write(kInvalidImageFd, - image_file.GetFilename(), - dup_oat->GetPath(), - dup_oat->GetPath()); + std::vector<const char*> dup_oat_filename(1, dup_oat->GetPath().c_str()); + std::vector<const char*> dup_image_filename(1, image_file.GetFilename().c_str()); + bool success_image = writer->Write(kInvalidImageFd, dup_image_filename, dup_oat_filename); ASSERT_TRUE(success_image); - bool success_fixup = ElfWriter::Fixup(dup_oat.get(), writer->GetOatDataBegin()); + bool success_fixup = ElfWriter::Fixup(dup_oat.get(), + writer->GetOatDataBegin(dup_oat_filename[0])); ASSERT_TRUE(success_fixup); ASSERT_EQ(dup_oat->FlushCloseOrErase(), 0) << "Could not flush and close oat file " @@ -203,10 +210,11 @@ void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { class_linker_ = runtime_->GetClassLinker(); gc::Heap* heap = Runtime::Current()->GetHeap(); - ASSERT_TRUE(heap->HasImageSpace()); + ASSERT_TRUE(heap->HasBootImageSpace()); ASSERT_TRUE(heap->GetNonMovingSpace()->IsMallocSpace()); - gc::space::ImageSpace* image_space = heap->GetBootImageSpace(); + // We loaded the runtime with an explicit image, so it must exist. + gc::space::ImageSpace* image_space = heap->GetBootImageSpaces()[0]; ASSERT_TRUE(image_space != nullptr); if (storage_mode == ImageHeader::kStorageModeUncompressed) { // Uncompressed, image should be smaller than file. diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 9545c83eaf..17d0f61a34 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -154,147 +154,174 @@ bool ImageWriter::PrepareImageAddressSpace() { } bool ImageWriter::Write(int image_fd, - const std::string& image_filename, - const std::string& oat_filename, - const std::string& oat_location) { - CHECK(!image_filename.empty()); - - std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str())); - if (oat_file.get() == nullptr) { - PLOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location; - return false; - } - std::string error_msg; - oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, nullptr, &error_msg); - if (oat_file_ == nullptr) { - PLOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location - << ": " << error_msg; - oat_file->Erase(); - return false; - } - Runtime::Current()->GetOatFileManager().RegisterOatFile( + const std::vector<const char*>& image_filenames, + const std::vector<const char*>& oat_filenames) { + CHECK(!image_filenames.empty()); + CHECK(!oat_filenames.empty()); + CHECK_EQ(image_filenames.size(), oat_filenames.size()); + + size_t oat_file_offset = 0; + + for (size_t i = 0; i < oat_filenames.size(); ++i) { + const char* oat_filename = oat_filenames[i]; + std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename)); + if (oat_file.get() == nullptr) { + PLOG(ERROR) << "Failed to open oat file " << oat_filename; + return false; + } + std::string error_msg; + oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_filename, nullptr, &error_msg); + if (oat_file_ == nullptr) { + PLOG(ERROR) << "Failed to open writable oat file " << oat_filename; + oat_file->Erase(); + return false; + } + Runtime::Current()->GetOatFileManager().RegisterOatFile( std::unique_ptr<const OatFile>(oat_file_)); - const OatHeader& oat_header = oat_file_->GetOatHeader(); - oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] = - oat_header.GetInterpreterToInterpreterBridgeOffset(); - oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] = - oat_header.GetInterpreterToCompiledCodeBridgeOffset(); - oat_address_offsets_[kOatAddressJNIDlsymLookup] = - oat_header.GetJniDlsymLookupOffset(); - oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] = - oat_header.GetQuickGenericJniTrampolineOffset(); - oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] = - oat_header.GetQuickImtConflictTrampolineOffset(); - oat_address_offsets_[kOatAddressQuickResolutionTrampoline] = - oat_header.GetQuickResolutionTrampolineOffset(); - oat_address_offsets_[kOatAddressQuickToInterpreterBridge] = - oat_header.GetQuickToInterpreterBridgeOffset(); + const OatHeader& oat_header = oat_file_->GetOatHeader(); + ImageInfo& image_info = GetImageInfo(oat_filename); + + size_t oat_loaded_size = 0; + size_t oat_data_offset = 0; + ElfWriter::GetOatElfInformation(oat_file.get(), &oat_loaded_size, &oat_data_offset); + + DCHECK_EQ(image_info.oat_offset_, oat_file_offset); + oat_file_offset += oat_loaded_size; + + if (i == 0) { + // Primary oat file, read the trampolines. + image_info.oat_address_offsets_[kOatAddressInterpreterToInterpreterBridge] = + oat_header.GetInterpreterToInterpreterBridgeOffset(); + image_info.oat_address_offsets_[kOatAddressInterpreterToCompiledCodeBridge] = + oat_header.GetInterpreterToCompiledCodeBridgeOffset(); + image_info.oat_address_offsets_[kOatAddressJNIDlsymLookup] = + oat_header.GetJniDlsymLookupOffset(); + image_info.oat_address_offsets_[kOatAddressQuickGenericJNITrampoline] = + oat_header.GetQuickGenericJniTrampolineOffset(); + image_info.oat_address_offsets_[kOatAddressQuickIMTConflictTrampoline] = + oat_header.GetQuickImtConflictTrampolineOffset(); + image_info.oat_address_offsets_[kOatAddressQuickResolutionTrampoline] = + oat_header.GetQuickResolutionTrampolineOffset(); + image_info.oat_address_offsets_[kOatAddressQuickToInterpreterBridge] = + oat_header.GetQuickToInterpreterBridgeOffset(); + } else { + // Other oat files use the primary trampolines. + // TODO: Dummy values to protect usage? b/26317072 + } - size_t oat_loaded_size = 0; - size_t oat_data_offset = 0; - ElfWriter::GetOatElfInformation(oat_file.get(), &oat_loaded_size, &oat_data_offset); + + { + ScopedObjectAccess soa(Thread::Current()); + CreateHeader(oat_loaded_size, oat_data_offset); + CopyAndFixupNativeData(); + } + + SetOatChecksumFromElfFile(oat_file.get()); + + if (oat_file->FlushCloseOrErase() != 0) { + LOG(ERROR) << "Failed to flush and close oat file " << oat_filename; + return false; + } + } { - ScopedObjectAccess soa(Thread::Current()); - CreateHeader(oat_loaded_size, oat_data_offset); - CopyAndFixupNativeData(); // TODO: heap validation can't handle these fix up passes. + ScopedObjectAccess soa(Thread::Current()); Runtime::Current()->GetHeap()->DisableObjectValidation(); CopyAndFixupObjects(); } - SetOatChecksumFromElfFile(oat_file.get()); - - if (oat_file->FlushCloseOrErase() != 0) { - LOG(ERROR) << "Failed to flush and close oat file " << oat_filename << " for " << oat_location; - return false; - } - std::unique_ptr<File> image_file; - if (image_fd != kInvalidImageFd) { - image_file.reset(new File(image_fd, image_filename, unix_file::kCheckSafeUsage)); - } else { - image_file.reset(OS::CreateEmptyFile(image_filename.c_str())); - } - if (image_file == nullptr) { - LOG(ERROR) << "Failed to open image file " << image_filename; - return false; - } - if (fchmod(image_file->Fd(), 0644) != 0) { - PLOG(ERROR) << "Failed to make image file world readable: " << image_filename; - image_file->Erase(); - return EXIT_FAILURE; - } - - std::unique_ptr<char[]> compressed_data; - // Image data size excludes the bitmap and the header. - ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_->Begin()); - const size_t image_data_size = image_header->GetImageSize() - sizeof(ImageHeader); - char* image_data = reinterpret_cast<char*>(image_->Begin()) + sizeof(ImageHeader); - size_t data_size; - const char* image_data_to_write; - - CHECK_EQ(image_header->storage_mode_, image_storage_mode_); - switch (image_storage_mode_) { - case ImageHeader::kStorageModeLZ4: { - size_t compressed_max_size = LZ4_compressBound(image_data_size); - compressed_data.reset(new char[compressed_max_size]); - data_size = LZ4_compress( - reinterpret_cast<char*>(image_->Begin()) + sizeof(ImageHeader), - &compressed_data[0], - image_data_size); - image_data_to_write = &compressed_data[0]; - VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size; - break; + for (size_t i = 0; i < image_filenames.size(); ++i) { + const char* image_filename = image_filenames[i]; + const char* oat_filename = oat_filenames[i]; + ImageInfo& image_info = GetImageInfo(oat_filename); + std::unique_ptr<File> image_file; + if (image_fd != kInvalidImageFd) { + image_file.reset(new File(image_fd, image_filename, unix_file::kCheckSafeUsage)); + } else { + image_file.reset(OS::CreateEmptyFile(image_filename)); } - case ImageHeader::kStorageModeUncompressed: { - data_size = image_data_size; - image_data_to_write = image_data; - break; + if (image_file == nullptr) { + LOG(ERROR) << "Failed to open image file " << image_filename; + return false; } - default: { - LOG(FATAL) << "Unsupported"; - UNREACHABLE(); + if (fchmod(image_file->Fd(), 0644) != 0) { + PLOG(ERROR) << "Failed to make image file world readable: " << image_filename; + image_file->Erase(); + return EXIT_FAILURE; } - } - // Write header first, as uncompressed. - image_header->data_size_ = data_size; - if (!image_file->WriteFully(image_->Begin(), sizeof(ImageHeader))) { - PLOG(ERROR) << "Failed to write image file header " << image_filename; - image_file->Erase(); - return false; - } + std::unique_ptr<char[]> compressed_data; + // Image data size excludes the bitmap and the header. + ImageHeader* const image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin()); + const size_t image_data_size = image_header->GetImageSize() - sizeof(ImageHeader); + char* image_data = reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader); + size_t data_size; + const char* image_data_to_write; + + CHECK_EQ(image_header->storage_mode_, image_storage_mode_); + switch (image_storage_mode_) { + case ImageHeader::kStorageModeLZ4: { + size_t compressed_max_size = LZ4_compressBound(image_data_size); + compressed_data.reset(new char[compressed_max_size]); + data_size = LZ4_compress( + reinterpret_cast<char*>(image_info.image_->Begin()) + sizeof(ImageHeader), + &compressed_data[0], + image_data_size); + image_data_to_write = &compressed_data[0]; + VLOG(compiler) << "Compressed from " << image_data_size << " to " << data_size; + break; + } + case ImageHeader::kStorageModeUncompressed: { + data_size = image_data_size; + image_data_to_write = image_data; + break; + } + default: { + LOG(FATAL) << "Unsupported"; + UNREACHABLE(); + } + } - // Write out the image + fields + methods. - const bool is_compressed = compressed_data != nullptr; - if (!image_file->WriteFully(image_data_to_write, data_size)) { - PLOG(ERROR) << "Failed to write image file data " << image_filename; - image_file->Erase(); - return false; - } + // Write header first, as uncompressed. + image_header->data_size_ = data_size; + if (!image_file->WriteFully(image_info.image_->Begin(), sizeof(ImageHeader))) { + PLOG(ERROR) << "Failed to write image file header " << image_filename; + image_file->Erase(); + return false; + } - // Write out the image bitmap at the page aligned start of the image end, also uncompressed for - // convenience. - const ImageSection& bitmap_section = image_header->GetImageSection( - ImageHeader::kSectionImageBitmap); - // Align up since data size may be unaligned if the image is compressed. - size_t bitmap_position_in_file = RoundUp(sizeof(ImageHeader) + data_size, kPageSize); - if (!is_compressed) { - CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset()); - } - if (!image_file->Write(reinterpret_cast<char*>(image_bitmap_->Begin()), - bitmap_section.Size(), - bitmap_position_in_file)) { - PLOG(ERROR) << "Failed to write image file " << image_filename; - image_file->Erase(); - return false; - } - CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(), - static_cast<size_t>(image_file->GetLength())); - if (image_file->FlushCloseOrErase() != 0) { - PLOG(ERROR) << "Failed to flush and close image file " << image_filename; - return false; + // Write out the image + fields + methods. + const bool is_compressed = compressed_data != nullptr; + if (!image_file->WriteFully(image_data_to_write, data_size)) { + PLOG(ERROR) << "Failed to write image file data " << image_filename; + image_file->Erase(); + return false; + } + + // Write out the image bitmap at the page aligned start of the image end, also uncompressed for + // convenience. + const ImageSection& bitmap_section = image_header->GetImageSection( + ImageHeader::kSectionImageBitmap); + // Align up since data size may be unaligned if the image is compressed. + size_t bitmap_position_in_file = RoundUp(sizeof(ImageHeader) + data_size, kPageSize); + if (!is_compressed) { + CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset()); + } + if (!image_file->Write(reinterpret_cast<char*>(image_info.image_bitmap_->Begin()), + bitmap_section.Size(), + bitmap_position_in_file)) { + PLOG(ERROR) << "Failed to write image file " << image_filename; + image_file->Erase(); + return false; + } + CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(), + static_cast<size_t>(image_file->GetLength())); + if (image_file->FlushCloseOrErase() != 0) { + PLOG(ERROR) << "Failed to flush and close image file " << image_filename; + return false; + } } return true; } @@ -319,12 +346,14 @@ void ImageWriter::AssignImageOffset(mirror::Object* object, ImageWriter::BinSlot DCHECK(object != nullptr); DCHECK_NE(image_objects_offset_begin_, 0u); - size_t bin_slot_offset = bin_slot_offsets_[bin_slot.GetBin()]; + const char* oat_filename = GetOatFilename(object); + ImageInfo& image_info = GetImageInfo(oat_filename); + size_t bin_slot_offset = image_info.bin_slot_offsets_[bin_slot.GetBin()]; size_t new_offset = bin_slot_offset + bin_slot.GetIndex(); DCHECK_ALIGNED(new_offset, kObjectAlignment); SetImageOffset(object, new_offset); - DCHECK_LT(new_offset, image_end_); + DCHECK_LT(new_offset, image_info.image_end_); } bool ImageWriter::IsImageOffsetAssigned(mirror::Object* object) const { @@ -338,7 +367,9 @@ size_t ImageWriter::GetImageOffset(mirror::Object* object) const { DCHECK(IsImageOffsetAssigned(object)); LockWord lock_word = object->GetLockWord(false); size_t offset = lock_word.ForwardingAddress(); - DCHECK_LT(offset, image_end_); + const char* oat_filename = GetOatFilename(object); + const ImageInfo& image_info = GetConstImageInfo(oat_filename); + DCHECK_LT(offset, image_info.image_end_); return offset; } @@ -377,15 +408,16 @@ void ImageWriter::SetImageBinSlot(mirror::Object* object, BinSlot bin_slot) { void ImageWriter::PrepareDexCacheArraySlots() { // Prepare dex cache array starts based on the ordering specified in the CompilerDriver. - uint32_t size = 0u; + // Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned() + // when AssignImageBinSlot() assigns their indexes out or order. for (const DexFile* dex_file : compiler_driver_.GetDexFilesForOatFile()) { - dex_cache_array_starts_.Put(dex_file, size); + auto it = dex_file_oat_filename_map_.find(dex_file); + DCHECK(it != dex_file_oat_filename_map_.end()) << dex_file->GetLocation(); + ImageInfo& image_info = GetImageInfo(it->second); + image_info.dex_cache_array_starts_.Put(dex_file, image_info.bin_slot_sizes_[kBinDexCacheArray]); DexCacheArraysLayout layout(target_ptr_size_, dex_file); - size += layout.Size(); + image_info.bin_slot_sizes_[kBinDexCacheArray] += layout.Size(); } - // Set the slot size early to avoid DCHECK() failures in IsImageBinSlotAssigned() - // when AssignImageBinSlot() assigns their indexes out or order. - bin_slot_sizes_[kBinDexCacheArray] = size; ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Thread* const self = Thread::Current(); @@ -399,24 +431,32 @@ void ImageWriter::PrepareDexCacheArraySlots() { const DexFile* dex_file = dex_cache->GetDexFile(); DexCacheArraysLayout layout(target_ptr_size_, dex_file); DCHECK(layout.Valid()); - uint32_t start = dex_cache_array_starts_.Get(dex_file); + const char* oat_filename = GetOatFilenameForDexCache(dex_cache); + ImageInfo& image_info = GetImageInfo(oat_filename); + uint32_t start = image_info.dex_cache_array_starts_.Get(dex_file); DCHECK_EQ(dex_file->NumTypeIds() != 0u, dex_cache->GetResolvedTypes() != nullptr); - AddDexCacheArrayRelocation(dex_cache->GetResolvedTypes(), start + layout.TypesOffset()); + AddDexCacheArrayRelocation(dex_cache->GetResolvedTypes(), + start + layout.TypesOffset(), + dex_cache); DCHECK_EQ(dex_file->NumMethodIds() != 0u, dex_cache->GetResolvedMethods() != nullptr); - AddDexCacheArrayRelocation(dex_cache->GetResolvedMethods(), start + layout.MethodsOffset()); + AddDexCacheArrayRelocation(dex_cache->GetResolvedMethods(), + start + layout.MethodsOffset(), + dex_cache); DCHECK_EQ(dex_file->NumFieldIds() != 0u, dex_cache->GetResolvedFields() != nullptr); - AddDexCacheArrayRelocation(dex_cache->GetResolvedFields(), start + layout.FieldsOffset()); + AddDexCacheArrayRelocation(dex_cache->GetResolvedFields(), + start + layout.FieldsOffset(), + dex_cache); DCHECK_EQ(dex_file->NumStringIds() != 0u, dex_cache->GetStrings() != nullptr); - AddDexCacheArrayRelocation(dex_cache->GetStrings(), start + layout.StringsOffset()); + AddDexCacheArrayRelocation(dex_cache->GetStrings(), start + layout.StringsOffset(), dex_cache); } } -void ImageWriter::AddDexCacheArrayRelocation(void* array, size_t offset) { +void ImageWriter::AddDexCacheArrayRelocation(void* array, size_t offset, DexCache* dex_cache) { if (array != nullptr) { DCHECK(!IsInBootImage(array)); - native_object_relocations_.emplace( - array, - NativeObjectRelocation { offset, kNativeObjectRelocationTypeDexCacheArray }); + const char* oat_filename = GetOatFilenameForDexCache(dex_cache); + native_object_relocations_.emplace(array, + NativeObjectRelocation { oat_filename, offset, kNativeObjectRelocationTypeDexCacheArray }); } } @@ -531,18 +571,21 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object) { } // else bin = kBinRegular } + const char* oat_filename = GetOatFilename(object); + ImageInfo& image_info = GetImageInfo(oat_filename); + size_t offset_delta = RoundUp(object_size, kObjectAlignment); // 64-bit alignment - current_offset = bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned). - // Move the current bin size up to accomodate the object we just assigned a bin slot. - bin_slot_sizes_[bin] += offset_delta; + current_offset = image_info.bin_slot_sizes_[bin]; // How many bytes the current bin is at (aligned). + // Move the current bin size up to accommodate the object we just assigned a bin slot. + image_info.bin_slot_sizes_[bin] += offset_delta; BinSlot new_bin_slot(bin, current_offset); SetImageBinSlot(object, new_bin_slot); - ++bin_slot_count_[bin]; + ++image_info.bin_slot_count_[bin]; // Grow the image closer to the end by the object we just assigned. - image_end_ += offset_delta; + image_info.image_end_ += offset_delta; } bool ImageWriter::WillMethodBeDirty(ArtMethod* m) const { @@ -565,7 +608,9 @@ bool ImageWriter::IsImageBinSlotAssigned(mirror::Object* object) const { LockWord lock_word = object->GetLockWord(false); size_t offset = lock_word.ForwardingAddress(); BinSlot bin_slot(offset); - DCHECK_LT(bin_slot.GetIndex(), bin_slot_sizes_[bin_slot.GetBin()]) + const char* oat_filename = GetOatFilename(object); + const ImageInfo& image_info = GetConstImageInfo(oat_filename); + DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]) << "bin slot offset should not exceed the size of that bin"; } return true; @@ -580,39 +625,42 @@ ImageWriter::BinSlot ImageWriter::GetImageBinSlot(mirror::Object* object) const DCHECK_LE(offset, std::numeric_limits<uint32_t>::max()); BinSlot bin_slot(static_cast<uint32_t>(offset)); - DCHECK_LT(bin_slot.GetIndex(), bin_slot_sizes_[bin_slot.GetBin()]); + const char* oat_filename = GetOatFilename(object); + const ImageInfo& image_info = GetConstImageInfo(oat_filename); + DCHECK_LT(bin_slot.GetIndex(), image_info.bin_slot_sizes_[bin_slot.GetBin()]); return bin_slot; } bool ImageWriter::AllocMemory() { - const size_t length = RoundUp(image_objects_offset_begin_ + - GetBinSizeSum() + - intern_table_bytes_ + - class_table_bytes_, - kPageSize); - std::string error_msg; - image_.reset(MemMap::MapAnonymous("image writer image", - nullptr, - length, - PROT_READ | PROT_WRITE, - false, - false, - &error_msg)); - if (UNLIKELY(image_.get() == nullptr)) { - LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg; - return false; - } + for (const char* oat_filename : oat_filenames_) { + ImageInfo& image_info = GetImageInfo(oat_filename); + const size_t length = RoundUp(image_objects_offset_begin_ + + GetBinSizeSum(image_info) + + intern_table_bytes_ + + class_table_bytes_, + kPageSize); + std::string error_msg; + image_info.image_.reset(MemMap::MapAnonymous("image writer image", + nullptr, + length, + PROT_READ | PROT_WRITE, + false, + false, + &error_msg)); + if (UNLIKELY(image_info.image_.get() == nullptr)) { + LOG(ERROR) << "Failed to allocate memory for image file generation: " << error_msg; + return false; + } - // Create the image bitmap, only needs to cover mirror object section which is up to image_end_. - CHECK_LE(image_end_, length); - image_bitmap_.reset(gc::accounting::ContinuousSpaceBitmap::Create( - "image bitmap", - image_->Begin(), - RoundUp(image_end_, kPageSize))); - if (image_bitmap_.get() == nullptr) { - LOG(ERROR) << "Failed to allocate memory for image bitmap"; - return false; + // Create the image bitmap, only needs to cover mirror object section which is up to image_end_. + CHECK_LE(image_info.image_end_, length); + image_info.image_bitmap_.reset(gc::accounting::ContinuousSpaceBitmap::Create( + "image bitmap", image_info.image_->Begin(), RoundUp(image_info.image_end_, kPageSize))); + if (image_info.image_bitmap_.get() == nullptr) { + LOG(ERROR) << "Failed to allocate memory for image bitmap"; + return false; + } } return true; } @@ -885,7 +933,7 @@ void ImageWriter::CalculateObjectBinSlots(Object* obj) { AssignImageBinSlot(obj); } -ObjectArray<Object>* ImageWriter::CreateImageRoots() const { +ObjectArray<Object>* ImageWriter::CreateImageRoots(const char* oat_filename) const { Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); Thread* self = Thread::Current(); @@ -893,6 +941,15 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots() const { Handle<Class> object_array_class(hs.NewHandle( class_linker->FindSystemClass(self, "[Ljava/lang/Object;"))); + std::unordered_set<const DexFile*> image_dex_files; + for (auto& pair : dex_file_oat_filename_map_) { + const DexFile* image_dex_file = pair.first; + const char* image_oat_filename = pair.second; + if (strcmp(oat_filename, image_oat_filename) == 0) { + image_dex_files.insert(image_dex_file); + } + } + // build an Object[] of all the DexCaches used in the source_space_. // Since we can't hold the dex lock when allocating the dex_caches // ObjectArray, we lock the dex lock twice, first to get the number @@ -905,7 +962,10 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots() const { for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root)); - dex_cache_count += IsInBootImage(dex_cache) ? 0u : 1u; + const DexFile* dex_file = dex_cache->GetDexFile(); + if (!IsInBootImage(dex_cache)) { + dex_cache_count += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u; + } } } Handle<ObjectArray<Object>> dex_caches( @@ -918,7 +978,10 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots() const { for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root)); - non_image_dex_caches += IsInBootImage(dex_cache) ? 0u : 1u; + const DexFile* dex_file = dex_cache->GetDexFile(); + if (!IsInBootImage(dex_cache)) { + non_image_dex_caches += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u; + } } CHECK_EQ(dex_cache_count, non_image_dex_caches) << "The number of non-image dex caches changed."; @@ -926,7 +989,8 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots() const { for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { mirror::DexCache* dex_cache = down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root)); - if (!IsInBootImage(dex_cache)) { + const DexFile* dex_file = dex_cache->GetDexFile(); + if (!IsInBootImage(dex_cache) && image_dex_files.find(dex_file) != image_dex_files.end()) { dex_caches->Set<false>(i, dex_cache); ++i; } @@ -997,9 +1061,12 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { } // Visit and assign offsets for fields and field arrays. auto* as_klass = h_obj->AsClass(); + mirror::DexCache* dex_cache = as_klass->GetDexCache(); LengthPrefixedArray<ArtField>* fields[] = { as_klass->GetSFieldsPtr(), as_klass->GetIFieldsPtr(), }; + const char* oat_file = GetOatFilenameForDexCache(dex_cache); + ImageInfo& image_info = GetImageInfo(oat_file); for (LengthPrefixedArray<ArtField>* cur_fields : fields) { // Total array length including header. if (cur_fields != nullptr) { @@ -1008,11 +1075,10 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { auto it = native_object_relocations_.find(cur_fields); CHECK(it == native_object_relocations_.end()) << "Field array " << cur_fields << " already forwarded"; - size_t& offset = bin_slot_sizes_[kBinArtField]; + size_t& offset = image_info.bin_slot_sizes_[kBinArtField]; DCHECK(!IsInBootImage(cur_fields)); - native_object_relocations_.emplace( - cur_fields, - NativeObjectRelocation {offset, kNativeObjectRelocationTypeArtFieldArray }); + native_object_relocations_.emplace(cur_fields, + NativeObjectRelocation {oat_file, offset, kNativeObjectRelocationTypeArtFieldArray }); offset += header_size; // Forward individual fields so that we can quickly find where they belong. for (size_t i = 0, count = cur_fields->size(); i < count; ++i) { @@ -1022,9 +1088,8 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { CHECK(it2 == native_object_relocations_.end()) << "Field at index=" << i << " already assigned " << PrettyField(field) << " static=" << field->IsStatic(); DCHECK(!IsInBootImage(field)); - native_object_relocations_.emplace( - field, - NativeObjectRelocation {offset, kNativeObjectRelocationTypeArtField }); + native_object_relocations_.emplace(field, + NativeObjectRelocation {oat_file, offset, kNativeObjectRelocationTypeArtField }); offset += sizeof(ArtField); } } @@ -1053,17 +1118,17 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { auto it = native_object_relocations_.find(array); CHECK(it == native_object_relocations_.end()) << "Method array " << array << " already forwarded"; - size_t& offset = bin_slot_sizes_[bin_type]; + size_t& offset = image_info.bin_slot_sizes_[bin_type]; DCHECK(!IsInBootImage(array)); - native_object_relocations_.emplace( - array, NativeObjectRelocation { - offset, - any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty - : kNativeObjectRelocationTypeArtMethodArrayClean - }); + native_object_relocations_.emplace(array, + NativeObjectRelocation { + oat_file, + offset, + any_dirty ? kNativeObjectRelocationTypeArtMethodArrayDirty + : kNativeObjectRelocationTypeArtMethodArrayClean }); offset += header_size; for (auto& m : as_klass->GetMethods(target_ptr_size_)) { - AssignMethodOffset(&m, type); + AssignMethodOffset(&m, type, oat_file); } (any_dirty ? dirty_methods_ : clean_methods_) += num_methods; } @@ -1089,13 +1154,16 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { } } -void ImageWriter::AssignMethodOffset(ArtMethod* method, NativeObjectRelocationType type) { +void ImageWriter::AssignMethodOffset(ArtMethod* method, + NativeObjectRelocationType type, + const char* oat_filename) { DCHECK(!IsInBootImage(method)); auto it = native_object_relocations_.find(method); CHECK(it == native_object_relocations_.end()) << "Method " << method << " already assigned " << PrettyMethod(method); - size_t& offset = bin_slot_sizes_[BinTypeForNativeRelocationType(type)]; - native_object_relocations_.emplace(method, NativeObjectRelocation { offset, type }); + ImageInfo& image_info = GetImageInfo(oat_filename); + size_t& offset = image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(type)]; + native_object_relocations_.emplace(method, NativeObjectRelocation { oat_filename, offset, type }); offset += ArtMethod::Size(target_ptr_size_); } @@ -1128,18 +1196,20 @@ void ImageWriter::UnbinObjectsIntoOffset(mirror::Object* obj) { void ImageWriter::CalculateNewObjectOffsets() { Thread* const self = Thread::Current(); - StackHandleScope<1> hs(self); - Handle<ObjectArray<Object>> image_roots(hs.NewHandle(CreateImageRoots())); + StackHandleScopeCollection handles(self); + std::vector<Handle<ObjectArray<Object>>> image_roots; + for (const char* oat_filename : oat_filenames_) { + std::string image_filename = oat_filename; + image_roots.push_back(handles.NewHandle(CreateImageRoots(image_filename.c_str()))); + } auto* runtime = Runtime::Current(); auto* heap = runtime->GetHeap(); - DCHECK_EQ(0U, image_end_); // Leave space for the header, but do not write it yet, we need to // know where image_roots is going to end up - image_end_ += RoundUp(sizeof(ImageHeader), kObjectAlignment); // 64-bit-alignment + image_objects_offset_begin_ = RoundUp(sizeof(ImageHeader), kObjectAlignment); // 64-bit-alignment - image_objects_offset_begin_ = image_end_; // Clear any pre-existing monitors which may have been in the monitor words, assign bin slots. heap->VisitObjects(WalkFieldsCallback, this); // Write the image runtime methods. @@ -1156,10 +1226,12 @@ void ImageWriter::CalculateNewObjectOffsets() { const auto image_method_type = kNativeObjectRelocationTypeArtMethodArrayClean; auto it = native_object_relocations_.find(&image_method_array_); CHECK(it == native_object_relocations_.end()); - size_t& offset = bin_slot_sizes_[BinTypeForNativeRelocationType(image_method_type)]; + ImageInfo& default_image_info = GetImageInfo(default_oat_filename_); + size_t& offset = + default_image_info.bin_slot_sizes_[BinTypeForNativeRelocationType(image_method_type)]; if (!compile_app_image_) { native_object_relocations_.emplace(&image_method_array_, - NativeObjectRelocation { offset, image_method_type }); + NativeObjectRelocation { default_oat_filename_, offset, image_method_type }); } size_t method_alignment = ArtMethod::Alignment(target_ptr_size_); const size_t array_size = LengthPrefixedArray<ArtMethod>::ComputeSize( @@ -1171,43 +1243,74 @@ void ImageWriter::CalculateNewObjectOffsets() { CHECK(m->IsRuntimeMethod()); DCHECK_EQ(compile_app_image_, IsInBootImage(m)) << "Trampolines should be in boot image"; if (!IsInBootImage(m)) { - AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean); + AssignMethodOffset(m, kNativeObjectRelocationTypeArtMethodClean, default_oat_filename_); } } // Calculate size of the dex cache arrays slot and prepare offsets. PrepareDexCacheArraySlots(); // Calculate bin slot offsets. - size_t bin_offset = image_objects_offset_begin_; - for (size_t i = 0; i != kBinSize; ++i) { - bin_slot_offsets_[i] = bin_offset; - bin_offset += bin_slot_sizes_[i]; - if (i == kBinArtField) { - static_assert(kBinArtField + 1 == kBinArtMethodClean, "Methods follow fields."); - static_assert(alignof(ArtField) == 4u, "ArtField alignment is 4."); - DCHECK_ALIGNED(bin_offset, 4u); - DCHECK(method_alignment == 4u || method_alignment == 8u); - bin_offset = RoundUp(bin_offset, method_alignment); + for (const char* oat_filename : oat_filenames_) { + ImageInfo& image_info = GetImageInfo(oat_filename); + size_t bin_offset = image_objects_offset_begin_; + for (size_t i = 0; i != kBinSize; ++i) { + image_info.bin_slot_offsets_[i] = bin_offset; + bin_offset += image_info.bin_slot_sizes_[i]; + if (i == kBinArtField) { + static_assert(kBinArtField + 1 == kBinArtMethodClean, "Methods follow fields."); + static_assert(alignof(ArtField) == 4u, "ArtField alignment is 4."); + DCHECK_ALIGNED(bin_offset, 4u); + DCHECK(method_alignment == 4u || method_alignment == 8u); + bin_offset = RoundUp(bin_offset, method_alignment); + } } + // NOTE: There may be additional padding between the bin slots and the intern table. + DCHECK_EQ(image_info.image_end_, + GetBinSizeSum(image_info, kBinMirrorCount) + image_objects_offset_begin_); + } + + // Calculate image offsets. + size_t image_offset = 0; + for (const char* oat_filename : oat_filenames_) { + ImageInfo& image_info = GetImageInfo(oat_filename); + image_info.image_begin_ = global_image_begin_ + image_offset; + image_info.image_offset_ = image_offset; + size_t native_sections_size = image_info.bin_slot_sizes_[kBinArtField] + + image_info.bin_slot_sizes_[kBinArtMethodDirty] + + image_info.bin_slot_sizes_[kBinArtMethodClean] + + image_info.bin_slot_sizes_[kBinDexCacheArray] + + intern_table_bytes_ + + class_table_bytes_; + size_t image_objects = RoundUp(image_info.image_end_, kPageSize); + size_t bitmap_size = + RoundUp(gc::accounting::ContinuousSpaceBitmap::ComputeBitmapSize(image_objects), kPageSize); + size_t heap_size = gc::accounting::ContinuousSpaceBitmap::ComputeHeapSize(bitmap_size); + size_t max = std::max(heap_size, image_info.image_end_ + native_sections_size + bitmap_size); + image_info.image_size_ = RoundUp(max, kPageSize); + image_offset += image_info.image_size_; } - // NOTE: There may be additional padding between the bin slots and the intern table. - - DCHECK_EQ(image_end_, GetBinSizeSum(kBinMirrorCount) + image_objects_offset_begin_); // Transform each object's bin slot into an offset which will be used to do the final copy. heap->VisitObjects(UnbinObjectsIntoOffsetCallback, this); - DCHECK_EQ(image_end_, GetBinSizeSum(kBinMirrorCount) + image_objects_offset_begin_); + // DCHECK_EQ(image_end_, GetBinSizeSum(kBinMirrorCount) + image_objects_offset_begin_); - image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots.Get())); + size_t i = 0; + for (const char* oat_filename : oat_filenames_) { + ImageInfo& image_info = GetImageInfo(oat_filename); + image_info.image_roots_address_ = PointerToLowMemUInt32(GetImageAddress(image_roots[i].Get())); + i++; + } // Update the native relocations by adding their bin sums. for (auto& pair : native_object_relocations_) { NativeObjectRelocation& relocation = pair.second; Bin bin_type = BinTypeForNativeRelocationType(relocation.type); - relocation.offset += bin_slot_offsets_[bin_type]; + ImageInfo& image_info = GetImageInfo(relocation.oat_filename); + relocation.offset += image_info.bin_slot_offsets_[bin_type]; } + /* TODO: Reenable the intern table and class table. b/26317072 // Calculate how big the intern table will be after being serialized. InternTable* const intern_table = runtime->GetInternTable(); CHECK_EQ(intern_table->WeakSize(), 0u) << " should have strong interned all the strings"; @@ -1231,41 +1334,45 @@ void ImageWriter::CalculateNewObjectOffsets() { class_table_bytes_ += table->WriteToMemory(nullptr); } } + */ - // Note that image_end_ is left at end of used mirror object section. + // Note that image_info.image_end_ is left at end of used mirror object section. } void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { CHECK_NE(0U, oat_loaded_size); - const uint8_t* oat_file_begin = GetOatFileBegin(); + const char* oat_filename = oat_file_->GetLocation().c_str(); + ImageInfo& image_info = GetImageInfo(oat_filename); + const uint8_t* oat_file_begin = GetOatFileBegin(oat_filename); const uint8_t* oat_file_end = oat_file_begin + oat_loaded_size; - oat_data_begin_ = oat_file_begin + oat_data_offset; - const uint8_t* oat_data_end = oat_data_begin_ + oat_file_->Size(); + image_info.oat_data_begin_ = const_cast<uint8_t*>(oat_file_begin) + oat_data_offset; + const uint8_t* oat_data_end = image_info.oat_data_begin_ + oat_file_->Size(); + image_info.oat_size_ = oat_file_->Size(); // Create the image sections. ImageSection sections[ImageHeader::kSectionCount]; // Objects section auto* objects_section = §ions[ImageHeader::kSectionObjects]; - *objects_section = ImageSection(0u, image_end_); + *objects_section = ImageSection(0u, image_info.image_end_); size_t cur_pos = objects_section->End(); // Add field section. auto* field_section = §ions[ImageHeader::kSectionArtFields]; - *field_section = ImageSection(cur_pos, bin_slot_sizes_[kBinArtField]); - CHECK_EQ(bin_slot_offsets_[kBinArtField], field_section->Offset()); + *field_section = ImageSection(cur_pos, image_info.bin_slot_sizes_[kBinArtField]); + CHECK_EQ(image_info.bin_slot_offsets_[kBinArtField], field_section->Offset()); cur_pos = field_section->End(); // Round up to the alignment the required by the method section. cur_pos = RoundUp(cur_pos, ArtMethod::Alignment(target_ptr_size_)); // Add method section. auto* methods_section = §ions[ImageHeader::kSectionArtMethods]; *methods_section = ImageSection(cur_pos, - bin_slot_sizes_[kBinArtMethodClean] + - bin_slot_sizes_[kBinArtMethodDirty]); - CHECK_EQ(bin_slot_offsets_[kBinArtMethodClean], methods_section->Offset()); + image_info.bin_slot_sizes_[kBinArtMethodClean] + + image_info.bin_slot_sizes_[kBinArtMethodDirty]); + CHECK_EQ(image_info.bin_slot_offsets_[kBinArtMethodClean], methods_section->Offset()); cur_pos = methods_section->End(); // Add dex cache arrays section. auto* dex_cache_arrays_section = §ions[ImageHeader::kSectionDexCacheArrays]; - *dex_cache_arrays_section = ImageSection(cur_pos, bin_slot_sizes_[kBinDexCacheArray]); - CHECK_EQ(bin_slot_offsets_[kBinDexCacheArray], dex_cache_arrays_section->Offset()); + *dex_cache_arrays_section = ImageSection(cur_pos, image_info.bin_slot_sizes_[kBinDexCacheArray]); + CHECK_EQ(image_info.bin_slot_offsets_[kBinDexCacheArray], dex_cache_arrays_section->Offset()); cur_pos = dex_cache_arrays_section->End(); // Round up to the alignment the string table expects. See HashSet::WriteToMemory. cur_pos = RoundUp(cur_pos, sizeof(uint64_t)); @@ -1282,42 +1389,51 @@ void ImageWriter::CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) { // Image end goes right before the start of the image bitmap. const size_t image_end = static_cast<uint32_t>(cur_pos); // Finally bitmap section. - const size_t bitmap_bytes = image_bitmap_->Size(); + const size_t bitmap_bytes = image_info.image_bitmap_->Size(); auto* bitmap_section = §ions[ImageHeader::kSectionImageBitmap]; *bitmap_section = ImageSection(RoundUp(cur_pos, kPageSize), RoundUp(bitmap_bytes, kPageSize)); cur_pos = bitmap_section->End(); - if (kIsDebugBuild) { + if (VLOG_IS_ON(compiler)) { + LOG(INFO) << "Creating header for " << oat_filename; size_t idx = 0; for (const ImageSection& section : sections) { LOG(INFO) << static_cast<ImageHeader::ImageSections>(idx) << " " << section; ++idx; } LOG(INFO) << "Methods: clean=" << clean_methods_ << " dirty=" << dirty_methods_; + LOG(INFO) << "Image roots address=" << std::hex << image_info.image_roots_address_ << std::dec; + LOG(INFO) << "Image begin=" << std::hex << reinterpret_cast<uintptr_t>(global_image_begin_) + << " Image offset=" << image_info.image_offset_ << std::dec; + LOG(INFO) << "Oat file begin=" << std::hex << reinterpret_cast<uintptr_t>(oat_file_begin) + << " Oat data begin=" << reinterpret_cast<uintptr_t>(image_info.oat_data_begin_) + << " Oat data end=" << reinterpret_cast<uintptr_t>(oat_data_end) + << " Oat file end=" << reinterpret_cast<uintptr_t>(oat_file_end); } - CHECK_EQ(AlignUp(image_begin_ + image_end, kPageSize), oat_file_begin) << - "Oat file should be right after the image."; + // Create the header, leave 0 for data size since we will fill this in as we are writing the // image. - new (image_->Begin()) ImageHeader(PointerToLowMemUInt32(image_begin_), - image_end, - sections, - image_roots_address_, - oat_file_->GetOatHeader().GetChecksum(), - PointerToLowMemUInt32(oat_file_begin), - PointerToLowMemUInt32(oat_data_begin_), - PointerToLowMemUInt32(oat_data_end), - PointerToLowMemUInt32(oat_file_end), - target_ptr_size_, - compile_pic_, - image_storage_mode_, - /*data_size*/0u); + new (image_info.image_->Begin()) ImageHeader(PointerToLowMemUInt32(image_info.image_begin_), + image_end, + sections, + image_info.image_roots_address_, + oat_file_->GetOatHeader().GetChecksum(), + PointerToLowMemUInt32(oat_file_begin), + PointerToLowMemUInt32(image_info.oat_data_begin_), + PointerToLowMemUInt32(oat_data_end), + PointerToLowMemUInt32(oat_file_end), + target_ptr_size_, + compile_pic_, + image_storage_mode_, + /*data_size*/0u); } ArtMethod* ImageWriter::GetImageMethodAddress(ArtMethod* method) { auto it = native_object_relocations_.find(method); CHECK(it != native_object_relocations_.end()) << PrettyMethod(method) << " @ " << method; - CHECK_GE(it->second.offset, image_end_) << "ArtMethods should be after Objects"; - return reinterpret_cast<ArtMethod*>(image_begin_ + it->second.offset); + const char* oat_filename = GetOatFilename(method->GetDexCache()); + ImageInfo& image_info = GetImageInfo(oat_filename); + CHECK_GE(it->second.offset, image_info.image_end_) << "ArtMethods should be after Objects"; + return reinterpret_cast<ArtMethod*>(image_info.image_begin_ + it->second.offset); } class FixupRootVisitor : public RootVisitor { @@ -1345,18 +1461,24 @@ class FixupRootVisitor : public RootVisitor { mirror::Object* ImageAddress(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_) { const size_t offset = image_writer_->GetImageOffset(obj); - auto* const dest = reinterpret_cast<Object*>(image_writer_->image_begin_ + offset); + auto* const dest = reinterpret_cast<Object*>(image_writer_->global_image_begin_ + offset); VLOG(compiler) << "Update root from " << obj << " to " << dest; return dest; } }; void ImageWriter::CopyAndFixupNativeData() { + const char* oat_filename = oat_file_->GetLocation().c_str(); + ImageInfo& image_info = GetImageInfo(oat_filename); // Copy ArtFields and methods to their locations and update the array for convenience. for (auto& pair : native_object_relocations_) { NativeObjectRelocation& relocation = pair.second; - auto* dest = image_->Begin() + relocation.offset; - DCHECK_GE(dest, image_->Begin() + image_end_); + // Only work with fields and methods that are in the current oat file. + if (strcmp(relocation.oat_filename, oat_filename) != 0) { + continue; + } + auto* dest = image_info.image_->Begin() + relocation.offset; + DCHECK_GE(dest, image_info.image_->Begin() + image_info.image_end_); DCHECK(!IsInBootImage(pair.first)); switch (relocation.type) { case kNativeObjectRelocationTypeArtField: { @@ -1368,7 +1490,8 @@ void ImageWriter::CopyAndFixupNativeData() { case kNativeObjectRelocationTypeArtMethodClean: case kNativeObjectRelocationTypeArtMethodDirty: { CopyAndFixupMethod(reinterpret_cast<ArtMethod*>(pair.first), - reinterpret_cast<ArtMethod*>(dest)); + reinterpret_cast<ArtMethod*>(dest), + image_info); break; } // For arrays, copy just the header since the elements will get copied by their corresponding @@ -1391,30 +1514,36 @@ void ImageWriter::CopyAndFixupNativeData() { } } // Fixup the image method roots. - auto* image_header = reinterpret_cast<ImageHeader*>(image_->Begin()); + auto* image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin()); const ImageSection& methods_section = image_header->GetMethodsSection(); for (size_t i = 0; i < ImageHeader::kImageMethodsCount; ++i) { ArtMethod* method = image_methods_[i]; CHECK(method != nullptr); + // Only place runtime methods in the image of the default oat file. + if (method->IsRuntimeMethod() && strcmp(default_oat_filename_, oat_filename) != 0) { + continue; + } if (!IsInBootImage(method)) { auto it = native_object_relocations_.find(method); - CHECK(it != native_object_relocations_.end()) << "No fowarding for " << PrettyMethod(method); + CHECK(it != native_object_relocations_.end()) << "No forwarding for " << PrettyMethod(method); NativeObjectRelocation& relocation = it->second; CHECK(methods_section.Contains(relocation.offset)) << relocation.offset << " not in " << methods_section; CHECK(relocation.IsArtMethodRelocation()) << relocation.type; - method = reinterpret_cast<ArtMethod*>(image_begin_ + it->second.offset); + method = reinterpret_cast<ArtMethod*>(global_image_begin_ + it->second.offset); } image_header->SetImageMethod(static_cast<ImageHeader::ImageMethod>(i), method); } FixupRootVisitor root_visitor(this); + /* TODO: Reenable the intern table and class table // Write the intern table into the image. const ImageSection& intern_table_section = image_header->GetImageSection( ImageHeader::kSectionInternedStrings); Runtime* const runtime = Runtime::Current(); InternTable* const intern_table = runtime->GetInternTable(); - uint8_t* const intern_table_memory_ptr = image_->Begin() + intern_table_section.Offset(); + uint8_t* const intern_table_memory_ptr = + image_info.image_->Begin() + intern_table_section.Offset(); const size_t intern_table_bytes = intern_table->WriteToMemory(intern_table_memory_ptr); CHECK_EQ(intern_table_bytes, intern_table_bytes_); // Fixup the pointers in the newly written intern table to contain image addresses. @@ -1430,10 +1559,11 @@ void ImageWriter::CopyAndFixupNativeData() { // Write the class table(s) into the image. class_table_bytes_ may be 0 if there are multiple // class loaders. Writing multiple class tables into the image is currently unsupported. if (class_table_bytes_ > 0u) { - ClassLinker* const class_linker = runtime->GetClassLinker(); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); const ImageSection& class_table_section = image_header->GetImageSection( ImageHeader::kSectionClassTable); - uint8_t* const class_table_memory_ptr = image_->Begin() + class_table_section.Offset(); + uint8_t* const class_table_memory_ptr = + image_info.image_->Begin() + class_table_section.Offset(); ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_); size_t class_table_bytes = 0; for (mirror::ClassLoader* loader : class_loaders_) { @@ -1453,6 +1583,7 @@ void ImageWriter::CopyAndFixupNativeData() { } CHECK_EQ(class_table_bytes, class_table_bytes_); } + */ } void ImageWriter::CopyAndFixupObjects() { @@ -1500,7 +1631,8 @@ void ImageWriter::FixupPointerArray(mirror::Object* dst, mirror::PointerArray* a } UNREACHABLE(); } else { - elem = image_begin_ + it->second.offset; + ImageInfo& image_info = GetImageInfo(it->second.oat_filename); + elem = image_info.image_begin_ + it->second.offset; } } dest_array->SetElementPtrSize<false, true>(i, elem, target_ptr_size_); @@ -1512,14 +1644,16 @@ void ImageWriter::CopyAndFixupObject(Object* obj) { return; } size_t offset = GetImageOffset(obj); - auto* dst = reinterpret_cast<Object*>(image_->Begin() + offset); - DCHECK_LT(offset, image_end_); + const char* oat_filename = GetOatFilename(obj); + ImageInfo& image_info = GetImageInfo(oat_filename); + auto* dst = reinterpret_cast<Object*>(image_info.image_->Begin() + offset); + DCHECK_LT(offset, image_info.image_end_); const auto* src = reinterpret_cast<const uint8_t*>(obj); - image_bitmap_->Set(dst); // Mark the obj as live. + image_info.image_bitmap_->Set(dst); // Mark the obj as live. const size_t n = obj->SizeOf(); - DCHECK_LE(offset + n, image_->Size()); + DCHECK_LE(offset + n, image_info.image_->Size()); memcpy(dst, src, n); // Write in a hash code of objects which have inflated monitors or a hash code in their monitor @@ -1595,34 +1729,55 @@ uintptr_t ImageWriter::NativeOffsetInImage(void* obj) { } template <typename T> -T* ImageWriter::NativeLocationInImage(T* obj) { - return (obj == nullptr || IsInBootImage(obj)) - ? obj - : reinterpret_cast<T*>(image_begin_ + NativeOffsetInImage(obj)); +T* ImageWriter::NativeLocationInImage(T* obj, const char* oat_filename) { + if (obj == nullptr || IsInBootImage(obj)) { + return obj; + } else { + ImageInfo& image_info = GetImageInfo(oat_filename); + return reinterpret_cast<T*>(image_info.image_begin_ + NativeOffsetInImage(obj)); + } } template <typename T> -T* ImageWriter::NativeCopyLocation(T* obj) { - return (obj == nullptr || IsInBootImage(obj)) - ? obj - : reinterpret_cast<T*>(image_->Begin() + NativeOffsetInImage(obj)); +T* ImageWriter::NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) { + if (obj == nullptr || IsInBootImage(obj)) { + return obj; + } else { + const char* oat_filename = GetOatFilenameForDexCache(dex_cache); + ImageInfo& image_info = GetImageInfo(oat_filename); + return reinterpret_cast<T*>(image_info.image_->Begin() + NativeOffsetInImage(obj)); + } } class NativeLocationVisitor { public: - explicit NativeLocationVisitor(ImageWriter* image_writer) : image_writer_(image_writer) {} + explicit NativeLocationVisitor(ImageWriter* image_writer, const char* oat_filename) + : image_writer_(image_writer), oat_filename_(oat_filename) {} template <typename T> - T* operator()(T* ptr) const { - return image_writer_->NativeLocationInImage(ptr); + T* operator()(T* ptr) const SHARED_REQUIRES(Locks::mutator_lock_) { + return image_writer_->NativeLocationInImage(ptr, oat_filename_); + } + + ArtMethod* operator()(ArtMethod* method) const SHARED_REQUIRES(Locks::mutator_lock_) { + const char* oat_filename = method->IsRuntimeMethod() ? image_writer_->GetDefaultOatFilename() : + image_writer_->GetOatFilenameForDexCache(method->GetDexCache()); + return image_writer_->NativeLocationInImage(method, oat_filename); + } + + ArtField* operator()(ArtField* field) const SHARED_REQUIRES(Locks::mutator_lock_) { + const char* oat_filename = image_writer_->GetOatFilenameForDexCache(field->GetDexCache()); + return image_writer_->NativeLocationInImage(field, oat_filename); } private: ImageWriter* const image_writer_; + const char* oat_filename_; }; void ImageWriter::FixupClass(mirror::Class* orig, mirror::Class* copy) { - orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this)); + const char* oat_filename = GetOatFilename(orig); + orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this, oat_filename)); FixupClassVisitor visitor(this, copy); static_cast<mirror::Object*>(orig)->VisitReferences(visitor, visitor); } @@ -1661,7 +1816,7 @@ void ImageWriter::FixupObject(Object* orig, Object* copy) { CHECK(it != native_object_relocations_.end()) << "Missing relocation for AbstractMethod.artMethod " << PrettyMethod(src_method); dest->SetArtMethod( - reinterpret_cast<ArtMethod*>(image_begin_ + it->second.offset)); + reinterpret_cast<ArtMethod*>(global_image_begin_ + it->second.offset)); } else if (!klass->IsArrayClass()) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); if (klass == class_linker->GetClassRoot(ClassLinker::kJavaLangDexCache)) { @@ -1702,41 +1857,52 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache, // 64-bit values here, clearing the top 32 bits for 32-bit targets. The zero-extension is // done by casting to the unsigned type uintptr_t before casting to int64_t, i.e. // static_cast<int64_t>(reinterpret_cast<uintptr_t>(image_begin_ + offset))). + const char* oat_filename = GetOatFilenameForDexCache(orig_dex_cache); GcRoot<mirror::String>* orig_strings = orig_dex_cache->GetStrings(); if (orig_strings != nullptr) { copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::StringsOffset(), - NativeLocationInImage(orig_strings), + NativeLocationInImage(orig_strings, oat_filename), /*pointer size*/8u); - orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings), ImageAddressVisitor(this)); + orig_dex_cache->FixupStrings(NativeCopyLocation(orig_strings, orig_dex_cache), + ImageAddressVisitor(this)); } GcRoot<mirror::Class>* orig_types = orig_dex_cache->GetResolvedTypes(); if (orig_types != nullptr) { copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedTypesOffset(), - NativeLocationInImage(orig_types), + NativeLocationInImage(orig_types, oat_filename), /*pointer size*/8u); - orig_dex_cache->FixupResolvedTypes(NativeCopyLocation(orig_types), ImageAddressVisitor(this)); + orig_dex_cache->FixupResolvedTypes(NativeCopyLocation(orig_types, orig_dex_cache), + ImageAddressVisitor(this)); } ArtMethod** orig_methods = orig_dex_cache->GetResolvedMethods(); if (orig_methods != nullptr) { copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedMethodsOffset(), - NativeLocationInImage(orig_methods), + NativeLocationInImage(orig_methods, oat_filename), /*pointer size*/8u); - ArtMethod** copy_methods = NativeCopyLocation(orig_methods); + ArtMethod** copy_methods = NativeCopyLocation(orig_methods, orig_dex_cache); for (size_t i = 0, num = orig_dex_cache->NumResolvedMethods(); i != num; ++i) { ArtMethod* orig = mirror::DexCache::GetElementPtrSize(orig_methods, i, target_ptr_size_); - ArtMethod* copy = NativeLocationInImage(orig); + const char* method_oat_filename; + if (orig == nullptr || orig->IsRuntimeMethod()) { + method_oat_filename = default_oat_filename_; + } else { + method_oat_filename = GetOatFilenameForDexCache(orig->GetDexCache()); + } + ArtMethod* copy = NativeLocationInImage(orig, method_oat_filename); mirror::DexCache::SetElementPtrSize(copy_methods, i, copy, target_ptr_size_); } } ArtField** orig_fields = orig_dex_cache->GetResolvedFields(); if (orig_fields != nullptr) { copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedFieldsOffset(), - NativeLocationInImage(orig_fields), + NativeLocationInImage(orig_fields, oat_filename), /*pointer size*/8u); - ArtField** copy_fields = NativeCopyLocation(orig_fields); + ArtField** copy_fields = NativeCopyLocation(orig_fields, orig_dex_cache); for (size_t i = 0, num = orig_dex_cache->NumResolvedFields(); i != num; ++i) { ArtField* orig = mirror::DexCache::GetElementPtrSize(orig_fields, i, target_ptr_size_); - ArtField* copy = NativeLocationInImage(orig); + const char* field_oat_filename = + orig == nullptr ? default_oat_filename_ : GetOatFilenameForDexCache(orig->GetDexCache()); + ArtField* copy = NativeLocationInImage(orig, field_oat_filename); mirror::DexCache::SetElementPtrSize(copy_fields, i, copy, target_ptr_size_); } } @@ -1747,9 +1913,10 @@ const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const { // If we are compiling an app image, we need to use the stubs of the boot image. if (compile_app_image_) { // Use the current image pointers. - gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace(); - DCHECK(image_space != nullptr); - const OatFile* oat_file = image_space->GetOatFile(); + std::vector<gc::space::ImageSpace*> image_spaces = + Runtime::Current()->GetHeap()->GetBootImageSpaces(); + DCHECK(!image_spaces.empty()); + const OatFile* oat_file = image_spaces[0]->GetOatFile(); CHECK(oat_file != nullptr); const OatHeader& header = oat_file->GetOatHeader(); switch (type) { @@ -1772,10 +1939,13 @@ const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const { UNREACHABLE(); } } - return GetOatAddressForOffset(oat_address_offsets_[type]); + const ImageInfo& primary_image_info = GetImageInfo(0); + return GetOatAddressForOffset(primary_image_info.oat_address_offsets_[type], primary_image_info); } -const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, bool* quick_is_interpreted) { +const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, + const ImageInfo& image_info, + bool* quick_is_interpreted) { DCHECK(!method->IsResolutionMethod()) << PrettyMethod(method); DCHECK(!method->IsImtConflictMethod()) << PrettyMethod(method); DCHECK(!method->IsImtUnimplementedMethod()) << PrettyMethod(method); @@ -1788,7 +1958,7 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, bool* quick_is_inter // Quick entrypoint: uint32_t quick_oat_code_offset = PointerToLowMemUInt32( method->GetEntryPointFromQuickCompiledCodePtrSize(target_ptr_size_)); - const uint8_t* quick_code = GetOatAddressForOffset(quick_oat_code_offset); + const uint8_t* quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info); *quick_is_interpreted = false; if (quick_code != nullptr && (!method->IsStatic() || method->IsConstructor() || method->GetDeclaringClass()->IsInitialized())) { @@ -1808,42 +1978,32 @@ const uint8_t* ImageWriter::GetQuickCode(ArtMethod* method, bool* quick_is_inter quick_code = GetOatAddress(kOatAddressQuickResolutionTrampoline); } if (!IsInBootOatFile(quick_code)) { - DCHECK_GE(quick_code, oat_data_begin_); + // DCHECK_GE(quick_code, oat_data_begin_); } return quick_code; } -const uint8_t* ImageWriter::GetQuickEntryPoint(ArtMethod* method) { - // Calculate the quick entry point following the same logic as FixupMethod() below. - // The resolution method has a special trampoline to call. - Runtime* runtime = Runtime::Current(); - if (UNLIKELY(method == runtime->GetResolutionMethod())) { - return GetOatAddress(kOatAddressQuickResolutionTrampoline); - } else if (UNLIKELY(method == runtime->GetImtConflictMethod() || - method == runtime->GetImtUnimplementedMethod())) { - return GetOatAddress(kOatAddressQuickIMTConflictTrampoline); - } else { - // We assume all methods have code. If they don't currently then we set them to the use the - // resolution trampoline. Abstract methods never have code and so we need to make sure their - // use results in an AbstractMethodError. We use the interpreter to achieve this. - if (UNLIKELY(!method->IsInvokable())) { - return GetOatAddress(kOatAddressQuickToInterpreterBridge); - } else { - bool quick_is_interpreted; - return GetQuickCode(method, &quick_is_interpreted); - } - } -} - -void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy) { +void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, + ArtMethod* copy, + const ImageInfo& image_info) { memcpy(copy, orig, ArtMethod::Size(target_ptr_size_)); copy->SetDeclaringClass(GetImageAddress(orig->GetDeclaringClassUnchecked())); + const char* oat_filename; + if (orig->IsRuntimeMethod()) { + oat_filename = default_oat_filename_; + } else { + auto it = dex_file_oat_filename_map_.find(orig->GetDexFile()); + DCHECK(it != dex_file_oat_filename_map_.end()) << orig->GetDexFile()->GetLocation(); + oat_filename = it->second; + } ArtMethod** orig_resolved_methods = orig->GetDexCacheResolvedMethods(target_ptr_size_); - copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods), target_ptr_size_); + copy->SetDexCacheResolvedMethods(NativeLocationInImage(orig_resolved_methods, oat_filename), + target_ptr_size_); GcRoot<mirror::Class>* orig_resolved_types = orig->GetDexCacheResolvedTypes(target_ptr_size_); - copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types), target_ptr_size_); + copy->SetDexCacheResolvedTypes(NativeLocationInImage(orig_resolved_types, oat_filename), + target_ptr_size_); // OatWriter replaces the code_ with an offset value. Here we re-adjust to a pointer relative to // oat_begin_ @@ -1877,7 +2037,7 @@ void ImageWriter::CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy) { GetOatAddress(kOatAddressQuickToInterpreterBridge), target_ptr_size_); } else { bool quick_is_interpreted; - const uint8_t* quick_code = GetQuickCode(orig, &quick_is_interpreted); + const uint8_t* quick_code = GetQuickCode(orig, image_info, &quick_is_interpreted); copy->SetEntryPointFromQuickCompiledCodePtrSize(quick_code, target_ptr_size_); // JNI entrypoint: @@ -1914,13 +2074,16 @@ void ImageWriter::SetOatChecksumFromElfFile(File* elf_file) { CHECK(oat_header != nullptr); CHECK(oat_header->IsValid()); - ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin()); + ImageInfo& image_info = GetImageInfo(oat_file_->GetLocation().c_str()); + ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_info.image_->Begin()); image_header->SetOatChecksum(oat_header->GetChecksum()); } -size_t ImageWriter::GetBinSizeSum(ImageWriter::Bin up_to) const { +size_t ImageWriter::GetBinSizeSum(ImageWriter::ImageInfo& image_info, ImageWriter::Bin up_to) const { DCHECK_LE(up_to, kBinSize); - return std::accumulate(&bin_slot_sizes_[0], &bin_slot_sizes_[up_to], /*init*/0); + return std::accumulate(&image_info.bin_slot_sizes_[0], + &image_info.bin_slot_sizes_[up_to], + /*init*/0); } ImageWriter::BinSlot::BinSlot(uint32_t lockword) : lockword_(lockword) { @@ -1946,15 +2109,18 @@ uint32_t ImageWriter::BinSlot::GetIndex() const { return lockword_ & ~kBinMask; } -uint8_t* ImageWriter::GetOatFileBegin() const { - DCHECK_GT(intern_table_bytes_, 0u); - size_t native_sections_size = bin_slot_sizes_[kBinArtField] + - bin_slot_sizes_[kBinArtMethodDirty] + - bin_slot_sizes_[kBinArtMethodClean] + - bin_slot_sizes_[kBinDexCacheArray] + - intern_table_bytes_ + - class_table_bytes_; - return image_begin_ + RoundUp(image_end_ + native_sections_size, kPageSize); +uint8_t* ImageWriter::GetOatFileBegin(const char* oat_filename) const { + // DCHECK_GT(intern_table_bytes_, 0u); TODO: Reenable intern table and class table. + uintptr_t last_image_end = 0; + for (const char* oat_fn : oat_filenames_) { + const ImageInfo& image_info = GetConstImageInfo(oat_fn); + DCHECK(image_info.image_begin_ != nullptr); + uintptr_t this_end = reinterpret_cast<uintptr_t>(image_info.image_begin_) + + image_info.image_size_; + last_image_end = std::max(this_end, last_image_end); + } + const ImageInfo& image_info = GetConstImageInfo(oat_filename); + return reinterpret_cast<uint8_t*>(last_image_end) + image_info.oat_offset_; } ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocationType type) { @@ -1974,4 +2140,61 @@ ImageWriter::Bin ImageWriter::BinTypeForNativeRelocationType(NativeObjectRelocat UNREACHABLE(); } +const char* ImageWriter::GetOatFilename(mirror::Object* obj) const { + if (compile_app_image_) { + return default_oat_filename_; + } else { + return GetOatFilenameForDexCache(obj->IsDexCache() ? obj->AsDexCache() : + obj->IsClass() ? obj->AsClass()->GetDexCache() : obj->GetClass()->GetDexCache()); + } +} + +const char* ImageWriter::GetOatFilenameForDexCache(mirror::DexCache* dex_cache) const { + if (compile_app_image_ || dex_cache == nullptr) { + return default_oat_filename_; + } else { + auto it = dex_file_oat_filename_map_.find(dex_cache->GetDexFile()); + DCHECK(it != dex_file_oat_filename_map_.end()) << dex_cache->GetDexFile()->GetLocation(); + return it->second; + } +} + +ImageWriter::ImageInfo& ImageWriter::GetImageInfo(const char* oat_filename) { + auto it = image_info_map_.find(oat_filename); + DCHECK(it != image_info_map_.end()); + return it->second; +} + +const ImageWriter::ImageInfo& ImageWriter::GetConstImageInfo(const char* oat_filename) const { + auto it = image_info_map_.find(oat_filename); + DCHECK(it != image_info_map_.end()); + return it->second; +} + +const ImageWriter::ImageInfo& ImageWriter::GetImageInfo(size_t index) const { + DCHECK_LT(index, oat_filenames_.size()); + return GetConstImageInfo(oat_filenames_[index]); +} + +void ImageWriter::UpdateOatFile(const char* oat_filename) { + std::unique_ptr<File> oat_file(OS::OpenFileForReading(oat_filename)); + DCHECK(oat_file != nullptr); + size_t oat_loaded_size = 0; + size_t oat_data_offset = 0; + ElfWriter::GetOatElfInformation(oat_file.get(), &oat_loaded_size, &oat_data_offset); + + ImageInfo& cur_image_info = GetImageInfo(oat_filename); + + // Update the oat_offset of the next image info. + auto it = std::find(oat_filenames_.begin(), oat_filenames_.end(), oat_filename); + DCHECK(it != oat_filenames_.end()); + + it++; + if (it != oat_filenames_.end()) { + // There is a following one. + ImageInfo& next_image_info = GetImageInfo(*it); + next_image_info.oat_offset_ = cur_image_info.oat_offset_ + oat_loaded_size; + } +} + } // namespace art diff --git a/compiler/image_writer.h b/compiler/image_writer.h index f1b2965a12..78297ae645 100644 --- a/compiler/image_writer.h +++ b/compiler/image_writer.h @@ -56,30 +56,31 @@ class ImageWriter FINAL { uintptr_t image_begin, bool compile_pic, bool compile_app_image, - ImageHeader::StorageMode image_storage_mode) + ImageHeader::StorageMode image_storage_mode, + const std::vector<const char*> oat_filenames, + const std::unordered_map<const DexFile*, const char*>& dex_file_oat_filename_map) : compiler_driver_(compiler_driver), - image_begin_(reinterpret_cast<uint8_t*>(image_begin)), - image_end_(0), + global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)), image_objects_offset_begin_(0), - image_roots_address_(0), oat_file_(nullptr), - oat_data_begin_(nullptr), compile_pic_(compile_pic), compile_app_image_(compile_app_image), boot_image_space_(nullptr), target_ptr_size_(InstructionSetPointerSize(compiler_driver_.GetInstructionSet())), - bin_slot_sizes_(), - bin_slot_offsets_(), - bin_slot_count_(), intern_table_bytes_(0u), image_method_array_(ImageHeader::kImageMethodsCount), dirty_methods_(0u), clean_methods_(0u), class_table_bytes_(0u), - image_storage_mode_(image_storage_mode) { + image_storage_mode_(image_storage_mode), + dex_file_oat_filename_map_(dex_file_oat_filename_map), + oat_filenames_(oat_filenames), + default_oat_filename_(oat_filenames[0]) { CHECK_NE(image_begin, 0U); + for (const char* oat_filename : oat_filenames) { + image_info_map_.emplace(oat_filename, ImageInfo()); + } std::fill_n(image_methods_, arraysize(image_methods_), nullptr); - std::fill_n(oat_address_offsets_, arraysize(oat_address_offsets_), 0); } ~ImageWriter() { @@ -88,14 +89,25 @@ class ImageWriter FINAL { bool PrepareImageAddressSpace(); bool IsImageAddressSpaceReady() const { - return image_roots_address_ != 0u; + bool ready = !image_info_map_.empty(); + for (auto& pair : image_info_map_) { + const ImageInfo& image_info = pair.second; + if (image_info.image_roots_address_ == 0u) { + return false; + } + } + return ready; } template <typename T> T* GetImageAddress(T* object) const SHARED_REQUIRES(Locks::mutator_lock_) { - return (object == nullptr || IsInBootImage(object)) - ? object - : reinterpret_cast<T*>(image_begin_ + GetImageOffset(object)); + if (object == nullptr || IsInBootImage(object)) { + return object; + } else { + const char* oat_filename = GetOatFilename(object); + const ImageInfo& image_info = GetConstImageInfo(oat_filename); + return reinterpret_cast<T*>(image_info.image_begin_ + GetImageOffset(object)); + } } ArtMethod* GetImageMethodAddress(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_); @@ -103,26 +115,36 @@ class ImageWriter FINAL { template <typename PtrType> PtrType GetDexCacheArrayElementImageAddress(const DexFile* dex_file, uint32_t offset) const SHARED_REQUIRES(Locks::mutator_lock_) { - auto it = dex_cache_array_starts_.find(dex_file); - DCHECK(it != dex_cache_array_starts_.end()); + auto oat_it = dex_file_oat_filename_map_.find(dex_file); + DCHECK(oat_it != dex_file_oat_filename_map_.end()); + const ImageInfo& image_info = GetConstImageInfo(oat_it->second); + auto it = image_info.dex_cache_array_starts_.find(dex_file); + DCHECK(it != image_info.dex_cache_array_starts_.end()); return reinterpret_cast<PtrType>( - image_begin_ + bin_slot_offsets_[kBinDexCacheArray] + it->second + offset); + image_info.image_begin_ + image_info.bin_slot_offsets_[kBinDexCacheArray] + + it->second + offset); } - uint8_t* GetOatFileBegin() const; + uint8_t* GetOatFileBegin(const char* oat_filename) const; // If image_fd is not kInvalidImageFd, then we use that for the file. Otherwise we open - // image_filename. + // the names in image_filenames. bool Write(int image_fd, - const std::string& image_filename, - const std::string& oat_filename, - const std::string& oat_location) + const std::vector<const char*>& image_filenames, + const std::vector<const char*>& oat_filenames) REQUIRES(!Locks::mutator_lock_); - uintptr_t GetOatDataBegin() { - return reinterpret_cast<uintptr_t>(oat_data_begin_); + uintptr_t GetOatDataBegin(const char* oat_filename) { + return reinterpret_cast<uintptr_t>(GetImageInfo(oat_filename).oat_data_begin_); } + const char* GetOatFilenameForDexCache(mirror::DexCache* dex_cache) const + SHARED_REQUIRES(Locks::mutator_lock_); + + // Update the oat size for the given oat file. This will make the oat_offset for the next oat + // file valid. + void UpdateOatFile(const char* oat_filename); + private: bool AllocMemory(); @@ -214,6 +236,58 @@ class ImageWriter FINAL { const uint32_t lockword_; }; + struct ImageInfo { + explicit ImageInfo() + : image_begin_(nullptr), + image_end_(RoundUp(sizeof(ImageHeader), kObjectAlignment)), + image_roots_address_(0), + image_offset_(0), + image_size_(0), + oat_offset_(0), + bin_slot_sizes_(), + bin_slot_offsets_(), + bin_slot_count_() {} + + std::unique_ptr<MemMap> image_; // Memory mapped for generating the image. + + // Target begin of this image. Notes: It is not valid to write here, this is the address + // of the target image, not necessarily where image_ is mapped. The address is only valid + // after layouting (otherwise null). + uint8_t* image_begin_; + + size_t image_end_; // Offset to the free space in image_, initially size of image header. + uint32_t image_roots_address_; // The image roots address in the image. + size_t image_offset_; // Offset of this image from the start of the first image. + + // Image size is the *address space* covered by this image. As the live bitmap is aligned + // to the page size, the live bitmap will cover more address space than necessary. But live + // bitmaps may not overlap, so an image has a "shadow," which is accounted for in the size. + // The next image may only start at image_begin_ + image_size_ (which is guaranteed to be + // page-aligned). + size_t image_size_; + + // Oat data. + size_t oat_offset_; // Offset of the oat file for this image from start of oat files. This is + // valid when the previous oat file has been written. + uint8_t* oat_data_begin_; // Start of oatdata in the corresponding oat file. This is + // valid when the images have been layed out. + size_t oat_size_; // Size of the corresponding oat data. + + // Image bitmap which lets us know where the objects inside of the image reside. + std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> image_bitmap_; + + // The start offsets of the dex cache arrays. + SafeMap<const DexFile*, size_t> dex_cache_array_starts_; + + // Offset from oat_data_begin_ to the stubs. + uint32_t oat_address_offsets_[kOatAddressCount]; + + // Bin slot tracking for dirty object packing. + size_t bin_slot_sizes_[kBinSize]; // Number of bytes in a bin. + size_t bin_slot_offsets_[kBinSize]; // Number of bytes in previous bins. + size_t bin_slot_count_[kBinSize]; // Number of objects in a bin. + }; + // We use the lock word to store the offset of the object in the image. void AssignImageOffset(mirror::Object* object, BinSlot bin_slot) SHARED_REQUIRES(Locks::mutator_lock_); @@ -233,7 +307,8 @@ class ImageWriter FINAL { SHARED_REQUIRES(Locks::mutator_lock_); BinSlot GetImageBinSlot(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_); - void AddDexCacheArrayRelocation(void* array, size_t offset) SHARED_REQUIRES(Locks::mutator_lock_); + void AddDexCacheArrayRelocation(void* array, size_t offset, mirror::DexCache* dex_cache) + SHARED_REQUIRES(Locks::mutator_lock_); void AddMethodPointerArray(mirror::PointerArray* arr) SHARED_REQUIRES(Locks::mutator_lock_); static void* GetImageAddressCallback(void* writer, mirror::Object* obj) @@ -244,19 +319,21 @@ class ImageWriter FINAL { mirror::Object* GetLocalAddress(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_) { size_t offset = GetImageOffset(object); - uint8_t* dst = image_->Begin() + offset; + const char* oat_filename = GetOatFilename(object); + const ImageInfo& image_info = GetConstImageInfo(oat_filename); + uint8_t* dst = image_info.image_->Begin() + offset; return reinterpret_cast<mirror::Object*>(dst); } // Returns the address in the boot image if we are compiling the app image. const uint8_t* GetOatAddress(OatAddress type) const; - const uint8_t* GetOatAddressForOffset(uint32_t offset) const { + const uint8_t* GetOatAddressForOffset(uint32_t offset, const ImageInfo& image_info) const { // With Quick, code is within the OatFile, as there are all in one - // .o ELF object. - DCHECK_LE(offset, oat_file_->Size()); - DCHECK(oat_data_begin_ != nullptr); - return offset == 0u ? nullptr : oat_data_begin_ + offset; + // .o ELF object. But interpret it as signed. + DCHECK_LE(static_cast<int32_t>(offset), static_cast<int32_t>(image_info.oat_size_)); + DCHECK(image_info.oat_data_begin_ != nullptr); + return offset == 0u ? nullptr : image_info.oat_data_begin_ + static_cast<int32_t>(offset); } // Returns true if the class was in the original requested image classes list. @@ -282,7 +359,7 @@ class ImageWriter FINAL { SHARED_REQUIRES(Locks::mutator_lock_); void CreateHeader(size_t oat_loaded_size, size_t oat_data_offset) SHARED_REQUIRES(Locks::mutator_lock_); - mirror::ObjectArray<mirror::Object>* CreateImageRoots() const + mirror::ObjectArray<mirror::Object>* CreateImageRoots(const char* oat_filename) const SHARED_REQUIRES(Locks::mutator_lock_); void CalculateObjectBinSlots(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_); @@ -304,7 +381,7 @@ class ImageWriter FINAL { static void CopyAndFixupObjectsCallback(mirror::Object* obj, void* arg) SHARED_REQUIRES(Locks::mutator_lock_); void CopyAndFixupObject(mirror::Object* obj) SHARED_REQUIRES(Locks::mutator_lock_); - void CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy) + void CopyAndFixupMethod(ArtMethod* orig, ArtMethod* copy, const ImageInfo& image_info) SHARED_REQUIRES(Locks::mutator_lock_); void FixupClass(mirror::Class* orig, mirror::Class* copy) SHARED_REQUIRES(Locks::mutator_lock_); @@ -319,23 +396,24 @@ class ImageWriter FINAL { SHARED_REQUIRES(Locks::mutator_lock_); // Get quick code for non-resolution/imt_conflict/abstract method. - const uint8_t* GetQuickCode(ArtMethod* method, bool* quick_is_interpreted) - SHARED_REQUIRES(Locks::mutator_lock_); - - const uint8_t* GetQuickEntryPoint(ArtMethod* method) + const uint8_t* GetQuickCode(ArtMethod* method, + const ImageInfo& image_info, + bool* quick_is_interpreted) SHARED_REQUIRES(Locks::mutator_lock_); // Patches references in OatFile to expect runtime addresses. void SetOatChecksumFromElfFile(File* elf_file); // Calculate the sum total of the bin slot sizes in [0, up_to). Defaults to all bins. - size_t GetBinSizeSum(Bin up_to = kBinSize) const; + size_t GetBinSizeSum(ImageInfo& image_info, Bin up_to = kBinSize) const; // Return true if a method is likely to be dirtied at runtime. bool WillMethodBeDirty(ArtMethod* m) const SHARED_REQUIRES(Locks::mutator_lock_); // Assign the offset for an ArtMethod. - void AssignMethodOffset(ArtMethod* method, NativeObjectRelocationType type) + void AssignMethodOffset(ArtMethod* method, + NativeObjectRelocationType type, + const char* oat_filename) SHARED_REQUIRES(Locks::mutator_lock_); // Return true if klass is loaded by the boot class loader but not in the boot image. @@ -359,11 +437,11 @@ class ImageWriter FINAL { // Location of where the object will be when the image is loaded at runtime. template <typename T> - T* NativeLocationInImage(T* obj); + T* NativeLocationInImage(T* obj, const char* oat_filename) SHARED_REQUIRES(Locks::mutator_lock_); // Location of where the temporary copy of the object currently is. template <typename T> - T* NativeCopyLocation(T* obj); + T* NativeCopyLocation(T* obj, mirror::DexCache* dex_cache) SHARED_REQUIRES(Locks::mutator_lock_); // Return true of obj is inside of the boot image space. This may only return true if we are // compiling an app image. @@ -372,46 +450,35 @@ class ImageWriter FINAL { // Return true if ptr is within the boot oat file. bool IsInBootOatFile(const void* ptr) const; - const CompilerDriver& compiler_driver_; + const char* GetOatFilename(mirror::Object* object) const SHARED_REQUIRES(Locks::mutator_lock_); - // Beginning target image address for the output image. - uint8_t* image_begin_; + const char* GetDefaultOatFilename() const { + return default_oat_filename_; + } + + ImageInfo& GetImageInfo(const char* oat_filename); + const ImageInfo& GetConstImageInfo(const char* oat_filename) const; + const ImageInfo& GetImageInfo(size_t index) const; + + const CompilerDriver& compiler_driver_; - // Offset to the free space in image_. - size_t image_end_; + // Beginning target image address for the first image. + uint8_t* global_image_begin_; // Offset from image_begin_ to where the first object is in image_. size_t image_objects_offset_begin_; - // The image roots address in the image. - uint32_t image_roots_address_; - // oat file with code for this image OatFile* oat_file_; - // Memory mapped for generating the image. - std::unique_ptr<MemMap> image_; - // Pointer arrays that need to be updated. Since these are only some int and long arrays, we need // to keep track. These include vtable arrays, iftable arrays, and dex caches. std::unordered_map<mirror::PointerArray*, Bin> pointer_arrays_; - // The start offsets of the dex cache arrays. - SafeMap<const DexFile*, size_t> dex_cache_array_starts_; - // Saved hash codes. We use these to restore lockwords which were temporarily used to have // forwarding addresses as well as copying over hash codes. std::unordered_map<mirror::Object*, uint32_t> saved_hashcode_map_; - // Beginning target oat address for the pointers from the output image to its oat file. - const uint8_t* oat_data_begin_; - - // Image bitmap which lets us know where the objects inside of the image reside. - std::unique_ptr<gc::accounting::ContinuousSpaceBitmap> image_bitmap_; - - // Offset from oat_data_begin_ to the stubs. - uint32_t oat_address_offsets_[kOatAddressCount]; - // Boolean flags. const bool compile_pic_; const bool compile_app_image_; @@ -422,10 +489,8 @@ class ImageWriter FINAL { // Size of pointers on the target architecture. size_t target_ptr_size_; - // Bin slot tracking for dirty object packing - size_t bin_slot_sizes_[kBinSize]; // Number of bytes in a bin - size_t bin_slot_offsets_[kBinSize]; // Number of bytes in previous bins. - size_t bin_slot_count_[kBinSize]; // Number of objects in a bin + // Mapping of oat filename to image data. + std::unordered_map<std::string, ImageInfo> image_info_map_; // Cached size of the intern table for when we allocate memory. size_t intern_table_bytes_; @@ -434,6 +499,7 @@ class ImageWriter FINAL { // have one entry per art field for convenience. ArtFields are placed right after the end of the // image objects (aka sum of bin_slot_sizes_). ArtMethods are placed right after the ArtFields. struct NativeObjectRelocation { + const char* oat_filename; uintptr_t offset; NativeObjectRelocationType type; @@ -468,6 +534,11 @@ class ImageWriter FINAL { // Which mode the image is stored as, see image.h const ImageHeader::StorageMode image_storage_mode_; + // Map of dex files to the oat filenames that they were compiled into. + const std::unordered_map<const DexFile*, const char*>& dex_file_oat_filename_map_; + const std::vector<const char*> oat_filenames_; + const char* default_oat_filename_; + friend class ContainsBootClassLoaderNonImageClassVisitor; friend class FixupClassVisitor; friend class FixupRootVisitor; diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index d001495442..b323d24038 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -84,6 +84,7 @@ JitCompiler::JitCompiler() : total_time_(0) { CompilerOptions::kDefaultNumDexMethodsThreshold, CompilerOptions::kDefaultInlineDepthLimit, CompilerOptions::kDefaultInlineMaxCodeUnits, + /* no_inline_from */ nullptr, /* include_patch_information */ false, CompilerOptions::kDefaultTopKProfileThreshold, Runtime::Current()->IsDebuggable(), @@ -154,7 +155,8 @@ JitCompiler::JitCompiler() : total_time_(0) { /* dump_cfg_append */ false, cumulative_logger_.get(), /* swap_fd */ -1, - /* profile_file */ "")); + /* profile_file */ "", + /* dex to oat map */ nullptr)); // Disable dedupe so we can remove compiled methods. compiler_driver_->SetDedupeEnabled(false); compiler_driver_->SetSupportBootImageFixup(false); diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index 92cf8ca7ff..877a674674 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -47,7 +47,7 @@ class RelativePatcherTest : public testing::Test { driver_(&compiler_options_, &verification_results_, &inliner_map_, Compiler::kQuick, instruction_set, nullptr, false, nullptr, nullptr, nullptr, 1u, - false, false, "", false, nullptr, -1, ""), + false, false, "", false, nullptr, -1, "", nullptr), error_msg_(), instruction_set_(instruction_set), features_(InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg_)), diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index cd0f0d2c6f..58f46d69a2 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -121,7 +121,8 @@ class OatTest : public CommonCompilerTest { false, timer_.get(), -1, - "")); + "", + nullptr)); } bool WriteElf(File* file, diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 53ac77b40f..025e35e178 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -716,6 +716,14 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) SHARED_REQUIRES(Locks::mutator_lock_) { + const DexFile::TypeId& type_id = + dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_); + const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id); + // Skip methods that are not in the image. + if (!writer_->GetCompilerDriver()->IsImageClass(class_descriptor)) { + return true; + } + OatClass* oat_class = &writer_->oat_classes_[oat_class_index_]; CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); @@ -958,7 +966,9 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { if (writer_->HasBootImage()) { auto* element = writer_->image_writer_->GetDexCacheArrayElementImageAddress<const uint8_t*>( patch.TargetDexCacheDexFile(), patch.TargetDexCacheElementOffset()); - const uint8_t* oat_data = writer_->image_writer_->GetOatFileBegin() + file_offset_; + const char* oat_filename = writer_->image_writer_->GetOatFilenameForDexCache(dex_cache_); + const uint8_t* oat_data = + writer_->image_writer_->GetOatFileBegin(oat_filename) + file_offset_; return element - oat_data; } else { size_t start = writer_->dex_cache_arrays_offsets_.Get(patch.TargetDexCacheDexFile()); @@ -994,9 +1004,15 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { // NOTE: We're using linker patches for app->boot references when the image can // be relocated and therefore we need to emit .oat_patches. We're not using this // for app->app references, so check that the method is an image method. - gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace(); - size_t method_offset = reinterpret_cast<const uint8_t*>(method) - image_space->Begin(); - CHECK(image_space->GetImageHeader().GetMethodsSection().Contains(method_offset)); + std::vector<gc::space::ImageSpace*> image_spaces = + Runtime::Current()->GetHeap()->GetBootImageSpaces(); + bool contains_method = false; + for (gc::space::ImageSpace* image_space : image_spaces) { + size_t method_offset = reinterpret_cast<const uint8_t*>(method) - image_space->Begin(); + contains_method |= + image_space->GetImageHeader().GetMethodsSection().Contains(method_offset); + } + CHECK(contains_method); } // Note: We only patch targeting ArtMethods in image which is in the low 4gb. uint32_t address = PointerToLowMemUInt32(method); @@ -1012,7 +1028,8 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { SHARED_REQUIRES(Locks::mutator_lock_) { uint32_t address = target_offset; if (writer_->HasBootImage()) { - address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin() + + const char* oat_filename = writer_->image_writer_->GetOatFilenameForDexCache(dex_cache_); + address = PointerToLowMemUInt32(writer_->image_writer_->GetOatFileBegin(oat_filename) + writer_->oat_data_offset_ + target_offset); } DCHECK_LE(offset + 4, code->size()); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index a4dcb3aeba..98421373e3 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -372,6 +372,18 @@ bool HInliner::TryInlinePolymorphicCall(HInvoke* invoke_instruction ATTRIBUTE_UN bool HInliner::TryInline(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) { const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); + + // Check whether we're allowed to inline. The outermost compilation unit is the relevant + // dex file here (though the transitivity of an inline chain would allow checking the calller). + if (!compiler_driver_->MayInline(method->GetDexFile(), + outer_compilation_unit_.GetDexFile())) { + VLOG(compiler) << "Won't inline " << PrettyMethod(method) << " in " + << outer_compilation_unit_.GetDexFile()->GetLocation() << " (" + << caller_compilation_unit_.GetDexFile()->GetLocation() << ") from " + << method->GetDexFile()->GetLocation(); + return false; + } + uint32_t method_index = FindMethodIndexIn( method, caller_dex_file, invoke_instruction->GetDexMethodIndex()); if (method_index == DexFile::kDexNoIndex) { diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 831b626c4f..dd1d193ca3 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -880,7 +880,11 @@ Compiler* CreateOptimizingCompiler(CompilerDriver* driver) { bool IsCompilingWithCoreImage() { const std::string& image = Runtime::Current()->GetImageLocation(); - return EndsWith(image, "core.art") || EndsWith(image, "core-optimizing.art"); + // TODO: This is under-approximating... + if (EndsWith(image, "core.art") || EndsWith(image, "core-optimizing.art")) { + return true; + } + return false; } bool OptimizingCompiler::JitCompile(Thread* self, diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 21ce73c7d4..bb4224b7c1 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -193,7 +193,7 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" corresponding to the file descriptor specified by --zip-fd."); UsageError(" Example: --zip-location=/system/app/Calculator.apk"); UsageError(""); - UsageError(" --oat-file=<file.oat>: specifies the oat output destination via a filename."); + UsageError(" --oat-file=<file.oat>: specifies an oat output destination via a filename."); UsageError(" Example: --oat-file=/system/framework/boot.oat"); UsageError(""); UsageError(" --oat-fd=<number>: specifies the oat output destination via a file descriptor."); @@ -203,10 +203,10 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" to the file descriptor specified by --oat-fd."); UsageError(" Example: --oat-location=/data/dalvik-cache/system@app@Calculator.apk.oat"); UsageError(""); - UsageError(" --oat-symbols=<file.oat>: specifies the oat output destination with full symbols."); + UsageError(" --oat-symbols=<file.oat>: specifies an oat output destination with full symbols."); UsageError(" Example: --oat-symbols=/symbols/system/framework/boot.oat"); UsageError(""); - UsageError(" --image=<file.art>: specifies the output image filename."); + UsageError(" --image=<file.art>: specifies an output image filename."); UsageError(" Example: --image=/system/framework/boot.art"); UsageError(""); UsageError(" --image-format=(uncompressed|lz4):"); @@ -355,6 +355,9 @@ NO_RETURN static void Usage(const char* fmt, ...) { UsageError(" --app-image-file=<file-name>: specify a file name for app image."); UsageError(" Example: --app-image-file=/data/dalvik-cache/system@app@Calculator.apk.art"); UsageError(""); + UsageError(" --multi-image: specify that separate oat and image files be generated for each " + "input dex file."); + UsageError(""); std::cerr << "See log for usage error information\n"; exit(EXIT_FAILURE); } @@ -536,7 +539,9 @@ class Dex2Oat FINAL { for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files_) { dex_file.release(); } - oat_file_.release(); + for (std::unique_ptr<File>& oat_file : oat_files_) { + oat_file.release(); + } runtime_.release(); verification_results_.release(); key_value_store_.release(); @@ -544,7 +549,7 @@ class Dex2Oat FINAL { } struct ParserOptions { - std::string oat_symbols; + std::vector<const char*> oat_symbols; std::string boot_image_filename; bool watch_dog_enabled = true; bool requested_specific_compiler = false; @@ -644,8 +649,8 @@ class Dex2Oat FINAL { } } - void ProcessOptions(ParserOptions* parser_options) { - boot_image_ = !image_filename_.empty(); + void ProcessOptions(ParserOptions* parser_options, bool multi_image) { + boot_image_ = !image_filenames_.empty(); app_image_ = app_image_fd_ != -1 || !app_image_file_name_.empty(); if (IsAppImage() && IsBootImage()) { @@ -657,11 +662,11 @@ class Dex2Oat FINAL { compiler_options_->debuggable_ = true; } - if (oat_filename_.empty() && oat_fd_ == -1) { + if (oat_filenames_.empty() && oat_fd_ == -1) { Usage("Output must be supplied with either --oat-file or --oat-fd"); } - if (!oat_filename_.empty() && oat_fd_ != -1) { + if (!oat_filenames_.empty() && oat_fd_ != -1) { Usage("--oat-file should not be used with --oat-fd"); } @@ -673,10 +678,19 @@ class Dex2Oat FINAL { Usage("--oat-symbols should not be used with --host"); } - if (oat_fd_ != -1 && !image_filename_.empty()) { + if (oat_fd_ != -1 && !image_filenames_.empty()) { Usage("--oat-fd should not be used with --image"); } + if (!parser_options->oat_symbols.empty() && + parser_options->oat_symbols.size() != oat_filenames_.size()) { + Usage("--oat-file arguments do not match --oat-symbols arguments"); + } + + if (!image_filenames_.empty() && image_filenames_.size() != oat_filenames_.size()) { + Usage("--oat-file arguments do not match --image arguments"); + } + if (android_root_.empty()) { const char* android_root_env_var = getenv("ANDROID_ROOT"); if (android_root_env_var == nullptr) { @@ -737,6 +751,12 @@ class Dex2Oat FINAL { Usage("--dex-location arguments do not match --dex-file arguments"); } + if (!dex_filenames_.empty() && !oat_filenames_.empty()) { + if (oat_filenames_.size() != 1 && oat_filenames_.size() != dex_filenames_.size()) { + Usage("--oat-file arguments must be singular or match --dex-file arguments"); + } + } + if (zip_fd_ != -1 && zip_location_.empty()) { Usage("--zip-location should be supplied with --zip-fd"); } @@ -747,11 +767,8 @@ class Dex2Oat FINAL { } } - oat_stripped_ = oat_filename_; if (!parser_options->oat_symbols.empty()) { - oat_unstripped_ = parser_options->oat_symbols; - } else { - oat_unstripped_ = oat_filename_; + oat_unstripped_ = std::move(parser_options->oat_symbols); } // If no instruction set feature was given, use the default one for the target @@ -816,6 +833,68 @@ class Dex2Oat FINAL { compiler_options_->verbose_methods_ = verbose_methods_.empty() ? nullptr : &verbose_methods_; + if (!IsBootImage() && multi_image) { + Usage("--multi-image can only be used when creating boot images"); + } + if (IsBootImage() && multi_image && image_filenames_.size() > 1) { + Usage("--multi-image cannot be used with multiple image names"); + } + + // For now, if we're on the host and compile the boot image, *always* use multiple image files. + if (!kIsTargetBuild && IsBootImage()) { + if (image_filenames_.size() == 1) { + multi_image = true; + } + } + + if (IsBootImage() && multi_image) { + // Expand the oat and image filenames. + std::string base_oat = oat_filenames_[0]; + size_t last_oat_slash = base_oat.rfind('/'); + if (last_oat_slash == std::string::npos) { + Usage("--multi-image used with unusable oat filename %s", base_oat.c_str()); + } + base_oat = base_oat.substr(0, last_oat_slash + 1); + + std::string base_img = image_filenames_[0]; + size_t last_img_slash = base_img.rfind('/'); + if (last_img_slash == std::string::npos) { + Usage("--multi-image used with unusable image filename %s", base_img.c_str()); + } + base_img = base_img.substr(0, last_img_slash + 1); + + // Now create the other names. + // Note: we have some special case here for our testing. We have to inject the differentiating + // parts for the different core images. + std::string infix; // Empty infix by default. + { + // Check the first name. + std::string dex_file = oat_filenames_[0]; + size_t last_dex_slash = dex_file.rfind('/'); + if (last_dex_slash != std::string::npos) { + dex_file = dex_file.substr(last_dex_slash + 1); + } + size_t last_dex_dot = dex_file.rfind('.'); + if (last_dex_dot != std::string::npos) { + dex_file = dex_file.substr(0, last_dex_dot); + } + if (StartsWith(dex_file, "core-")) { + infix = dex_file.substr(strlen("core")); + } + } + // Use a counted loop to skip the first one. + for (size_t i = 1; i < dex_locations_.size(); ++i) { + // TODO: Make everything properly std::string. + std::string image_name = CreateMultiImageName(dex_locations_[i], infix, ".art"); + char_backing_storage_.push_back(base_img + image_name); + image_filenames_.push_back((char_backing_storage_.end() - 1)->c_str()); + + std::string oat_name = CreateMultiImageName(dex_locations_[i], infix, ".oat"); + char_backing_storage_.push_back(base_oat + oat_name); + oat_filenames_.push_back((char_backing_storage_.end() - 1)->c_str()); + } + } + // Done with usage checks, enable watchdog if requested if (parser_options->watch_dog_enabled) { watchdog_.reset(new WatchDog(true)); @@ -825,6 +904,26 @@ class Dex2Oat FINAL { key_value_store_.reset(new SafeMap<std::string, std::string>()); } + static std::string CreateMultiImageName(std::string in, + const std::string& infix, + const char* suffix) { + size_t last_dex_slash = in.rfind('/'); + if (last_dex_slash != std::string::npos) { + in = in.substr(last_dex_slash + 1); + } + if (!infix.empty()) { + // Inject infix. + size_t last_dot = in.rfind('.'); + if (last_dot != std::string::npos) { + in.insert(last_dot, infix); + } + } + if (EndsWith(in, ".jar")) { + in = in.substr(0, in.length() - strlen(".jar")) + (suffix != nullptr ? suffix : ""); + } + return in; + } + void InsertCompileOptions(int argc, char** argv) { std::ostringstream oss; for (int i = 0; i < argc; ++i) { @@ -865,6 +964,8 @@ class Dex2Oat FINAL { std::unique_ptr<ParserOptions> parser_options(new ParserOptions()); compiler_options_.reset(new CompilerOptions()); + bool multi_image = false; + for (int i = 0; i < argc; i++) { const StringPiece option(argv[i]); const bool log_options = false; @@ -880,9 +981,9 @@ class Dex2Oat FINAL { } else if (option.starts_with("--zip-location=")) { zip_location_ = option.substr(strlen("--zip-location=")).data(); } else if (option.starts_with("--oat-file=")) { - oat_filename_ = option.substr(strlen("--oat-file=")).data(); + oat_filenames_.push_back(option.substr(strlen("--oat-file=")).data()); } else if (option.starts_with("--oat-symbols=")) { - parser_options->oat_symbols = option.substr(strlen("--oat-symbols=")).data(); + parser_options->oat_symbols.push_back(option.substr(strlen("--oat-symbols=")).data()); } else if (option.starts_with("--oat-fd=")) { ParseOatFd(option); } else if (option == "--watch-dog") { @@ -894,7 +995,7 @@ class Dex2Oat FINAL { } else if (option.starts_with("--oat-location=")) { oat_location_ = option.substr(strlen("--oat-location=")).data(); } else if (option.starts_with("--image=")) { - image_filename_ = option.substr(strlen("--image=")).data(); + image_filenames_.push_back(option.substr(strlen("--image=")).data()); } else if (option.starts_with("--image-classes=")) { image_classes_filename_ = option.substr(strlen("--image-classes=")).data(); } else if (option.starts_with("--image-classes-zip=")) { @@ -961,41 +1062,56 @@ class Dex2Oat FINAL { // conditional on having verbost methods. gLogVerbosity.compiler = false; Split(option.substr(strlen("--verbose-methods=")).ToString(), ',', &verbose_methods_); + } else if (option == "--multi-image") { + multi_image = true; + } else if (option.starts_with("--no-inline-from=")) { + no_inline_from_string_ = option.substr(strlen("--no-inline-from=")).data(); } else if (!compiler_options_->ParseCompilerOption(option, Usage)) { Usage("Unknown argument %s", option.data()); } } - ProcessOptions(parser_options.get()); + ProcessOptions(parser_options.get(), multi_image); // Insert some compiler things. InsertCompileOptions(argc, argv); } - // Check whether the oat output file is writable, and open it for later. Also open a swap file, - // if a name is given. + // Check whether the oat output files are writable, and open them for later. Also open a swap + // file, if a name is given. bool OpenFile() { - bool create_file = !oat_unstripped_.empty(); // as opposed to using open file descriptor + bool create_file = oat_fd_ == -1; // as opposed to using open file descriptor if (create_file) { - oat_file_.reset(OS::CreateEmptyFile(oat_unstripped_.c_str())); - if (oat_location_.empty()) { - oat_location_ = oat_filename_; + for (const char* oat_filename : oat_filenames_) { + std::unique_ptr<File> oat_file(OS::CreateEmptyFile(oat_filename)); + if (oat_file.get() == nullptr) { + PLOG(ERROR) << "Failed to create oat file: " << oat_filename; + return false; + } + if (create_file && fchmod(oat_file->Fd(), 0644) != 0) { + PLOG(ERROR) << "Failed to make oat file world readable: " << oat_filename; + oat_file->Erase(); + return false; + } + oat_files_.push_back(std::move(oat_file)); } } else { - oat_file_.reset(new File(oat_fd_, oat_location_, true)); - oat_file_->DisableAutoClose(); - if (oat_file_->SetLength(0) != 0) { + std::unique_ptr<File> oat_file(new File(oat_fd_, oat_location_, true)); + oat_file->DisableAutoClose(); + if (oat_file->SetLength(0) != 0) { PLOG(WARNING) << "Truncating oat file " << oat_location_ << " failed."; } - } - if (oat_file_.get() == nullptr) { - PLOG(ERROR) << "Failed to create oat file: " << oat_location_; - return false; - } - if (create_file && fchmod(oat_file_->Fd(), 0644) != 0) { - PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location_; - oat_file_->Erase(); - return false; + if (oat_file.get() == nullptr) { + PLOG(ERROR) << "Failed to create oat file: " << oat_location_; + return false; + } + if (create_file && fchmod(oat_file->Fd(), 0644) != 0) { + PLOG(ERROR) << "Failed to make oat file world readable: " << oat_location_; + oat_file->Erase(); + return false; + } + oat_filenames_.push_back(oat_location_.c_str()); + oat_files_.push_back(std::move(oat_file)); } // Swap file handling. @@ -1020,10 +1136,12 @@ class Dex2Oat FINAL { return true; } - void EraseOatFile() { - DCHECK(oat_file_.get() != nullptr); - oat_file_->Erase(); - oat_file_.reset(); + void EraseOatFiles() { + for (size_t i = 0; i < oat_files_.size(); ++i) { + DCHECK(oat_files_[i].get() != nullptr); + oat_files_[i]->Erase(); + oat_files_[i].reset(); + } } void Shutdown() { @@ -1158,9 +1276,35 @@ class Dex2Oat FINAL { } } + // Organize inputs, handling multi-dex and multiple oat file outputs. + CreateDexOatMappings(); + return true; } + void CreateDexOatMappings() { + if (oat_files_.size() > 1) { + size_t index = 0; + for (size_t i = 0; i < oat_files_.size(); ++i) { + std::vector<const DexFile*> dex_files = { 1, dex_files_[index] }; + dex_file_oat_filename_map_.emplace(dex_files_[index], oat_filenames_[i]); + index++; + while (index < dex_files_.size() && + (dex_files_[index]->GetBaseLocation() == dex_files_[index - 1]->GetBaseLocation())) { + dex_file_oat_filename_map_.emplace(dex_files_[index], oat_filenames_[i]); + dex_files.push_back(dex_files_[index]); + index++; + } + dex_files_per_oat_file_.push_back(std::move(dex_files)); + } + } else { + dex_files_per_oat_file_.push_back(dex_files_); + for (const DexFile* dex_file : dex_files_) { + dex_file_oat_filename_map_.emplace(dex_file, oat_filenames_[0]); + } + } + } + // Create and invoke the compiler driver. This will compile all the dex files. void Compile() { TimingLogger::ScopedTiming t("dex2oat Compile", timings_); @@ -1191,6 +1335,85 @@ class Dex2Oat FINAL { class_loader = class_linker->CreatePathClassLoader(self, dex_files_, class_path_class_loader); } + // Find the dex file we should not inline from. + + // For now, on the host always have core-oj removed. + if (!kIsTargetBuild && no_inline_from_string_.empty()) { + no_inline_from_string_ = "core-oj"; + } + + if (!no_inline_from_string_.empty()) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_); + std::vector<const std::vector<const DexFile*>*> dex_file_vectors = { + &class_linker->GetBootClassPath(), + &class_path_files, + &dex_files_ + }; + for (const std::vector<const DexFile*>* dex_file_vector : dex_file_vectors) { + if (dex_file_vector == nullptr) { + continue; + } + + bool found = false; + + for (const DexFile* dex_file : *dex_file_vector) { + // Try the complete location first. + found = no_inline_from_string_ == dex_file->GetLocation(); + // The try just the name. + if (!found) { + size_t last_slash = dex_file->GetLocation().rfind('/'); + if (last_slash != std::string::npos) { + found = StartsWith(dex_file->GetLocation().substr(last_slash + 1), + no_inline_from_string_.c_str()); + } + } + + if (found) { + VLOG(compiler) << "Disabling inlining from " << dex_file->GetLocation(); + compiler_options_->no_inline_from_ = dex_file; + break; + } + } + + if (found) { + break; + } + } + } + + if (IsBootImage() && image_filenames_.size() > 1) { + // If we're compiling the boot image, store the boot classpath into the Key-Value store. If + // the image filename was adapted (e.g., for our tests), we need to change this here, too. We + // need this for the multi-image case. + std::ostringstream bootcp_oss; + bool first_bootcp = true; + for (size_t i = 0; i < dex_locations_.size(); ++i) { + if (!first_bootcp) { + bootcp_oss << ":"; + } + + std::string dex_loc = dex_locations_[i]; + std::string image_filename = image_filenames_[i]; + + // Use the dex_loc path, but the image_filename name. + size_t dex_last_slash = dex_loc.rfind('/'); + size_t image_last_slash = image_filename.rfind('/'); + if (dex_last_slash == std::string::npos) { + dex_loc = image_filename.substr(image_last_slash + 1); + } else { + dex_loc = dex_loc.substr(0, dex_last_slash + 1) + + image_filename.substr(image_last_slash + 1); + } + + // Image filenames already end with .art, no need to replace. + + bootcp_oss << dex_loc; + first_bootcp = false; + } + key_value_store_->Put(OatHeader::kBootClassPath, bootcp_oss.str()); + } + driver_.reset(new CompilerDriver(compiler_options_.get(), verification_results_.get(), &method_inliner_map_, @@ -1208,12 +1431,14 @@ class Dex2Oat FINAL { dump_cfg_append_, compiler_phases_timings_.get(), swap_fd_, - profile_file_)); + profile_file_, + &dex_file_oat_filename_map_)); driver_->SetDexFilesForOatFile(dex_files_); driver_->CompileAll(class_loader, dex_files_, timings_); } + // TODO: Update comments about how this works for multi image. b/26317072 // Notes on the interleaving of creating the image and oat file to // ensure the references between the two are correct. // @@ -1275,17 +1500,16 @@ class Dex2Oat FINAL { // Steps 1.-3. are done by the CreateOatFile() above, steps 4.-5. // are done by the CreateImageFile() below. - // Write out the generated code part. Calls the OatWriter and ElfBuilder. Also prepares the // ImageWriter, if necessary. // Note: Flushing (and closing) the file is the caller's responsibility, except for the failure // case (when the file will be explicitly erased). - bool CreateOatFile() { + bool CreateOatFiles() { CHECK(key_value_store_.get() != nullptr); TimingLogger::ScopedTiming t("dex2oat Oat", timings_); - std::unique_ptr<OatWriter> oat_writer; + std::vector<std::unique_ptr<OatWriter>> oat_writers; { TimingLogger::ScopedTiming t2("dex2oat OatWriter", timings_); std::string image_file_location; @@ -1294,10 +1518,14 @@ class Dex2Oat FINAL { int32_t image_patch_delta = 0; if (app_image_ && image_base_ == 0) { - gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace(); - image_base_ = RoundUp( - reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatFileEnd()), - kPageSize); + std::vector<gc::space::ImageSpace*> image_spaces = + Runtime::Current()->GetHeap()->GetBootImageSpaces(); + for (gc::space::ImageSpace* image_space : image_spaces) { + // TODO: IS THIS IN ORDER? JUST TAKE THE LAST ONE? + image_base_ = std::max(image_base_, RoundUp( + reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatFileEnd()), + kPageSize)); + } VLOG(compiler) << "App image base=" << reinterpret_cast<void*>(image_base_); } @@ -1307,27 +1535,36 @@ class Dex2Oat FINAL { if (!IsBootImage()) { TimingLogger::ScopedTiming t3("Loading image checksum", timings_); - gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace(); - image_file_location_oat_checksum = image_space->GetImageHeader().GetOatChecksum(); + std::vector<gc::space::ImageSpace*> image_spaces = + Runtime::Current()->GetHeap()->GetBootImageSpaces(); + image_file_location_oat_checksum = image_spaces[0]->GetImageHeader().GetOatChecksum(); image_file_location_oat_data_begin = - reinterpret_cast<uintptr_t>(image_space->GetImageHeader().GetOatDataBegin()); - image_file_location = image_space->GetImageFilename(); - image_patch_delta = image_space->GetImageHeader().GetPatchDelta(); + reinterpret_cast<uintptr_t>(image_spaces[0]->GetImageHeader().GetOatDataBegin()); + image_patch_delta = image_spaces[0]->GetImageHeader().GetPatchDelta(); + std::vector<std::string> image_filenames; + for (const gc::space::ImageSpace* image_space : image_spaces) { + image_filenames.push_back(image_space->GetImageFilename()); + } + image_file_location = Join(image_filenames, ':'); } if (!image_file_location.empty()) { key_value_store_->Put(OatHeader::kImageLocationKey, image_file_location); } - oat_writer.reset(new OatWriter(dex_files_, - image_file_location_oat_checksum, - image_file_location_oat_data_begin, - image_patch_delta, - driver_.get(), - image_writer_.get(), - IsBootImage(), - timings_, - key_value_store_.get())); + for (size_t i = 0; i < oat_files_.size(); ++i) { + std::vector<const DexFile*>& dex_files = dex_files_per_oat_file_[i]; + std::unique_ptr<OatWriter> oat_writer(new OatWriter(dex_files, + image_file_location_oat_checksum, + image_file_location_oat_data_begin, + image_patch_delta, + driver_.get(), + image_writer_.get(), + IsBootImage(), + timings_, + key_value_store_.get())); + oat_writers.push_back(std::move(oat_writer)); + } } if (IsImage()) { @@ -1342,37 +1579,56 @@ class Dex2Oat FINAL { { TimingLogger::ScopedTiming t2("dex2oat Write ELF", timings_); - std::unique_ptr<ElfWriter> elf_writer = - CreateElfWriterQuick(instruction_set_, compiler_options_.get(), oat_file_.get()); + for (size_t i = 0; i < oat_files_.size(); ++i) { + std::unique_ptr<File>& oat_file = oat_files_[i]; + std::unique_ptr<OatWriter>& oat_writer = oat_writers[i]; + std::unique_ptr<ElfWriter> elf_writer = + CreateElfWriterQuick(instruction_set_, compiler_options_.get(), oat_file.get()); - elf_writer->Start(); + elf_writer->Start(); - OutputStream* rodata = elf_writer->StartRoData(); - if (!oat_writer->WriteRodata(rodata)) { - LOG(ERROR) << "Failed to write .rodata section to the ELF file " << oat_file_->GetPath(); - return false; - } - elf_writer->EndRoData(rodata); + OutputStream* rodata = elf_writer->StartRoData(); + if (!oat_writer->WriteRodata(rodata)) { + LOG(ERROR) << "Failed to write .rodata section to the ELF file " << oat_file->GetPath(); + return false; + } + elf_writer->EndRoData(rodata); - OutputStream* text = elf_writer->StartText(); - if (!oat_writer->WriteCode(text)) { - LOG(ERROR) << "Failed to write .text section to the ELF file " << oat_file_->GetPath(); - return false; - } - elf_writer->EndText(text); + OutputStream* text = elf_writer->StartText(); + if (!oat_writer->WriteCode(text)) { + LOG(ERROR) << "Failed to write .text section to the ELF file " << oat_file->GetPath(); + return false; + } + elf_writer->EndText(text); - elf_writer->SetBssSize(oat_writer->GetBssSize()); - elf_writer->WriteDynamicSection(); - elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo()); - elf_writer->WritePatchLocations(oat_writer->GetAbsolutePatchLocations()); + elf_writer->SetBssSize(oat_writer->GetBssSize()); + elf_writer->WriteDynamicSection(); + elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo()); + elf_writer->WritePatchLocations(oat_writer->GetAbsolutePatchLocations()); - if (!elf_writer->End()) { - LOG(ERROR) << "Failed to write ELF file " << oat_file_->GetPath(); - return false; + if (!elf_writer->End()) { + LOG(ERROR) << "Failed to write ELF file " << oat_file->GetPath(); + return false; + } + + // Flush the oat file. + if (oat_files_[i] != nullptr) { + if (oat_files_[i]->Flush() != 0) { + PLOG(ERROR) << "Failed to flush oat file: " << oat_filenames_[i]; + oat_files_[i]->Erase(); + return false; + } + } + + if (IsImage()) { + // Update oat estimates. + UpdateImageWriter(i); + } + + VLOG(compiler) << "Oat file written successfully: " << oat_filenames_[i]; } } - VLOG(compiler) << "Oat file written successfully (unstripped): " << oat_location_; return true; } @@ -1383,70 +1639,80 @@ class Dex2Oat FINAL { if (!CreateImageFile()) { return false; } - VLOG(compiler) << "Image written successfully: " << image_filename_; + VLOG(compiler) << "Images written successfully"; } return true; } - // Create a copy from unstripped to stripped. - bool CopyUnstrippedToStripped() { - // If we don't want to strip in place, copy from unstripped location to stripped location. - // We need to strip after image creation because FixupElf needs to use .strtab. - if (oat_unstripped_ != oat_stripped_) { - // If the oat file is still open, flush it. - if (oat_file_.get() != nullptr && oat_file_->IsOpened()) { - if (!FlushCloseOatFile()) { - return false; + // Create a copy from stripped to unstripped. + bool CopyStrippedToUnstripped() { + for (size_t i = 0; i < oat_unstripped_.size(); ++i) { + // If we don't want to strip in place, copy from stripped location to unstripped location. + // We need to strip after image creation because FixupElf needs to use .strtab. + if (strcmp(oat_unstripped_[i], oat_filenames_[i]) != 0) { + // If the oat file is still open, flush it. + if (oat_files_[i].get() != nullptr && oat_files_[i]->IsOpened()) { + if (!FlushCloseOatFile(i)) { + return false; + } } - } - TimingLogger::ScopedTiming t("dex2oat OatFile copy", timings_); - std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped_.c_str())); - std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped_.c_str())); - size_t buffer_size = 8192; - std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); - while (true) { - int bytes_read = TEMP_FAILURE_RETRY(read(in->Fd(), buffer.get(), buffer_size)); - if (bytes_read <= 0) { - break; + TimingLogger::ScopedTiming t("dex2oat OatFile copy", timings_); + std::unique_ptr<File> in(OS::OpenFileForReading(oat_filenames_[i])); + std::unique_ptr<File> out(OS::CreateEmptyFile(oat_unstripped_[i])); + size_t buffer_size = 8192; + std::unique_ptr<uint8_t[]> buffer(new uint8_t[buffer_size]); + while (true) { + int bytes_read = TEMP_FAILURE_RETRY(read(in->Fd(), buffer.get(), buffer_size)); + if (bytes_read <= 0) { + break; + } + bool write_ok = out->WriteFully(buffer.get(), bytes_read); + CHECK(write_ok); } - bool write_ok = out->WriteFully(buffer.get(), bytes_read); - CHECK(write_ok); - } - if (out->FlushCloseOrErase() != 0) { - PLOG(ERROR) << "Failed to flush and close copied oat file: " << oat_stripped_; - return false; + if (out->FlushCloseOrErase() != 0) { + PLOG(ERROR) << "Failed to flush and close copied oat file: " << oat_unstripped_[i]; + return false; + } + VLOG(compiler) << "Oat file copied successfully (unstripped): " << oat_unstripped_[i]; } - VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped_; } return true; } - bool FlushOatFile() { - if (oat_file_.get() != nullptr) { - TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_); - if (oat_file_->Flush() != 0) { - PLOG(ERROR) << "Failed to flush oat file: " << oat_location_ << " / " - << oat_filename_; - oat_file_->Erase(); - return false; + bool FlushOatFiles() { + TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_); + for (size_t i = 0; i < oat_files_.size(); ++i) { + if (oat_files_[i].get() != nullptr) { + if (oat_files_[i]->Flush() != 0) { + PLOG(ERROR) << "Failed to flush oat file: " << oat_filenames_[i]; + oat_files_[i]->Erase(); + return false; + } } } return true; } - bool FlushCloseOatFile() { - if (oat_file_.get() != nullptr) { - std::unique_ptr<File> tmp(oat_file_.release()); + bool FlushCloseOatFile(size_t i) { + if (oat_files_[i].get() != nullptr) { + std::unique_ptr<File> tmp(oat_files_[i].release()); if (tmp->FlushCloseOrErase() != 0) { - PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location_ << " / " - << oat_filename_; + PLOG(ERROR) << "Failed to flush and close oat file: " << oat_filenames_[i]; return false; } } return true; } + bool FlushCloseOatFiles() { + bool result = true; + for (size_t i = 0; i < oat_files_.size(); ++i) { + result &= FlushCloseOatFile(i); + } + return result; + } + void DumpTiming() { if (dump_timing_ || (dump_slow_timing_ && timings_->GetTotalNs() > MsToNs(1000))) { LOG(INFO) << Dumpable<TimingLogger>(*timings_); @@ -1707,42 +1973,52 @@ class Dex2Oat FINAL { image_base, compiler_options_->GetCompilePic(), IsAppImage(), - image_storage_mode_)); + image_storage_mode_, + oat_filenames_, + dex_file_oat_filename_map_)); } - // Let the ImageWriter write the image file. If we do not compile PIC, also fix up the oat file. + // Let the ImageWriter write the image files. If we do not compile PIC, also fix up the oat files. bool CreateImageFile() REQUIRES(!Locks::mutator_lock_) { CHECK(image_writer_ != nullptr); - if (!image_writer_->Write(app_image_fd_, - IsBootImage() ? image_filename_ : app_image_file_name_, - oat_unstripped_, - oat_location_)) { - LOG(ERROR) << "Failed to create image file " << image_filename_; + if (!IsBootImage()) { + image_filenames_.push_back(app_image_file_name_.c_str()); + } + if (!image_writer_->Write(app_image_fd_, image_filenames_, oat_filenames_)) { + LOG(ERROR) << "Failure during image file creation"; return false; } - uintptr_t oat_data_begin = image_writer_->GetOatDataBegin(); + // We need the OatDataBegin entries. + std::map<const char*, uintptr_t> oat_data_begins; + for (const char* oat_filename : oat_filenames_) { + oat_data_begins.emplace(oat_filename, image_writer_->GetOatDataBegin(oat_filename)); + } // Destroy ImageWriter before doing FixupElf. image_writer_.reset(); - // Do not fix up the ELF file if we are --compile-pic or compiing the app image - if (!compiler_options_->GetCompilePic() && IsBootImage()) { - std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_unstripped_.c_str())); - if (oat_file.get() == nullptr) { - PLOG(ERROR) << "Failed to open ELF file: " << oat_unstripped_; - return false; - } + for (const char* oat_filename : oat_filenames_) { + // Do not fix up the ELF file if we are --compile-pic or compiling the app image + if (!compiler_options_->GetCompilePic() && IsBootImage()) { + std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename)); + if (oat_file.get() == nullptr) { + PLOG(ERROR) << "Failed to open ELF file: " << oat_filename; + return false; + } - if (!ElfWriter::Fixup(oat_file.get(), oat_data_begin)) { - oat_file->Erase(); - LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath(); - return false; - } + uintptr_t oat_data_begin = oat_data_begins.find(oat_filename)->second; - if (oat_file->FlushCloseOrErase()) { - PLOG(ERROR) << "Failed to flush and close fixed ELF file " << oat_file->GetPath(); - return false; + if (!ElfWriter::Fixup(oat_file.get(), oat_data_begin)) { + oat_file->Erase(); + LOG(ERROR) << "Failed to fixup ELF file " << oat_file->GetPath(); + return false; + } + + if (oat_file->FlushCloseOrErase()) { + PLOG(ERROR) << "Failed to flush and close fixed ELF file " << oat_file->GetPath(); + return false; + } } } @@ -1845,6 +2121,33 @@ class Dex2Oat FINAL { ""); } + std::string StripIsaFrom(const char* image_filename, InstructionSet isa) { + std::string res(image_filename); + size_t last_slash = res.rfind('/'); + if (last_slash == std::string::npos || last_slash == 0) { + return res; + } + size_t penultimate_slash = res.rfind('/', last_slash - 1); + if (penultimate_slash == std::string::npos) { + return res; + } + // Check that the string in-between is the expected one. + if (res.substr(penultimate_slash + 1, last_slash - penultimate_slash - 1) != + GetInstructionSetString(isa)) { + LOG(WARNING) << "Unexpected string when trying to strip isa: " << res; + return res; + } + return res.substr(0, penultimate_slash) + res.substr(last_slash); + } + + // Update the estimate for the oat file with the given index. + void UpdateImageWriter(size_t index) { + DCHECK(image_writer_ != nullptr); + DCHECK_LT(index, oat_filenames_.size()); + + image_writer_->UpdateOatFile(oat_filenames_[index]); + } + std::unique_ptr<CompilerOptions> compiler_options_; Compiler::Kind compiler_kind_; @@ -1866,11 +2169,10 @@ class Dex2Oat FINAL { size_t thread_count_; uint64_t start_ns_; std::unique_ptr<WatchDog> watchdog_; - std::unique_ptr<File> oat_file_; - std::string oat_stripped_; - std::string oat_unstripped_; + std::vector<std::unique_ptr<File>> oat_files_; std::string oat_location_; - std::string oat_filename_; + std::vector<const char*> oat_filenames_; + std::vector<const char*> oat_unstripped_; int oat_fd_; std::vector<const char*> dex_filenames_; std::vector<const char*> dex_locations_; @@ -1878,7 +2180,7 @@ class Dex2Oat FINAL { std::string zip_location_; std::string boot_image_filename_; std::vector<const char*> runtime_args_; - std::string image_filename_; + std::vector<const char*> image_filenames_; uintptr_t image_base_; const char* image_classes_zip_filename_; const char* image_classes_filename_; @@ -1895,6 +2197,7 @@ class Dex2Oat FINAL { bool is_host_; std::string android_root_; std::vector<const DexFile*> dex_files_; + std::string no_inline_from_string_; std::vector<jobject> dex_caches_; std::vector<std::unique_ptr<const DexFile>> opened_dex_files_; @@ -1915,6 +2218,11 @@ class Dex2Oat FINAL { std::string profile_file_; // Profile file to use TimingLogger* timings_; std::unique_ptr<CumulativeLogger> compiler_phases_timings_; + std::vector<std::vector<const DexFile*>> dex_files_per_oat_file_; + std::unordered_map<const DexFile*, const char*> dex_file_oat_filename_map_; + + // Backing storage. + std::vector<std::string> char_backing_storage_; DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat); }; @@ -1942,19 +2250,18 @@ static void b13564922() { static int CompileImage(Dex2Oat& dex2oat) { dex2oat.Compile(); - // Create the boot.oat. - if (!dex2oat.CreateOatFile()) { - dex2oat.EraseOatFile(); + if (!dex2oat.CreateOatFiles()) { + dex2oat.EraseOatFiles(); return EXIT_FAILURE; } - // Flush and close the boot.oat. We always expect the output file by name, and it will be - // re-opened from the unstripped name. - if (!dex2oat.FlushCloseOatFile()) { + // Close the image oat files. We always expect the output file by name, and it will be + // re-opened from the unstripped name. Note: it's easier to *flush* and close... + if (!dex2oat.FlushCloseOatFiles()) { return EXIT_FAILURE; } - // Creates the boot.art and patches the boot.oat. + // Creates the boot.art and patches the oat files. if (!dex2oat.HandleImage()) { return EXIT_FAILURE; } @@ -1965,13 +2272,13 @@ static int CompileImage(Dex2Oat& dex2oat) { return EXIT_SUCCESS; } - // Copy unstripped to stripped location, if necessary. - if (!dex2oat.CopyUnstrippedToStripped()) { + // Copy stripped to unstripped location, if necessary. + if (!dex2oat.CopyStrippedToUnstripped()) { return EXIT_FAILURE; } - // FlushClose again, as stripping might have re-opened the oat file. - if (!dex2oat.FlushCloseOatFile()) { + // FlushClose again, as stripping might have re-opened the oat files. + if (!dex2oat.FlushCloseOatFiles()) { return EXIT_FAILURE; } @@ -1982,21 +2289,17 @@ static int CompileImage(Dex2Oat& dex2oat) { static int CompileApp(Dex2Oat& dex2oat) { dex2oat.Compile(); - // Create the app oat. - if (!dex2oat.CreateOatFile()) { - dex2oat.EraseOatFile(); + if (!dex2oat.CreateOatFiles()) { + dex2oat.EraseOatFiles(); return EXIT_FAILURE; } - // Do not close the oat file here. We might haven gotten the output file by file descriptor, + // Do not close the oat files here. We might have gotten the output file by file descriptor, // which we would lose. - if (!dex2oat.FlushOatFile()) { - return EXIT_FAILURE; - } // When given --host, finish early without stripping. if (dex2oat.IsHost()) { - if (!dex2oat.FlushCloseOatFile()) { + if (!dex2oat.FlushCloseOatFiles()) { return EXIT_FAILURE; } @@ -2004,14 +2307,14 @@ static int CompileApp(Dex2Oat& dex2oat) { return EXIT_SUCCESS; } - // Copy unstripped to stripped location, if necessary. This will implicitly flush & close the - // unstripped version. If this is given, we expect to be able to open writable files by name. - if (!dex2oat.CopyUnstrippedToStripped()) { + // Copy stripped to unstripped location, if necessary. This will implicitly flush & close the + // stripped versions. If this is given, we expect to be able to open writable files by name. + if (!dex2oat.CopyStrippedToUnstripped()) { return EXIT_FAILURE; } - // Flush and close the file. - if (!dex2oat.FlushCloseOatFile()) { + // Flush and close the files. + if (!dex2oat.FlushCloseOatFiles()) { return EXIT_FAILURE; } @@ -2047,7 +2350,7 @@ static int dex2oat(int argc, char** argv) { } if (!dex2oat.Setup()) { - dex2oat.EraseOatFile(); + dex2oat.EraseOatFiles(); return EXIT_FAILURE; } diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc index 5e710530b9..b8a72afb68 100644 --- a/imgdiag/imgdiag.cc +++ b/imgdiag/imgdiag.cc @@ -814,9 +814,9 @@ class ImgDiagDumper { static const ImageHeader& GetBootImageHeader() { gc::Heap* heap = Runtime::Current()->GetHeap(); - gc::space::ImageSpace* image_space = heap->GetBootImageSpace(); - CHECK(image_space != nullptr); - const ImageHeader& image_header = image_space->GetImageHeader(); + std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces(); + CHECK(!image_spaces.empty()); + const ImageHeader& image_header = image_spaces[0]->GetImageHeader(); return image_header; } @@ -834,22 +834,25 @@ class ImgDiagDumper { DISALLOW_COPY_AND_ASSIGN(ImgDiagDumper); }; -static int DumpImage(Runtime* runtime, const char* image_location, - std::ostream* os, pid_t image_diff_pid) { +static int DumpImage(Runtime* runtime, std::ostream* os, pid_t image_diff_pid) { ScopedObjectAccess soa(Thread::Current()); gc::Heap* heap = runtime->GetHeap(); - gc::space::ImageSpace* image_space = heap->GetBootImageSpace(); - CHECK(image_space != nullptr); - const ImageHeader& image_header = image_space->GetImageHeader(); - if (!image_header.IsValid()) { - fprintf(stderr, "Invalid image header %s\n", image_location); - return EXIT_FAILURE; - } - - ImgDiagDumper img_diag_dumper(os, image_header, image_location, image_diff_pid); + std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces(); + CHECK(!image_spaces.empty()); + for (gc::space::ImageSpace* image_space : image_spaces) { + const ImageHeader& image_header = image_space->GetImageHeader(); + if (!image_header.IsValid()) { + fprintf(stderr, "Invalid image header %s\n", image_space->GetImageLocation().c_str()); + return EXIT_FAILURE; + } - bool success = img_diag_dumper.Dump(); - return (success) ? EXIT_SUCCESS : EXIT_FAILURE; + ImgDiagDumper img_diag_dumper( + os, image_header, image_space->GetImageLocation().c_str(), image_diff_pid); + if (!img_diag_dumper.Dump()) { + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; } struct ImgDiagArgs : public CmdlineArgs { @@ -935,7 +938,6 @@ struct ImgDiagMain : public CmdlineMain<ImgDiagArgs> { CHECK(args_ != nullptr); return DumpImage(runtime, - args_->boot_image_location_, args_->os_, args_->image_diff_pid_) == EXIT_SUCCESS; } diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc index a926ca53da..dc101e50b7 100644 --- a/imgdiag/imgdiag_test.cc +++ b/imgdiag/imgdiag_test.cc @@ -47,9 +47,10 @@ class ImgDiagTest : public CommonRuntimeTest { CommonRuntimeTest::SetUp(); // We loaded the runtime with an explicit image. Therefore the image space must exist. - gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace(); - ASSERT_TRUE(image_space != nullptr); - boot_image_location_ = image_space->GetImageLocation(); + std::vector<gc::space::ImageSpace*> image_spaces = + Runtime::Current()->GetHeap()->GetBootImageSpaces(); + ASSERT_TRUE(!image_spaces.empty()); + boot_image_location_ = image_spaces[0]->GetImageLocation(); } virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { diff --git a/oatdump/Android.mk b/oatdump/Android.mk index a3ef38db85..5c75f20a82 100644 --- a/oatdump/Android.mk +++ b/oatdump/Android.mk @@ -74,14 +74,14 @@ endif .PHONY: dump-oat-boot-$(TARGET_ARCH) ifeq ($(ART_BUILD_TARGET_NDEBUG),true) dump-oat-boot-$(TARGET_ARCH): $(DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) $(OATDUMP) - $(OATDUMP) --image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION) \ + $(OATDUMP) $(addprefix --image=,$(DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)) \ --output=$(ART_DUMP_OAT_PATH)/boot.$(TARGET_ARCH).oatdump.txt --instruction-set=$(TARGET_ARCH) @echo Output in $(ART_DUMP_OAT_PATH)/boot.$(TARGET_ARCH).oatdump.txt endif ifdef TARGET_2ND_ARCH dump-oat-boot-$(TARGET_2ND_ARCH): $(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME) $(OATDUMP) - $(OATDUMP) --image=$(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION) \ + $(OATDUMP) $(addprefix --image=,$(2ND_DEFAULT_DEX_PREOPT_BUILT_IMAGE_LOCATION)) \ --output=$(ART_DUMP_OAT_PATH)/boot.$(TARGET_2ND_ARCH).oatdump.txt --instruction-set=$(TARGET_2ND_ARCH) @echo Output in $(ART_DUMP_OAT_PATH)/boot.$(TARGET_2ND_ARCH).oatdump.txt endif diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index bad928e9e8..52c65247ca 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1492,6 +1492,8 @@ class ImageDumper { os << "MAGIC: " << image_header_.GetMagic() << "\n\n"; + os << "IMAGE LOCATION: " << image_space_.GetImageLocation() << "\n\n"; + os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n"; os << "IMAGE SIZE: " << image_header_.GetImageSize() << "\n\n"; @@ -1599,9 +1601,8 @@ class ImageDumper { os << "OBJECTS:\n" << std::flush; - // Loop through all the image spaces and dump their objects. + // Loop through the image space and dump its objects. gc::Heap* heap = runtime->GetHeap(); - const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces(); Thread* self = Thread::Current(); { { @@ -1629,21 +1630,16 @@ class ImageDumper { } } ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); - for (const auto& space : spaces) { - if (space->IsImageSpace()) { - auto* image_space = space->AsImageSpace(); - // Dump the normal objects before ArtMethods. - image_space->GetLiveBitmap()->Walk(ImageDumper::Callback, this); - indent_os << "\n"; - // TODO: Dump fields. - // Dump methods after. - const auto& methods_section = image_header_.GetMethodsSection(); - const size_t pointer_size = - InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet()); - DumpArtMethodVisitor visitor(this); - methods_section.VisitPackedArtMethods(&visitor, image_space->Begin(), pointer_size); - } - } + // Dump the normal objects before ArtMethods. + image_space_.GetLiveBitmap()->Walk(ImageDumper::Callback, this); + indent_os << "\n"; + // TODO: Dump fields. + // Dump methods after. + const auto& methods_section = image_header_.GetMethodsSection(); + const size_t pointer_size = + InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet()); + DumpArtMethodVisitor visitor(this); + methods_section.VisitPackedArtMethods(&visitor, image_space_.Begin(), pointer_size); // Dump the large objects separately. heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(ImageDumper::Callback, this); indent_os << "\n"; @@ -2163,6 +2159,9 @@ class ImageDumper { size_t sum_of_expansion = 0; size_t sum_of_expansion_squared = 0; size_t n = method_outlier_size.size(); + if (n == 0) { + return; + } for (size_t i = 0; i < n; i++) { size_t cur_size = method_outlier_size[i]; sum_of_sizes += cur_size; @@ -2377,26 +2376,28 @@ class ImageDumper { DISALLOW_COPY_AND_ASSIGN(ImageDumper); }; -static int DumpImage(Runtime* runtime, const char* image_location, OatDumperOptions* options, - std::ostream* os) { +static int DumpImage(Runtime* runtime, OatDumperOptions* options, std::ostream* os) { // Dumping the image, no explicit class loader. ScopedNullHandle<mirror::ClassLoader> null_class_loader; options->class_loader_ = &null_class_loader; ScopedObjectAccess soa(Thread::Current()); gc::Heap* heap = runtime->GetHeap(); - gc::space::ImageSpace* image_space = heap->GetBootImageSpace(); - CHECK(image_space != nullptr); - const ImageHeader& image_header = image_space->GetImageHeader(); - if (!image_header.IsValid()) { - fprintf(stderr, "Invalid image header %s\n", image_location); - return EXIT_FAILURE; - } - - ImageDumper image_dumper(os, *image_space, image_header, options); + std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces(); + CHECK(!image_spaces.empty()); + for (gc::space::ImageSpace* image_space : image_spaces) { + const ImageHeader& image_header = image_space->GetImageHeader(); + if (!image_header.IsValid()) { + fprintf(stderr, "Invalid image header %s\n", image_space->GetImageLocation().c_str()); + return EXIT_FAILURE; + } - bool success = image_dumper.Dump(); - return (success) ? EXIT_SUCCESS : EXIT_FAILURE; + ImageDumper image_dumper(os, *image_space, image_header, options); + if (!image_dumper.Dump()) { + return EXIT_FAILURE; + } + } + return EXIT_SUCCESS; } static int DumpOatWithRuntime(Runtime* runtime, OatFile* oat_file, OatDumperOptions* options, @@ -2689,8 +2690,7 @@ struct OatdumpMain : public CmdlineMain<OatdumpArgs> { args_->os_) == EXIT_SUCCESS; } - return DumpImage(runtime, args_->image_location_, oat_dumper_options_.get(), args_->os_) - == EXIT_SUCCESS; + return DumpImage(runtime, oat_dumper_options_.get(), args_->os_) == EXIT_SUCCESS; } std::unique_ptr<OatDumperOptions> oat_dumper_options_; diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 46ab34bea3..b403abd2b6 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -118,6 +118,38 @@ static bool ReadOatPatchDelta(const ElfFile* elf_file, off_t* delta, std::string return true; } +static File* CreateOrOpen(const char* name, bool* created) { + if (OS::FileExists(name)) { + *created = false; + return OS::OpenFileReadWrite(name); + } else { + *created = true; + std::unique_ptr<File> f(OS::CreateEmptyFile(name)); + if (f.get() != nullptr) { + if (fchmod(f->Fd(), 0644) != 0) { + PLOG(ERROR) << "Unable to make " << name << " world readable"; + TEMP_FAILURE_RETRY(unlink(name)); + return nullptr; + } + } + return f.release(); + } +} + +// Either try to close the file (close=true), or erase it. +static bool FinishFile(File* file, bool close) { + if (close) { + if (file->FlushCloseOrErase() != 0) { + PLOG(ERROR) << "Failed to flush and close file."; + return false; + } + return true; + } else { + file->Erase(); + return false; + } +} + bool PatchOat::Patch(const std::string& image_location, off_t delta, File* output_image, InstructionSet isa, TimingLogger* timings) { @@ -195,11 +227,12 @@ bool PatchOat::Patch(const std::string& image_location, off_t delta, LOG(ERROR) << "unable to map image file " << input_image->GetPath() << " : " << error_msg; return false; } - gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetBootImageSpace(); + // TODO: Support multi-image when patchoat is only patching images. Ever used? b/26317072 + gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetBootImageSpaces()[0]; PatchOat p(isa, image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(), delta, timings); t.NewTiming("Patching files"); - if (!p.PatchImage()) { + if (!p.PatchImage(true)) { LOG(ERROR) << "Failed to patch image file " << input_image->GetPath(); return false; } @@ -214,7 +247,7 @@ bool PatchOat::Patch(const std::string& image_location, off_t delta, bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t delta, File* output_oat, File* output_image, InstructionSet isa, TimingLogger* timings, - bool output_oat_opened_from_fd, + bool output_oat_opened_from_fd ATTRIBUTE_UNUSED, bool new_oat_out) { CHECK(Runtime::Current() == nullptr); CHECK(output_image != nullptr); @@ -236,31 +269,6 @@ bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t d isa = GetInstructionSetFromELF(elf_hdr.e_machine, elf_hdr.e_flags); } const char* isa_name = GetInstructionSetString(isa); - std::string image_filename; - if (!LocationToFilename(image_location, isa, &image_filename)) { - LOG(ERROR) << "Unable to find image at location " << image_location; - return false; - } - std::unique_ptr<File> input_image(OS::OpenFileForReading(image_filename.c_str())); - if (input_image.get() == nullptr) { - LOG(ERROR) << "unable to open input image file at " << image_filename - << " for location " << image_location; - return false; - } - int64_t image_len = input_image->GetLength(); - if (image_len < 0) { - LOG(ERROR) << "Error while getting image length"; - return false; - } - ImageHeader image_header; - if (sizeof(image_header) != input_image->Read(reinterpret_cast<char*>(&image_header), - sizeof(image_header), 0)) { - LOG(ERROR) << "Unable to read image header from image file " << input_image->GetPath(); - } - - /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath()); - // Nothing special to do right now since the image always needs to get patched. - // Perhaps in some far-off future we may have images with relative addresses that are true-PIC. // Set up the runtime RuntimeOptions options; @@ -279,70 +287,169 @@ bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t d Thread::Current()->TransitionFromRunnableToSuspended(kNative); ScopedObjectAccess soa(Thread::Current()); + std::string output_directory = + output_image->GetPath().substr(0, output_image->GetPath().find_last_of("/")); t.NewTiming("Image and oat Patching setup"); - // Create the map where we will write the image patches to. - std::string error_msg; - std::unique_ptr<MemMap> image(MemMap::MapFile(image_len, - PROT_READ | PROT_WRITE, - MAP_PRIVATE, - input_image->Fd(), - 0, - /*low_4gb*/false, - input_image->GetPath().c_str(), - &error_msg)); - if (image.get() == nullptr) { - LOG(ERROR) << "unable to map image file " << input_image->GetPath() << " : " << error_msg; - return false; - } - gc::space::ImageSpace* ispc = Runtime::Current()->GetHeap()->GetBootImageSpace(); + std::vector<gc::space::ImageSpace*> spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); + std::map<gc::space::ImageSpace*, std::unique_ptr<File>> space_to_file_map; + std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>> space_to_memmap_map; + std::map<gc::space::ImageSpace*, PatchOat> space_to_patchoat_map; + std::map<gc::space::ImageSpace*, bool> space_to_skip_patching_map; + + for (size_t i = 0; i < spaces.size(); ++i) { + gc::space::ImageSpace* space = spaces[i]; + std::string input_image_filename = space->GetImageFilename(); + std::unique_ptr<File> input_image(OS::OpenFileForReading(input_image_filename.c_str())); + if (input_image.get() == nullptr) { + LOG(ERROR) << "Unable to open input image file at " << input_image_filename; + return false; + } - std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat, - PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg)); - if (elf.get() == nullptr) { - LOG(ERROR) << "unable to open oat file " << input_oat->GetPath() << " : " << error_msg; - return false; - } + int64_t image_len = input_image->GetLength(); + if (image_len < 0) { + LOG(ERROR) << "Error while getting image length"; + return false; + } + ImageHeader image_header; + if (sizeof(image_header) != input_image->Read(reinterpret_cast<char*>(&image_header), + sizeof(image_header), 0)) { + LOG(ERROR) << "Unable to read image header from image file " << input_image->GetPath(); + } - bool skip_patching_oat = false; - MaybePic is_oat_pic = IsOatPic(elf.get()); - if (is_oat_pic >= ERROR_FIRST) { - // Error logged by IsOatPic - return false; - } else if (is_oat_pic == PIC) { - // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching. - if (!ReplaceOatFileWithSymlink(input_oat->GetPath(), - output_oat->GetPath(), - output_oat_opened_from_fd, - new_oat_out)) { - // Errors already logged by above call. + /*bool is_image_pic = */IsImagePic(image_header, input_image->GetPath()); + // Nothing special to do right now since the image always needs to get patched. + // Perhaps in some far-off future we may have images with relative addresses that are true-PIC. + + // Create the map where we will write the image patches to. + std::string error_msg; + std::unique_ptr<MemMap> image(MemMap::MapFile(image_len, + PROT_READ | PROT_WRITE, + MAP_PRIVATE, + input_image->Fd(), + 0, + /*low_4gb*/false, + input_image->GetPath().c_str(), + &error_msg)); + if (image.get() == nullptr) { + LOG(ERROR) << "Unable to map image file " << input_image->GetPath() << " : " << error_msg; + return false; + } + space_to_file_map.emplace(space, std::move(input_image)); + space_to_memmap_map.emplace(space, std::move(image)); + } + + for (size_t i = 0; i < spaces.size(); ++i) { + gc::space::ImageSpace* space = spaces[i]; + std::string input_image_filename = space->GetImageFilename(); + std::string input_oat_filename = + ImageHeader::GetOatLocationFromImageLocation(input_image_filename); + std::unique_ptr<File> input_oat_file(OS::OpenFileForReading(input_oat_filename.c_str())); + if (input_oat_file.get() == nullptr) { + LOG(ERROR) << "Unable to open input oat file at " << input_oat_filename; + return false; + } + std::string error_msg; + std::unique_ptr<ElfFile> elf(ElfFile::Open(input_oat_file.get(), + PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg)); + if (elf.get() == nullptr) { + LOG(ERROR) << "Unable to open oat file " << input_oat_file->GetPath() << " : " << error_msg; return false; } - // Don't patch the OAT, since we just symlinked it. Image still needs patching. - skip_patching_oat = true; - } else { - CHECK(is_oat_pic == NOT_PIC); - } - PatchOat p(isa, elf.release(), image.release(), ispc->GetLiveBitmap(), ispc->GetMemMap(), - delta, timings); - t.NewTiming("Patching files"); - if (!skip_patching_oat && !p.PatchElf()) { - LOG(ERROR) << "Failed to patch oat file " << input_oat->GetPath(); - return false; - } - if (!p.PatchImage()) { - LOG(ERROR) << "Failed to patch image file " << input_image->GetPath(); - return false; - } + bool skip_patching_oat = false; + MaybePic is_oat_pic = IsOatPic(elf.get()); + if (is_oat_pic >= ERROR_FIRST) { + // Error logged by IsOatPic + return false; + } else if (is_oat_pic == PIC) { + // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching. + + std::string converted_image_filename = space->GetImageLocation(); + std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@'); + std::string output_image_filename = output_directory + + (StartsWith(converted_image_filename, "/") ? "" : "/") + + converted_image_filename; + std::string output_oat_filename = + ImageHeader::GetOatLocationFromImageLocation(output_image_filename); + + if (!ReplaceOatFileWithSymlink(input_oat_file->GetPath(), + output_oat_filename, + false, + true)) { + // Errors already logged by above call. + return false; + } + // Don't patch the OAT, since we just symlinked it. Image still needs patching. + skip_patching_oat = true; + } else { + CHECK(is_oat_pic == NOT_PIC); + } - t.NewTiming("Writing files"); - if (!skip_patching_oat && !p.WriteElf(output_oat)) { - LOG(ERROR) << "Failed to write oat file " << input_oat->GetPath(); - return false; + PatchOat& p = space_to_patchoat_map.emplace(space, + PatchOat( + isa, + elf.release(), + space_to_memmap_map.find(space)->second.get(), + space->GetLiveBitmap(), + space->GetMemMap(), + delta, + &space_to_memmap_map, + timings)).first->second; + + t.NewTiming("Patching files"); + if (!skip_patching_oat && !p.PatchElf()) { + LOG(ERROR) << "Failed to patch oat file " << input_oat_file->GetPath(); + return false; + } + if (!p.PatchImage(i == 0)) { + LOG(ERROR) << "Failed to patch image file " << input_image_filename; + return false; + } + + space_to_skip_patching_map.emplace(space, skip_patching_oat); } - if (!p.WriteImage(output_image)) { - LOG(ERROR) << "Failed to write image file " << input_image->GetPath(); - return false; + + for (size_t i = 0; i < spaces.size(); ++i) { + gc::space::ImageSpace* space = spaces[i]; + std::string input_image_filename = space->GetImageFilename(); + + t.NewTiming("Writing files"); + std::string converted_image_filename = space->GetImageLocation(); + std::replace(converted_image_filename.begin() + 1, converted_image_filename.end(), '/', '@'); + std::string output_image_filename = output_directory + + (StartsWith(converted_image_filename, "/") ? "" : "/") + + converted_image_filename; + std::unique_ptr<File> + output_image_file(CreateOrOpen(output_image_filename.c_str(), &new_oat_out)); + if (output_image_file.get() == nullptr) { + LOG(ERROR) << "Failed to open output image file at " << output_image_filename; + return false; + } + + PatchOat& p = space_to_patchoat_map.find(space)->second; + + if (!p.WriteImage(output_image_file.get())) { + LOG(ERROR) << "Failed to write image file " << output_image_file->GetPath(); + return false; + } + FinishFile(output_image_file.get(), true); + + bool skip_patching_oat = space_to_skip_patching_map.find(space)->second; + if (!skip_patching_oat) { + std::string output_oat_filename = + ImageHeader::GetOatLocationFromImageLocation(output_image_filename); + std::unique_ptr<File> + output_oat_file(CreateOrOpen(output_oat_filename.c_str(), &new_oat_out)); + if (output_oat_file.get() == nullptr) { + LOG(ERROR) << "Failed to open output oat file at " << output_oat_filename; + return false; + } + if (!p.WriteElf(output_oat_file.get())) { + LOG(ERROR) << "Failed to write oat file " << output_oat_file->GetPath(); + return false; + } + FinishFile(output_oat_file.get(), true); + } } return true; } @@ -616,7 +723,7 @@ void PatchOat::PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots } } -bool PatchOat::PatchImage() { +bool PatchOat::PatchImage(bool primary_image) { ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin()); CHECK_GT(image_->Size(), sizeof(ImageHeader)); // These are the roots from the original file. @@ -630,9 +737,12 @@ bool PatchOat::PatchImage() { // Patch dex file int/long arrays which point to ArtFields. PatchDexFileArrays(img_roots); - VisitObject(img_roots); + if (primary_image) { + VisitObject(img_roots); + } + if (!image_header->IsValid()) { - LOG(ERROR) << "reloction renders image header invalid"; + LOG(ERROR) << "relocation renders image header invalid"; return false; } @@ -655,7 +765,8 @@ bool PatchOat::InHeap(mirror::Object* o) { void PatchOat::PatchVisitor::operator() (mirror::Object* obj, MemberOffset off, bool is_static_unused ATTRIBUTE_UNUSED) const { mirror::Object* referent = obj->GetFieldObject<mirror::Object, kVerifyNone>(off); - DCHECK(patcher_->InHeap(referent)) << "Referent is not in the heap."; + // TODO: Modify check for multi-image support? b/26317072 + // DCHECK(patcher_->InHeap(referent)) << "Referent is not in the heap."; mirror::Object* moved_object = patcher_->RelocatedAddressOfPointer(referent); copy_->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(off, moved_object); } @@ -664,7 +775,8 @@ void PatchOat::PatchVisitor::operator() (mirror::Class* cls ATTRIBUTE_UNUSED, mirror::Reference* ref) const { MemberOffset off = mirror::Reference::ReferentOffset(); mirror::Object* referent = ref->GetReferent(); - DCHECK(patcher_->InHeap(referent)) << "Referent is not in the heap."; + // TODO: Modify check for multi-image support? b/26317072 + // DCHECK(patcher_->InHeap(referent)) << "Referent is not in the heap."; mirror::Object* moved_object = patcher_->RelocatedAddressOfPointer(referent); copy_->SetFieldObjectWithoutWriteBarrier<false, true, kVerifyNone>(off, moved_object); } @@ -691,7 +803,7 @@ void PatchOat::VisitObject(mirror::Object* object) { klass->FixupNativePointers(copy_klass, pointer_size, native_visitor); auto* vtable = klass->GetVTable(); if (vtable != nullptr) { - vtable->Fixup(RelocatedCopyOf(vtable), pointer_size, native_visitor); + vtable->Fixup(RelocatedCopyOfFollowImages(vtable), pointer_size, native_visitor); } auto* iftable = klass->GetIfTable(); if (iftable != nullptr) { @@ -699,7 +811,9 @@ void PatchOat::VisitObject(mirror::Object* object) { if (iftable->GetMethodArrayCount(i) > 0) { auto* method_array = iftable->GetMethodArray(i); CHECK(method_array != nullptr); - method_array->Fixup(RelocatedCopyOf(method_array), pointer_size, native_visitor); + method_array->Fixup(RelocatedCopyOfFollowImages(method_array), + pointer_size, + native_visitor); } } } @@ -972,38 +1086,6 @@ static bool ReadBaseDelta(const char* name, off_t* delta, std::string* error_msg return true; } -static File* CreateOrOpen(const char* name, bool* created) { - if (OS::FileExists(name)) { - *created = false; - return OS::OpenFileReadWrite(name); - } else { - *created = true; - std::unique_ptr<File> f(OS::CreateEmptyFile(name)); - if (f.get() != nullptr) { - if (fchmod(f->Fd(), 0644) != 0) { - PLOG(ERROR) << "Unable to make " << name << " world readable"; - TEMP_FAILURE_RETRY(unlink(name)); - return nullptr; - } - } - return f.release(); - } -} - -// Either try to close the file (close=true), or erase it. -static bool FinishFile(File* file, bool close) { - if (close) { - if (file->FlushCloseOrErase() != 0) { - PLOG(ERROR) << "Failed to flush and close file."; - return false; - } - return true; - } else { - file->Erase(); - return false; - } -} - static int patchoat(int argc, char **argv) { InitLogging(argv); MemMap::Init(); diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h index 38bd865b22..cb0d14b2c2 100644 --- a/patchoat/patchoat.h +++ b/patchoat/patchoat.h @@ -23,8 +23,10 @@ #include "elf_file.h" #include "elf_utils.h" #include "gc/accounting/space_bitmap.h" +#include "gc/space/image_space.h" #include "gc/heap.h" #include "os.h" +#include "runtime.h" namespace art { @@ -57,21 +59,23 @@ class PatchOat { bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ? bool new_oat_out); // Output oat was a new file created by us? + ~PatchOat() {} + PatchOat(PatchOat&&) = default; + private: // Takes ownership only of the ElfFile. All other pointers are only borrowed. PatchOat(ElfFile* oat_file, off_t delta, TimingLogger* timings) : oat_file_(oat_file), image_(nullptr), bitmap_(nullptr), heap_(nullptr), delta_(delta), - isa_(kNone), timings_(timings) {} + isa_(kNone), space_map_(nullptr), timings_(timings) {} PatchOat(InstructionSet isa, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta, TimingLogger* timings) : image_(image), bitmap_(bitmap), heap_(heap), - delta_(delta), isa_(isa), timings_(timings) {} + delta_(delta), isa_(isa), space_map_(nullptr), timings_(timings) {} PatchOat(InstructionSet isa, ElfFile* oat_file, MemMap* image, gc::accounting::ContinuousSpaceBitmap* bitmap, MemMap* heap, off_t delta, - TimingLogger* timings) + std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>>* map, TimingLogger* timings) : oat_file_(oat_file), image_(image), bitmap_(bitmap), heap_(heap), - delta_(delta), isa_(isa), timings_(timings) {} - ~PatchOat() {} + delta_(delta), isa_(isa), space_map_(map), timings_(timings) {} // Was the .art image at image_path made with --compile-pic ? static bool IsImagePic(const ImageHeader& image_header, const std::string& image_path); @@ -111,7 +115,7 @@ class PatchOat { template <typename ElfFileImpl> bool PatchOatHeader(ElfFileImpl* oat_file); - bool PatchImage() SHARED_REQUIRES(Locks::mutator_lock_); + bool PatchImage(bool primary_image) SHARED_REQUIRES(Locks::mutator_lock_); void PatchArtFields(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_); void PatchArtMethods(const ImageHeader* image_header) SHARED_REQUIRES(Locks::mutator_lock_); void PatchInternedStrings(const ImageHeader* image_header) @@ -129,15 +133,34 @@ class PatchOat { if (obj == nullptr) { return nullptr; } - DCHECK_GT(reinterpret_cast<uintptr_t>(obj), reinterpret_cast<uintptr_t>(heap_->Begin())); - DCHECK_LT(reinterpret_cast<uintptr_t>(obj), reinterpret_cast<uintptr_t>(heap_->End())); + // TODO: Fix these checks for multi-image. Some may still be valid. b/26317072 + // DCHECK_GT(reinterpret_cast<uintptr_t>(obj), reinterpret_cast<uintptr_t>(heap_->Begin())); + // DCHECK_LT(reinterpret_cast<uintptr_t>(obj), reinterpret_cast<uintptr_t>(heap_->End())); uintptr_t heap_off = reinterpret_cast<uintptr_t>(obj) - reinterpret_cast<uintptr_t>(heap_->Begin()); - DCHECK_LT(heap_off, image_->Size()); + // DCHECK_LT(heap_off, image_->Size()); return reinterpret_cast<T*>(image_->Begin() + heap_off); } template <typename T> + T* RelocatedCopyOfFollowImages(T* obj) const { + if (obj == nullptr) { + return nullptr; + } + // Find ImageSpace this belongs to. + auto image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); + for (gc::space::ImageSpace* image_space : image_spaces) { + if (image_space->Contains(obj)) { + uintptr_t heap_off = reinterpret_cast<uintptr_t>(obj) - + reinterpret_cast<uintptr_t>(image_space->GetMemMap()->Begin()); + return reinterpret_cast<T*>(space_map_->find(image_space)->second->Begin() + heap_off); + } + } + LOG(FATAL) << "Did not find object in boot image space " << obj; + UNREACHABLE(); + } + + template <typename T> T* RelocatedAddressOfPointer(T* obj) const { if (obj == nullptr) { return obj; @@ -197,6 +220,8 @@ class PatchOat { // Active instruction set, used to know the entrypoint size. const InstructionSet isa_; + const std::map<gc::space::ImageSpace*, std::unique_ptr<MemMap>>* space_map_; + TimingLogger* timings_; friend class FixupRootVisitor; diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 342e1d976d..0518911a86 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -330,7 +330,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b Runtime* const runtime = Runtime::Current(); gc::Heap* const heap = runtime->GetHeap(); - CHECK(!heap->HasImageSpace()) << "Runtime has image. We should use it."; + CHECK(!heap->HasBootImageSpace()) << "Runtime has image. We should use it."; CHECK(!init_done_); // Use the pointer size from the runtime since we are probably creating the image. @@ -736,7 +736,7 @@ void ClassLinker::RunRootClinits() { static void SanityCheckArtMethod(ArtMethod* m, mirror::Class* expected_class, - gc::space::ImageSpace* space) + std::vector<gc::space::ImageSpace*> spaces) SHARED_REQUIRES(Locks::mutator_lock_) { if (m->IsRuntimeMethod()) { CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m); @@ -745,18 +745,22 @@ static void SanityCheckArtMethod(ArtMethod* m, } else if (expected_class != nullptr) { CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << PrettyMethod(m); } - if (space != nullptr) { - auto& header = space->GetImageHeader(); - auto& methods = header.GetMethodsSection(); - auto offset = reinterpret_cast<uint8_t*>(m) - space->Begin(); - CHECK(methods.Contains(offset)) << m << " not in " << methods; + if (!spaces.empty()) { + bool contains = false; + for (gc::space::ImageSpace* space : spaces) { + auto& header = space->GetImageHeader(); + auto& methods = header.GetMethodsSection(); + auto offset = reinterpret_cast<uint8_t*>(m) - space->Begin(); + contains |= methods.Contains(offset); + } + CHECK(contains) << m << " not found"; } } static void SanityCheckArtMethodPointerArray(mirror::PointerArray* arr, mirror::Class* expected_class, size_t pointer_size, - gc::space::ImageSpace* space) + std::vector<gc::space::ImageSpace*> spaces) SHARED_REQUIRES(Locks::mutator_lock_) { CHECK(arr != nullptr); for (int32_t j = 0; j < arr->GetLength(); ++j) { @@ -766,11 +770,12 @@ static void SanityCheckArtMethodPointerArray(mirror::PointerArray* arr, CHECK(method != nullptr); } if (method != nullptr) { - SanityCheckArtMethod(method, expected_class, space); + SanityCheckArtMethod(method, expected_class, spaces); } } } +/* TODO: Modify check to support multiple image spaces and reenable. b/26317072 static void SanityCheckArtMethodPointerArray( ArtMethod** arr, size_t size, @@ -790,6 +795,7 @@ static void SanityCheckArtMethodPointerArray( } } } +*/ static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_UNUSED) SHARED_REQUIRES(Locks::mutator_lock_) { @@ -805,29 +811,30 @@ static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_ CHECK_EQ(field.GetDeclaringClass(), klass); } auto* runtime = Runtime::Current(); - auto* image_space = runtime->GetHeap()->GetBootImageSpace(); + auto image_spaces = runtime->GetHeap()->GetBootImageSpaces(); auto pointer_size = runtime->GetClassLinker()->GetImagePointerSize(); for (auto& m : klass->GetMethods(pointer_size)) { - SanityCheckArtMethod(&m, klass, image_space); + SanityCheckArtMethod(&m, klass, image_spaces); } auto* vtable = klass->GetVTable(); if (vtable != nullptr) { - SanityCheckArtMethodPointerArray(vtable, nullptr, pointer_size, image_space); + SanityCheckArtMethodPointerArray(vtable, nullptr, pointer_size, image_spaces); } if (klass->ShouldHaveEmbeddedImtAndVTable()) { for (size_t i = 0; i < mirror::Class::kImtSize; ++i) { - SanityCheckArtMethod(klass->GetEmbeddedImTableEntry(i, pointer_size), nullptr, image_space); + SanityCheckArtMethod( + klass->GetEmbeddedImTableEntry(i, pointer_size), nullptr, image_spaces); } for (int32_t i = 0; i < klass->GetEmbeddedVTableLength(); ++i) { - SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr, image_space); + SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr, image_spaces); } } auto* iftable = klass->GetIfTable(); if (iftable != nullptr) { for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { if (iftable->GetMethodArrayCount(i) > 0) { - SanityCheckArtMethodPointerArray(iftable->GetMethodArray(i), nullptr, pointer_size, - image_space); + SanityCheckArtMethodPointerArray( + iftable->GetMethodArray(i), nullptr, pointer_size, image_spaces); } } } @@ -856,6 +863,33 @@ class SetInterpreterEntrypointArtMethodVisitor : public ArtMethodVisitor { DISALLOW_COPY_AND_ASSIGN(SetInterpreterEntrypointArtMethodVisitor); }; +struct TrampolineCheckData { + const void* quick_resolution_trampoline; + const void* quick_imt_conflict_trampoline; + const void* quick_generic_jni_trampoline; + const void* quick_to_interpreter_bridge_trampoline; + size_t pointer_size; + ArtMethod* m; + bool error; +}; +static void CheckTrampolines(mirror::Object* obj, void* arg) NO_THREAD_SAFETY_ANALYSIS { + if (obj->IsClass()) { + mirror::Class* klass = obj->AsClass(); + TrampolineCheckData* d = reinterpret_cast<TrampolineCheckData*>(arg); + for (ArtMethod& m : klass->GetMethods(d->pointer_size)) { + const void* entrypoint = m.GetEntryPointFromQuickCompiledCodePtrSize(d->pointer_size); + if (entrypoint == d->quick_resolution_trampoline || + entrypoint == d->quick_imt_conflict_trampoline || + entrypoint == d->quick_generic_jni_trampoline || + entrypoint == d->quick_to_interpreter_bridge_trampoline) { + d->m = &m; + d->error = true; + return; + } + } + } +} + bool ClassLinker::InitFromImage(std::string* error_msg) { VLOG(startup) << "ClassLinker::InitFromImage entering"; CHECK(!init_done_); @@ -863,28 +897,71 @@ bool ClassLinker::InitFromImage(std::string* error_msg) { Runtime* const runtime = Runtime::Current(); Thread* const self = Thread::Current(); gc::Heap* const heap = runtime->GetHeap(); - gc::space::ImageSpace* const space = heap->GetBootImageSpace(); - CHECK(space != nullptr); - image_pointer_size_ = space->GetImageHeader().GetPointerSize(); + std::vector<gc::space::ImageSpace*> spaces = heap->GetBootImageSpaces(); + CHECK(!spaces.empty()); + image_pointer_size_ = spaces[0]->GetImageHeader().GetPointerSize(); dex_cache_boot_image_class_lookup_required_ = true; - const OatFile* oat_file = runtime->GetOatFileManager().RegisterImageOatFile(space); - DCHECK(oat_file != nullptr); - CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0U); - CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0U); - const char* image_file_location = oat_file->GetOatHeader(). + std::vector<const OatFile*> oat_files = + runtime->GetOatFileManager().RegisterImageOatFiles(spaces); + DCHECK(!oat_files.empty()); + const OatHeader& default_oat_header = oat_files[0]->GetOatHeader(); + CHECK_EQ(default_oat_header.GetImageFileLocationOatChecksum(), 0U); + CHECK_EQ(default_oat_header.GetImageFileLocationOatDataBegin(), 0U); + const char* image_file_location = oat_files[0]->GetOatHeader(). GetStoreValueByKey(OatHeader::kImageLocationKey); CHECK(image_file_location == nullptr || *image_file_location == 0); - quick_resolution_trampoline_ = oat_file->GetOatHeader().GetQuickResolutionTrampoline(); - quick_imt_conflict_trampoline_ = oat_file->GetOatHeader().GetQuickImtConflictTrampoline(); - quick_generic_jni_trampoline_ = oat_file->GetOatHeader().GetQuickGenericJniTrampoline(); - quick_to_interpreter_bridge_trampoline_ = oat_file->GetOatHeader().GetQuickToInterpreterBridge(); - StackHandleScope<2> hs(self); - mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches); - Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches( - hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>())); + quick_resolution_trampoline_ = default_oat_header.GetQuickResolutionTrampoline(); + quick_imt_conflict_trampoline_ = default_oat_header.GetQuickImtConflictTrampoline(); + quick_generic_jni_trampoline_ = default_oat_header.GetQuickGenericJniTrampoline(); + quick_to_interpreter_bridge_trampoline_ = default_oat_header.GetQuickToInterpreterBridge(); + if (kIsDebugBuild) { + // Check that the other images use the same trampoline. + for (size_t i = 1; i < oat_files.size(); ++i) { + const OatHeader& ith_oat_header = oat_files[i]->GetOatHeader(); + const void* ith_quick_resolution_trampoline = + ith_oat_header.GetQuickResolutionTrampoline(); + const void* ith_quick_imt_conflict_trampoline = + ith_oat_header.GetQuickImtConflictTrampoline(); + const void* ith_quick_generic_jni_trampoline = + ith_oat_header.GetQuickGenericJniTrampoline(); + const void* ith_quick_to_interpreter_bridge_trampoline = + ith_oat_header.GetQuickToInterpreterBridge(); + if (ith_quick_resolution_trampoline != quick_resolution_trampoline_ || + ith_quick_imt_conflict_trampoline != quick_imt_conflict_trampoline_ || + ith_quick_generic_jni_trampoline != quick_generic_jni_trampoline_ || + ith_quick_to_interpreter_bridge_trampoline != quick_to_interpreter_bridge_trampoline_) { + // Make sure that all methods in this image do not contain those trampolines as + // entrypoints. Otherwise the class-linker won't be able to work with a single set. + TrampolineCheckData data; + data.error = false; + data.pointer_size = GetImagePointerSize(); + data.quick_resolution_trampoline = ith_quick_resolution_trampoline; + data.quick_imt_conflict_trampoline = ith_quick_imt_conflict_trampoline; + data.quick_generic_jni_trampoline = ith_quick_generic_jni_trampoline; + data.quick_to_interpreter_bridge_trampoline = ith_quick_to_interpreter_bridge_trampoline; + ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_); + spaces[i]->GetLiveBitmap()->Walk(CheckTrampolines, &data); + if (data.error) { + ArtMethod* m = data.m; + LOG(ERROR) << "Found a broken ArtMethod: " << PrettyMethod(m); + *error_msg = "Found an ArtMethod with a bad entrypoint"; + return false; + } + } + } + } + + StackHandleScopeCollection handles(self); + std::vector<Handle<mirror::ObjectArray<mirror::DexCache>>> dex_caches_vector; + for (gc::space::ImageSpace* space : spaces) { + Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(handles.NewHandle( + space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches)-> + AsObjectArray<mirror::DexCache>())); + dex_caches_vector.push_back(dex_caches); + } - Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle( - space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)-> + Handle<mirror::ObjectArray<mirror::Class>> class_roots(handles.NewHandle( + spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)-> AsObjectArray<mirror::Class>())); class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get()); @@ -896,56 +973,70 @@ bool ClassLinker::InitFromImage(std::string* error_msg) { java_lang_Object->SetObjectSize(sizeof(mirror::Object)); // Allocate in non-movable so that it's possible to check if a JNI weak global ref has been // cleared without triggering the read barrier and unintentionally mark the sentinel alive. - runtime->SetSentinel(heap->AllocNonMovableObject<true>(self, - java_lang_Object, - java_lang_Object->GetObjectSize(), - VoidFunctor())); + runtime->SetSentinel(heap->AllocNonMovableObject<true>( + self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor())); - if (oat_file->GetOatHeader().GetDexFileCount() != - static_cast<uint32_t>(dex_caches->GetLength())) { + uint32_t dex_file_count = 0; + for (const OatFile* oat_file : oat_files) { + dex_file_count += oat_file->GetOatHeader().GetDexFileCount(); + } + uint32_t dex_caches_count = 0; + for (auto dex_caches : dex_caches_vector) { + dex_caches_count += dex_caches->GetLength(); + } + if (dex_file_count != dex_caches_count) { *error_msg = "Dex cache count and dex file count mismatch while trying to initialize from " "image"; return false; } - for (int32_t i = 0; i < dex_caches->GetLength(); i++) { - StackHandleScope<1> hs2(self); - Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i))); - const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8()); - const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file_location.c_str(), - nullptr); - if (oat_dex_file == nullptr) { - *error_msg = StringPrintf("Failed finding oat dex file for %s %s", - oat_file->GetLocation().c_str(), - dex_file_location.c_str()); - return false; - } - std::string inner_error_msg; - std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&inner_error_msg); - if (dex_file == nullptr) { - *error_msg = StringPrintf("Failed to open dex file %s from within oat file %s error '%s'", - dex_file_location.c_str(), - oat_file->GetLocation().c_str(), - inner_error_msg.c_str()); - return false; - } + for (auto dex_caches : dex_caches_vector) { + for (int32_t i = 0; i < dex_caches->GetLength(); i++) { + StackHandleScope<1> hs2(self); + Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i))); + const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8()); + const OatFile::OatDexFile* oat_dex_file = nullptr; + for (const OatFile* oat_file : oat_files) { + const OatFile::OatDexFile* oat_dex = + oat_file->GetOatDexFile(dex_file_location.c_str(), nullptr, false); + if (oat_dex != nullptr) { + DCHECK(oat_dex_file == nullptr); + oat_dex_file = oat_dex; + } + } - if (kSanityCheckObjects) { - SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(), - dex_cache->NumResolvedMethods(), - image_pointer_size_, - space); - } + if (oat_dex_file == nullptr) { + *error_msg = StringPrintf("Failed finding oat dex file for %s", + dex_file_location.c_str()); + return false; + } + std::string inner_error_msg; + std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&inner_error_msg); + if (dex_file == nullptr) { + *error_msg = StringPrintf("Failed to open dex file %s error '%s'", + dex_file_location.c_str(), + inner_error_msg.c_str()); + return false; + } - if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) { - *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x", - dex_file_location.c_str(), - dex_file->GetLocationChecksum(), - oat_dex_file->GetDexFileLocationChecksum()); - return false; - } + // TODO: Modify check to support multiple image spaces and reenable. +// if (kSanityCheckObjects) { +// SanityCheckArtMethodPointerArray(dex_cache->GetResolvedMethods(), +// dex_cache->NumResolvedMethods(), +// image_pointer_size_, +// spaces); +// } + + if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) { + *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x", + dex_file_location.c_str(), + dex_file->GetLocationChecksum(), + oat_dex_file->GetDexFileLocationChecksum()); + return false; + } - AppendToBootClassPath(*dex_file.get(), dex_cache); - opened_dex_files_.push_back(std::move(dex_file)); + AppendToBootClassPath(*dex_file.get(), dex_cache); + opened_dex_files_.push_back(std::move(dex_file)); + } } if (!ValidPointerSize(image_pointer_size_)) { @@ -968,12 +1059,14 @@ bool ClassLinker::InitFromImage(std::string* error_msg) { } if (kSanityCheckObjects) { - for (int32_t i = 0; i < dex_caches->GetLength(); i++) { - auto* dex_cache = dex_caches->Get(i); - for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) { - auto* field = dex_cache->GetResolvedField(j, image_pointer_size_); - if (field != nullptr) { - CHECK(field->GetDeclaringClass()->GetClass() != nullptr); + for (auto dex_caches : dex_caches_vector) { + for (int32_t i = 0; i < dex_caches->GetLength(); i++) { + auto* dex_cache = dex_caches->Get(i); + for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) { + auto* field = dex_cache->GetResolvedField(j, image_pointer_size_); + if (field != nullptr) { + CHECK(field->GetDeclaringClass()->GetClass() != nullptr); + } } } } @@ -982,10 +1075,12 @@ bool ClassLinker::InitFromImage(std::string* error_msg) { // Set entry point to interpreter if in InterpretOnly mode. if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) { - const ImageHeader& header = space->GetImageHeader(); - const ImageSection& methods = header.GetMethodsSection(); - SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_); - methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_); + for (gc::space::ImageSpace* space : spaces) { + const ImageHeader& header = space->GetImageHeader(); + const ImageSection& methods = header.GetMethodsSection(); + SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_); + methods.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_); + } } // reinit class_roots_ @@ -1014,13 +1109,15 @@ bool ClassLinker::InitFromImage(std::string* error_msg) { mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable)); mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement)); - const ImageHeader& header = space->GetImageHeader(); - const ImageSection& section = header.GetImageSection(ImageHeader::kSectionClassTable); - if (section.Size() > 0u) { - WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); - ClassTable* const class_table = InsertClassTableForClassLoader(nullptr); - class_table->ReadFromMemory(space->Begin() + section.Offset()); - dex_cache_boot_image_class_lookup_required_ = false; + for (gc::space::ImageSpace* space : spaces) { + const ImageHeader& header = space->GetImageHeader(); + const ImageSection& section = header.GetImageSection(ImageHeader::kSectionClassTable); + if (section.Size() > 0u) { + WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); + ClassTable* const class_table = InsertClassTableForClassLoader(nullptr); + class_table->ReadFromMemory(space->Begin() + section.Offset()); + dex_cache_boot_image_class_lookup_required_ = false; + } } FinishInit(self); @@ -1974,7 +2071,7 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { } Runtime* runtime = Runtime::Current(); if (!runtime->IsStarted()) { - if (runtime->IsAotCompiler() || runtime->GetHeap()->HasImageSpace()) { + if (runtime->IsAotCompiler() || runtime->GetHeap()->HasBootImageSpace()) { return; // OAT file unavailable. } } @@ -2783,23 +2880,27 @@ mirror::Class* ClassLinker::LookupClass(Thread* self, return result; } -static mirror::ObjectArray<mirror::DexCache>* GetImageDexCaches(gc::space::ImageSpace* image_space) - SHARED_REQUIRES(Locks::mutator_lock_) { - CHECK(image_space != nullptr); - mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches); - DCHECK(root != nullptr); - return root->AsObjectArray<mirror::DexCache>(); +static std::vector<mirror::ObjectArray<mirror::DexCache>*> GetImageDexCaches( + std::vector<gc::space::ImageSpace*> image_spaces) SHARED_REQUIRES(Locks::mutator_lock_) { + CHECK(!image_spaces.empty()); + std::vector<mirror::ObjectArray<mirror::DexCache>*> dex_caches_vector; + for (gc::space::ImageSpace* image_space : image_spaces) { + mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches); + DCHECK(root != nullptr); + dex_caches_vector.push_back(root->AsObjectArray<mirror::DexCache>()); + } + return dex_caches_vector; } void ClassLinker::AddBootImageClassesToClassTable() { if (dex_cache_boot_image_class_lookup_required_) { - AddImageClassesToClassTable(Runtime::Current()->GetHeap()->GetBootImageSpace(), + AddImageClassesToClassTable(Runtime::Current()->GetHeap()->GetBootImageSpaces(), /*class_loader*/nullptr); dex_cache_boot_image_class_lookup_required_ = false; } } -void ClassLinker::AddImageClassesToClassTable(gc::space::ImageSpace* image_space, +void ClassLinker::AddImageClassesToClassTable(std::vector<gc::space::ImageSpace*> image_spaces, mirror::ClassLoader* class_loader) { Thread* self = Thread::Current(); WriterMutexLock mu(self, *Locks::classlinker_classes_lock_); @@ -2807,25 +2908,28 @@ void ClassLinker::AddImageClassesToClassTable(gc::space::ImageSpace* image_space ClassTable* const class_table = InsertClassTableForClassLoader(class_loader); - mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches(image_space); std::string temp; - for (int32_t i = 0; i < dex_caches->GetLength(); i++) { - mirror::DexCache* dex_cache = dex_caches->Get(i); - GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes(); - for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) { - mirror::Class* klass = types[j].Read(); - if (klass != nullptr) { - DCHECK_EQ(klass->GetClassLoader(), class_loader); - const char* descriptor = klass->GetDescriptor(&temp); - size_t hash = ComputeModifiedUtf8Hash(descriptor); - mirror::Class* existing = class_table->Lookup(descriptor, hash); - if (existing != nullptr) { - CHECK_EQ(existing, klass) << PrettyClassAndClassLoader(existing) << " != " - << PrettyClassAndClassLoader(klass); - } else { - class_table->Insert(klass); - if (log_new_class_table_roots_) { - new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); + std::vector<mirror::ObjectArray<mirror::DexCache>*> dex_caches_vector = + GetImageDexCaches(image_spaces); + for (mirror::ObjectArray<mirror::DexCache>* dex_caches : dex_caches_vector) { + for (int32_t i = 0; i < dex_caches->GetLength(); i++) { + mirror::DexCache* dex_cache = dex_caches->Get(i); + GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes(); + for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) { + mirror::Class* klass = types[j].Read(); + if (klass != nullptr) { + DCHECK_EQ(klass->GetClassLoader(), class_loader); + const char* descriptor = klass->GetDescriptor(&temp); + size_t hash = ComputeModifiedUtf8Hash(descriptor); + mirror::Class* existing = class_table->Lookup(descriptor, hash); + if (existing != nullptr) { + CHECK_EQ(existing, klass) << PrettyClassAndClassLoader(existing) << " != " + << PrettyClassAndClassLoader(klass); + } else { + class_table->Insert(klass); + if (log_new_class_table_roots_) { + new_class_roots_.push_back(GcRoot<mirror::Class>(klass)); + } } } } @@ -2856,18 +2960,20 @@ void ClassLinker::MoveClassTableToPreZygote() { mirror::Class* ClassLinker::LookupClassFromBootImage(const char* descriptor) { ScopedAssertNoThreadSuspension ants(Thread::Current(), "Image class lookup"); - mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches( - Runtime::Current()->GetHeap()->GetBootImageSpace()); - for (int32_t i = 0; i < dex_caches->GetLength(); ++i) { - mirror::DexCache* dex_cache = dex_caches->Get(i); - const DexFile* dex_file = dex_cache->GetDexFile(); - // Try binary searching the type index by descriptor. - const DexFile::TypeId* type_id = dex_file->FindTypeId(descriptor); - if (type_id != nullptr) { - uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id); - mirror::Class* klass = dex_cache->GetResolvedType(type_idx); - if (klass != nullptr) { - return klass; + std::vector<mirror::ObjectArray<mirror::DexCache>*> dex_caches_vector = + GetImageDexCaches(Runtime::Current()->GetHeap()->GetBootImageSpaces()); + for (mirror::ObjectArray<mirror::DexCache>* dex_caches : dex_caches_vector) { + for (int32_t i = 0; i < dex_caches->GetLength(); ++i) { + mirror::DexCache* dex_cache = dex_caches->Get(i); + const DexFile* dex_file = dex_cache->GetDexFile(); + // Try binary searching the type index by descriptor. + const DexFile::TypeId* type_id = dex_file->FindTypeId(descriptor); + if (type_id != nullptr) { + uint16_t type_idx = dex_file->GetIndexForTypeId(*type_id); + mirror::Class* klass = dex_cache->GetResolvedType(type_idx); + if (klass != nullptr) { + return klass; + } } } } @@ -3167,7 +3273,7 @@ bool ClassLinker::VerifyClassUsingOatFile(const DexFile& dex_file, // the runtime isn't started. On the other hand, app classes can be re-verified even if they are // already pre-opted, as then the runtime is started. if (!Runtime::Current()->IsAotCompiler() && - !Runtime::Current()->GetHeap()->HasImageSpace() && + !Runtime::Current()->GetHeap()->HasBootImageSpace() && klass->GetClassLoader() != nullptr) { return false; } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index f16fe92d80..9d432c660d 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -505,7 +505,7 @@ class ClassLinker { SHARED_REQUIRES(Locks::mutator_lock_); // Add image classes to the class table. - void AddImageClassesToClassTable(gc::space::ImageSpace* image_space, + void AddImageClassesToClassTable(std::vector<gc::space::ImageSpace*> image_spaces, mirror::ClassLoader* class_loader) REQUIRES(!Locks::classlinker_classes_lock_) SHARED_REQUIRES(Locks::mutator_lock_); diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc index 8f7bb946c3..d16afd947e 100644 --- a/runtime/gc/accounting/mod_union_table.cc +++ b/runtime/gc/accounting/mod_union_table.cc @@ -487,7 +487,9 @@ void ModUnionTableCardCache::ClearCards() { // Mark all references to the alloc space(s). void ModUnionTableCardCache::UpdateAndMarkReferences(MarkObjectVisitor* visitor) { - auto* image_space = heap_->GetBootImageSpace(); + // TODO: Needs better support for multi-images? b/26317072 + space::ImageSpace* image_space = + heap_->GetBootImageSpaces().empty() ? nullptr : heap_->GetBootImageSpaces()[0]; // If we don't have an image space, just pass in space_ as the immune space. Pass in the same // space_ instead of image_space to avoid a null check in ModUnionUpdateObjectReferencesVisitor. CardBitVisitor bit_visitor(visitor, space_, image_space != nullptr ? image_space : space_, diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h index 3be7181df2..61c67f86c4 100644 --- a/runtime/gc/accounting/space_bitmap-inl.h +++ b/runtime/gc/accounting/space_bitmap-inl.h @@ -167,7 +167,10 @@ inline bool SpaceBitmap<kAlignment>::Modify(const mirror::Object* obj) { uintptr_t* address = &bitmap_begin_[index]; uintptr_t old_word = *address; if (kSetBit) { - *address = old_word | mask; + if ((old_word & mask) == 0) { + // Avoid dirtying the page if possible. + *address = old_word | mask; + } } else { *address = old_word & ~mask; } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 6d72f3142e..3e432c7c9b 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -233,8 +233,7 @@ Heap::Heap(size_t initial_size, backtrace_lock_(nullptr), seen_backtrace_count_(0u), unique_backtrace_count_(0u), - gc_disabled_for_shutdown_(false), - boot_image_space_(nullptr) { + gc_disabled_for_shutdown_(false) { if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() entering"; } @@ -260,23 +259,107 @@ Heap::Heap(size_t initial_size, CHECK_GE(300 * MB, non_moving_space_capacity); requested_alloc_space_begin = reinterpret_cast<uint8_t*>(300 * MB) - non_moving_space_capacity; } + + // Load image space(s). if (!image_file_name.empty()) { - ATRACE_BEGIN("ImageSpace::Create"); - std::string error_msg; - boot_image_space_ = space::ImageSpace::Create(image_file_name.c_str(), - image_instruction_set, - &error_msg); - ATRACE_END(); - if (boot_image_space_ != nullptr) { - AddSpace(boot_image_space_); - // Oat files referenced by image files immediately follow them in memory, ensure alloc space - // isn't going to get in the middle - uint8_t* oat_file_end_addr = boot_image_space_->GetImageHeader().GetOatFileEnd(); - CHECK_GT(oat_file_end_addr, boot_image_space_->End()); - requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize); - } else { - LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. " - << "Attempting to fall back to imageless running. Error was: " << error_msg; + // For code reuse, handle this like a work queue. + std::vector<std::string> image_file_names; + image_file_names.push_back(image_file_name); + + for (size_t index = 0; index < image_file_names.size(); ++index) { + std::string& image_name = image_file_names[index]; + ATRACE_BEGIN("ImageSpace::Create"); + std::string error_msg; + space::ImageSpace* boot_image_space = space::ImageSpace::Create(image_name.c_str(), + image_instruction_set, + index > 0, + &error_msg); + ATRACE_END(); + if (boot_image_space != nullptr) { + AddSpace(boot_image_space); + // Oat files referenced by image files immediately follow them in memory, ensure alloc space + // isn't going to get in the middle + uint8_t* oat_file_end_addr = boot_image_space->GetImageHeader().GetOatFileEnd(); + CHECK_GT(oat_file_end_addr, boot_image_space->End()); + requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize); + boot_image_spaces_.push_back(boot_image_space); + + if (index == 0) { + // If this was the first space, check whether there are more images to load. + const OatFile* boot_oat_file = boot_image_space->GetOatFile(); + if (boot_oat_file == nullptr) { + continue; + } + + const OatHeader& boot_oat_header = boot_oat_file->GetOatHeader(); + const char* boot_classpath = + boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath); + if (boot_classpath == nullptr) { + continue; + } + + std::vector<std::string> images; + Split(boot_classpath, ':', &images); + + // Add the rest into the list. We have to adjust locations, possibly: + // + // For example, image_file_name is /a/b/c/d/e.art + // images[0] is f/c/d/e.art + // ---------------------------------------------- + // images[1] is g/h/i/j.art -> /a/b/h/i/j.art + + // Derive pattern. + std::vector<std::string> left; + Split(image_file_name, '/', &left); + std::vector<std::string> right; + Split(images[0], '/', &right); + + size_t common = 1; + while (common < left.size() && common < right.size()) { + if (left[left.size() - common - 1] != right[right.size() - common - 1]) { + break; + } + common++; + } + + std::vector<std::string> prefix_vector(left.begin(), left.end() - common); + std::string common_prefix = Join(prefix_vector, '/'); + if (!common_prefix.empty() && common_prefix[0] != '/' && image_file_name[0] == '/') { + common_prefix = "/" + common_prefix; + } + + // Apply pattern to images[1] .. images[n]. + for (size_t i = 1; i < images.size(); ++i) { + std::string image = images[i]; + + size_t rslash = std::string::npos; + for (size_t j = 0; j < common; ++j) { + if (rslash != std::string::npos) { + rslash--; + } + + rslash = image.rfind('/', rslash); + if (rslash == std::string::npos) { + rslash = 0; + } + if (rslash == 0) { + break; + } + } + std::string image_part = image.substr(rslash); + + std::string new_image = common_prefix + (StartsWith(image_part, "/") ? "" : "/") + + image_part; + image_file_names.push_back(new_image); + } + } + } else { + LOG(ERROR) << "Could not create image space with image file '" << image_file_name << "'. " + << "Attempting to fall back to imageless running. Error was: " << error_msg + << "\nAttempted image: " << image_name; + // TODO: Remove already loaded spaces. + break; + } } } /* @@ -456,13 +539,15 @@ Heap::Heap(size_t initial_size, rb_table_.reset(new accounting::ReadBarrierTable()); DCHECK(rb_table_->IsAllCleared()); } - if (GetBootImageSpace() != nullptr) { + if (HasBootImageSpace()) { // Don't add the image mod union table if we are running without an image, this can crash if // we use the CardCache implementation. - accounting::ModUnionTable* mod_union_table = new accounting::ModUnionTableToZygoteAllocspace( - "Image mod-union table", this, GetBootImageSpace()); - CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table"; - AddModUnionTable(mod_union_table); + for (space::ImageSpace* image_space : GetBootImageSpaces()) { + accounting::ModUnionTable* mod_union_table = new accounting::ModUnionTableToZygoteAllocspace( + "Image mod-union table", this, image_space); + CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table"; + AddModUnionTable(mod_union_table); + } } if (collector::SemiSpace::kUseRememberedSet && non_moving_space_ != main_space_) { accounting::RememberedSet* non_moving_space_rem_set = @@ -525,11 +610,12 @@ Heap::Heap(size_t initial_size, garbage_collectors_.push_back(mark_compact_collector_); } } - if (GetBootImageSpace() != nullptr && non_moving_space_ != nullptr && + if (!GetBootImageSpaces().empty() && non_moving_space_ != nullptr && (is_zygote || separate_non_moving_space || foreground_collector_type_ == kCollectorTypeGSS)) { // Check that there's no gap between the image space and the non moving space so that the // immune region won't break (eg. due to a large object allocated in the gap). This is only // required when we're the zygote or using GSS. + /* TODO: Modify this check to support multi-images. b/26317072 bool no_gap = MemMap::CheckNoGaps(GetBootImageSpace()->GetMemMap(), non_moving_space_->GetMemMap()); if (!no_gap) { @@ -537,6 +623,7 @@ Heap::Heap(size_t initial_size, MemMap::DumpMaps(LOG(ERROR), true); LOG(FATAL) << "There's a gap between the image space and the non-moving space"; } + */ } instrumentation::Instrumentation* const instrumentation = runtime->GetInstrumentation(); if (gc_stress_mode_) { @@ -1202,8 +1289,8 @@ space::Space* Heap::FindSpaceFromObject(const mirror::Object* obj, bool fail_ok) return FindDiscontinuousSpaceFromObject(obj, fail_ok); } -space::ImageSpace* Heap::GetBootImageSpace() const { - return boot_image_space_; +std::vector<space::ImageSpace*> Heap::GetBootImageSpaces() const { + return boot_image_spaces_; } void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) { diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index e23b1a3166..e7ea983410 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -580,9 +580,8 @@ class Heap { // Unbind any bound bitmaps. void UnBindBitmaps() REQUIRES(Locks::heap_bitmap_lock_); - // Returns the boot image space. There may be multiple image spaces, but there is only one boot - // image space. - space::ImageSpace* GetBootImageSpace() const; + // Returns the boot image spaces. There may be multiple boot image spaces. + std::vector<space::ImageSpace*> GetBootImageSpaces() const; // Permenantly disable moving garbage collection. void DisableMovingGc() REQUIRES(!*gc_complete_lock_); @@ -660,8 +659,8 @@ class Heap { void RemoveRememberedSet(space::Space* space); bool IsCompilingBoot() const; - bool HasImageSpace() const { - return boot_image_space_ != nullptr; + bool HasBootImageSpace() const { + return !boot_image_spaces_.empty(); } ReferenceProcessor* GetReferenceProcessor() { @@ -1322,8 +1321,8 @@ class Heap { // allocating. bool gc_disabled_for_shutdown_ GUARDED_BY(gc_complete_lock_); - // Boot image space. - space::ImageSpace* boot_image_space_; + // Boot image spaces. + std::vector<space::ImageSpace*> boot_image_spaces_; friend class CollectorTransitionTask; friend class collector::GarbageCollector; diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 8f67c213a6..952759ca7d 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -43,12 +43,17 @@ namespace space { Atomic<uint32_t> ImageSpace::bitmap_index_(0); -ImageSpace::ImageSpace(const std::string& image_filename, const char* image_location, - MemMap* mem_map, accounting::ContinuousSpaceBitmap* live_bitmap, - uint8_t* end) +ImageSpace::ImageSpace(const std::string& image_filename, + const char* image_location, + MemMap* mem_map, + accounting::ContinuousSpaceBitmap* live_bitmap, + uint8_t* end, + MemMap* shadow_map) : MemMapSpace(image_filename, mem_map, mem_map->Begin(), end, end, kGcRetentionPolicyNeverCollect), - image_location_(image_location) { + oat_file_non_owned_(nullptr), + image_location_(image_location), + shadow_map_(shadow_map) { DCHECK(live_bitmap != nullptr); live_bitmap_.reset(live_bitmap); } @@ -470,6 +475,7 @@ static bool CheckSpace(const std::string& cache_filename, std::string* error_msg ImageSpace* ImageSpace::Create(const char* image_location, const InstructionSet image_isa, + bool secondary_image, std::string* error_msg) { std::string system_filename; bool has_system = false; @@ -481,7 +487,7 @@ ImageSpace* ImageSpace::Create(const char* image_location, &has_system, &cache_filename, &dalvik_cache_exists, &has_cache, &is_global_cache); - if (Runtime::Current()->IsZygote()) { + if (Runtime::Current()->IsZygote() && !secondary_image) { MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots()); } @@ -686,7 +692,7 @@ ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_locat return nullptr; } - if (kIsDebugBuild) { + if (VLOG_IS_ON(startup)) { LOG(INFO) << "Dumping image sections"; for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) { const auto section_idx = static_cast<ImageHeader::ImageSections>(i); @@ -799,11 +805,52 @@ ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_locat return nullptr; } + // In case of multi-images, the images are spaced apart so that the bitmaps don't overlap. We + // need to reserve the slack, as otherwise the large object space might allocate in there. + // TODO: Reconsider the multi-image layout. b/26317072 + std::unique_ptr<MemMap> shadow_map; + { + uintptr_t image_begin = reinterpret_cast<uintptr_t>(image_header.GetImageBegin()); + uintptr_t image_end = RoundUp(image_begin + image_header.GetImageSize(), kPageSize); + uintptr_t oat_begin = reinterpret_cast<uintptr_t>(image_header.GetOatFileBegin()); + if (image_end < oat_begin) { + // There's a gap. Could be multi-image, could be the oat file spaced apart. Go ahead and + // dummy-reserve the space covered by the bitmap (which will be a shadow that introduces + // a gap to the next image). + uintptr_t heap_size = bitmap->HeapSize(); + uintptr_t bitmap_coverage_end = RoundUp(image_begin + heap_size, kPageSize); + if (bitmap_coverage_end > image_end) { + VLOG(startup) << "Reserving bitmap shadow [" + << std::hex << image_end << ";" + << std::hex << bitmap_coverage_end << ";] (oat file begins at " + << std::hex << oat_begin; + // Note: we cannot use MemMap::Dummy here, as that won't reserve the space in 32-bit mode. + shadow_map.reset(MemMap::MapAnonymous("Image bitmap shadow", + reinterpret_cast<uint8_t*>(image_end), + bitmap_coverage_end - image_end, + PROT_NONE, + false, + false, + error_msg)); + if (shadow_map == nullptr) { + return nullptr; + } + // madvise it away, we don't really want it, just reserve the address space. + // TODO: Should we use MadviseDontNeedAndZero? b/26317072 + madvise(shadow_map->BaseBegin(), shadow_map->BaseSize(), MADV_DONTNEED); + } + } + } + // We only want the mirror object, not the ArtFields and ArtMethods. uint8_t* const image_end = map->Begin() + image_header.GetImageSection(ImageHeader::kSectionObjects).End(); - std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename, image_location, - map.release(), bitmap.release(), image_end)); + std::unique_ptr<ImageSpace> space(new ImageSpace(image_filename, + image_location, + map.release(), + bitmap.release(), + image_end, + shadow_map.release())); // VerifyImageAllocations() will be called later in Runtime::Init() // as some class roots like ArtMethod::java_lang_reflect_ArtMethod_ @@ -826,16 +873,18 @@ ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_locat Runtime* runtime = Runtime::Current(); runtime->SetInstructionSet(space->oat_file_->GetOatHeader().GetInstructionSet()); - runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod)); - runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod)); - runtime->SetImtUnimplementedMethod( - image_header.GetImageMethod(ImageHeader::kImtUnimplementedMethod)); - runtime->SetCalleeSaveMethod( - image_header.GetImageMethod(ImageHeader::kCalleeSaveMethod), Runtime::kSaveAll); - runtime->SetCalleeSaveMethod( - image_header.GetImageMethod(ImageHeader::kRefsOnlySaveMethod), Runtime::kRefsOnly); - runtime->SetCalleeSaveMethod( - image_header.GetImageMethod(ImageHeader::kRefsAndArgsSaveMethod), Runtime::kRefsAndArgs); + if (!runtime->HasResolutionMethod()) { + runtime->SetResolutionMethod(image_header.GetImageMethod(ImageHeader::kResolutionMethod)); + runtime->SetImtConflictMethod(image_header.GetImageMethod(ImageHeader::kImtConflictMethod)); + runtime->SetImtUnimplementedMethod( + image_header.GetImageMethod(ImageHeader::kImtUnimplementedMethod)); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kCalleeSaveMethod), Runtime::kSaveAll); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kRefsOnlySaveMethod), Runtime::kRefsOnly); + runtime->SetCalleeSaveMethod( + image_header.GetImageMethod(ImageHeader::kRefsAndArgsSaveMethod), Runtime::kRefsAndArgs); + } if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "ImageSpace::Init exiting (" << PrettyDuration(NanoTime() - start_time) diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h index babd672cee..a54358a7eb 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -43,7 +43,10 @@ class ImageSpace : public MemMapSpace { // creation of the alloc space. The ReleaseOatFile will later be // used to transfer ownership of the OatFile to the ClassLinker when // it is initialized. - static ImageSpace* Create(const char* image, InstructionSet image_isa, std::string* error_msg) + static ImageSpace* Create(const char* image, + InstructionSet image_isa, + bool secondary_image, + std::string* error_msg) SHARED_REQUIRES(Locks::mutator_lock_); // Reads the image header from the specified image location for the @@ -158,8 +161,12 @@ class ImageSpace : public MemMapSpace { std::unique_ptr<accounting::ContinuousSpaceBitmap> live_bitmap_; - ImageSpace(const std::string& name, const char* image_location, - MemMap* mem_map, accounting::ContinuousSpaceBitmap* live_bitmap, uint8_t* end); + ImageSpace(const std::string& name, + const char* image_location, + MemMap* mem_map, + accounting::ContinuousSpaceBitmap* live_bitmap, + uint8_t* end, + MemMap* shadow_map = nullptr); // The OatFile associated with the image during early startup to // reserve space contiguous to the image. It is later released to @@ -172,6 +179,10 @@ class ImageSpace : public MemMapSpace { const std::string image_location_; + // A MemMap reserving the space of the bitmap "shadow," so that we don't allocate into it. Only + // used in the multi-image case. + std::unique_ptr<MemMap> shadow_map_; + private: DISALLOW_COPY_AND_ASSIGN(ImageSpace); }; diff --git a/runtime/image.cc b/runtime/image.cc index f8f930b1dd..3856787e2f 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -55,7 +55,6 @@ ImageHeader::ImageHeader(uint32_t image_begin, CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize)); CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize)); CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize)); - CHECK_LT(image_begin, image_roots); CHECK_LT(image_roots, oat_file_begin); CHECK_LE(oat_file_begin, oat_data_begin); CHECK_LT(oat_data_begin, oat_data_end); @@ -100,9 +99,6 @@ bool ImageHeader::IsValid() const { if (oat_file_begin_ >= oat_data_begin_) { return false; } - if (image_roots_ <= image_begin_ || oat_file_begin_ <= image_roots_) { - return false; - } if (!IsAligned<kPageSize>(patch_delta_)) { return false; } diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index e2e4782d00..d035f5d960 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -187,24 +187,27 @@ mirror::String* InternTable::LookupStringFromImage(mirror::String* s) { if (image_added_to_intern_table_) { return nullptr; } - gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetBootImageSpace(); - if (image == nullptr) { + std::vector<gc::space::ImageSpace*> image_spaces = + Runtime::Current()->GetHeap()->GetBootImageSpaces(); + if (image_spaces.empty()) { return nullptr; // No image present. } - mirror::Object* root = image->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches); - mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>(); const std::string utf8 = s->ToModifiedUtf8(); - for (int32_t i = 0; i < dex_caches->GetLength(); ++i) { - mirror::DexCache* dex_cache = dex_caches->Get(i); - const DexFile* dex_file = dex_cache->GetDexFile(); - // Binary search the dex file for the string index. - const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str()); - if (string_id != nullptr) { - uint32_t string_idx = dex_file->GetIndexForStringId(*string_id); - // GetResolvedString() contains a RB. - mirror::String* image_string = dex_cache->GetResolvedString(string_idx); - if (image_string != nullptr) { - return image_string; + for (gc::space::ImageSpace* image_space : image_spaces) { + mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches); + mirror::ObjectArray<mirror::DexCache>* dex_caches = root->AsObjectArray<mirror::DexCache>(); + for (int32_t i = 0; i < dex_caches->GetLength(); ++i) { + mirror::DexCache* dex_cache = dex_caches->Get(i); + const DexFile* dex_file = dex_cache->GetDexFile(); + // Binary search the dex file for the string index. + const DexFile::StringId* string_id = dex_file->FindStringId(utf8.c_str()); + if (string_id != nullptr) { + uint32_t string_idx = dex_file->GetIndexForStringId(*string_id); + // GetResolvedString() contains a RB. + mirror::String* image_string = dex_cache->GetResolvedString(string_idx); + if (image_string != nullptr) { + return image_string; + } } } } diff --git a/runtime/oat.h b/runtime/oat.h index 5ed197715d..13fd6a47e5 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -39,6 +39,7 @@ class PACKED(4) OatHeader { static constexpr const char* kPicKey = "pic"; static constexpr const char* kDebuggableKey = "debuggable"; static constexpr const char* kClassPathKey = "classpath"; + static constexpr const char* kBootClassPath = "bootclasspath"; static constexpr const char kTrueValue[] = "true"; static constexpr const char kFalseValue[] = "false"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 83e594b169..e3de14b667 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -983,6 +983,7 @@ const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, LOG(WARNING) << "Failed to find OatDexFile for DexFile " << dex_location << " ( canonical path " << dex_canonical_location << ")" << " with checksum " << checksum << " in OatFile " << GetLocation(); + /* TODO: Modify for multi-image support and reenable. b/26317072 if (kIsDebugBuild) { for (const OatDexFile* odf : oat_dex_files_storage_) { LOG(WARNING) << "OatFile " << GetLocation() @@ -991,6 +992,7 @@ const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, << " with checksum 0x" << std::hex << odf->GetDexFileLocationChecksum(); } } + */ } return nullptr; diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 0f3a013d53..cb80cc91a4 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -846,11 +846,7 @@ std::string OatFileAssistant::OldProfileFileName() { std::string OatFileAssistant::ImageLocation() { Runtime* runtime = Runtime::Current(); - const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetBootImageSpace(); - if (image_space == nullptr) { - return ""; - } - return image_space->GetImageLocation(); + return runtime->GetHeap()->GetBootImageSpaces()[0]->GetImageLocation(); } const uint32_t* OatFileAssistant::GetRequiredDexChecksum() { @@ -949,12 +945,13 @@ const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() { image_info_load_attempted_ = true; Runtime* runtime = Runtime::Current(); - const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetBootImageSpace(); - if (image_space != nullptr) { - cached_image_info_.location = image_space->GetImageLocation(); + std::vector<gc::space::ImageSpace*> image_spaces = runtime->GetHeap()->GetBootImageSpaces(); + if (!image_spaces.empty()) { + // TODO: Better support multi-images? b/26317072 + cached_image_info_.location = image_spaces[0]->GetImageLocation(); if (isa_ == kRuntimeISA) { - const ImageHeader& image_header = image_space->GetImageHeader(); + const ImageHeader& image_header = image_spaces[0]->GetImageHeader(); cached_image_info_.oat_checksum = image_header.GetOatChecksum(); cached_image_info_.oat_data_begin = reinterpret_cast<uintptr_t>( image_header.GetOatDataBegin()); @@ -969,7 +966,7 @@ const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() { cached_image_info_.patch_delta = image_header->GetPatchDelta(); } } - image_info_load_succeeded_ = (image_space != nullptr); + image_info_load_succeeded_ = (!image_spaces.empty()); } return image_info_load_succeeded_ ? &cached_image_info_ : nullptr; } diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 40cd50b02c..f994f0c99d 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -223,9 +223,10 @@ class OatFileAssistantTest : public CommonRuntimeTest { false, dex_location.c_str(), &error_msg)); ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; - const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetBootImageSpace(); - ASSERT_TRUE(image_space != nullptr); - const ImageHeader& image_header = image_space->GetImageHeader(); + const std::vector<gc::space::ImageSpace*> image_spaces = + runtime->GetHeap()->GetBootImageSpaces(); + ASSERT_TRUE(!image_spaces.empty() && image_spaces[0] != nullptr); + const ImageHeader& image_header = image_spaces[0]->GetImageHeader(); const OatHeader& oat_header = odex_file->GetOatHeader(); EXPECT_FALSE(odex_file->IsPic()); EXPECT_EQ(image_header.GetOatChecksum(), oat_header.GetImageFileLocationOatChecksum()); diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index ea6d3ff3dd..36a967f4ab 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -78,17 +78,23 @@ const OatFile* OatFileManager::FindOpenedOatFileFromOatLocationLocked( return nullptr; } -const OatFile* OatFileManager::GetBootOatFile() const { - gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace(); - return (image_space == nullptr) ? nullptr : image_space->GetOatFile(); +std::vector<const OatFile*> OatFileManager::GetBootOatFiles() const { + std::vector<const OatFile*> oat_files; + std::vector<gc::space::ImageSpace*> image_spaces = + Runtime::Current()->GetHeap()->GetBootImageSpaces(); + for (gc::space::ImageSpace* image_space : image_spaces) { + oat_files.push_back(image_space->GetOatFile()); + } + return oat_files; } const OatFile* OatFileManager::GetPrimaryOatFile() const { ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); - const OatFile* boot_oat_file = GetBootOatFile(); - if (boot_oat_file != nullptr) { + std::vector<const OatFile*> boot_oat_files = GetBootOatFiles(); + if (!boot_oat_files.empty()) { for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) { - if (oat_file.get() != boot_oat_file) { + if (std::find(boot_oat_files.begin(), boot_oat_files.end(), oat_file.get()) == + boot_oat_files.end()) { return oat_file.get(); } } @@ -102,8 +108,13 @@ OatFileManager::~OatFileManager() { oat_files_.clear(); } -const OatFile* OatFileManager::RegisterImageOatFile(gc::space::ImageSpace* space) { - return RegisterOatFile(space->ReleaseOatFile()); +std::vector<const OatFile*> OatFileManager::RegisterImageOatFiles( + std::vector<gc::space::ImageSpace*> spaces) { + std::vector<const OatFile*> oat_files; + for (gc::space::ImageSpace* space : spaces) { + oat_files.push_back(RegisterOatFile(space->ReleaseOatFile())); + } + return oat_files; } class DexFileAndClassPair : ValueObject { @@ -213,7 +224,7 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, std::priority_queue<DexFileAndClassPair> queue; // Add dex files from already loaded oat files, but skip boot. - const OatFile* boot_oat = GetBootOatFile(); + std::vector<const OatFile*> boot_oat_files = GetBootOatFiles(); // The same OatFile can be loaded multiple times at different addresses. In this case, we don't // need to check both against each other since they would have resolved the same way at compile // time. @@ -221,8 +232,8 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, for (const std::unique_ptr<const OatFile>& loaded_oat_file : oat_files_) { DCHECK_NE(loaded_oat_file.get(), oat_file); const std::string& location = loaded_oat_file->GetLocation(); - if (loaded_oat_file.get() != boot_oat && - location != oat_file->GetLocation() && + if (std::find(boot_oat_files.begin(), boot_oat_files.end(), loaded_oat_file.get()) == + boot_oat_files.end() && location != oat_file->GetLocation() && unique_locations.find(location) == unique_locations.end()) { unique_locations.insert(location); AddDexFilesFromOat(loaded_oat_file.get(), /*already_loaded*/true, &queue); diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h index af7efb4262..4690e4550b 100644 --- a/runtime/oat_file_manager.h +++ b/runtime/oat_file_manager.h @@ -73,15 +73,15 @@ class OatFileManager { return have_non_pic_oat_file_; } - // Returns the boot image oat file. - const OatFile* GetBootOatFile() const; + // Returns the boot image oat files. + std::vector<const OatFile*> GetBootOatFiles() const; // Returns the first non-image oat file in the class path. const OatFile* GetPrimaryOatFile() const REQUIRES(!Locks::oat_file_manager_lock_); - // Return the oat file for an image, registers the oat file. Takes ownership of the imagespace's - // underlying oat file. - const OatFile* RegisterImageOatFile(gc::space::ImageSpace* space) + // Returns the oat files for the images, registers the oat files. + // Takes ownership of the imagespace's underlying oat files. + std::vector<const OatFile*> RegisterImageOatFiles(std::vector<gc::space::ImageSpace*> spaces) REQUIRES(!Locks::oat_file_manager_lock_); // Finds or creates the oat file holding dex_location. Then loads and returns diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h index 03cad0835e..564373958a 100644 --- a/runtime/oat_quick_method_header.h +++ b/runtime/oat_quick_method_header.h @@ -44,7 +44,8 @@ class PACKED(4) OatQuickMethodHeader { uintptr_t code = reinterpret_cast<uintptr_t>(code_ptr); uintptr_t header = code - OFFSETOF_MEMBER(OatQuickMethodHeader, code_); DCHECK(IsAlignedParam(code, GetInstructionSetAlignment(kRuntimeISA)) || - IsAlignedParam(header, GetInstructionSetAlignment(kRuntimeISA))); + IsAlignedParam(header, GetInstructionSetAlignment(kRuntimeISA))) + << std::hex << code << " " << std::hex << header; return reinterpret_cast<OatQuickMethodHeader*>(header); } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index eeaadd4990..be64bffd40 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -547,15 +547,15 @@ bool Runtime::Start() { // Use !IsAotCompiler so that we get test coverage, tests are never the zygote. if (!IsAotCompiler()) { ScopedObjectAccess soa(self); - gc::space::ImageSpace* image_space = heap_->GetBootImageSpace(); - if (image_space != nullptr) { + std::vector<gc::space::ImageSpace*> image_spaces = heap_->GetBootImageSpaces(); + for (gc::space::ImageSpace* image_space : image_spaces) { ATRACE_BEGIN("AddImageStringsToTable"); GetInternTable()->AddImageStringsToTable(image_space); ATRACE_END(); - ATRACE_BEGIN("MoveImageClassesToClassTable"); - GetClassLinker()->AddBootImageClassesToClassTable(); - ATRACE_END(); } + ATRACE_BEGIN("MoveImageClassesToClassTable"); + GetClassLinker()->AddBootImageClassesToClassTable(); + ATRACE_END(); } // If we are the zygote then we need to wait until after forking to create the code cache @@ -564,7 +564,7 @@ bool Runtime::Start() { CreateJit(); } - if (!IsImageDex2OatEnabled() || !GetHeap()->HasImageSpace()) { + if (!IsImageDex2OatEnabled() || !GetHeap()->HasBootImageSpace()) { ScopedObjectAccess soa(self); StackHandleScope<1> hs(soa.Self()); auto klass(hs.NewHandle<mirror::Class>(mirror::Class::GetJavaLangClass())); @@ -754,61 +754,96 @@ void Runtime::StartDaemonThreads() { VLOG(startup) << "Runtime::StartDaemonThreads exiting"; } +// Attempts to open dex files from image(s). Given the image location, try to find the oat file +// and open it to get the stored dex file. If the image is the first for a multi-image boot +// classpath, go on and also open the other images. static bool OpenDexFilesFromImage(const std::string& image_location, std::vector<std::unique_ptr<const DexFile>>* dex_files, size_t* failures) { DCHECK(dex_files != nullptr) << "OpenDexFilesFromImage: out-param is nullptr"; - std::string system_filename; - bool has_system = false; - std::string cache_filename_unused; - bool dalvik_cache_exists_unused; - bool has_cache_unused; - bool is_global_cache_unused; - bool found_image = gc::space::ImageSpace::FindImageFilename(image_location.c_str(), - kRuntimeISA, - &system_filename, - &has_system, - &cache_filename_unused, - &dalvik_cache_exists_unused, - &has_cache_unused, - &is_global_cache_unused); - *failures = 0; - if (!found_image || !has_system) { - return false; - } - std::string error_msg; - // We are falling back to non-executable use of the oat file because patching failed, presumably - // due to lack of space. - std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(system_filename.c_str()); - std::string oat_location = ImageHeader::GetOatLocationFromImageLocation(image_location.c_str()); - std::unique_ptr<File> file(OS::OpenFileForReading(oat_filename.c_str())); - if (file.get() == nullptr) { - return false; - } - std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(), false, false, &error_msg)); - if (elf_file.get() == nullptr) { - return false; - } - std::unique_ptr<const OatFile> oat_file( - OatFile::OpenWithElfFile(elf_file.release(), oat_location, nullptr, &error_msg)); - if (oat_file == nullptr) { - LOG(WARNING) << "Unable to use '" << oat_filename << "' because " << error_msg; - return false; - } - for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { - if (oat_dex_file == nullptr) { - *failures += 1; - continue; + // Use a work-list approach, so that we can easily reuse the opening code. + std::vector<std::string> image_locations; + image_locations.push_back(image_location); + + for (size_t index = 0; index < image_locations.size(); ++index) { + std::string system_filename; + bool has_system = false; + std::string cache_filename_unused; + bool dalvik_cache_exists_unused; + bool has_cache_unused; + bool is_global_cache_unused; + bool found_image = gc::space::ImageSpace::FindImageFilename(image_locations[index].c_str(), + kRuntimeISA, + &system_filename, + &has_system, + &cache_filename_unused, + &dalvik_cache_exists_unused, + &has_cache_unused, + &is_global_cache_unused); + + if (!found_image || !has_system) { + return false; } - std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg); - if (dex_file.get() == nullptr) { - *failures += 1; - } else { - dex_files->push_back(std::move(dex_file)); + + // We are falling back to non-executable use of the oat file because patching failed, presumably + // due to lack of space. + std::string oat_filename = + ImageHeader::GetOatLocationFromImageLocation(system_filename.c_str()); + std::string oat_location = + ImageHeader::GetOatLocationFromImageLocation(image_locations[index].c_str()); + // Note: in the multi-image case, the image location may end in ".jar," and not ".art." Handle + // that here. + if (EndsWith(oat_location, ".jar")) { + oat_location.replace(oat_location.length() - 3, 3, "oat"); + } + + std::unique_ptr<File> file(OS::OpenFileForReading(oat_filename.c_str())); + if (file.get() == nullptr) { + return false; } + std::string error_msg; + std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(), false, false, &error_msg)); + if (elf_file.get() == nullptr) { + return false; + } + std::unique_ptr<const OatFile> oat_file( + OatFile::OpenWithElfFile(elf_file.release(), oat_location, nullptr, &error_msg)); + if (oat_file == nullptr) { + LOG(WARNING) << "Unable to use '" << oat_filename << "' because " << error_msg; + return false; + } + + for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { + if (oat_dex_file == nullptr) { + *failures += 1; + continue; + } + std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg); + if (dex_file.get() == nullptr) { + *failures += 1; + } else { + dex_files->push_back(std::move(dex_file)); + } + } + + if (index == 0) { + // First file. See if this is a multi-image environment, and if so, enqueue the other images. + const OatHeader& boot_oat_header = oat_file->GetOatHeader(); + const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath); + if (boot_cp != nullptr) { + std::vector<std::string> cp; + Split(boot_cp, ':', &cp); + + if (cp.size() > 1) { + // More images, enqueue (skipping the first). + image_locations.insert(image_locations.end(), cp.begin() + 1, cp.end()); + } + } + } + + Runtime::Current()->GetOatFileManager().RegisterOatFile(std::move(oat_file)); } - Runtime::Current()->GetOatFileManager().RegisterOatFile(std::move(oat_file)); return true; } @@ -946,7 +981,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs)); ATRACE_END(); - if (heap_->GetBootImageSpace() == nullptr && !allow_dex_file_fallback_) { + if (!heap_->HasBootImageSpace() && !allow_dex_file_fallback_) { LOG(ERROR) << "Dex file fallback disabled, cannot continue without image."; ATRACE_END(); return false; @@ -1054,7 +1089,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U); class_linker_ = new ClassLinker(intern_table_); - if (GetHeap()->HasImageSpace()) { + if (GetHeap()->HasBootImageSpace()) { ATRACE_BEGIN("InitFromImage"); std::string error_msg; bool result = class_linker_->InitFromImage(&error_msg); @@ -1063,9 +1098,13 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { LOG(ERROR) << "Could not initialize from image: " << error_msg; return false; } + /* TODO: Modify check to support multiple image spaces and reenable. b/26317072 if (kIsDebugBuild) { - GetHeap()->GetBootImageSpace()->VerifyImageAllocations(); + for (auto image_space : GetHeap()->GetBootImageSpaces()) { + image_space->VerifyImageAllocations(); + } } + */ if (boot_class_path_string_.empty()) { // The bootclasspath is not explicitly specified: construct it from the loaded dex files. const std::vector<const DexFile*>& boot_class_path = GetClassLinker()->GetBootClassPath(); diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc index b6b1c43589..1337442e5b 100644 --- a/test/117-nopatchoat/nopatchoat.cc +++ b/test/117-nopatchoat/nopatchoat.cc @@ -35,8 +35,9 @@ class NoPatchoatTest { } static bool isRelocationDeltaZero() { - gc::space::ImageSpace* space = Runtime::Current()->GetHeap()->GetBootImageSpace(); - return space != nullptr && space->GetImageHeader().GetPatchDelta() == 0; + std::vector<gc::space::ImageSpace*> spaces = + Runtime::Current()->GetHeap()->GetBootImageSpaces(); + return !spaces.empty() && spaces[0]->GetImageHeader().GetPatchDelta() == 0; } static bool hasExecutableOat(jclass cls) { diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc index 7762b2d2e3..9bfe42922b 100644 --- a/test/137-cfi/cfi.cc +++ b/test/137-cfi/cfi.cc @@ -92,9 +92,10 @@ static bool CheckStack(Backtrace* bt, const std::vector<std::string>& seq) { // detecting this. #if __linux__ static bool IsPicImage() { - gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetBootImageSpace(); - CHECK(image_space != nullptr); // We should be running with an image. - const OatFile* oat_file = image_space->GetOatFile(); + std::vector<gc::space::ImageSpace*> image_spaces = + Runtime::Current()->GetHeap()->GetBootImageSpaces(); + CHECK(!image_spaces.empty()); // We should be running with an image. + const OatFile* oat_file = image_spaces[0]->GetOatFile(); CHECK(oat_file != nullptr); // We should have an oat file to go with the image. return oat_file->IsPic(); } diff --git a/test/464-checker-inline-sharpen-calls/src/Main.java b/test/464-checker-inline-sharpen-calls/src/Main.java index 5080f142b1..2222e0fa0c 100644 --- a/test/464-checker-inline-sharpen-calls/src/Main.java +++ b/test/464-checker-inline-sharpen-calls/src/Main.java @@ -16,6 +16,14 @@ public final class Main { + public final static class Helper { + private int foo = 3; + + public int getFoo() { + return foo; + } + } + public void invokeVirtual() { } @@ -31,25 +39,25 @@ public final class Main { m.invokeVirtual(); } - /// CHECK-START: int Main.inlineSharpenStringInvoke() ssa_builder (after) - /// CHECK-DAG: <<Invoke:i\d+>> InvokeVirtual + /// CHECK-START: int Main.inlineSharpenHelperInvoke() ssa_builder (after) + /// CHECK-DAG: <<Invoke:i\d+>> InvokeVirtual {{.*\.getFoo.*}} /// CHECK-DAG: Return [<<Invoke>>] - /// CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after) - /// CHECK-NOT: InvokeStaticOrDirect - /// CHECK-NOT: InvokeVirtual + /// CHECK-START: int Main.inlineSharpenHelperInvoke() inliner (after) + /// CHECK-NOT: InvokeStaticOrDirect {{.*\.getFoo.*}} + /// CHECK-NOT: InvokeVirtual {{.*\.getFoo.*}} - /// CHECK-START: int Main.inlineSharpenStringInvoke() inliner (after) + /// CHECK-START: int Main.inlineSharpenHelperInvoke() inliner (after) /// CHECK-DAG: <<Field:i\d+>> InstanceFieldGet /// CHECK-DAG: Return [<<Field>>] - public static int inlineSharpenStringInvoke() { - return "Foo".length(); + public static int inlineSharpenHelperInvoke() { + return new Helper().getFoo(); } public static void main(String[] args) { inlineSharpenInvokeVirtual(new Main()); - if (inlineSharpenStringInvoke() != 3) { + if (inlineSharpenHelperInvoke() != 3) { throw new Error("Expected 3"); } } diff --git a/test/490-checker-inline/src/Main.java b/test/490-checker-inline/src/Main.java index 21a01897e2..2e2deea7cc 100644 --- a/test/490-checker-inline/src/Main.java +++ b/test/490-checker-inline/src/Main.java @@ -39,7 +39,7 @@ public class Main implements Itf { /// CHECK-DAG: InvokeInterface /// CHECK-START: void Main.testMethod() inliner (after) - /// CHECK-NOT: Invoke{{.*}} + /// CHECK-NOT: Invoke{{.*Object\.<init>.*}} public static void testMethod() { createMain().invokeVirtual(); diff --git a/test/492-checker-inline-invoke-interface/src/Main.java b/test/492-checker-inline-invoke-interface/src/Main.java index a8b63075be..3106ce4f3e 100644 --- a/test/492-checker-inline-invoke-interface/src/Main.java +++ b/test/492-checker-inline-invoke-interface/src/Main.java @@ -32,14 +32,14 @@ public class Main implements Itf { } /// CHECK-START: void Main.main(java.lang.String[]) ssa_builder (after) - /// CHECK: InvokeStaticOrDirect + /// CHECK: InvokeStaticOrDirect {{.*Main.<init>.*}} /// CHECK: InvokeInterface /// CHECK-START: void Main.main(java.lang.String[]) inliner (before) /// CHECK-NOT: ClinitCheck /// CHECK-START: void Main.main(java.lang.String[]) inliner (after) - /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: InvokeStaticOrDirect {{.*Main.<init>.*}} /// CHECK-NOT: InvokeVirtual /// CHECK-NOT: InvokeInterface diff --git a/test/493-checker-inline-invoke-interface/src/Main.java b/test/493-checker-inline-invoke-interface/src/Main.java index 44b727fe55..171405ca44 100644 --- a/test/493-checker-inline-invoke-interface/src/Main.java +++ b/test/493-checker-inline-invoke-interface/src/Main.java @@ -36,7 +36,7 @@ public class Main implements Itf { /// CHECK: InvokeInterface /// CHECK-START: void Main.main(java.lang.String[]) inliner (after) - /// CHECK-NOT: Invoke{{.*}} + /// CHECK-NOT: Invoke{{.*Object\.<init>.*}} public static void main(String[] args) { Itf itf = bar(); itf.foo(); diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java index 3f65d5a312..be666e94fa 100644 --- a/test/536-checker-intrinsic-optimization/src/Main.java +++ b/test/536-checker-intrinsic-optimization/src/Main.java @@ -47,7 +47,7 @@ public class Main { } /// CHECK-START-X86: boolean Main.stringArgumentNotNull(java.lang.Object) disassembly (after) - /// CHECK: InvokeVirtual + /// CHECK: InvokeVirtual {{.*\.equals.*}} /// CHECK-NOT: test public static boolean stringArgumentNotNull(Object obj) { obj.getClass(); diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index afd833eb43..81cfb7003a 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -155,8 +155,14 @@ IMAGE_TYPES := image ifeq ($(ART_TEST_RUN_TEST_NO_IMAGE),true) IMAGE_TYPES += no-image endif +ifeq ($(ART_TEST_RUN_TEST_MULTI_IMAGE),true) + IMAGE_TYPES := multiimage +endif ifeq ($(ART_TEST_PIC_IMAGE),true) IMAGE_TYPES += picimage + ifeq ($(ART_TEST_RUN_TEST_MULTI_IMAGE),true) + IMAGE_TYPES := multipicimage + endif endif PICTEST_TYPES := npictest ifeq ($(ART_TEST_PIC_TEST),true) @@ -581,6 +587,19 @@ endif TEST_ART_BROKEN_DEFAULT_HEAP_POISONING_RUN_TESTS := TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS := + +# Tests broken by multi-image. b/26317072 +TEST_ART_BROKEN_MULTI_IMAGE_RUN_TESTS := \ + 476-checker-ctor-memory-barrier \ + 530-checker-lse + +ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ + $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ + $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), \ + $(TEST_ART_BROKEN_MULTI_IMAGE_RUN_TESTS), $(ALL_ADDRESS_SIZES)) + +TEST_ART_BROKEN_MULTI_IMAGE_RUN_TESTS := + # Clear variables ahead of appending to them when defining tests. $(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=)) $(foreach target, $(TARGET_TYPES), \ @@ -839,7 +858,27 @@ define define-test-art-run-test prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_pic_$(13)) endif else - $$(error found $(9) expected $(IMAGE_TYPES)) + ifeq ($(9),multiimage) + test_groups += ART_RUN_TEST_$$(uc_host_or_target)_IMAGE_RULES + run_test_options += --multi-image + ifeq ($(1),host) + prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_no-pic_multi_$(13)) + else + prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_no-pic_multi_$(13)) + endif + else + ifeq ($(9),multipicimage) + test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PICIMAGE_RULES + run_test_options += --pic-image --multi-image + ifeq ($(1),host) + prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_pic_multi_$(13)) + else + prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_pic_multi_$(13)) + endif + else + $$(error found $(9) expected $(IMAGE_TYPES)) + endif + endif endif endif endif diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc index 082c9b3c8d..fd41fd281f 100644 --- a/test/common/runtime_state.cc +++ b/test/common/runtime_state.cc @@ -56,7 +56,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isDex2OatEnabled(JNIEnv* env ATT extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasImage(JNIEnv* env ATTRIBUTE_UNUSED, jclass cls ATTRIBUTE_UNUSED) { - return Runtime::Current()->GetHeap()->HasImageSpace(); + return Runtime::Current()->GetHeap()->HasBootImageSpace(); } // public static native boolean isImageDex2OatEnabled(); diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 3efa6ff079..6ff356faaf 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -361,13 +361,17 @@ fi dex2oat_cmdline="true" mkdir_cmdline="mkdir -p ${DEX_LOCATION}/dalvik-cache/$ISA" +# TODO: allow app-image to work with multi-image. b/26317072 +app_image="" +# app_image="--app-image-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.art | cut -d/ -f 2- | sed "s:/:@:g")" + if [ "$PREBUILD" = "y" ]; then dex2oat_cmdline="$INVOKE_WITH $ANDROID_ROOT/bin/dex2oatd \ $COMPILE_FLAGS \ --boot-image=${BOOT_IMAGE} \ --dex-file=$DEX_LOCATION/$TEST_NAME.jar \ --oat-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.dex | cut -d/ -f 2- | sed "s:/:@:g") \ - --app-image-file=$DEX_LOCATION/dalvik-cache/$ISA/$(echo $DEX_LOCATION/$TEST_NAME.jar/classes.art | cut -d/ -f 2- | sed "s:/:@:g") \ + ${app_image} \ --instruction-set=$ISA" if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then dex2oat_cmdline="${dex2oat_cmdline} --instruction-set-features=${INSTRUCTION_SET_FEATURES}" diff --git a/test/run-test b/test/run-test index ac2b52c28c..d07668726b 100755 --- a/test/run-test +++ b/test/run-test @@ -135,6 +135,7 @@ have_patchoat="yes" have_image="yes" image_suffix="" pic_image_suffix="" +multi_image_suffix="" android_root="/system" while true; do @@ -184,6 +185,9 @@ while true; do elif [ "x$1" = "x--pic-image" ]; then pic_image_suffix="-pic" shift + elif [ "x$1" = "x--multi-image" ]; then + multi_image_suffix="-multi" + shift elif [ "x$1" = "x--pic-test" ]; then run_args="${run_args} --pic-test" shift @@ -470,12 +474,12 @@ elif [ "$runtime" = "art" ]; then export ANDROID_HOST_OUT=${OUT_DIR:-$ANDROID_BUILD_TOP/out/}host/linux-x86 fi guess_host_arch_name - run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}${pic_image_suffix}.art" + run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art" run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}" else guess_target_arch_name run_args="${run_args} --runtime-option -Djava.library.path=/data/art-test/${target_arch_name}" - run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}.art" + run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art" fi if [ "$relocate" = "yes" ]; then run_args="${run_args} --relocate" @@ -612,6 +616,8 @@ if [ "$usage" = "yes" ]; then echo " Set instruction-set-features for compilation." echo " --pic-image Use an image compiled with position independent code for the" echo " boot class path." + echo " --multi-image Use a set of images compiled with dex2oat multi-image for" + echo " the boot class path." echo " --pic-test Compile the test code position independent." echo " --quiet Don't print anything except failure messages" ) 1>&2 # Direct to stderr so usage is not printed if --quiet is set. |