diff options
42 files changed, 833 insertions, 182 deletions
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index ad687f8256..60f8f98757 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1623,7 +1623,6 @@ void CodeGeneratorARM64::SetupBlockedRegisters() const { while (!reserved_core_registers.IsEmpty()) { blocked_core_registers_[reserved_core_registers.PopLowestIndex().GetCode()] = true; } - blocked_core_registers_[X18] = true; CPURegList reserved_fp_registers = vixl_reserved_fp_registers; while (!reserved_fp_registers.IsEmpty()) { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 73afbad184..9b6771d3b5 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1398,8 +1398,8 @@ class Dex2Oat FINAL { std::unique_ptr<linker::BufferedOutputStream> vdex_out = std::make_unique<linker::BufferedOutputStream>( std::make_unique<linker::FileOutputStream>(vdex_files_.back().get())); - if (!vdex_out->WriteFully(&VdexFile::Header::kVdexInvalidMagic, - arraysize(VdexFile::Header::kVdexInvalidMagic))) { + if (!vdex_out->WriteFully(&VdexFile::VerifierDepsHeader::kVdexInvalidMagic, + arraysize(VdexFile::VerifierDepsHeader::kVdexInvalidMagic))) { PLOG(ERROR) << "Failed to invalidate vdex header. File: " << vdex_out->GetLocation(); return false; } diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 96dd319946..09ff14e4ba 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1465,14 +1465,14 @@ TEST_F(Dex2oatTest, LayoutSections) { // Test that generating compact dex works. TEST_F(Dex2oatTest, GenerateCompactDex) { - std::unique_ptr<const DexFile> dex(OpenTestDexFile("ManyMethods")); // Generate a compact dex based odex. const std::string dir = GetScratchDir(); const std::string oat_filename = dir + "/base.oat"; const std::string vdex_filename = dir + "/base.vdex"; + const std::string dex_location = GetTestDexFileName("MultiDex"); std::string error_msg; const int res = GenerateOdexForTestWithStatus( - {dex->GetLocation()}, + { dex_location }, oat_filename, CompilerFilter::Filter::kQuicken, &error_msg, @@ -1485,16 +1485,43 @@ TEST_F(Dex2oatTest, GenerateCompactDex) { nullptr, false, /*low_4gb*/false, - dex->GetLocation().c_str(), + dex_location.c_str(), &error_msg)); ASSERT_TRUE(odex_file != nullptr); std::vector<const OatDexFile*> oat_dex_files = odex_file->GetOatDexFiles(); - ASSERT_EQ(oat_dex_files.size(), 1u); - // Check that each dex is a compact dex. + ASSERT_GT(oat_dex_files.size(), 1u); + // Check that each dex is a compact dex file. + std::vector<std::unique_ptr<const CompactDexFile>> compact_dex_files; for (const OatDexFile* oat_dex : oat_dex_files) { std::unique_ptr<const DexFile> dex_file(oat_dex->OpenDexFile(&error_msg)); ASSERT_TRUE(dex_file != nullptr) << error_msg; ASSERT_TRUE(dex_file->IsCompactDexFile()); + compact_dex_files.push_back( + std::unique_ptr<const CompactDexFile>(dex_file.release()->AsCompactDexFile())); + } + for (const std::unique_ptr<const CompactDexFile>& dex_file : compact_dex_files) { + // Test that every code item is in the owned section. + const CompactDexFile::Header& header = dex_file->GetHeader(); + EXPECT_LE(header.OwnedDataBegin(), header.OwnedDataEnd()); + EXPECT_LE(header.OwnedDataBegin(), header.data_size_); + EXPECT_LE(header.OwnedDataEnd(), header.data_size_); + for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); + class_def.VisitMethods(dex_file.get(), [&](const ClassDataItemIterator& it) { + if (it.GetMethodCodeItemOffset() != 0u) { + ASSERT_GE(it.GetMethodCodeItemOffset(), header.OwnedDataBegin()); + ASSERT_LT(it.GetMethodCodeItemOffset(), header.OwnedDataEnd()); + } + }); + } + // Test that the owned sections don't overlap. + for (const std::unique_ptr<const CompactDexFile>& other_dex : compact_dex_files) { + if (dex_file != other_dex) { + ASSERT_TRUE( + (dex_file->GetHeader().OwnedDataBegin() >= other_dex->GetHeader().OwnedDataEnd()) || + (dex_file->GetHeader().OwnedDataEnd() <= other_dex->GetHeader().OwnedDataBegin())); + } + } } } @@ -1834,7 +1861,7 @@ TEST_F(Dex2oatTest, DontExtract) { /*unquicken*/ false, &error_msg)); ASSERT_TRUE(vdex != nullptr); - EXPECT_EQ(vdex->GetHeader().GetDexSize(), 0u) << output_; + EXPECT_FALSE(vdex->HasDexSection()) << output_; } std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), odex_location.c_str(), diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index c72beea6ce..089aa80ad4 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -555,8 +555,9 @@ bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file, const char* location, CreateTypeLookupTable create_type_lookup_table) { DCHECK(write_state_ == WriteState::kAddingDexFileSources); + DCHECK(vdex_file.HasDexSection()); const uint8_t* current_dex_data = nullptr; - for (size_t i = 0; i < vdex_file.GetHeader().GetNumberOfDexFiles(); ++i) { + for (size_t i = 0; i < vdex_file.GetVerifierDepsHeader().GetNumberOfDexFiles(); ++i) { current_dex_data = vdex_file.GetNextDexFileData(current_dex_data); if (current_dex_data == nullptr) { LOG(ERROR) << "Unexpected number of dex files in vdex " << location; @@ -659,7 +660,8 @@ bool OatWriter::WriteAndOpenDexFiles( // Initialize VDEX and OAT headers. // Reserve space for Vdex header and checksums. - vdex_size_ = sizeof(VdexFile::Header) + oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum); + vdex_size_ = sizeof(VdexFile::VerifierDepsHeader) + + oat_dex_files_.size() * sizeof(VdexFile::VdexChecksum); oat_size_ = InitOatHeader(instruction_set, instruction_set_features, dchecked_integral_cast<uint32_t>(oat_dex_files_.size()), @@ -3333,8 +3335,6 @@ bool OatWriter::WriteDexFiles(OutputStream* out, CopyOption copy_dex_files) { TimingLogger::ScopedTiming split("Write Dex files", timings_); - vdex_dex_files_offset_ = vdex_size_; - // If extraction is enabled, only do it if not all the dex files are aligned and uncompressed. if (copy_dex_files == CopyOption::kOnlyIfCompressed) { extract_dex_files_into_vdex_ = false; @@ -3357,6 +3357,9 @@ bool OatWriter::WriteDexFiles(OutputStream* out, } if (extract_dex_files_into_vdex_) { + // Add the dex section header. + vdex_size_ += sizeof(VdexFile::DexSectionHeader); + vdex_dex_files_offset_ = vdex_size_; // Write dex files. for (OatDexFile& oat_dex_file : oat_dex_files_) { if (!WriteDexFile(out, file, &oat_dex_file, update_input_vdex)) { @@ -4076,8 +4079,9 @@ bool OatWriter::WriteDexLayoutSections( bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) { // Write checksums - off_t actual_offset = vdex_out->Seek(sizeof(VdexFile::Header), kSeekSet); - if (actual_offset != sizeof(VdexFile::Header)) { + off_t checksums_offset = sizeof(VdexFile::VerifierDepsHeader); + off_t actual_offset = vdex_out->Seek(checksums_offset, kSeekSet); + if (actual_offset != checksums_offset) { PLOG(ERROR) << "Failed to seek to the checksum location of vdex file. Actual: " << actual_offset << " File: " << vdex_out->GetLocation(); return false; @@ -4094,6 +4098,27 @@ bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) { size_vdex_checksums_ += sizeof(VdexFile::VdexChecksum); } + // Maybe write dex section header. + DCHECK_NE(vdex_verifier_deps_offset_, 0u); + DCHECK_NE(vdex_quickening_info_offset_, 0u); + + bool has_dex_section = extract_dex_files_into_vdex_; + if (has_dex_section) { + DCHECK_NE(vdex_dex_files_offset_, 0u); + size_t dex_section_size = vdex_dex_shared_data_offset_ - vdex_dex_files_offset_; + size_t dex_shared_data_size = vdex_verifier_deps_offset_ - vdex_dex_shared_data_offset_; + size_t quickening_info_section_size = vdex_size_ - vdex_quickening_info_offset_; + + VdexFile::DexSectionHeader dex_section_header(dex_section_size, + dex_shared_data_size, + quickening_info_section_size); + if (!vdex_out->WriteFully(&dex_section_header, sizeof(VdexFile::DexSectionHeader))) { + PLOG(ERROR) << "Failed to write vdex header. File: " << vdex_out->GetLocation(); + return false; + } + size_vdex_header_ += sizeof(VdexFile::DexSectionHeader); + } + // Write header. actual_offset = vdex_out->Seek(0, kSeekSet); if (actual_offset != 0) { @@ -4102,25 +4127,15 @@ bool OatWriter::WriteChecksumsAndVdexHeader(OutputStream* vdex_out) { return false; } - DCHECK_NE(vdex_dex_files_offset_, 0u); - DCHECK_NE(vdex_verifier_deps_offset_, 0u); - DCHECK_NE(vdex_quickening_info_offset_, 0u); - - size_t dex_section_size = vdex_dex_shared_data_offset_ - vdex_dex_files_offset_; - size_t dex_shared_data_size = vdex_verifier_deps_offset_ - vdex_dex_shared_data_offset_; size_t verifier_deps_section_size = vdex_quickening_info_offset_ - vdex_verifier_deps_offset_; - size_t quickening_info_section_size = vdex_size_ - vdex_quickening_info_offset_; - - VdexFile::Header vdex_header(oat_dex_files_.size(), - dex_section_size, - dex_shared_data_size, - verifier_deps_section_size, - quickening_info_section_size); - if (!vdex_out->WriteFully(&vdex_header, sizeof(VdexFile::Header))) { + + VdexFile::VerifierDepsHeader deps_header( + oat_dex_files_.size(), verifier_deps_section_size, has_dex_section); + if (!vdex_out->WriteFully(&deps_header, sizeof(VdexFile::VerifierDepsHeader))) { PLOG(ERROR) << "Failed to write vdex header. File: " << vdex_out->GetLocation(); return false; } - size_vdex_header_ = sizeof(VdexFile::Header); + size_vdex_header_ += sizeof(VdexFile::VerifierDepsHeader); if (!vdex_out->Flush()) { PLOG(ERROR) << "Failed to flush stream after writing to vdex file." diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 00b9abe69b..0148a5792c 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -663,7 +663,8 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { dex_file2_data->GetHeader().file_size_)); ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation()); - const VdexFile::Header &vdex_header = opened_oat_file->GetVdexFile()->GetHeader(); + const VdexFile::DexSectionHeader &vdex_header = + opened_oat_file->GetVdexFile()->GetDexSectionHeader(); if (!compiler_driver_->GetCompilerOptions().IsQuickeningCompilationEnabled()) { // If quickening is enabled we will always write the table since there is no special logic that // checks for all methods not being quickened (not worth the complexity). @@ -672,7 +673,7 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { int64_t actual_vdex_size = vdex_file.GetFile()->GetLength(); ASSERT_GE(actual_vdex_size, 0); - ASSERT_EQ((uint64_t) actual_vdex_size, vdex_header.GetComputedFileSize()); + ASSERT_EQ((uint64_t) actual_vdex_size, opened_oat_file->GetVdexFile()->GetComputedFileSize()); } TEST_F(OatTest, DexFileInputCheckOutput) { diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc index bd76bf11d3..2b4144c611 100644 --- a/dexlayout/compact_dex_writer.cc +++ b/dexlayout/compact_dex_writer.cc @@ -298,6 +298,8 @@ void CompactDexWriter::WriteHeader(Stream* stream) { header.class_defs_off_ = collections.ClassDefsOffset(); header.data_size_ = header_->DataSize(); header.data_off_ = header_->DataOffset(); + header.owned_data_begin_ = owned_data_begin_; + header.owned_data_end_ = owned_data_end_; // Compact dex specific flags. header.debug_info_offsets_pos_ = debug_info_offsets_pos_; @@ -426,6 +428,7 @@ bool CompactDexWriter::Write(DexContainer* output, std::string* error_msg) { // Data section. data_stream->AlignTo(kDataSectionAlignment); } + owned_data_begin_ = data_stream->Tell(); // Write code item first to minimize the space required for encoded methods. // For cdex, the code items don't depend on the debug info. @@ -490,6 +493,7 @@ bool CompactDexWriter::Write(DexContainer* output, std::string* error_msg) { WriteDebugInfoOffsetTable(data_stream); data_stream->AlignTo(kDataSectionAlignment); + owned_data_end_ = data_stream->Tell(); if (compute_offsets_) { header_->SetDataSize(data_stream->Tell()); if (header_->DataSize() != 0) { @@ -497,7 +501,6 @@ bool CompactDexWriter::Write(DexContainer* output, std::string* error_msg) { main_stream->AlignTo(kDataSectionAlignment); // For now, default to saying the data is right after the main stream. header_->SetDataOffset(main_stream->Tell()); - header_->SetDataOffset(0u); } else { header_->SetDataOffset(0u); } diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h index eaf85185f1..4b142a85bb 100644 --- a/dexlayout/compact_dex_writer.h +++ b/dexlayout/compact_dex_writer.h @@ -169,6 +169,10 @@ class CompactDexWriter : public DexWriter { // Base offset of where debug info starts in the dex file. uint32_t debug_info_base_ = 0u; + // Part of the shared data section owned by this file. + uint32_t owned_data_begin_ = 0u; + uint32_t owned_data_end_ = 0u; + // State for where we are deduping. Deduper* code_item_dedupe_ = nullptr; Deduper* data_item_dedupe_ = nullptr; diff --git a/libdexfile/dex/compact_dex_file.h b/libdexfile/dex/compact_dex_file.h index 78cd76818a..affc9a20b0 100644 --- a/libdexfile/dex/compact_dex_file.h +++ b/libdexfile/dex/compact_dex_file.h @@ -51,6 +51,16 @@ class CompactDexFile : public DexFile { return data_size_; } + // Range of the shared data section owned by the dex file. Owned in this context refers to data + // for this DEX that was not deduplicated to another DEX. + uint32_t OwnedDataBegin() const { + return owned_data_begin_; + } + + uint32_t OwnedDataEnd() const { + return owned_data_end_; + } + private: uint32_t feature_flags_ = 0u; @@ -63,6 +73,10 @@ class CompactDexFile : public DexFile { // Base offset of where debug info starts in the dex file. uint32_t debug_info_base_ = 0u; + // Range of the shared data section owned by the dex file. + uint32_t owned_data_begin_ = 0u; + uint32_t owned_data_end_ = 0u; + friend class CompactDexFile; friend class CompactDexWriter; }; diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h index ae0c2f415b..d1b32007c3 100644 --- a/libdexfile/dex/dex_file-inl.h +++ b/libdexfile/dex/dex_file-inl.h @@ -515,6 +515,18 @@ inline const uint8_t* DexFile::GetCatchHandlerData(const DexInstructionIterator& return handler_data + offset; } +template <typename Visitor> +inline void DexFile::ClassDef::VisitMethods(const DexFile* dex_file, const Visitor& visitor) const { + const uint8_t* class_data = dex_file->GetClassData(*this); + if (class_data != nullptr) { + ClassDataItemIterator it(*dex_file, class_data); + it.SkipAllFields(); + for (; it.HasNext(); it.Next()) { + visitor(it); + } + } +} + } // namespace art #endif // ART_LIBDEXFILE_DEX_DEX_FILE_INL_H_ diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h index 5560cf19c0..aeb49d2c25 100644 --- a/libdexfile/dex/dex_file.h +++ b/libdexfile/dex/dex_file.h @@ -196,6 +196,15 @@ class DexFile { DISALLOW_COPY_AND_ASSIGN(MethodId); }; + // Base code_item, compact dex and standard dex have different code item layouts. + struct CodeItem { + protected: + CodeItem() = default; + + private: + DISALLOW_COPY_AND_ASSIGN(CodeItem); + }; + // Raw class_def_item. struct ClassDef { dex::TypeIndex class_idx_; // index into type_ids_ array for this class @@ -227,6 +236,9 @@ class DexFile { } } + template <typename Visitor> + void VisitMethods(const DexFile* dex_file, const Visitor& visitor) const; + private: DISALLOW_COPY_AND_ASSIGN(ClassDef); }; @@ -300,15 +312,6 @@ class DexFile { DISALLOW_COPY_AND_ASSIGN(CallSiteIdItem); }; - // Base code_item, compact dex and standard dex have different code item layouts. - struct CodeItem { - protected: - CodeItem() = default; - - private: - DISALLOW_COPY_AND_ASSIGN(CodeItem); - }; - // Raw try_item. struct TryItem { static constexpr size_t kAlignment = sizeof(uint32_t); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 8069408b5a..9a49fb4752 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -567,7 +567,7 @@ class OatDumper { if (!options_.dump_header_only_) { VariableIndentationOutputStream vios(&os); - VdexFile::Header vdex_header = oat_file_.GetVdexFile()->GetHeader(); + VdexFile::VerifierDepsHeader vdex_header = oat_file_.GetVdexFile()->GetVerifierDepsHeader(); if (vdex_header.IsValid()) { std::string error_msg; std::vector<const DexFile*> dex_files; @@ -584,8 +584,10 @@ class OatDumper { } else { os << "UNRECOGNIZED vdex file, magic " << vdex_header.GetMagic() - << ", version " - << vdex_header.GetVersion() + << ", verifier deps version " + << vdex_header.GetVerifierDepsVersion() + << ", dex section version " + << vdex_header.GetDexSectionVersion() << "\n"; } for (size_t i = 0; i < oat_dex_files_.size(); i++) { diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 3df640902a..b9a9a69ab5 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -65,6 +65,8 @@ namespace art { using android::base::StringPrintf; +namespace { + static const OatHeader* GetOatHeader(const ElfFile* elf_file) { uint64_t off = 0; if (!elf_file->GetSectionOffsetAndSize(".rodata", &off, nullptr)) { @@ -127,6 +129,38 @@ static bool SymlinkFile(const std::string& input_filename, const std::string& ou return true; } +// Holder class for runtime options and related objects. +class PatchoatRuntimeOptionsHolder { + public: + PatchoatRuntimeOptionsHolder(const std::string& image_location, InstructionSet isa) { + options_.push_back(std::make_pair("compilercallbacks", &callbacks_)); + img_ = "-Ximage:" + image_location; + options_.push_back(std::make_pair(img_.c_str(), nullptr)); + isa_name_ = GetInstructionSetString(isa); + options_.push_back(std::make_pair("imageinstructionset", + reinterpret_cast<const void*>(isa_name_.c_str()))); + options_.push_back(std::make_pair("-Xno-sig-chain", nullptr)); + // We do not want the runtime to attempt to patch the image. + options_.push_back(std::make_pair("-Xnorelocate", nullptr)); + // Don't try to compile. + options_.push_back(std::make_pair("-Xnoimage-dex2oat", nullptr)); + // Do not accept broken image. + options_.push_back(std::make_pair("-Xno-dex-file-fallback", nullptr)); + } + + const RuntimeOptions& GetRuntimeOptions() { + return options_; + } + + private: + RuntimeOptions options_; + NoopCompilerCallbacks callbacks_; + std::string isa_name_; + std::string img_; +}; + +} // namespace + bool PatchOat::GeneratePatch( const MemMap& original, const MemMap& relocated, @@ -440,17 +474,10 @@ bool PatchOat::Patch(const std::string& image_location, TimingLogger::ScopedTiming t("Runtime Setup", timings); CHECK_NE(isa, InstructionSet::kNone); - const char* isa_name = GetInstructionSetString(isa); // Set up the runtime - RuntimeOptions options; - NoopCompilerCallbacks callbacks; - options.push_back(std::make_pair("compilercallbacks", &callbacks)); - std::string img = "-Ximage:" + image_location; - options.push_back(std::make_pair(img.c_str(), nullptr)); - options.push_back(std::make_pair("imageinstructionset", reinterpret_cast<const void*>(isa_name))); - options.push_back(std::make_pair("-Xno-sig-chain", nullptr)); - if (!Runtime::Create(options, false)) { + PatchoatRuntimeOptionsHolder options_holder(image_location, isa); + if (!Runtime::Create(options_holder.GetRuntimeOptions(), false)) { LOG(ERROR) << "Unable to initialize runtime"; return false; } @@ -608,17 +635,10 @@ bool PatchOat::Verify(const std::string& image_location, TimingLogger::ScopedTiming t("Runtime Setup", timings); CHECK_NE(isa, InstructionSet::kNone); - const char* isa_name = GetInstructionSetString(isa); // Set up the runtime - RuntimeOptions options; - NoopCompilerCallbacks callbacks; - options.push_back(std::make_pair("compilercallbacks", &callbacks)); - std::string img = "-Ximage:" + image_location; - options.push_back(std::make_pair(img.c_str(), nullptr)); - options.push_back(std::make_pair("imageinstructionset", reinterpret_cast<const void*>(isa_name))); - options.push_back(std::make_pair("-Xno-sig-chain", nullptr)); - if (!Runtime::Create(options, false)) { + PatchoatRuntimeOptionsHolder options_holder(image_location, isa); + if (!Runtime::Create(options_holder.GetRuntimeOptions(), false)) { LOG(ERROR) << "Unable to initialize runtime"; return false; } diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index e3568ea264..09fc2c2c8a 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -323,7 +323,7 @@ SAVE_TWO_REGS x11, x12, 352 SAVE_TWO_REGS x13, x14, 368 SAVE_TWO_REGS x15, x16, 384 - SAVE_REG x17, 400 + SAVE_TWO_REGS x17, x18, 400 SAVE_TWO_REGS x19, x20, 416 SAVE_TWO_REGS x21, x22, 432 SAVE_TWO_REGS x23, x24, 448 @@ -386,7 +386,7 @@ RESTORE_TWO_REGS x11, x12, 352 RESTORE_TWO_REGS x13, x14, 368 RESTORE_TWO_REGS x15, x16, 384 - RESTORE_REG x17, 400 + RESTORE_TWO_REGS x17, x18, 400 RESTORE_TWO_REGS x19, x20, 416 RESTORE_TWO_REGS x21, x22, 432 RESTORE_TWO_REGS x23, x24, 448 @@ -1181,9 +1181,7 @@ ENTRY art_quick_do_long_jump ldp x24, x25, [x0], #-16 ldp x22, x23, [x0], #-16 ldp x20, x21, [x0], #-16 - add x0, x0, #8 - ldr x19, [x0] - sub x0, x0, #24 + ldp x18, x19, [x0], #-16 // X18 & xSELF ldp x16, x17, [x0], #-16 ldp x14, x15, [x0], #-16 ldp x12, x13, [x0], #-16 @@ -2582,7 +2580,7 @@ ENTRY \name SAVE_TWO_REGS x10, x11, 80 SAVE_TWO_REGS x12, x13, 96 SAVE_TWO_REGS x14, x15, 112 - SAVE_REG x17, 128 // Skip x16, i.e. IP0. + SAVE_TWO_REGS x17, x18, 128 // Skip x16, i.e. IP0. SAVE_TWO_REGS x19, xLR, 144 // Save also return address. // Save all potentially live caller-save floating-point registers. stp d0, d1, [sp, #160] @@ -2616,7 +2614,7 @@ ENTRY \name POP_REGS_NE x10, x11, 80, \xreg POP_REGS_NE x12, x13, 96, \xreg POP_REGS_NE x14, x15, 112, \xreg - POP_REG_NE x17, 128, \xreg + POP_REGS_NE x17, x18, 128, \xreg POP_REGS_NE x19, xLR, 144, \xreg // Restore also return address. // Restore floating-point registers. ldp d0, d1, [sp, #160] @@ -2701,7 +2699,7 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg29, w29, x29 SELECT_X_OR_W_FOR_MACRO \macro_for_register, x15, w15, \xreg \macro_for_reserved_register // IP0 is reserved \macro_for_reserved_register // IP1 is reserved - \macro_for_reserved_register // X18 is reserved + SELECT_X_OR_W_FOR_MACRO \macro_for_register, x18, w18, \xreg SELECT_X_OR_W_FOR_MACRO \macro_for_register, x19, w19, \xreg SELECT_X_OR_W_FOR_MACRO \macro_for_register, x20, w20, \xreg SELECT_X_OR_W_FOR_MACRO \macro_for_register, x21, w21, \xreg @@ -2759,7 +2757,7 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg29, w29, x29 SAVE_TWO_REGS x10, x11, 80 SAVE_TWO_REGS x12, x13, 96 SAVE_TWO_REGS x14, x15, 112 - SAVE_REG x19, 136 // Skip x16, x17, i.e. IP0, IP1. + SAVE_TWO_REGS x18, x19, 128 // Skip x16, x17, i.e. IP0, IP1. SAVE_REG xLR, 144 // Save return address, skip padding at 152. // Save all potentially live caller-save floating-point registers. stp d0, d1, [sp, #160] @@ -2788,7 +2786,7 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg29, w29, x29 RESTORE_TWO_REGS x10, x11, 80 RESTORE_TWO_REGS x12, x13, 96 RESTORE_TWO_REGS x14, x15, 112 - RESTORE_REG x19, 136 // Skip x16, x17, i.e. IP0, IP1. + RESTORE_TWO_REGS x18, x19, 128 // Skip x16, x17, i.e. IP0, IP1. RESTORE_REG xLR, 144 // Restore return address. // Restore caller-save floating-point registers. ldp d0, d1, [sp, #160] diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h index 8bf91d9da1..1565644380 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -374,13 +374,14 @@ inline ObjPtr<mirror::Class> ArtMethod::ResolveReturnType() { return ResolveClassFromTypeIndex(GetReturnTypeIndex()); } +template <ReadBarrierOption kReadBarrierOption> inline bool ArtMethod::HasSingleImplementation() { - if (IsFinal() || GetDeclaringClass()->IsFinal()) { + if (IsFinal<kReadBarrierOption>() || GetDeclaringClass<kReadBarrierOption>()->IsFinal()) { // We don't set kAccSingleImplementation for these cases since intrinsic // can use the flag also. return true; } - return (GetAccessFlags() & kAccSingleImplementation) != 0; + return (GetAccessFlags<kReadBarrierOption>() & kAccSingleImplementation) != 0; } inline bool ArtMethod::IsHiddenIntrinsic(uint32_t ordinal) { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index bbc60072b6..f3c495957f 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -88,13 +88,18 @@ ArtMethod* ArtMethod::GetNonObsoleteMethod() { } } +template <ReadBarrierOption kReadBarrierOption> ArtMethod* ArtMethod::GetSingleImplementation(PointerSize pointer_size) { - if (!IsAbstract()) { + if (!IsAbstract<kReadBarrierOption>()) { // A non-abstract's single implementation is itself. return this; } return reinterpret_cast<ArtMethod*>(GetDataPtrSize(pointer_size)); } +template ArtMethod* ArtMethod::GetSingleImplementation<ReadBarrierOption::kWithReadBarrier>( + PointerSize pointer_size); +template ArtMethod* ArtMethod::GetSingleImplementation<ReadBarrierOption::kWithoutReadBarrier>( + PointerSize pointer_size); ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject jlr_method) { diff --git a/runtime/art_method.h b/runtime/art_method.h index 5d9b729847..bd9b64df36 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -172,12 +172,14 @@ class ArtMethod FINAL { return (GetAccessFlags() & synchonized) != 0; } + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsFinal() { - return (GetAccessFlags() & kAccFinal) != 0; + return (GetAccessFlags<kReadBarrierOption>() & kAccFinal) != 0; } + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsIntrinsic() { - return (GetAccessFlags() & kAccIntrinsic) != 0; + return (GetAccessFlags<kReadBarrierOption>() & kAccIntrinsic) != 0; } ALWAYS_INLINE void SetIntrinsic(uint32_t intrinsic) REQUIRES_SHARED(Locks::mutator_lock_); @@ -241,10 +243,11 @@ class ArtMethod FINAL { } // This is set by the class linker. + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsDefault() { static_assert((kAccDefault & (kAccIntrinsic | kAccIntrinsicBits)) == 0, "kAccDefault conflicts with intrinsic modifier"); - return (GetAccessFlags() & kAccDefault) != 0; + return (GetAccessFlags<kReadBarrierOption>() & kAccDefault) != 0; } template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> @@ -279,8 +282,9 @@ class ArtMethod FINAL { return (GetAccessFlags() & mask) == mask; } + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool IsAbstract() { - return (GetAccessFlags() & kAccAbstract) != 0; + return (GetAccessFlags<kReadBarrierOption>() & kAccAbstract) != 0; } bool IsSynthetic() { @@ -315,12 +319,13 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccPreviouslyWarm) != 0; } + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> void SetPreviouslyWarm() { - if (IsIntrinsic()) { + if (IsIntrinsic<kReadBarrierOption>()) { // kAccPreviouslyWarm overlaps with kAccIntrinsicBits. return; } - AddAccessFlags(kAccPreviouslyWarm); + AddAccessFlags<kReadBarrierOption>(kAccPreviouslyWarm); } // Should this method be run in the interpreter and count locks (e.g., failed structured- @@ -494,6 +499,7 @@ class ArtMethod FINAL { return DataOffset(kRuntimePointerSize); } + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE bool HasSingleImplementation() REQUIRES_SHARED(Locks::mutator_lock_); ALWAYS_INLINE void SetHasSingleImplementation(bool single_impl) { @@ -511,12 +517,15 @@ class ArtMethod FINAL { ArtMethod* GetCanonicalMethod(PointerSize pointer_size = kRuntimePointerSize) REQUIRES_SHARED(Locks::mutator_lock_); + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ArtMethod* GetSingleImplementation(PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE void SetSingleImplementation(ArtMethod* method, PointerSize pointer_size) { - DCHECK(!IsNative()); - DCHECK(IsAbstract()); // Non-abstract method's single implementation is just itself. + DCHECK(!IsNative<kReadBarrierOption>()); + // Non-abstract method's single implementation is just itself. + DCHECK(IsAbstract<kReadBarrierOption>()); SetDataPtrSize(method, pointer_size); } @@ -839,8 +848,11 @@ class ArtMethod FINAL { } // This setter guarantees atomicity. + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier> void AddAccessFlags(uint32_t flag) { - DCHECK(!IsIntrinsic() || !OverlapsIntrinsicBits(flag) || IsValidIntrinsicUpdate(flag)); + DCHECK(!IsIntrinsic<kReadBarrierOption>() || + !OverlapsIntrinsicBits(flag) || + IsValidIntrinsicUpdate(flag)); uint32_t old_access_flags; uint32_t new_access_flags; do { diff --git a/runtime/cha.cc b/runtime/cha.cc index a53d7e5b25..f2e6a7314e 100644 --- a/runtime/cha.cc +++ b/runtime/cha.cc @@ -21,6 +21,7 @@ #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "linear_alloc.h" +#include "mirror/class_loader.h" #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "stack.h" @@ -77,6 +78,106 @@ void ClassHierarchyAnalysis::RemoveDependentsWithMethodHeaders( } } +void ClassHierarchyAnalysis::ResetSingleImplementationInHierarchy(ObjPtr<mirror::Class> klass, + const LinearAlloc* alloc, + const PointerSize pointer_size) + const { + // Presumably called from some sort of class visitor, no null pointers expected. + DCHECK(klass != nullptr); + DCHECK(alloc != nullptr); + + // Skip interfaces since they cannot provide SingleImplementations to work with. + if (klass->IsInterface()) { + return; + } + + // This method is called while visiting classes in the class table of a class loader. + // That means, some 'klass'es can belong to other classloaders. Argument 'alloc' + // allows to explicitly indicate a classloader, which is going to be deleted. + // Filter out classes, that do not belong to it. + if (!alloc->ContainsUnsafe(klass->GetMethodsPtr())) { + return; + } + + // CHA analysis is only applied to resolved classes. + if (!klass->IsResolved()) { + return; + } + + ObjPtr<mirror::Class> super = klass->GetSuperClass<kDefaultVerifyFlags, kWithoutReadBarrier>(); + + // Skip Object class and primitive classes. + if (super == nullptr) { + return; + } + + // The class is going to be deleted. Iterate over the virtual methods of its superclasses to see + // if they have SingleImplementations methods defined by 'klass'. + // Skip all virtual methods that do not override methods from super class since they cannot be + // SingleImplementations for anything. + int32_t vtbl_size = super->GetVTableLength<kDefaultVerifyFlags, kWithoutReadBarrier>(); + ObjPtr<mirror::ClassLoader> loader = + klass->GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>(); + for (int vtbl_index = 0; vtbl_index < vtbl_size; ++vtbl_index) { + ArtMethod* method = + klass->GetVTableEntry<kDefaultVerifyFlags, kWithoutReadBarrier>(vtbl_index, pointer_size); + if (!alloc->ContainsUnsafe(method)) { + continue; + } + + // Find all occurrences of virtual methods in parents' SingleImplementations fields + // and reset them. + // No need to reset SingleImplementations for the method itself (it will be cleared anyways), + // so start with a superclass and move up looking into a corresponding vtbl slot. + for (ObjPtr<mirror::Class> super_it = super; + super_it != nullptr && + super_it->GetVTableLength<kDefaultVerifyFlags, kWithoutReadBarrier>() > vtbl_index; + super_it = super_it->GetSuperClass<kDefaultVerifyFlags, kWithoutReadBarrier>()) { + // Skip superclasses that are also going to be unloaded. + ObjPtr<mirror::ClassLoader> super_loader = super_it-> + GetClassLoader<kDefaultVerifyFlags, kWithoutReadBarrier>(); + if (super_loader == loader) { + continue; + } + + ArtMethod* super_method = super_it-> + GetVTableEntry<kDefaultVerifyFlags, kWithoutReadBarrier>(vtbl_index, pointer_size); + if (super_method->IsAbstract<kWithoutReadBarrier>() && + super_method->HasSingleImplementation<kWithoutReadBarrier>() && + super_method->GetSingleImplementation<kWithoutReadBarrier>(pointer_size) == method) { + // Do like there was no single implementation defined previously + // for this method of the superclass. + super_method->SetSingleImplementation<kWithoutReadBarrier>(nullptr, pointer_size); + } else { + // No related SingleImplementations could possibly be found any further. + DCHECK(!super_method->HasSingleImplementation<kWithoutReadBarrier>()); + break; + } + } + } + + // Check all possible interface methods too. + ObjPtr<mirror::IfTable> iftable = klass->GetIfTable<kDefaultVerifyFlags, kWithoutReadBarrier>(); + const size_t ifcount = klass->GetIfTableCount<kDefaultVerifyFlags, kWithoutReadBarrier>(); + for (size_t i = 0; i < ifcount; ++i) { + ObjPtr<mirror::Class> interface = + iftable->GetInterface<kDefaultVerifyFlags, kWithoutReadBarrier>(i); + for (size_t j = 0, + count = iftable->GetMethodArrayCount<kDefaultVerifyFlags, kWithoutReadBarrier>(i); + j < count; + ++j) { + ArtMethod* method = interface->GetVirtualMethod(j, pointer_size); + if (method->HasSingleImplementation<kWithoutReadBarrier>() && + alloc->ContainsUnsafe( + method->GetSingleImplementation<kWithoutReadBarrier>(pointer_size)) && + !method->IsDefault<kWithoutReadBarrier>()) { + // Do like there was no single implementation defined previously for this method. + method->SetSingleImplementation<kWithoutReadBarrier>(nullptr, pointer_size); + } + } + } +} + // This stack visitor walks the stack and for compiled code with certain method // headers, sets the should_deoptimize flag on stack to 1. // TODO: also set the register value to 1 when should_deoptimize is allocated in diff --git a/runtime/cha.h b/runtime/cha.h index 40999dd15b..d1a1b7cfae 100644 --- a/runtime/cha.h +++ b/runtime/cha.h @@ -110,6 +110,17 @@ class ClassHierarchyAnalysis { const std::unordered_set<OatQuickMethodHeader*>& method_headers) REQUIRES(Locks::cha_lock_); + // If a given class belongs to a linear allocation that is about to be deleted, in all its + // superclasses and superinterfaces reset SingleImplementation fields of their methods + // that might be affected by the deletion. + // The method is intended to be called during GC before ReclaimPhase, since it gets info from + // Java objects that are going to be collected. + // For the same reason it's important to access objects without read barrier to not revive them. + void ResetSingleImplementationInHierarchy(ObjPtr<mirror::Class> klass, + const LinearAlloc* alloc, + PointerSize pointer_size) + const REQUIRES_SHARED(Locks::mutator_lock_); + // Update CHA info for methods that `klass` overrides, after loading `klass`. void UpdateAfterLoadingOf(Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 3f84ffd964..bd8da619cd 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1169,6 +1169,25 @@ static bool FlattenPathClassLoader(ObjPtr<mirror::ClassLoader> class_loader, return true; } +class CHAOnDeleteUpdateClassVisitor { + public: + explicit CHAOnDeleteUpdateClassVisitor(LinearAlloc* alloc) + : allocator_(alloc), cha_(Runtime::Current()->GetClassLinker()->GetClassHierarchyAnalysis()), + pointer_size_(Runtime::Current()->GetClassLinker()->GetImagePointerSize()), + self_(Thread::Current()) {} + + bool operator()(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { + // This class is going to be unloaded. Tell CHA about it. + cha_->ResetSingleImplementationInHierarchy(klass, allocator_, pointer_size_); + return true; + } + private: + const LinearAlloc* allocator_; + const ClassHierarchyAnalysis* cha_; + const PointerSize pointer_size_; + const Thread* self_; +}; + class VerifyDeclaringClassVisitor : public ArtMethodVisitor { public: VerifyDeclaringClassVisitor() REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) @@ -2168,12 +2187,14 @@ ClassLinker::~ClassLinker() { mirror::EmulatedStackFrame::ResetClass(); Thread* const self = Thread::Current(); for (const ClassLoaderData& data : class_loaders_) { - DeleteClassLoader(self, data); + // CHA unloading analysis is not needed. No negative consequences are expected because + // all the classloaders are deleted at the same time. + DeleteClassLoader(self, data, false /*cleanup_cha*/); } class_loaders_.clear(); } -void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data) { +void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data, bool cleanup_cha) { Runtime* const runtime = Runtime::Current(); JavaVMExt* const vm = runtime->GetJavaVM(); vm->DeleteWeakGlobalRef(self, data.weak_root); @@ -2188,6 +2209,12 @@ void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data) { // If we don't have a JIT, we need to manually remove the CHA dependencies manually. cha_->RemoveDependenciesForLinearAlloc(data.allocator); } + // Cleanup references to single implementation ArtMethods that will be deleted. + if (cleanup_cha) { + CHAOnDeleteUpdateClassVisitor visitor(data.allocator); + data.class_table->Visit<CHAOnDeleteUpdateClassVisitor, kWithoutReadBarrier>(visitor); + } + delete data.allocator; delete data.class_table; } @@ -8952,7 +8979,8 @@ void ClassLinker::CleanupClassLoaders() { } } for (ClassLoaderData& data : to_delete) { - DeleteClassLoader(self, data); + // CHA unloading analysis and SingleImplementaion cleanups are required. + DeleteClassLoader(self, data, true /*cleanup_cha*/); } } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 712e3aeffa..8ab3f72c1b 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -752,7 +752,7 @@ class ClassLinker { REQUIRES(!Locks::dex_lock_) REQUIRES_SHARED(Locks::mutator_lock_); - void DeleteClassLoader(Thread* self, const ClassLoaderData& data) + void DeleteClassLoader(Thread* self, const ClassLoaderData& data, bool cleanup_cha) REQUIRES_SHARED(Locks::mutator_lock_); void VisitClassesInternal(ClassVisitor* visitor) diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h index 718e93a97d..c59e2e881d 100644 --- a/runtime/class_table-inl.h +++ b/runtime/class_table-inl.h @@ -60,12 +60,12 @@ void ClassTable::VisitRoots(const Visitor& visitor) { } } -template <typename Visitor> +template <typename Visitor, ReadBarrierOption kReadBarrierOption> bool ClassTable::Visit(Visitor& visitor) { ReaderMutexLock mu(Thread::Current(), lock_); for (ClassSet& class_set : classes_) { for (TableSlot& table_slot : class_set) { - if (!visitor(table_slot.Read())) { + if (!visitor(table_slot.Read<kReadBarrierOption>())) { return false; } } @@ -73,12 +73,12 @@ bool ClassTable::Visit(Visitor& visitor) { return true; } -template <typename Visitor> +template <typename Visitor, ReadBarrierOption kReadBarrierOption> bool ClassTable::Visit(const Visitor& visitor) { ReaderMutexLock mu(Thread::Current(), lock_); for (ClassSet& class_set : classes_) { for (TableSlot& table_slot : class_set) { - if (!visitor(table_slot.Read())) { + if (!visitor(table_slot.Read<kReadBarrierOption>())) { return false; } } diff --git a/runtime/class_table.h b/runtime/class_table.h index 48129b1241..19c29b5768 100644 --- a/runtime/class_table.h +++ b/runtime/class_table.h @@ -190,11 +190,11 @@ class ClassTable { REQUIRES_SHARED(Locks::mutator_lock_); // Stops visit if the visitor returns false. - template <typename Visitor> + template <typename Visitor, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool Visit(Visitor& visitor) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); - template <typename Visitor> + template <typename Visitor, ReadBarrierOption kReadBarrierOption = kWithReadBarrier> bool Visit(const Visitor& visitor) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 3dc2cb572e..3011c37f3a 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -145,15 +145,15 @@ static constexpr bool kUsePartialTlabs = true; #if defined(__LP64__) || !defined(ADDRESS_SANITIZER) // 300 MB (0x12c00000) - (default non-moving space capacity). -static uint8_t* const kPreferredAllocSpaceBegin = - reinterpret_cast<uint8_t*>(300 * MB - Heap::kDefaultNonMovingSpaceCapacity); +uint8_t* const Heap::kPreferredAllocSpaceBegin = + reinterpret_cast<uint8_t*>(300 * MB - kDefaultNonMovingSpaceCapacity); #else #ifdef __ANDROID__ // For 32-bit Android, use 0x20000000 because asan reserves 0x04000000 - 0x20000000. -static uint8_t* const kPreferredAllocSpaceBegin = reinterpret_cast<uint8_t*>(0x20000000); +uint8_t* const Heap::kPreferredAllocSpaceBegin = reinterpret_cast<uint8_t*>(0x20000000); #else // For 32-bit host, use 0x40000000 because asan uses most of the space below this. -static uint8_t* const kPreferredAllocSpaceBegin = reinterpret_cast<uint8_t*>(0x40000000); +uint8_t* const Heap::kPreferredAllocSpaceBegin = reinterpret_cast<uint8_t*>(0x40000000); #endif #endif @@ -382,10 +382,10 @@ Heap::Heap(size_t initial_size, const char* space_name = is_zygote ? kZygoteSpaceName : kNonMovingSpaceName; // Reserve the non moving mem map before the other two since it needs to be at a specific // address. - non_moving_space_mem_map.reset( - MemMap::MapAnonymous(space_name, requested_alloc_space_begin, - non_moving_space_capacity, PROT_READ | PROT_WRITE, true, false, - &error_str)); + non_moving_space_mem_map.reset(MapAnonymousPreferredAddress(space_name, + requested_alloc_space_begin, + non_moving_space_capacity, + &error_str)); CHECK(non_moving_space_mem_map != nullptr) << error_str; // Try to reserve virtual memory at a lower address if we have a separate non moving space. request_begin = kPreferredAllocSpaceBegin + non_moving_space_capacity; diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 4de03318a0..5ce01bc9d2 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -144,6 +144,7 @@ class Heap { static constexpr size_t kDefaultLargeObjectThreshold = kMinLargeObjectThreshold; // Whether or not parallel GC is enabled. If not, then we never create the thread pool. static constexpr bool kDefaultEnableParallelGC = false; + static uint8_t* const kPreferredAllocSpaceBegin; // Whether or not we use the free list large object space. Only use it if USE_ART_LOW_4G_ALLOCATOR // since this means that we have to use the slow msync loop in MemMap::MapAnonymous. diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc index 2def52450b..c6b2120f5b 100644 --- a/runtime/gc/heap_test.cc +++ b/runtime/gc/heap_test.cc @@ -27,7 +27,26 @@ namespace art { namespace gc { -class HeapTest : public CommonRuntimeTest {}; +class HeapTest : public CommonRuntimeTest { + public: + void SetUp() OVERRIDE { + MemMap::Init(); + std::string error_msg; + // Reserve the preferred address to force the heap to use another one for testing. + reserved_.reset(MemMap::MapAnonymous("ReserveMap", + gc::Heap::kPreferredAllocSpaceBegin, + 16 * KB, + PROT_READ, + /*low_4gb*/ true, + /*reuse*/ false, + &error_msg)); + ASSERT_TRUE(reserved_ != nullptr) << error_msg; + CommonRuntimeTest::SetUp(); + } + + private: + std::unique_ptr<MemMap> reserved_; +}; TEST_F(HeapTest, ClearGrowthLimit) { Heap* heap = Runtime::Current()->GetHeap(); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 51a63ddf8a..f0f0df4f5e 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -684,7 +684,11 @@ void JitCodeCache::CopyInlineCacheInto(const InlineCache& ic, static void ClearMethodCounter(ArtMethod* method, bool was_warm) { if (was_warm) { - method->SetPreviouslyWarm(); + // Don't do any read barrier, as the declaring class of `method` may + // be in the process of being GC'ed (reading the declaring class is done + // when DCHECKing the declaring class is resolved, which we know it is + // at this point). + method->SetPreviouslyWarm<kWithoutReadBarrier>(); } // We reset the counter to 1 so that the profile knows that the method was executed at least once. // This is required for layout purposes. diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index ee7d217e8d..f63f105c3a 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -304,20 +304,25 @@ inline bool Class::HasVTable() { return GetVTable() != nullptr || ShouldHaveEmbeddedVTable(); } + template<VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption> inline int32_t Class::GetVTableLength() { - if (ShouldHaveEmbeddedVTable()) { + if (ShouldHaveEmbeddedVTable<kVerifyFlags, kReadBarrierOption>()) { return GetEmbeddedVTableLength(); } - return GetVTable() != nullptr ? GetVTable()->GetLength() : 0; + return GetVTable<kVerifyFlags, kReadBarrierOption>() != nullptr ? + GetVTable<kVerifyFlags, kReadBarrierOption>()->GetLength() : 0; } + template<VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption> inline ArtMethod* Class::GetVTableEntry(uint32_t i, PointerSize pointer_size) { - if (ShouldHaveEmbeddedVTable()) { + if (ShouldHaveEmbeddedVTable<kVerifyFlags, kReadBarrierOption>()) { return GetEmbeddedVTableEntry(i, pointer_size); } - auto* vtable = GetVTable(); + auto* vtable = GetVTable<kVerifyFlags, kReadBarrierOption>(); DCHECK(vtable != nullptr); - return vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size); + return vtable->template GetElementPtrSize<ArtMethod*, kVerifyFlags, kReadBarrierOption>(i, pointer_size); } inline int32_t Class::GetEmbeddedVTableLength() { @@ -627,8 +632,10 @@ inline IfTable* Class::GetIfTable() { return ret.Ptr(); } +template<VerifyObjectFlags kVerifyFlags, + ReadBarrierOption kReadBarrierOption> inline int32_t Class::GetIfTableCount() { - return GetIfTable()->Count(); + return GetIfTable<kVerifyFlags, kReadBarrierOption>()->Count(); } inline void Class::SetIfTable(ObjPtr<IfTable> new_iftable) { diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index ea065676a0..51d1376a3c 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -808,8 +808,12 @@ class MANAGED Class FINAL : public Object { static MemberOffset EmbeddedVTableEntryOffset(uint32_t i, PointerSize pointer_size); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> int32_t GetVTableLength() REQUIRES_SHARED(Locks::mutator_lock_); + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ArtMethod* GetVTableEntry(uint32_t i, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); @@ -941,6 +945,8 @@ class MANAGED Class FINAL : public Object { return (GetAccessFlags() & kAccRecursivelyInitialized) != 0; } + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE int32_t GetIfTableCount() REQUIRES_SHARED(Locks::mutator_lock_); template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, diff --git a/runtime/mirror/iftable.h b/runtime/mirror/iftable.h index 296c163ef7..d72c7866c5 100644 --- a/runtime/mirror/iftable.h +++ b/runtime/mirror/iftable.h @@ -25,8 +25,11 @@ namespace mirror { class MANAGED IfTable FINAL : public ObjectArray<Object> { public: + template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, + ReadBarrierOption kReadBarrierOption = kWithReadBarrier> ALWAYS_INLINE Class* GetInterface(int32_t i) REQUIRES_SHARED(Locks::mutator_lock_) { - Class* interface = GetWithoutChecks((i * kMax) + kInterface)->AsClass(); + Class* interface = + GetWithoutChecks<kVerifyFlags, kReadBarrierOption>((i * kMax) + kInterface)->AsClass(); DCHECK(interface != nullptr); return interface; } diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 5888c37582..38ca4c9623 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -408,7 +408,7 @@ bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* er return true; } - uint32_t number_of_dex_files = file.GetHeader().GetNumberOfDexFiles(); + uint32_t number_of_dex_files = file.GetVerifierDepsHeader().GetNumberOfDexFiles(); if (required_dex_checksums->size() != number_of_dex_files) { *error_msg = StringPrintf("expected %zu dex files but found %u", required_dex_checksums->size(), diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index ba640556df..ec4dc417d3 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -36,32 +36,52 @@ namespace art { -constexpr uint8_t VdexFile::Header::kVdexInvalidMagic[4]; -constexpr uint8_t VdexFile::Header::kVdexMagic[4]; -constexpr uint8_t VdexFile::Header::kVdexVersion[4]; +constexpr uint8_t VdexFile::VerifierDepsHeader::kVdexInvalidMagic[4]; +constexpr uint8_t VdexFile::VerifierDepsHeader::kVdexMagic[4]; +constexpr uint8_t VdexFile::VerifierDepsHeader::kVerifierDepsVersion[4]; +constexpr uint8_t VdexFile::VerifierDepsHeader::kDexSectionVersion[4]; +constexpr uint8_t VdexFile::VerifierDepsHeader::kDexSectionVersionEmpty[4]; -bool VdexFile::Header::IsMagicValid() const { +bool VdexFile::VerifierDepsHeader::IsMagicValid() const { return (memcmp(magic_, kVdexMagic, sizeof(kVdexMagic)) == 0); } -bool VdexFile::Header::IsVersionValid() const { - return (memcmp(version_, kVdexVersion, sizeof(kVdexVersion)) == 0); +bool VdexFile::VerifierDepsHeader::IsVerifierDepsVersionValid() const { + return (memcmp(verifier_deps_version_, kVerifierDepsVersion, sizeof(kVerifierDepsVersion)) == 0); } -VdexFile::Header::Header(uint32_t number_of_dex_files, - uint32_t dex_size, - uint32_t dex_shared_data_size, - uint32_t verifier_deps_size, - uint32_t quickening_info_size) +bool VdexFile::VerifierDepsHeader::IsDexSectionVersionValid() const { + return (memcmp(dex_section_version_, kDexSectionVersion, sizeof(kDexSectionVersion)) == 0) || + (memcmp(dex_section_version_, kDexSectionVersionEmpty, sizeof(kDexSectionVersionEmpty)) == 0); +} + +bool VdexFile::VerifierDepsHeader::HasDexSection() const { + return (memcmp(dex_section_version_, kDexSectionVersion, sizeof(kDexSectionVersion)) == 0); +} + +VdexFile::VerifierDepsHeader::VerifierDepsHeader(uint32_t number_of_dex_files, + uint32_t verifier_deps_size, + bool has_dex_section) : number_of_dex_files_(number_of_dex_files), - dex_size_(dex_size), - dex_shared_data_size_(dex_shared_data_size), - verifier_deps_size_(verifier_deps_size), - quickening_info_size_(quickening_info_size) { + verifier_deps_size_(verifier_deps_size) { memcpy(magic_, kVdexMagic, sizeof(kVdexMagic)); - memcpy(version_, kVdexVersion, sizeof(kVdexVersion)); + memcpy(verifier_deps_version_, kVerifierDepsVersion, sizeof(kVerifierDepsVersion)); + if (has_dex_section) { + memcpy(dex_section_version_, kDexSectionVersion, sizeof(kDexSectionVersion)); + } else { + memcpy(dex_section_version_, kDexSectionVersionEmpty, sizeof(kDexSectionVersionEmpty)); + } DCHECK(IsMagicValid()); - DCHECK(IsVersionValid()); + DCHECK(IsVerifierDepsVersionValid()); + DCHECK(IsDexSectionVersionValid()); +} + +VdexFile::DexSectionHeader::DexSectionHeader(uint32_t dex_size, + uint32_t dex_shared_data_size, + uint32_t quickening_info_size) + : dex_size_(dex_size), + dex_shared_data_size_(dex_shared_data_size), + quickening_info_size_(quickening_info_size) { } std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr, @@ -145,7 +165,7 @@ std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr, return nullptr; } - if (unquicken) { + if (unquicken && vdex->HasDexSection()) { std::vector<std::unique_ptr<const DexFile>> unique_ptr_dex_files; if (!vdex->OpenAllDexFiles(&unique_ptr_dex_files, error_msg)) { return nullptr; @@ -153,7 +173,8 @@ std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr, vdex->Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), /* decompile_return_instruction */ false); // Update the quickening info size to pretend there isn't any. - reinterpret_cast<Header*>(vdex->mmap_->Begin())->quickening_info_size_ = 0; + size_t offset = vdex->GetDexSectionHeaderOffset(); + reinterpret_cast<DexSectionHeader*>(vdex->mmap_->Begin() + offset)->quickening_info_size_ = 0; } *error_msg = "Success"; diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index 72f03f266a..326fcbc1fe 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -35,51 +35,53 @@ class DexFile; // memory and provides tools for accessing its individual sections. // // File format: -// VdexFile::Header fixed-length header +// VdexFile::VerifierDepsHeader fixed-length header +// Dex file checksums +// +// Optionally: +// VdexFile::DexSectionHeader fixed-length header +// +// quicken_table_off[0] offset into QuickeningInfo section for offset table for DEX[0]. +// DEX[0] array of the input DEX files, the bytecode may have been quickened. +// quicken_table_off[1] +// DEX[1] +// ... +// DEX[D] // -// quicken_table_off[0] offset into QuickeningInfo section for offset table for DEX[0]. -// DEX[0] array of the input DEX files, the bytecode may have been quickened. -// quicken_table_off[1] -// DEX[1] -// ... -// DEX[D] // VerifierDeps // uint8[D][] verification dependencies -// QuickeningInfo -// uint8[D][] quickening data -// uint32[D][] quickening data offset tables +// +// Optionally: +// QuickeningInfo +// uint8[D][] quickening data +// uint32[D][] quickening data offset tables class VdexFile { public: - struct Header { + struct VerifierDepsHeader { public: - Header(uint32_t number_of_dex_files_, - uint32_t dex_size, - uint32_t dex_shared_data_size, - uint32_t verifier_deps_size, - uint32_t quickening_info_size); + VerifierDepsHeader(uint32_t number_of_dex_files_, + uint32_t verifier_deps_size, + bool has_dex_section); const char* GetMagic() const { return reinterpret_cast<const char*>(magic_); } - const char* GetVersion() const { return reinterpret_cast<const char*>(version_); } + const char* GetVerifierDepsVersion() const { + return reinterpret_cast<const char*>(verifier_deps_version_); + } + const char* GetDexSectionVersion() const { + return reinterpret_cast<const char*>(dex_section_version_); + } bool IsMagicValid() const; - bool IsVersionValid() const; - bool IsValid() const { return IsMagicValid() && IsVersionValid(); } + bool IsVerifierDepsVersionValid() const; + bool IsDexSectionVersionValid() const; + bool IsValid() const { + return IsMagicValid() && IsVerifierDepsVersionValid() && IsDexSectionVersionValid(); + } + bool HasDexSection() const; - uint32_t GetDexSize() const { return dex_size_; } - uint32_t GetDexSharedDataSize() const { return dex_shared_data_size_; } uint32_t GetVerifierDepsSize() const { return verifier_deps_size_; } - uint32_t GetQuickeningInfoSize() const { return quickening_info_size_; } uint32_t GetNumberOfDexFiles() const { return number_of_dex_files_; } - size_t GetComputedFileSize() const { - return sizeof(Header) + - GetSizeOfChecksumsSection() + - GetDexSize() + - GetDexSharedDataSize() + - GetVerifierDepsSize() + - GetQuickeningInfoSize(); - } - size_t GetSizeOfChecksumsSection() const { return sizeof(VdexChecksum) * GetNumberOfDexFiles(); } @@ -88,20 +90,63 @@ class VdexFile { private: static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; - // Last update: Change quickening info table format. - static constexpr uint8_t kVdexVersion[] = { '0', '1', '8', '\0' }; + + // The format version of the verifier deps header and the verifier deps. + // Last update: Add DexSectionHeader + static constexpr uint8_t kVerifierDepsVersion[] = { '0', '1', '9', '\0' }; + + // The format version of the dex section header and the dex section, containing + // both the dex code and the quickening data. + // Last update: Add owned section for CompactDex. + static constexpr uint8_t kDexSectionVersion[] = { '0', '0', '2', '\0' }; + + // If the .vdex file has no dex section (hence no dex code nor quickening data), + // we encode this magic version. + static constexpr uint8_t kDexSectionVersionEmpty[] = { '0', '0', '0', '\0' }; uint8_t magic_[4]; - uint8_t version_[4]; + uint8_t verifier_deps_version_[4]; + uint8_t dex_section_version_[4]; uint32_t number_of_dex_files_; + uint32_t verifier_deps_size_; + }; + + struct DexSectionHeader { + public: + DexSectionHeader(uint32_t dex_size, + uint32_t dex_shared_data_size, + uint32_t quickening_info_size); + + uint32_t GetDexSize() const { return dex_size_; } + uint32_t GetDexSharedDataSize() const { return dex_shared_data_size_; } + uint32_t GetQuickeningInfoSize() const { return quickening_info_size_; } + + size_t GetDexSectionSize() const { + return sizeof(DexSectionHeader) + + GetDexSize() + + GetDexSharedDataSize(); + } + + private: uint32_t dex_size_; uint32_t dex_shared_data_size_; - uint32_t verifier_deps_size_; uint32_t quickening_info_size_; - friend class VdexFile; + friend class VdexFile; // For updatig quickening_info_size_. }; + size_t GetComputedFileSize() const { + size_t size = sizeof(VerifierDepsHeader); + const VerifierDepsHeader& header = GetVerifierDepsHeader(); + size += header.GetVerifierDepsSize(); + size += header.GetSizeOfChecksumsSection(); + if (header.HasDexSection()) { + size += GetDexSectionHeader().GetDexSectionSize(); + size += GetDexSectionHeader().GetQuickeningInfoSize(); + } + return size; + } + // Note: The file is called "primary" to match the naming with profiles. static const constexpr char* kVdexNameInDmFile = "primary.vdex"; @@ -174,24 +219,48 @@ class VdexFile { const uint8_t* End() const { return mmap_->End(); } size_t Size() const { return mmap_->Size(); } - const Header& GetHeader() const { - return *reinterpret_cast<const Header*>(Begin()); + const VerifierDepsHeader& GetVerifierDepsHeader() const { + return *reinterpret_cast<const VerifierDepsHeader*>(Begin()); + } + + uint32_t GetDexSectionHeaderOffset() const { + return sizeof(VerifierDepsHeader) + GetVerifierDepsHeader().GetSizeOfChecksumsSection(); + } + + const DexSectionHeader& GetDexSectionHeader() const { + DCHECK(GetVerifierDepsHeader().HasDexSection()); + return *reinterpret_cast<const DexSectionHeader*>(Begin() + GetDexSectionHeaderOffset()); + } + + const uint8_t* GetVerifierDepsStart() const { + const uint8_t* result = Begin() + GetDexSectionHeaderOffset(); + if (GetVerifierDepsHeader().HasDexSection()) { + // When there is a dex section, the verifier deps are after it, but before the quickening. + return result + GetDexSectionHeader().GetDexSectionSize(); + } else { + // When there is no dex section, the verifier deps are just after the header. + return result; + } } ArrayRef<const uint8_t> GetVerifierDepsData() const { return ArrayRef<const uint8_t>( - DexBegin() + GetHeader().GetDexSize() + GetHeader().GetDexSharedDataSize(), - GetHeader().GetVerifierDepsSize()); + GetVerifierDepsStart(), + GetVerifierDepsHeader().GetVerifierDepsSize()); } ArrayRef<const uint8_t> GetQuickeningInfo() const { - return ArrayRef<const uint8_t>( - GetVerifierDepsData().data() + GetHeader().GetVerifierDepsSize(), - GetHeader().GetQuickeningInfoSize()); + if (GetVerifierDepsHeader().HasDexSection()) { + return ArrayRef<const uint8_t>( + GetVerifierDepsData().data() + GetVerifierDepsHeader().GetVerifierDepsSize(), + GetDexSectionHeader().GetQuickeningInfoSize()); + } else { + return ArrayRef<const uint8_t>(); + } } bool IsValid() const { - return mmap_->Size() >= sizeof(Header) && GetHeader().IsValid(); + return mmap_->Size() >= sizeof(VerifierDepsHeader) && GetVerifierDepsHeader().IsValid(); } // This method is for iterating over the dex files in the vdex. If `cursor` is null, @@ -202,8 +271,8 @@ class VdexFile { // Get the location checksum of the dex file number `dex_file_index`. uint32_t GetLocationChecksum(uint32_t dex_file_index) const { - DCHECK_LT(dex_file_index, GetHeader().GetNumberOfDexFiles()); - return reinterpret_cast<const uint32_t*>(Begin() + sizeof(Header))[dex_file_index]; + DCHECK_LT(dex_file_index, GetVerifierDepsHeader().GetNumberOfDexFiles()); + return reinterpret_cast<const uint32_t*>(Begin() + sizeof(VerifierDepsHeader))[dex_file_index]; } // Open all the dex files contained in this vdex file. @@ -228,7 +297,7 @@ class VdexFile { uint32_t dex_method_idx) const; bool HasDexSection() const { - return GetHeader().GetDexSize() != 0; + return GetVerifierDepsHeader().HasDexSection(); } private: @@ -250,11 +319,13 @@ class VdexFile { bool ContainsDexFile(const DexFile& dex_file) const; const uint8_t* DexBegin() const { - return Begin() + sizeof(Header) + GetHeader().GetSizeOfChecksumsSection(); + DCHECK(HasDexSection()); + return Begin() + GetDexSectionHeaderOffset() + sizeof(DexSectionHeader); } const uint8_t* DexEnd() const { - return DexBegin() + GetHeader().GetDexSize(); + DCHECK(HasDexSection()); + return DexBegin() + GetDexSectionHeader().GetDexSize(); } std::unique_ptr<MemMap> mmap_; diff --git a/test/616-cha-unloading/cha_unload.cc b/test/616-cha-unloading/cha_unload.cc new file mode 100644 index 0000000000..4be3456e3d --- /dev/null +++ b/test/616-cha-unloading/cha_unload.cc @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jni.h" + +#include <iostream> + +#include "art_method.h" +#include "jit/jit.h" +#include "linear_alloc.h" +#include "nativehelper/ScopedUtfChars.h" +#include "runtime.h" +#include "scoped_thread_state_change-inl.h" +#include "thread-current-inl.h" + +namespace art { +namespace { + +extern "C" JNIEXPORT jlong JNICALL Java_Main_getArtMethod(JNIEnv* env, + jclass, + jobject java_method) { + ScopedObjectAccess soa(env); + ArtMethod* method = ArtMethod::FromReflectedMethod(soa, java_method); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(method)); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_reuseArenaOfMethod(JNIEnv*, + jclass, + jlong art_method) { + // Create a new allocation and use it to request a specified amount of arenas. + // Hopefully one of them is a reused one, the one that covers the art_method pointer. + std::unique_ptr<LinearAlloc> alloc(Runtime::Current()->CreateLinearAlloc()); + do { + // Ask for a byte - it's sufficient to get an arena and not have issues with size. + alloc->Alloc(Thread::Current(), 1); + } while (!alloc->Contains(reinterpret_cast<void*>(static_cast<uintptr_t>(art_method)))); +} + +} // namespace +} // namespace art diff --git a/test/616-cha-unloading/expected.txt b/test/616-cha-unloading/expected.txt new file mode 100644 index 0000000000..77a1486479 --- /dev/null +++ b/test/616-cha-unloading/expected.txt @@ -0,0 +1,2 @@ +JNI_OnLoad called +Done diff --git a/test/616-cha-unloading/info.txt b/test/616-cha-unloading/info.txt new file mode 100644 index 0000000000..563380b6b2 --- /dev/null +++ b/test/616-cha-unloading/info.txt @@ -0,0 +1 @@ +Test that class unloading updated single implementations. diff --git a/test/616-cha-unloading/run b/test/616-cha-unloading/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-unloading/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-unloading/src-art/AbstractCHATester.java b/test/616-cha-unloading/src-art/AbstractCHATester.java new file mode 100644 index 0000000000..e11094584a --- /dev/null +++ b/test/616-cha-unloading/src-art/AbstractCHATester.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public abstract class AbstractCHATester { + public abstract void lonelyMethod(); +} diff --git a/test/616-cha-unloading/src-art/Main.java b/test/616-cha-unloading/src-art/Main.java new file mode 100644 index 0000000000..effa315e25 --- /dev/null +++ b/test/616-cha-unloading/src-art/Main.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +public class Main { + static final String DEX_FILE = System.getenv("DEX_LOCATION") + "/616-cha-unloading-ex.jar"; + static final String LIBRARY_SEARCH_PATH = System.getProperty("java.library.path"); + static Constructor<? extends ClassLoader> sConstructor; + + private static class CHAUnloaderRetType { + private CHAUnloaderRetType(WeakReference<ClassLoader> cl, + AbstractCHATester obj, + long methodPtr) { + this.cl = cl; + this.obj = obj; + this.methodPtr = methodPtr; + } + public WeakReference<ClassLoader> cl; + public AbstractCHATester obj; + public long methodPtr; + } + + public static void main(String[] args) throws Exception { + System.loadLibrary(args[0]); + + Class<ClassLoader> pathClassLoader = (Class<ClassLoader>) Class.forName("dalvik.system.PathClassLoader"); + sConstructor = + pathClassLoader.getDeclaredConstructor(String.class, String.class, ClassLoader.class); + + testUnload(); + } + + private static void testUnload() throws Exception { + // Load a concrete class, then unload it. Get a deleted ArtMethod to test if it'll be inlined. + CHAUnloaderRetType result = doUnloadLoader(); + WeakReference<ClassLoader> loader = result.cl; + long methodPtr = result.methodPtr; + // Check that the classloader is indeed unloaded. + if (loader.get() != null) { + throw new Error("Expected class loader to be unloaded"); + } + + // Reuse the linear alloc used by the unloaded class loader. + reuseArenaOfMethod(methodPtr); + + // Try to JIT-compile under dangerous conditions. + ensureJitCompiled(Main.class, "targetMethodForJit"); + System.out.println("Done"); + } + + private static void doUnloading() { + // Do multiple GCs to prevent rare flakiness if some other thread is keeping the + // classloader live. + for (int i = 0; i < 5; ++i) { + Runtime.getRuntime().gc(); + } + } + + private static CHAUnloaderRetType setupLoader() + throws Exception { + ClassLoader loader = sConstructor.newInstance( + DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader()); + Class<?> concreteCHATester = loader.loadClass("ConcreteCHATester"); + + // Preemptively compile methods to prevent delayed JIT tasks from blocking the unloading. + ensureJitCompiled(concreteCHATester, "<init>"); + ensureJitCompiled(concreteCHATester, "lonelyMethod"); + + Object obj = concreteCHATester.newInstance(); + Method lonelyMethod = concreteCHATester.getDeclaredMethod("lonelyMethod"); + + // Get a pointer to a region that shall be not used after the unloading. + long artMethod = getArtMethod(lonelyMethod); + + AbstractCHATester ret = null; + return new CHAUnloaderRetType(new WeakReference(loader), ret, artMethod); + } + + private static CHAUnloaderRetType targetMethodForJit(int mode) + throws Exception { + CHAUnloaderRetType ret = new CHAUnloaderRetType(null, null, 0); + if (mode == 0) { + ret = setupLoader(); + } else if (mode == 1) { + // This branch is not supposed to be executed. It shall trigger "lonelyMethod" inlining + // during jit compilation of "targetMethodForJit". + ret = setupLoader(); + AbstractCHATester obj = ret.obj; + obj.lonelyMethod(); + } + return ret; + } + + private static CHAUnloaderRetType doUnloadLoader() + throws Exception { + CHAUnloaderRetType result = targetMethodForJit(0); + doUnloading(); + return result; + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); + private static native long getArtMethod(Object javaMethod); + private static native void reuseArenaOfMethod(long artMethod); +} diff --git a/test/616-cha-unloading/src-ex/AbstractCHATester.java b/test/616-cha-unloading/src-ex/AbstractCHATester.java new file mode 100644 index 0000000000..e11094584a --- /dev/null +++ b/test/616-cha-unloading/src-ex/AbstractCHATester.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public abstract class AbstractCHATester { + public abstract void lonelyMethod(); +} diff --git a/test/616-cha-unloading/src-ex/ConcreteCHATester.java b/test/616-cha-unloading/src-ex/ConcreteCHATester.java new file mode 100644 index 0000000000..ee2be9c3f5 --- /dev/null +++ b/test/616-cha-unloading/src-ex/ConcreteCHATester.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class ConcreteCHATester extends AbstractCHATester { + public void lonelyMethod() {} +} diff --git a/test/Android.bp b/test/Android.bp index 17a50421a8..0c1edcaab8 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -429,6 +429,7 @@ cc_defaults { "596-app-images/app_images.cc", "596-monitor-inflation/monitor_inflation.cc", "597-deopt-new-string/deopt.cc", + "616-cha-unloading/cha_unload.cc", "626-const-class-linking/clear_dex_cache_types.cc", "642-fp-callees/fp_callees.cc", "647-jni-get-field-id/get_field_id.cc", diff --git a/test/knownfailures.json b/test/knownfailures.json index 41f4e1a09f..6993e2c490 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -975,7 +975,17 @@ ], "variant": "jvm", "bug": "b/73888836", - "description": ["Failing on JVM. Needs further investigating."] + "description": ["Failing on RI. Needs further investigating."] + }, + { + "tests": ["616-cha-unloading"], + "variant": "jvm", + "description": ["Doesn't run on RI."] + }, + { + "tests": ["616-cha-unloading"], + "variant": "trace", + "description": ["Trace prevents class unloading."] }, { "tests": "677-fsi", |