summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/code_generator_arm64.cc1
-rw-r--r--dex2oat/dex2oat.cc4
-rw-r--r--dex2oat/dex2oat_test.cc39
-rw-r--r--dex2oat/linker/oat_writer.cc57
-rw-r--r--dex2oat/linker/oat_writer_test.cc5
-rw-r--r--dexlayout/compact_dex_writer.cc5
-rw-r--r--dexlayout/compact_dex_writer.h4
-rw-r--r--libdexfile/dex/compact_dex_file.h14
-rw-r--r--libdexfile/dex/dex_file-inl.h12
-rw-r--r--libdexfile/dex/dex_file.h21
-rw-r--r--oatdump/oatdump.cc8
-rw-r--r--patchoat/patchoat.cc56
-rw-r--r--runtime/arch/arm64/quick_entrypoints_arm64.S18
-rw-r--r--runtime/art_method-inl.h5
-rw-r--r--runtime/art_method.cc7
-rw-r--r--runtime/art_method.h30
-rw-r--r--runtime/cha.cc101
-rw-r--r--runtime/cha.h11
-rw-r--r--runtime/class_linker.cc34
-rw-r--r--runtime/class_linker.h2
-rw-r--r--runtime/class_table-inl.h8
-rw-r--r--runtime/class_table.h4
-rw-r--r--runtime/gc/heap.cc16
-rw-r--r--runtime/gc/heap.h1
-rw-r--r--runtime/gc/heap_test.cc21
-rw-r--r--runtime/jit/jit_code_cache.cc6
-rw-r--r--runtime/mirror/class-inl.h19
-rw-r--r--runtime/mirror/class.h6
-rw-r--r--runtime/mirror/iftable.h5
-rw-r--r--runtime/oat_file_assistant.cc2
-rw-r--r--runtime/vdex_file.cc59
-rw-r--r--runtime/vdex_file.h169
-rw-r--r--test/616-cha-unloading/cha_unload.cc53
-rw-r--r--test/616-cha-unloading/expected.txt2
-rw-r--r--test/616-cha-unloading/info.txt1
-rw-r--r--test/616-cha-unloading/run18
-rw-r--r--test/616-cha-unloading/src-art/AbstractCHATester.java19
-rw-r--r--test/616-cha-unloading/src-art/Main.java121
-rw-r--r--test/616-cha-unloading/src-ex/AbstractCHATester.java19
-rw-r--r--test/616-cha-unloading/src-ex/ConcreteCHATester.java19
-rw-r--r--test/Android.bp1
-rw-r--r--test/knownfailures.json12
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",