Snap for 4448085 from 38a95a9cd6cf4829f7b6d3362e83e49a053b731e to oc-m3-release
Change-Id: If3253c181aee77f61aa8d50c457a508b7c3870b7
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 8fc8f4b..5277f3a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1348,7 +1348,7 @@
DCHECK(!oat_filenames_.empty());
for (const char* oat_filename : oat_filenames_) {
std::unique_ptr<File> oat_file(OS::CreateEmptyFile(oat_filename));
- if (oat_file.get() == nullptr) {
+ if (oat_file == nullptr) {
PLOG(ERROR) << "Failed to create oat file: " << oat_filename;
return false;
}
@@ -1378,7 +1378,7 @@
vdex_files_.push_back(std::move(vdex_file));
} else {
std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_filename.c_str()));
- if (vdex_file.get() == nullptr) {
+ if (vdex_file == nullptr) {
PLOG(ERROR) << "Failed to open vdex file: " << vdex_filename;
return false;
}
@@ -1392,13 +1392,15 @@
}
} else {
std::unique_ptr<File> oat_file(new File(oat_fd_, oat_location_, /* check_usage */ true));
- if (oat_file.get() == nullptr) {
+ if (oat_file == nullptr) {
PLOG(ERROR) << "Failed to create oat file: " << oat_location_;
return false;
}
oat_file->DisableAutoClose();
if (oat_file->SetLength(0) != 0) {
PLOG(WARNING) << "Truncating oat file " << oat_location_ << " failed.";
+ oat_file->Erase();
+ return false;
}
oat_files_.push_back(std::move(oat_file));
@@ -1427,7 +1429,7 @@
DCHECK_NE(output_vdex_fd_, -1);
std::string vdex_location = ReplaceFileExtension(oat_location_, "vdex");
std::unique_ptr<File> vdex_file(new File(output_vdex_fd_, vdex_location, /* check_usage */ true));
- if (vdex_file.get() == nullptr) {
+ if (vdex_file == nullptr) {
PLOG(ERROR) << "Failed to create vdex file: " << vdex_location;
return false;
}
@@ -1437,6 +1439,7 @@
} else {
if (vdex_file->SetLength(0) != 0) {
PLOG(ERROR) << "Truncating vdex file " << vdex_location << " failed.";
+ vdex_file->Erase();
return false;
}
}
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index 9f6e8a4..bcb7ea6 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -59,8 +59,7 @@
bool show_section_headers_ = false;
bool show_section_statistics_ = false;
bool verbose_ = false;
- // TODO: Set verify_output_ back to false by default. Was set to true for debugging b/62840842.
- bool verify_output_ = true;
+ bool verify_output_ = false;
bool visualize_pattern_ = false;
OutputFormat output_format_ = kOutputPlain;
const char* output_dex_directory_ = nullptr;
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index fc72bbd..51a67ca 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -229,6 +229,8 @@
if (oat_file_assistant.IsInBootClassPath()) {
return kNoDexOptNeeded;
}
+
+ // TODO(calin): Pass the class loader context as an argument to dexoptanalyzer. b/62269291.
int dexoptNeeded = oat_file_assistant.GetDexOptNeeded(
compiler_filter_, assume_profile_changed_, downgrade_);
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index 63fbbc8..75cfd2c 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -31,13 +31,17 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/unix_file/fd_file.h"
+#include "class_linker.h"
#include "gc/space/image_space.h"
#include "gc/heap.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "image.h"
-#include "scoped_thread_state_change-inl.h"
+#include "oat.h"
+#include "oat_file.h"
+#include "oat_file_manager.h"
#include "os.h"
+#include "scoped_thread_state_change-inl.h"
#include "cmdline.h"
#include "backtrace/BacktraceMap.h"
@@ -326,6 +330,37 @@
};
// Region analysis for mirror::Objects
+class ImgObjectVisitor : public ObjectVisitor {
+ public:
+ using ComputeDirtyFunc = std::function<void(mirror::Object* object,
+ const uint8_t* begin_image_ptr,
+ const std::set<size_t>& dirty_pages)>;
+ ImgObjectVisitor(ComputeDirtyFunc dirty_func,
+ const uint8_t* begin_image_ptr,
+ const std::set<size_t>& dirty_pages) :
+ dirty_func_(dirty_func),
+ begin_image_ptr_(begin_image_ptr),
+ dirty_pages_(dirty_pages) { }
+
+ virtual ~ImgObjectVisitor() OVERRIDE { }
+
+ virtual void Visit(mirror::Object* object) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Sanity check that we are reading a real mirror::Object
+ CHECK(object->GetClass() != nullptr) << "Image object at address "
+ << object
+ << " has null class";
+ if (kUseBakerReadBarrier) {
+ object->AssertReadBarrierState();
+ }
+ dirty_func_(object, begin_image_ptr_, dirty_pages_);
+ }
+
+ private:
+ ComputeDirtyFunc dirty_func_;
+ const uint8_t* begin_image_ptr_;
+ const std::set<size_t>& dirty_pages_;
+};
+
template<>
class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object> {
public:
@@ -339,24 +374,14 @@
os_(*os),
dump_dirty_objects_(dump_dirty_objects) { }
- void CheckEntrySanity(const uint8_t* current) const
- REQUIRES_SHARED(Locks::mutator_lock_) {
- CHECK_ALIGNED(current, kObjectAlignment);
- mirror::Object* entry = reinterpret_cast<mirror::Object*>(const_cast<uint8_t*>(current));
- // Sanity check that we are reading a real mirror::Object
- CHECK(entry->GetClass() != nullptr) << "Image object at address "
- << entry
- << " has null class";
- if (kUseBakerReadBarrier) {
- entry->AssertReadBarrierState();
- }
- }
+ // Define a common public type name for use by RegionData.
+ using VisitorClass = ImgObjectVisitor;
- mirror::Object* GetNextEntry(mirror::Object* entry)
+ void VisitEntries(VisitorClass* visitor,
+ uint8_t* base,
+ PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
- uint8_t* next =
- reinterpret_cast<uint8_t*>(entry) + RoundUp(EntrySize(entry), kObjectAlignment);
- return reinterpret_cast<mirror::Object*>(next);
+ RegionCommon<mirror::Object>::image_header_.VisitObjects(visitor, base, pointer_size);
}
void VisitEntry(mirror::Object* entry)
@@ -616,40 +641,93 @@
};
// Region analysis for ArtMethods.
-// TODO: most of these need work.
+class ImgArtMethodVisitor : public ArtMethodVisitor {
+ public:
+ using ComputeDirtyFunc = std::function<void(ArtMethod*,
+ const uint8_t*,
+ const std::set<size_t>&)>;
+ ImgArtMethodVisitor(ComputeDirtyFunc dirty_func,
+ const uint8_t* begin_image_ptr,
+ const std::set<size_t>& dirty_pages) :
+ dirty_func_(dirty_func),
+ begin_image_ptr_(begin_image_ptr),
+ dirty_pages_(dirty_pages) { }
+ virtual ~ImgArtMethodVisitor() OVERRIDE { }
+ virtual void Visit(ArtMethod* method) OVERRIDE {
+ dirty_func_(method, begin_image_ptr_, dirty_pages_);
+ }
+
+ private:
+ ComputeDirtyFunc dirty_func_;
+ const uint8_t* begin_image_ptr_;
+ const std::set<size_t>& dirty_pages_;
+};
+
+// Struct and functor for computing offsets of members of ArtMethods.
+// template <typename RegionType>
+struct MemberInfo {
+ template <typename T>
+ void operator() (const ArtMethod* method, const T* member_address, const std::string& name) {
+ // Check that member_address is a pointer inside *method.
+ DCHECK(reinterpret_cast<uintptr_t>(method) <= reinterpret_cast<uintptr_t>(member_address));
+ DCHECK(reinterpret_cast<uintptr_t>(member_address) + sizeof(T) <=
+ reinterpret_cast<uintptr_t>(method) + sizeof(ArtMethod));
+ size_t offset =
+ reinterpret_cast<uintptr_t>(member_address) - reinterpret_cast<uintptr_t>(method);
+ offset_to_name_size_.insert({offset, NameAndSize(sizeof(T), name)});
+ }
+
+ struct NameAndSize {
+ size_t size_;
+ std::string name_;
+ NameAndSize(size_t size, const std::string& name) : size_(size), name_(name) { }
+ NameAndSize() : size_(0), name_("INVALID") { }
+ };
+
+ std::map<size_t, NameAndSize> offset_to_name_size_;
+};
+
template<>
-class RegionSpecializedBase<ArtMethod> : RegionCommon<ArtMethod> {
+class RegionSpecializedBase<ArtMethod> : public RegionCommon<ArtMethod> {
public:
RegionSpecializedBase(std::ostream* os,
std::vector<uint8_t>* remote_contents,
std::vector<uint8_t>* zygote_contents,
const backtrace_map_t& boot_map,
- const ImageHeader& image_header) :
- RegionCommon<ArtMethod>(os, remote_contents, zygote_contents, boot_map, image_header),
- os_(*os) { }
-
- void CheckEntrySanity(const uint8_t* current ATTRIBUTE_UNUSED) const
- REQUIRES_SHARED(Locks::mutator_lock_) {
+ const ImageHeader& image_header,
+ bool dump_dirty_objects ATTRIBUTE_UNUSED)
+ : RegionCommon<ArtMethod>(os, remote_contents, zygote_contents, boot_map, image_header),
+ os_(*os) {
+ // Prepare the table for offset to member lookups.
+ ArtMethod* art_method = reinterpret_cast<ArtMethod*>(&(*remote_contents)[0]);
+ art_method->VisitMembers(member_info_);
+ // Prepare the table for address to symbolic entry point names.
+ BuildEntryPointNames();
+ class_linker_ = Runtime::Current()->GetClassLinker();
}
- ArtMethod* GetNextEntry(ArtMethod* entry)
+ // Define a common public type name for use by RegionData.
+ using VisitorClass = ImgArtMethodVisitor;
+
+ void VisitEntries(VisitorClass* visitor,
+ uint8_t* base,
+ PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
- uint8_t* next = reinterpret_cast<uint8_t*>(entry) + RoundUp(EntrySize(entry), kObjectAlignment);
- return reinterpret_cast<ArtMethod*>(next);
+ RegionCommon<ArtMethod>::image_header_.VisitPackedArtMethods(visitor, base, pointer_size);
}
void VisitEntry(ArtMethod* method ATTRIBUTE_UNUSED)
REQUIRES_SHARED(Locks::mutator_lock_) {
}
+ void AddCleanEntry(ArtMethod* method ATTRIBUTE_UNUSED) {
+ }
+
void AddFalseDirtyEntry(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) {
RegionCommon<ArtMethod>::AddFalseDirtyEntry(method);
}
- void AddCleanEntry(ArtMethod* method ATTRIBUTE_UNUSED) {
- }
-
void AddDirtyEntry(ArtMethod* method, ArtMethod* method_remote)
REQUIRES_SHARED(Locks::mutator_lock_) {
size_t entry_size = EntrySize(method);
@@ -667,14 +745,50 @@
dirty_entries_.push_back(method);
}
- void DiffEntryContents(ArtMethod* method ATTRIBUTE_UNUSED,
- uint8_t* remote_bytes ATTRIBUTE_UNUSED,
- const uint8_t* base_ptr ATTRIBUTE_UNUSED)
+ void DiffEntryContents(ArtMethod* method,
+ uint8_t* remote_bytes,
+ const uint8_t* base_ptr,
+ bool log_dirty_objects ATTRIBUTE_UNUSED)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ const char* tabs = " ";
+ os_ << tabs << "ArtMethod " << ArtMethod::PrettyMethod(method) << "\n";
+
+ std::unordered_set<size_t> dirty_members;
+ // Examine the members comprising the ArtMethod, computing which members are dirty.
+ for (const std::pair<size_t, MemberInfo::NameAndSize>& p : member_info_.offset_to_name_size_) {
+ const size_t offset = p.first;
+ if (memcmp(base_ptr + offset, remote_bytes + offset, p.second.size_) != 0) {
+ dirty_members.insert(p.first);
+ }
+ }
+ // Dump different fields.
+ if (!dirty_members.empty()) {
+ os_ << tabs << "Dirty members " << dirty_members.size() << "\n";
+ for (size_t offset : dirty_members) {
+ const MemberInfo::NameAndSize& member_info = member_info_.offset_to_name_size_[offset];
+ os_ << tabs << member_info.name_
+ << " original=" << StringFromBytes(base_ptr + offset, member_info.size_)
+ << " remote=" << StringFromBytes(remote_bytes + offset, member_info.size_)
+ << "\n";
+ }
+ }
+ os_ << "\n";
+ }
+
+ void DumpDirtyObjects() REQUIRES_SHARED(Locks::mutator_lock_) {
}
void DumpDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) {
DumpSamplesAndOffsetCount();
+ os_ << " offset to field map:\n";
+ for (const std::pair<size_t, MemberInfo::NameAndSize>& p : member_info_.offset_to_name_size_) {
+ const size_t offset = p.first;
+ const size_t size = p.second.size_;
+ os_ << StringPrintf(" %zu-%zu: ", offset, offset + size - 1)
+ << p.second.name_
+ << std::endl;
+ }
+
os_ << " field contents:\n";
for (ArtMethod* method : dirty_entries_) {
// remote method
@@ -694,6 +808,7 @@
}
void DumpFalseDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) {
+ os_ << "\n" << " False-dirty ArtMethods\n";
os_ << " field contents:\n";
for (ArtMethod* method : false_dirty_entries_) {
// local class
@@ -707,6 +822,84 @@
private:
std::ostream& os_;
+ MemberInfo member_info_;
+ std::map<const void*, std::string> entry_point_names_;
+ ClassLinker* class_linker_;
+
+ // Compute a map of addresses to names in the boot OAT file(s).
+ void BuildEntryPointNames() {
+ OatFileManager& oat_file_manager = Runtime::Current()->GetOatFileManager();
+ std::vector<const OatFile*> boot_oat_files = oat_file_manager.GetBootOatFiles();
+ for (const OatFile* oat_file : boot_oat_files) {
+ const OatHeader& oat_header = oat_file->GetOatHeader();
+ const void* i2ib = oat_header.GetInterpreterToInterpreterBridge();
+ if (i2ib != nullptr) {
+ entry_point_names_[i2ib] = "InterpreterToInterpreterBridge (from boot oat file)";
+ }
+ const void* i2ccb = oat_header.GetInterpreterToCompiledCodeBridge();
+ if (i2ccb != nullptr) {
+ entry_point_names_[i2ccb] = "InterpreterToCompiledCodeBridge (from boot oat file)";
+ }
+ const void* jdl = oat_header.GetJniDlsymLookup();
+ if (jdl != nullptr) {
+ entry_point_names_[jdl] = "JniDlsymLookup (from boot oat file)";
+ }
+ const void* qgjt = oat_header.GetQuickGenericJniTrampoline();
+ if (qgjt != nullptr) {
+ entry_point_names_[qgjt] = "QuickGenericJniTrampoline (from boot oat file)";
+ }
+ const void* qrt = oat_header.GetQuickResolutionTrampoline();
+ if (qrt != nullptr) {
+ entry_point_names_[qrt] = "QuickResolutionTrampoline (from boot oat file)";
+ }
+ const void* qict = oat_header.GetQuickImtConflictTrampoline();
+ if (qict != nullptr) {
+ entry_point_names_[qict] = "QuickImtConflictTrampoline (from boot oat file)";
+ }
+ const void* q2ib = oat_header.GetQuickToInterpreterBridge();
+ if (q2ib != nullptr) {
+ entry_point_names_[q2ib] = "QuickToInterpreterBridge (from boot oat file)";
+ }
+ }
+ }
+
+ std::string StringFromBytes(const uint8_t* bytes, size_t size) {
+ switch (size) {
+ case 1:
+ return StringPrintf("%" PRIx8, *bytes);
+ case 2:
+ return StringPrintf("%" PRIx16, *reinterpret_cast<const uint16_t*>(bytes));
+ case 4:
+ case 8: {
+ // Compute an address if the bytes might contain one.
+ uint64_t intval;
+ if (size == 4) {
+ intval = *reinterpret_cast<const uint32_t*>(bytes);
+ } else {
+ intval = *reinterpret_cast<const uint64_t*>(bytes);
+ }
+ const void* addr = reinterpret_cast<const void*>(intval);
+ // Match the address against those that have Is* methods in the ClassLinker.
+ if (class_linker_->IsQuickToInterpreterBridge(addr)) {
+ return "QuickToInterpreterBridge";
+ } else if (class_linker_->IsQuickGenericJniStub(addr)) {
+ return "QuickGenericJniStub";
+ } else if (class_linker_->IsQuickResolutionStub(addr)) {
+ return "QuickResolutionStub";
+ } else if (class_linker_->IsJniDlsymLookupStub(addr)) {
+ return "JniDlsymLookupStub";
+ }
+ // Match the address against those that we saved from the boot OAT files.
+ if (entry_point_names_.find(addr) != entry_point_names_.end()) {
+ return entry_point_names_[addr];
+ }
+ return StringPrintf("%" PRIx64, intval);
+ }
+ default:
+ LOG(WARNING) << "Don't know how to convert " << size << " bytes to integer";
+ return "<UNKNOWN>";
+ }
+ }
void DumpOneArtMethod(ArtMethod* art_method,
mirror::Class* declaring_class,
@@ -721,7 +914,10 @@
art_method->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size))
<< ", ";
os_ << " isNative? " << (art_method->IsNative() ? "yes" : "no") << ", ";
- os_ << " class_status (local): " << declaring_class->GetStatus();
+ // Null for runtime metionds.
+ if (declaring_class != nullptr) {
+ os_ << " class_status (local): " << declaring_class->GetStatus();
+ }
if (remote_declaring_class != nullptr) {
os_ << ", class_status (remote): " << remote_declaring_class->GetStatus();
}
@@ -755,16 +951,20 @@
// collecting and reporting data regarding dirty, difference, etc.
void ProcessRegion(const MappingData& mapping_data,
RemoteProcesses remotes,
- const uint8_t* begin_image_ptr,
- const uint8_t* end_image_ptr)
+ const uint8_t* begin_image_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
- const uint8_t* current = begin_image_ptr + RoundUp(sizeof(ImageHeader), kObjectAlignment);
- T* entry = reinterpret_cast<T*>(const_cast<uint8_t*>(current));
- while (reinterpret_cast<uintptr_t>(entry) < reinterpret_cast<uintptr_t>(end_image_ptr)) {
- ComputeEntryDirty(entry, begin_image_ptr, mapping_data.dirty_page_set);
-
- entry = RegionSpecializedBase<T>::GetNextEntry(entry);
- }
+ typename RegionSpecializedBase<T>::VisitorClass visitor(
+ [this](T* entry,
+ const uint8_t* begin_image_ptr,
+ const std::set<size_t>& dirty_page_set) REQUIRES_SHARED(Locks::mutator_lock_) {
+ this->ComputeEntryDirty(entry, begin_image_ptr, dirty_page_set);
+ },
+ begin_image_ptr,
+ mapping_data.dirty_page_set);
+ PointerSize pointer_size = InstructionSetPointerSize(Runtime::Current()->GetInstructionSet());
+ RegionSpecializedBase<T>::VisitEntries(&visitor,
+ const_cast<uint8_t*>(begin_image_ptr),
+ pointer_size);
// Looking at only dirty pages, figure out how many of those bytes belong to dirty entries.
// TODO: fix this now that there are multiple regions in a mapping.
@@ -1208,8 +1408,6 @@
<< "\n\n";
const uint8_t* image_begin_unaligned = image_header_.GetImageBegin();
- const uint8_t* image_mirror_end_unaligned = image_begin_unaligned +
- image_header_.GetImageSection(ImageHeader::kSectionObjects).Size();
const uint8_t* image_end_unaligned = image_begin_unaligned + image_header_.GetImageSize();
// Adjust range to nearest page
@@ -1235,14 +1433,6 @@
if (!ComputeDirtyBytes(image_begin, &mapping_data)) {
return false;
}
-
- RegionData<mirror::Object> object_region_data(os_,
- &remote_contents_,
- &zygote_contents_,
- boot_map_,
- image_header_,
- dump_dirty_objects_);
-
RemoteProcesses remotes;
if (zygote_pid_only_) {
remotes = RemoteProcesses::kZygoteOnly;
@@ -1252,11 +1442,27 @@
remotes = RemoteProcesses::kImageOnly;
}
+ // Check all the mirror::Object entries in the image.
+ RegionData<mirror::Object> object_region_data(os_,
+ &remote_contents_,
+ &zygote_contents_,
+ boot_map_,
+ image_header_,
+ dump_dirty_objects_);
object_region_data.ProcessRegion(mapping_data,
remotes,
- image_begin_unaligned,
- image_mirror_end_unaligned);
+ image_begin_unaligned);
+ // Check all the ArtMethod entries in the image.
+ RegionData<ArtMethod> artmethod_region_data(os_,
+ &remote_contents_,
+ &zygote_contents_,
+ boot_map_,
+ image_header_,
+ dump_dirty_objects_);
+ artmethod_region_data.ProcessRegion(mapping_data,
+ remotes,
+ image_begin_unaligned);
return true;
}
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc
index c238f0d..ff02b5d 100644
--- a/profman/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -23,8 +23,11 @@
// Minimum number of new methods/classes that profiles
// must contain to enable recompilation.
-static constexpr const uint32_t kMinNewMethodsForCompilation = 10;
-static constexpr const uint32_t kMinNewClassesForCompilation = 10;
+static constexpr const uint32_t kMinNewMethodsForCompilation = 100;
+static constexpr const uint32_t kMinNewMethodsPercentChangeForCompilation = 2;
+static constexpr const uint32_t kMinNewClassesForCompilation = 50;
+static constexpr const uint32_t kMinNewClassesPercentChangeForCompilation = 2;
+
ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
const std::vector<ScopedFlock>& profile_files,
@@ -55,9 +58,16 @@
}
}
+ uint32_t min_change_in_methods_for_compilation = std::max(
+ (kMinNewMethodsPercentChangeForCompilation * number_of_methods) / 100,
+ kMinNewMethodsForCompilation);
+ uint32_t min_change_in_classes_for_compilation = std::max(
+ (kMinNewClassesPercentChangeForCompilation * number_of_classes) / 100,
+ kMinNewClassesForCompilation);
// Check if there is enough new information added by the current profiles.
- if (((info.GetNumberOfMethods() - number_of_methods) < kMinNewMethodsForCompilation) &&
- ((info.GetNumberOfResolvedClasses() - number_of_classes) < kMinNewClassesForCompilation)) {
+ if (((info.GetNumberOfMethods() - number_of_methods) < min_change_in_methods_for_compilation) &&
+ ((info.GetNumberOfResolvedClasses() - number_of_classes)
+ < min_change_in_classes_for_compilation)) {
return kSkipCompilation;
}
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index c78d34e..955ec96 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -335,6 +335,46 @@
ASSERT_EQ(expected_clases.size(), found);
}
+ int CheckCompilationMethodPercentChange(uint16_t methods_in_cur_profile,
+ uint16_t methods_in_ref_profile) {
+ ScratchFile profile;
+ ScratchFile reference_profile;
+ std::vector<int> profile_fds({ GetFd(profile)});
+ int reference_profile_fd = GetFd(reference_profile);
+ std::vector<uint32_t> hot_methods_cur;
+ std::vector<uint32_t> hot_methods_ref;
+ std::vector<uint32_t> empty_vector;
+ for (size_t i = 0; i < methods_in_cur_profile; ++i) {
+ hot_methods_cur.push_back(i);
+ }
+ for (size_t i = 0; i < methods_in_ref_profile; ++i) {
+ hot_methods_ref.push_back(i);
+ }
+ ProfileCompilationInfo info1;
+ uint16_t methods_in_profile = std::max(methods_in_cur_profile, methods_in_ref_profile);
+ SetupBasicProfile("p1", 1, methods_in_profile, hot_methods_cur, empty_vector, empty_vector,
+ profile, &info1);
+ ProfileCompilationInfo info2;
+ SetupBasicProfile("p1", 1, methods_in_profile, hot_methods_ref, empty_vector, empty_vector,
+ reference_profile, &info2);
+ return ProcessProfiles(profile_fds, reference_profile_fd);
+ }
+
+ int CheckCompilationClassPercentChange(uint16_t classes_in_cur_profile,
+ uint16_t classes_in_ref_profile) {
+ ScratchFile profile;
+ ScratchFile reference_profile;
+
+ std::vector<int> profile_fds({ GetFd(profile)});
+ int reference_profile_fd = GetFd(reference_profile);
+
+ ProfileCompilationInfo info1;
+ SetupProfile("p1", 1, 0, classes_in_cur_profile, profile, &info1);
+ ProfileCompilationInfo info2;
+ SetupProfile("p1", 1, 0, classes_in_ref_profile, reference_profile, &info2);
+ return ProcessProfiles(profile_fds, reference_profile_fd);
+ }
+
std::unique_ptr<ArenaAllocator> arena_;
// Cache of inline caches generated during tests.
@@ -460,7 +500,7 @@
GetFd(profile2)});
int reference_profile_fd = GetFd(reference_profile);
- const uint16_t kNumberOfMethodsToSkipCompilation = 1;
+ const uint16_t kNumberOfMethodsToSkipCompilation = 24; // Threshold is 100.
ProfileCompilationInfo info1;
SetupProfile("p1", 1, kNumberOfMethodsToSkipCompilation, 0, profile1, &info1);
ProfileCompilationInfo info2;
@@ -489,6 +529,42 @@
CheckProfileInfo(profile2, info2);
}
+TEST_F(ProfileAssistantTest, DoNotAdviseCompilationMethodPercentage) {
+ const uint16_t kNumberOfMethodsInRefProfile = 6000;
+ const uint16_t kNumberOfMethodsInCurProfile = 6100; // Threshold is 2%.
+ // We should not advise compilation.
+ ASSERT_EQ(ProfileAssistant::kSkipCompilation,
+ CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
+ kNumberOfMethodsInRefProfile));
+}
+
+TEST_F(ProfileAssistantTest, ShouldAdviseCompilationMethodPercentage) {
+ const uint16_t kNumberOfMethodsInRefProfile = 6000;
+ const uint16_t kNumberOfMethodsInCurProfile = 6200; // Threshold is 2%.
+ // We should advise compilation.
+ ASSERT_EQ(ProfileAssistant::kCompile,
+ CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
+ kNumberOfMethodsInRefProfile));
+}
+
+TEST_F(ProfileAssistantTest, DoNotdviseCompilationClassPercentage) {
+ const uint16_t kNumberOfClassesInRefProfile = 6000;
+ const uint16_t kNumberOfClassesInCurProfile = 6110; // Threshold is 2%.
+ // We should not advise compilation.
+ ASSERT_EQ(ProfileAssistant::kSkipCompilation,
+ CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
+ kNumberOfClassesInRefProfile));
+}
+
+TEST_F(ProfileAssistantTest, ShouldAdviseCompilationClassPercentage) {
+ const uint16_t kNumberOfClassesInRefProfile = 6000;
+ const uint16_t kNumberOfClassesInCurProfile = 6120; // Threshold is 2%.
+ // We should advise compilation.
+ ASSERT_EQ(ProfileAssistant::kCompile,
+ CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
+ kNumberOfClassesInRefProfile));
+}
+
TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) {
ScratchFile profile1;
ScratchFile profile2;
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 511ac83..0de4efd 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -684,6 +684,27 @@
template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
ALWAYS_INLINE void UpdateEntrypoints(const Visitor& visitor, PointerSize pointer_size);
+ // Visit the individual members of an ArtMethod. Used by imgdiag.
+ // As imgdiag does not support mixing instruction sets or pointer sizes (e.g., using imgdiag32
+ // to inspect 64-bit images, etc.), we can go beneath the accessors directly to the class members.
+ template <typename VisitorFunc>
+ void VisitMembers(VisitorFunc& visitor) {
+ DCHECK(IsImagePointerSize(kRuntimePointerSize));
+ visitor(this, &declaring_class_, "declaring_class_");
+ visitor(this, &access_flags_, "access_flags_");
+ visitor(this, &dex_code_item_offset_, "dex_code_item_offset_");
+ visitor(this, &dex_method_index_, "dex_method_index_");
+ visitor(this, &method_index_, "method_index_");
+ visitor(this, &hotness_count_, "hotness_count_");
+ visitor(this,
+ &ptr_sized_fields_.dex_cache_resolved_methods_,
+ "ptr_sized_fields_.dex_cache_resolved_methods_");
+ visitor(this, &ptr_sized_fields_.data_, "ptr_sized_fields_.data_");
+ visitor(this,
+ &ptr_sized_fields_.entry_point_from_quick_compiled_code_,
+ "ptr_sized_fields_.entry_point_from_quick_compiled_code_");
+ }
+
protected:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
// The class we are a part of.
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 20e0cfa..fd567af 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -69,7 +69,7 @@
if (guard_state_ < GuardState::kClosed) {
LOG(ERROR) << "File " << file_path_ << " wasn't explicitly closed before destruction.";
}
- CHECK_GE(guard_state_, GuardState::kClosed);
+ DCHECK_GE(guard_state_, GuardState::kClosed);
}
if (auto_close_ && fd_ != -1) {
if (Close() != 0) {
@@ -134,7 +134,7 @@
bool FdFile::Open(const std::string& path, int flags, mode_t mode) {
static_assert(O_RDONLY == 0, "Readonly flag has unexpected value.");
- CHECK_EQ(fd_, -1) << path;
+ DCHECK_EQ(fd_, -1) << path;
read_only_mode_ = ((flags & O_ACCMODE) == O_RDONLY);
fd_ = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
if (fd_ == -1) {
@@ -157,7 +157,7 @@
// Test here, so the file is closed and not leaked.
if (kCheckSafeUsage) {
- CHECK_GE(guard_state_, GuardState::kFlushed) << "File " << file_path_
+ DCHECK_GE(guard_state_, GuardState::kFlushed) << "File " << file_path_
<< " has not been flushed before closing.";
moveUp(GuardState::kClosed, nullptr);
}
@@ -173,13 +173,20 @@
int FdFile::Flush() {
DCHECK(!read_only_mode_);
+
#ifdef __linux__
int rc = TEMP_FAILURE_RETRY(fdatasync(fd_));
#else
int rc = TEMP_FAILURE_RETRY(fsync(fd_));
#endif
+
moveUp(GuardState::kFlushed, "Flushing closed file.");
- return (rc == -1) ? -errno : rc;
+ if (rc == 0) {
+ return 0;
+ }
+
+ // Don't report failure if we just tried to flush a pipe or socket.
+ return errno == EINVAL ? 0 : -errno;
}
int64_t FdFile::Read(char* buf, int64_t byte_count, int64_t offset) const {
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index 7db077d..8fc6e02 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -94,6 +94,7 @@
int SetLength(int64_t new_length) OVERRIDE WARN_UNUSED;
int64_t GetLength() const OVERRIDE;
int64_t Write(const char* buf, int64_t byte_count, int64_t offset) OVERRIDE WARN_UNUSED;
+
int Flush() OVERRIDE WARN_UNUSED;
// Short for SetLength(0); Flush(); Close();
diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc
index 8b1a115..042fbc9 100644
--- a/runtime/base/unix_file/fd_file_test.cc
+++ b/runtime/base/unix_file/fd_file_test.cc
@@ -274,4 +274,15 @@
EXPECT_EQ(reset_compare(tmp, tmp6), 0);
}
+TEST_F(FdFileTest, PipeFlush) {
+ int pipefd[2];
+ ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
+
+ FdFile file(pipefd[1], true);
+ ASSERT_TRUE(file.WriteFully("foo", 3));
+ ASSERT_EQ(0, file.Flush());
+ ASSERT_EQ(0, file.FlushCloseOrErase());
+ close(pipefd[0]);
+}
+
} // namespace unix_file
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index dfb278d..855359e 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8590,6 +8590,10 @@
(quick_generic_jni_trampoline_ == entry_point);
}
+bool ClassLinker::IsJniDlsymLookupStub(const void* entry_point) const {
+ return entry_point == GetJniDlsymLookupStub();
+}
+
const void* ClassLinker::GetRuntimeQuickGenericJniStub() const {
return GetQuickGenericJniStub();
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 2c2f30b..d18c15d 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -506,6 +506,9 @@
// Is the given entry point quick code to run the generic JNI stub?
bool IsQuickGenericJniStub(const void* entry_point) const;
+ // Is the given entry point the JNI dlsym lookup stub?
+ bool IsJniDlsymLookupStub(const void* entry_point) const;
+
const void* GetQuickToInterpreterBridgeTrampoline() const {
return quick_to_interpreter_bridge_trampoline_;
}
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index e97c6a0..ef274b1 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -187,7 +187,10 @@
// Opens requested class path files and appends them to opened_dex_files. If the dex files have
// been stripped, this opens them from their oat files (which get added to opened_oat_files).
bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& classpath_dir) {
- CHECK(!dex_files_open_attempted_) << "OpenDexFiles should not be called twice";
+ if (dex_files_open_attempted_) {
+ // Do not attempt to re-open the files if we already tried.
+ return dex_files_open_result_;
+ }
dex_files_open_attempted_ = true;
// Assume we can open all dex files. If not, we will set this to false as we go.
@@ -203,6 +206,7 @@
// TODO(calin): Refine the dex opening interface to be able to tell if an archive contains
// no dex files. So that we can distinguish the real failures...
for (ClassLoaderInfo& info : class_loader_chain_) {
+ size_t opened_dex_files_index = info.opened_dex_files.size();
for (const std::string& cp_elem : info.classpath) {
// If path is relative, append it to the provided base directory.
std::string raw_location = cp_elem;
@@ -249,6 +253,23 @@
}
}
}
+
+ // We finished opening the dex files from the classpath.
+ // Now update the classpath and the checksum with the locations of the dex files.
+ //
+ // We do this because initially the classpath contains the paths of the dex files; and
+ // some of them might be multi-dexes. So in order to have a consistent view we replace all the
+ // file paths with the actual dex locations being loaded.
+ // This will allow the context to VerifyClassLoaderContextMatch which expects or multidex
+ // location in the class paths.
+ // Note that this will also remove the paths that could not be opened.
+ info.classpath.clear();
+ info.checksums.clear();
+ for (size_t k = opened_dex_files_index; k < info.opened_dex_files.size(); k++) {
+ std::unique_ptr<const DexFile>& dex = info.opened_dex_files[k];
+ info.classpath.push_back(dex->GetLocation());
+ info.checksums.push_back(dex->GetLocationChecksum());
+ }
}
return dex_files_open_result_;
@@ -632,14 +653,25 @@
}
}
+static bool IsAbsoluteLocation(const std::string& location) {
+ return !location.empty() && location[0] == '/';
+}
+
bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const {
+ DCHECK(dex_files_open_attempted_);
+ DCHECK(dex_files_open_result_);
+
ClassLoaderContext expected_context;
if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) {
LOG(WARNING) << "Invalid class loader context: " << context_spec;
return false;
}
- if (expected_context.special_shared_library_) {
+ // Special shared library contexts always match. They essentially instruct the runtime
+ // to ignore the class path check because the oat file is known to be loaded in different
+ // contexts. OatFileManager will further verify if the oat file can be loaded based on the
+ // collision check.
+ if (special_shared_library_ || expected_context.special_shared_library_) {
return true;
}
@@ -673,18 +705,52 @@
DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size());
for (size_t k = 0; k < info.classpath.size(); k++) {
- if (info.classpath[k] != expected_info.classpath[k]) {
+ // Compute the dex location that must be compared.
+ // We shouldn't do a naive comparison `info.classpath[k] == expected_info.classpath[k]`
+ // because even if they refer to the same file, one could be encoded as a relative location
+ // and the other as an absolute one.
+ bool is_dex_name_absolute = IsAbsoluteLocation(info.classpath[k]);
+ bool is_expected_dex_name_absolute = IsAbsoluteLocation(expected_info.classpath[k]);
+ std::string dex_name;
+ std::string expected_dex_name;
+
+ if (is_dex_name_absolute == is_expected_dex_name_absolute) {
+ // If both locations are absolute or relative then compare them as they are.
+ // This is usually the case for: shared libraries and secondary dex files.
+ dex_name = info.classpath[k];
+ expected_dex_name = expected_info.classpath[k];
+ } else if (is_dex_name_absolute) {
+ // The runtime name is absolute but the compiled name (the expected one) is relative.
+ // This is the case for split apks which depend on base or on other splits.
+ dex_name = info.classpath[k];
+ expected_dex_name = OatFile::ResolveRelativeEncodedDexLocation(
+ info.classpath[k].c_str(), expected_info.classpath[k]);
+ } else {
+ // The runtime name is relative but the compiled name is absolute.
+ // There is no expected use case that would end up here as dex files are always loaded
+ // with their absolute location. However, be tolerant and do the best effort (in case
+ // there are unexpected new use case...).
+ DCHECK(is_expected_dex_name_absolute);
+ dex_name = OatFile::ResolveRelativeEncodedDexLocation(
+ expected_info.classpath[k].c_str(), info.classpath[k]);
+ expected_dex_name = expected_info.classpath[k];
+ }
+
+ // Compare the locations.
+ if (dex_name != expected_dex_name) {
LOG(WARNING) << "ClassLoaderContext classpath element mismatch for position " << i
<< ". expected=" << expected_info.classpath[k]
<< ", found=" << info.classpath[k]
<< " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
+
+ // Compare the checksums.
if (info.checksums[k] != expected_info.checksums[k]) {
LOG(WARNING) << "ClassLoaderContext classpath element checksum mismatch for position " << i
- << ". expected=" << expected_info.checksums[k]
- << ", found=" << info.checksums[k]
- << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
+ << ". expected=" << expected_info.checksums[k]
+ << ", found=" << info.checksums[k]
+ << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
return false;
}
}
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 9afa880..692a6cd 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -41,7 +41,12 @@
// to ClassLoaderInfo::opened_oat_files. The 'classpath_dir' argument specifies the directory to
// use for the relative class paths.
// Returns true if all dex files where successfully opened.
- // It may be called only once per ClassLoaderContext. The second call will abort.
+ // It may be called only once per ClassLoaderContext. Subsequent calls will return the same
+ // result without doing anything.
+ //
+ // This will replace the class path locations with the locations of the opened dex files.
+ // (Note that one dex file can contain multidexes. Each multidex will be added to the classpath
+ // separately.)
//
// Note that a "false" return could mean that either an apk/jar contained no dex files or
// that we hit a I/O or checksum mismatch error.
@@ -98,6 +103,7 @@
// - the number and type of the class loaders from the chain matches
// - the class loader from the same position have the same classpath
// (the order and checksum of the dex files matches)
+ // This should be called after OpenDexFiles().
bool VerifyClassLoaderContextMatch(const std::string& context_spec) const;
// Creates the class loader context from the given string.
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 60b5fa4..ca6900f 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -74,39 +74,29 @@
void VerifyOpenDexFiles(
ClassLoaderContext* context,
size_t index,
- std::vector<std::vector<std::unique_ptr<const DexFile>>*>& all_dex_files) {
+ std::vector<std::unique_ptr<const DexFile>>* all_dex_files) {
ASSERT_TRUE(context != nullptr);
ASSERT_TRUE(context->dex_files_open_attempted_);
ASSERT_TRUE(context->dex_files_open_result_);
ClassLoaderContext::ClassLoaderInfo& info = context->class_loader_chain_[index];
- ASSERT_EQ(all_dex_files.size(), info.classpath.size());
+ ASSERT_EQ(all_dex_files->size(), info.classpath.size());
+ ASSERT_EQ(all_dex_files->size(), info.opened_dex_files.size());
size_t cur_open_dex_index = 0;
- for (size_t k = 0; k < all_dex_files.size(); k++) {
- std::vector<std::unique_ptr<const DexFile>>& dex_files_for_cp_elem = *(all_dex_files[k]);
- for (size_t i = 0; i < dex_files_for_cp_elem.size(); i++) {
- ASSERT_LT(cur_open_dex_index, info.opened_dex_files.size());
-
- std::unique_ptr<const DexFile>& opened_dex_file =
+ for (size_t k = 0; k < all_dex_files->size(); k++) {
+ std::unique_ptr<const DexFile>& opened_dex_file =
info.opened_dex_files[cur_open_dex_index++];
- std::unique_ptr<const DexFile>& expected_dex_file = dex_files_for_cp_elem[i];
+ std::unique_ptr<const DexFile>& expected_dex_file = (*all_dex_files)[k];
- std::string expected_location = expected_dex_file->GetBaseLocation();
- UniqueCPtr<const char[]> expected_real_location(
- realpath(expected_location.c_str(), nullptr));
- ASSERT_TRUE(expected_real_location != nullptr) << expected_location;
- expected_location.assign(expected_real_location.get());
- expected_location += DexFile::GetMultiDexSuffix(expected_dex_file->GetLocation());
+ std::string expected_location = expected_dex_file->GetBaseLocation();
+ UniqueCPtr<const char[]> expected_real_location(
+ realpath(expected_location.c_str(), nullptr));
+ ASSERT_TRUE(expected_real_location != nullptr) << expected_location;
+ expected_location.assign(expected_real_location.get());
+ expected_location += DexFile::GetMultiDexSuffix(expected_dex_file->GetLocation());
- ASSERT_EQ(expected_location, opened_dex_file->GetLocation());
- ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum());
-
- std::string class_path_location = info.classpath[k];
- UniqueCPtr<const char[]> class_path_location_real(
- realpath(class_path_location.c_str(), nullptr));
- ASSERT_TRUE(class_path_location_real != nullptr);
- class_path_location.assign(class_path_location_real.get());
- ASSERT_EQ(class_path_location, opened_dex_file->GetBaseLocation());
- }
+ ASSERT_EQ(expected_location, opened_dex_file->GetLocation());
+ ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum());
+ ASSERT_EQ(info.classpath[k], opened_dex_file->GetLocation());
}
}
@@ -148,6 +138,11 @@
}
}
+ void PretendContextOpenedDexFiles(ClassLoaderContext* context) {
+ context->dex_files_open_attempted_ = true;
+ context->dex_files_open_result_ = true;
+ }
+
private:
void VerifyClassLoaderInfo(ClassLoaderContext* context,
size_t index,
@@ -167,11 +162,9 @@
ClassLoaderContext::ClassLoaderType type,
const std::string& test_name) {
std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(test_name.c_str());
- std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files;
- all_dex_files.push_back(&dex_files);
VerifyClassLoaderInfo(context, index, type, GetTestDexFileName(test_name.c_str()));
- VerifyOpenDexFiles(context, index, all_dex_files);
+ VerifyOpenDexFiles(context, index, &dex_files);
}
};
@@ -242,11 +235,8 @@
TEST_F(ClassLoaderContextTest, OpenValidDexFiles) {
std::string multidex_name = GetTestDexFileName("MultiDex");
- std::vector<std::unique_ptr<const DexFile>> multidex_files = OpenTestDexFiles("MultiDex");
std::string myclass_dex_name = GetTestDexFileName("MyClass");
- std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
std::string dex_name = GetTestDexFileName("Main");
- std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Main");
std::unique_ptr<ClassLoaderContext> context =
ClassLoaderContext::Create(
@@ -256,14 +246,16 @@
ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ ""));
VerifyContextSize(context.get(), 2);
- std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0;
- all_dex_files0.push_back(&multidex_files);
- all_dex_files0.push_back(&myclass_dex_files);
- std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files1;
- all_dex_files1.push_back(&dex_files);
- VerifyOpenDexFiles(context.get(), 0, all_dex_files0);
- VerifyOpenDexFiles(context.get(), 1, all_dex_files1);
+ std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex");
+ std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
+ for (size_t i = 0; i < myclass_dex_files.size(); i++) {
+ all_dex_files0.emplace_back(myclass_dex_files[i].release());
+ }
+ VerifyOpenDexFiles(context.get(), 0, &all_dex_files0);
+
+ std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main");
+ VerifyOpenDexFiles(context.get(), 1, &all_dex_files1);
}
class ScratchSymLink {
@@ -296,11 +288,10 @@
ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ ""));
VerifyContextSize(context.get(), 1);
- std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0;
- std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
- all_dex_files0.push_back(&myclass_dex_files);
- VerifyOpenDexFiles(context.get(), 0, all_dex_files0);
+ std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
+
+ VerifyOpenDexFiles(context.get(), 0, &myclass_dex_files);
}
TEST_F(ClassLoaderContextTest, OpenInvalidDexFilesMix) {
@@ -552,6 +543,9 @@
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) {
std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]";
std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
+ // Pretend that we successfully open the dex files to pass the DCHECKS.
+ // (as it's much easier to test all the corner cases without relying on actual dex files).
+ PretendContextOpenedDexFiles(context.get());
VerifyContextSize(context.get(), 2);
VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");
@@ -589,7 +583,17 @@
std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
- ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")));
+ std::string context_with_no_base_dir = context->EncodeContextForOatFile("");
+ ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir));
+
+ std::string dex_location = GetTestDexFileName("ForClassLoaderA");
+ size_t pos = dex_location.rfind('/');
+ ASSERT_NE(std::string::npos, pos);
+ std::string parent = dex_location.substr(0, pos);
+
+ std::string context_with_base_dir = context->EncodeContextForOatFile(parent);
+ ASSERT_NE(context_with_base_dir, context_with_no_base_dir);
+ ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir));
}
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) {
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 6d4f77b..036f912 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1251,44 +1251,6 @@
CHECK(called != nullptr) << orig_called->PrettyMethod() << " "
<< mirror::Object::PrettyTypeOf(receiver) << " "
<< invoke_type << " " << orig_called->GetVtableIndex();
-
- // We came here because of sharpening. Ensure the dex cache is up-to-date on the method index
- // of the sharpened method avoiding dirtying the dex cache if possible.
- // Note, called_method.dex_method_index references the dex method before the
- // FindVirtualMethodFor... This is ok for FindDexMethodIndexInOtherDexFile that only cares
- // about the name and signature.
- uint32_t update_dex_cache_method_index = called->GetDexMethodIndex();
- if (!called->HasSameDexCacheResolvedMethods(caller, kRuntimePointerSize)) {
- // Calling from one dex file to another, need to compute the method index appropriate to
- // the caller's dex file. Since we get here only if the original called was a runtime
- // method, we've got the correct dex_file and a dex_method_idx from above.
- DCHECK(!called_method_known_on_entry);
- DCHECK_EQ(caller->GetDexFile(), called_method.dex_file);
- const DexFile* caller_dex_file = called_method.dex_file;
- uint32_t caller_method_name_and_sig_index = called_method.dex_method_index;
- update_dex_cache_method_index =
- called->FindDexMethodIndexInOtherDexFile(*caller_dex_file,
- caller_method_name_and_sig_index);
- }
- if ((update_dex_cache_method_index != DexFile::kDexNoIndex) &&
- (caller->GetDexCacheResolvedMethod(
- update_dex_cache_method_index, kRuntimePointerSize) != called)) {
- caller->SetDexCacheResolvedMethod(update_dex_cache_method_index,
- called,
- kRuntimePointerSize);
- }
- } else if (invoke_type == kStatic) {
- const auto called_dex_method_idx = called->GetDexMethodIndex();
- // For static invokes, we may dispatch to the static method in the superclass but resolve
- // using the subclass. To prevent getting slow paths on each invoke, we force set the
- // resolved method for the super class dex method index if we are in the same dex file.
- // b/19175856
- if (called->GetDexFile() == called_method.dex_file &&
- called_method.dex_method_index != called_dex_method_idx) {
- called->GetDexCache()->SetResolvedMethod(called_dex_method_idx,
- called,
- kRuntimePointerSize);
- }
}
// Ensure that the called method's class is initialized.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index f1685b2..6129181 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -132,10 +132,6 @@
// Dump the rosalloc stats on SIGQUIT.
static constexpr bool kDumpRosAllocStatsOnSigQuit = false;
-// Extra added to the heap growth multiplier. Used to adjust the GC ergonomics for the read barrier
-// config.
-static constexpr double kExtraHeapGrowthMultiplier = kUseReadBarrier ? 1.0 : 0.0;
-
static const char* kRegionSpaceName = "main space (region space)";
// If true, we log all GCs in the both the foreground and background. Used for debugging.
@@ -255,8 +251,7 @@
min_free_(min_free),
max_free_(max_free),
target_utilization_(target_utilization),
- foreground_heap_growth_multiplier_(
- foreground_heap_growth_multiplier + kExtraHeapGrowthMultiplier),
+ foreground_heap_growth_multiplier_(foreground_heap_growth_multiplier),
total_wait_time_(0),
verify_object_mode_(kVerifyObjectModeDisabled),
disable_moving_gc_count_(0),
@@ -3428,7 +3423,7 @@
double Heap::HeapGrowthMultiplier() const {
// If we don't care about pause times we are background, so return 1.0.
- if (!CareAboutPauseTimes() || IsLowMemoryMode()) {
+ if (!CareAboutPauseTimes()) {
return 1.0;
}
return foreground_heap_growth_multiplier_;
diff --git a/runtime/image.cc b/runtime/image.cc
index 7d0a709..e3a4060 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -149,6 +149,19 @@
return os << "size=" << section.Size() << " range=" << section.Offset() << "-" << section.End();
}
+void ImageHeader::VisitObjects(ObjectVisitor* visitor,
+ uint8_t* base,
+ PointerSize pointer_size) const {
+ DCHECK_EQ(pointer_size, GetPointerSize());
+ const ImageSection& objects = GetObjectsSection();
+ static const size_t kStartPos = RoundUp(sizeof(ImageHeader), kObjectAlignment);
+ for (size_t pos = kStartPos; pos < objects.Size(); ) {
+ mirror::Object* object = reinterpret_cast<mirror::Object*>(base + objects.Offset() + pos);
+ visitor->Visit(object);
+ pos += RoundUp(object->SizeOf(), kObjectAlignment);
+ }
+}
+
void ImageHeader::VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const {
const ImageSection& fields = GetFieldsSection();
for (size_t pos = 0; pos < fields.Size(); ) {
diff --git a/runtime/image.h b/runtime/image.h
index 6c76f49..7d3be03 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -28,6 +28,13 @@
class ArtField;
class ArtMethod;
+class ObjectVisitor {
+ public:
+ virtual ~ObjectVisitor() {}
+
+ virtual void Visit(mirror::Object* object) = 0;
+};
+
class ArtMethodVisitor {
public:
virtual ~ArtMethodVisitor() {}
@@ -216,6 +223,10 @@
const ImageSection& GetImageSection(ImageSections index) const;
+ const ImageSection& GetObjectsSection() const {
+ return GetImageSection(kSectionObjects);
+ }
+
const ImageSection& GetMethodsSection() const {
return GetImageSection(kSectionArtMethods);
}
@@ -278,6 +289,13 @@
return boot_image_size_ != 0u;
}
+ // Visit mirror::Objects in the section starting at base.
+ // TODO: Delete base parameter if it is always equal to GetImageBegin.
+ void VisitObjects(ObjectVisitor* visitor,
+ uint8_t* base,
+ PointerSize pointer_size) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Visit ArtMethods in the section starting at base. Includes runtime methods.
// TODO: Delete base parameter if it is always equal to GetImageBegin.
void VisitPackedArtMethods(ArtMethodVisitor* visitor,
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index ee2c44a..77efce2 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -140,8 +140,7 @@
if (data == nullptr) {
return false;
}
- data->AddMethod(flags, ref.dex_method_index);
- return true;
+ return data->AddMethod(flags, ref.dex_method_index);
}
bool ProfileCompilationInfo::AddMethodIndex(MethodHotness::Flag flags,
@@ -155,8 +154,7 @@
if (data == nullptr) {
return false;
}
- data->AddMethod(flags, method_idx);
- return true;
+ return data->AddMethod(flags, method_idx);
}
bool ProfileCompilationInfo::AddMethods(const std::vector<ProfileMethodInfo>& methods) {
@@ -557,7 +555,14 @@
// This should always be the case since since the cache map is managed by ProfileCompilationInfo.
DCHECK_EQ(profile_key, result->profile_key);
DCHECK_EQ(profile_index, result->profile_index);
- DCHECK_EQ(num_method_ids, result->num_method_ids);
+
+ if (num_method_ids != result->num_method_ids) {
+ // This should not happen... added to help investigating b/65812889.
+ LOG(ERROR) << "num_method_ids mismatch for dex " << profile_key
+ << ", expected=" << num_method_ids
+ << ", actual=" << result->num_method_ids;
+ return nullptr;
+ }
return result;
}
@@ -1251,9 +1256,9 @@
DexFileData* dex_data = GetOrAddDexFileData(method_ref.dex_file);
if (dex_data != nullptr) {
// TODO: Add inline caches.
- dex_data->AddMethod(static_cast<MethodHotness::Flag>(hotness.GetFlags()),
- method_ref.dex_method_index);
- return true;
+ return dex_data->AddMethod(
+ static_cast<MethodHotness::Flag>(hotness.GetFlags()),
+ method_ref.dex_method_index);
}
return false;
}
@@ -1618,7 +1623,12 @@
}
// Mark a method as executed at least once.
-void ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, size_t index) {
+bool ProfileCompilationInfo::DexFileData::AddMethod(MethodHotness::Flag flags, size_t index) {
+ if (index >= num_method_ids) {
+ LOG(ERROR) << "Invalid method index " << index << ". num_method_ids=" << num_method_ids;
+ return false;
+ }
+
if ((flags & MethodHotness::kFlagStartup) != 0) {
method_bitmap.StoreBit(MethodBitIndex(/*startup*/ true, index), /*value*/ true);
}
@@ -1630,6 +1640,7 @@
index,
InlineCacheMap(std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)));
}
+ return true;
}
ProfileCompilationInfo::MethodHotness ProfileCompilationInfo::DexFileData::GetHotnessInfo(
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index 4ab8be8..29fa79c 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -280,7 +280,9 @@
}
for (Iterator it = index_begin; it != index_end; ++it) {
DCHECK_LT(*it, data->num_method_ids);
- data->AddMethod(flags, *it);
+ if (!data->AddMethod(flags, *it)) {
+ return false;
+ }
}
return true;
}
@@ -428,7 +430,7 @@
}
// Mark a method as executed at least once.
- void AddMethod(MethodHotness::Flag flags, size_t index);
+ bool AddMethod(MethodHotness::Flag flags, size_t index);
void MergeBitmap(const DexFileData& other) {
DCHECK_EQ(bitmap_storage.size(), other.bitmap_storage.size());
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 07dfb65..d40e6d9 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -493,6 +493,8 @@
if (oat_file_assistant.IsInBootClassPath()) {
return OatFileAssistant::kNoDexOptNeeded;
}
+
+ // TODO(calin): Extend DexFile.getDexOptNeeded to accept the class loader context. b/62269291.
return oat_file_assistant.GetDexOptNeeded(filter, profile_changed, downgrade);
}
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 8688205..8b141173 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -74,9 +74,6 @@
// For debugging, Open will print DlOpen error message if set to true.
static constexpr bool kPrintDlOpenErrorMessage = false;
-// If true, we advise the kernel about dex file mem map accesses.
-static constexpr bool kMadviseDexFileAccesses = true;
-
// Note for OatFileBase and descendents:
//
// These are used in OatFile::Open to try all our loaders.
@@ -1498,20 +1495,19 @@
// Madvise the dex file based on the state we are moving to.
void OatDexFile::MadviseDexFile(const DexFile& dex_file, MadviseState state) {
- const bool low_ram = Runtime::Current()->GetHeap()->IsLowMemoryMode();
+ Runtime* const runtime = Runtime::Current();
+ const bool low_ram = runtime->GetHeap()->IsLowMemoryMode();
// TODO: Also do madvise hints for non low ram devices.
- if (!kMadviseDexFileAccesses || !low_ram) {
+ if (!low_ram) {
return;
}
- if (state == MadviseState::kMadviseStateAtLoad) {
- if (low_ram) {
- // Default every dex file to MADV_RANDOM when its loaded by default for low ram devices.
- // Other devices have enough page cache to get performance benefits from loading more pages
- // into the page cache.
- MadviseLargestPageAlignedRegion(dex_file.Begin(),
- dex_file.Begin() + dex_file.Size(),
- MADV_RANDOM);
- }
+ if (state == MadviseState::kMadviseStateAtLoad && runtime->MAdviseRandomAccess()) {
+ // Default every dex file to MADV_RANDOM when its loaded by default for low ram devices.
+ // Other devices have enough page cache to get performance benefits from loading more pages
+ // into the page cache.
+ MadviseLargestPageAlignedRegion(dex_file.Begin(),
+ dex_file.Begin() + dex_file.Size(),
+ MADV_RANDOM);
}
const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile();
if (oat_dex_file != nullptr) {
@@ -1619,6 +1615,10 @@
return GetOatHeader().GetCompilerFilter();
}
+std::string OatFile::GetClassLoaderContext() const {
+ return GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey);
+};
+
OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file,
uint16_t class_def_idx,
bool* found) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 9a7fe51..0a05b29 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -115,6 +115,8 @@
CompilerFilter::Filter GetCompilerFilter() const;
+ std::string GetClassLoaderContext() const;
+
const std::string& GetLocation() const {
return location_;
}
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 5814df9..475a1c2 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -190,9 +190,13 @@
int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target,
bool profile_changed,
- bool downgrade) {
+ bool downgrade,
+ ClassLoaderContext* class_loader_context) {
OatFileInfo& info = GetBestInfo();
- DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target, profile_changed, downgrade);
+ DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target,
+ profile_changed,
+ downgrade,
+ class_loader_context);
if (info.IsOatLocation() || dexopt_needed == kDex2OatFromScratch) {
return dexopt_needed;
}
@@ -227,7 +231,7 @@
OatFileAssistant::ResultOfAttemptToUpdate
OatFileAssistant::MakeUpToDate(bool profile_changed,
- const std::string& class_loader_context,
+ ClassLoaderContext* class_loader_context,
std::string* error_msg) {
CompilerFilter::Filter target;
if (!GetRuntimeCompilerFilterOption(&target, error_msg)) {
@@ -245,7 +249,8 @@
// - however, MakeUpToDate will not always succeed (e.g. for primary apks, or for dex files
// loaded in other processes). So it boils down to how far do we want to complicate
// the logic in order to enable the use of oat files. Maybe its time to try simplify it.
- switch (info.GetDexOptNeeded(target, profile_changed, /*downgrade*/ false)) {
+ switch (info.GetDexOptNeeded(
+ target, profile_changed, /*downgrade*/ false, class_loader_context)) {
case kNoDexOptNeeded:
return kUpdateSucceeded;
@@ -640,10 +645,34 @@
return true;
}
+class Dex2oatFileWrapper {
+ public:
+ explicit Dex2oatFileWrapper(File* file)
+ : file_(file),
+ unlink_file_at_destruction_(true) {
+ }
+
+ ~Dex2oatFileWrapper() {
+ if (unlink_file_at_destruction_ && (file_ != nullptr)) {
+ file_->Erase(/*unlink*/ true);
+ }
+ }
+
+ File* GetFile() { return file_.get(); }
+
+ void DisableUnlinkAtDestruction() {
+ unlink_file_at_destruction_ = false;
+ };
+
+ private:
+ std::unique_ptr<File> file_;
+ bool unlink_file_at_destruction_;
+};
+
OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChecks(
OatFileAssistant::OatFileInfo& info,
CompilerFilter::Filter filter,
- const std::string& class_loader_context,
+ const ClassLoaderContext* class_loader_context,
std::string* error_msg) {
CHECK(error_msg != nullptr);
@@ -685,8 +714,9 @@
(dex_path_stat.st_mode & S_IRGRP) |
(dex_path_stat.st_mode & S_IROTH);
- std::unique_ptr<File> vdex_file(OS::CreateEmptyFile(vdex_file_name.c_str()));
- if (vdex_file.get() == nullptr) {
+ Dex2oatFileWrapper vdex_file_wrapper(OS::CreateEmptyFile(vdex_file_name.c_str()));
+ File* vdex_file = vdex_file_wrapper.GetFile();
+ if (vdex_file == nullptr) {
*error_msg = "Generation of oat file " + oat_file_name
+ " not attempted because the vdex file " + vdex_file_name
+ " could not be opened.";
@@ -700,8 +730,9 @@
return kUpdateNotAttempted;
}
- std::unique_ptr<File> oat_file(OS::CreateEmptyFile(oat_file_name.c_str()));
- if (oat_file.get() == nullptr) {
+ Dex2oatFileWrapper oat_file_wrapper(OS::CreateEmptyFile(oat_file_name.c_str()));
+ File* oat_file = oat_file_wrapper.GetFile();
+ if (oat_file == nullptr) {
*error_msg = "Generation of oat file " + oat_file_name
+ " not attempted because the oat file could not be created.";
return kUpdateNotAttempted;
@@ -710,7 +741,6 @@
if (fchmod(oat_file->Fd(), file_mode) != 0) {
*error_msg = "Generation of oat file " + oat_file_name
+ " not attempted because the oat file could not be made world readable.";
- oat_file->Erase();
return kUpdateNotAttempted;
}
@@ -720,32 +750,31 @@
args.push_back("--oat-fd=" + std::to_string(oat_file->Fd()));
args.push_back("--oat-location=" + oat_file_name);
args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
- args.push_back("--class-loader-context=" + class_loader_context);
+ const std::string dex2oat_context = class_loader_context == nullptr
+ ? OatFile::kSpecialSharedLibrary
+ : class_loader_context->EncodeContextForDex2oat(/*base_dir*/ "");
+ args.push_back("--class-loader-context=" + dex2oat_context);
if (!Dex2Oat(args, error_msg)) {
- // Manually delete the oat and vdex files. This ensures there is no garbage
- // left over if the process unexpectedly died.
- vdex_file->Erase();
- unlink(vdex_file_name.c_str());
- oat_file->Erase();
- unlink(oat_file_name.c_str());
return kUpdateFailed;
}
if (vdex_file->FlushCloseOrErase() != 0) {
*error_msg = "Unable to close vdex file " + vdex_file_name;
- unlink(vdex_file_name.c_str());
return kUpdateFailed;
}
if (oat_file->FlushCloseOrErase() != 0) {
*error_msg = "Unable to close oat file " + oat_file_name;
- unlink(oat_file_name.c_str());
return kUpdateFailed;
}
// Mark that the odex file has changed and we should try to reload.
info.Reset();
+ // We have compiled successfully. Disable the auto-unlink.
+ vdex_file_wrapper.DisableUnlinkAtDestruction();
+ oat_file_wrapper.DisableUnlinkAtDestruction();
+
return kUpdateSucceeded;
}
@@ -1016,31 +1045,40 @@
}
OatFileAssistant::DexOptNeeded OatFileAssistant::OatFileInfo::GetDexOptNeeded(
- CompilerFilter::Filter target, bool profile_changed, bool downgrade) {
+ CompilerFilter::Filter target,
+ bool profile_changed,
+ bool downgrade,
+ ClassLoaderContext* context) {
+
bool compilation_desired = CompilerFilter::IsAotCompilationEnabled(target);
bool filter_okay = CompilerFilterIsOkay(target, profile_changed, downgrade);
+ bool class_loader_context_okay = ClassLoaderContextIsOkay(context);
- if (filter_okay && Status() == kOatUpToDate) {
- // The oat file is in good shape as is.
- return kNoDexOptNeeded;
- }
+ // Only check the filter and relocation if the class loader context is ok.
+ // If it is not, we will return kDex2OatFromScratch as the compilation needs to be redone.
+ if (class_loader_context_okay) {
+ if (filter_okay && Status() == kOatUpToDate) {
+ // The oat file is in good shape as is.
+ return kNoDexOptNeeded;
+ }
- if (filter_okay && !compilation_desired && Status() == kOatRelocationOutOfDate) {
- // If no compilation is desired, then it doesn't matter if the oat
- // file needs relocation. It's in good shape as is.
- return kNoDexOptNeeded;
- }
+ if (filter_okay && !compilation_desired && Status() == kOatRelocationOutOfDate) {
+ // If no compilation is desired, then it doesn't matter if the oat
+ // file needs relocation. It's in good shape as is.
+ return kNoDexOptNeeded;
+ }
- if (filter_okay && Status() == kOatRelocationOutOfDate) {
- return kDex2OatForRelocation;
- }
+ if (filter_okay && Status() == kOatRelocationOutOfDate) {
+ return kDex2OatForRelocation;
+ }
- if (IsUseable()) {
- return kDex2OatForFilter;
- }
+ if (IsUseable()) {
+ return kDex2OatForFilter;
+ }
- if (Status() == kOatBootImageOutOfDate) {
- return kDex2OatForBootImage;
+ if (Status() == kOatBootImageOutOfDate) {
+ return kDex2OatForBootImage;
+ }
}
if (oat_file_assistant_->HasOriginalDexFiles()) {
@@ -1090,6 +1128,36 @@
CompilerFilter::IsAsGoodAs(current, target);
}
+bool OatFileAssistant::OatFileInfo::ClassLoaderContextIsOkay(ClassLoaderContext* context) {
+ if (context == nullptr) {
+ VLOG(oat) << "ClassLoaderContext check ignored: null context";
+ return true;
+ }
+
+ const OatFile* file = GetFile();
+ if (file == nullptr) {
+ return false;
+ }
+
+ size_t dir_index = file->GetLocation().rfind('/');
+ std::string classpath_dir = (dir_index != std::string::npos)
+ ? file->GetLocation().substr(0, dir_index)
+ : "";
+
+ if (!context->OpenDexFiles(oat_file_assistant_->isa_, classpath_dir)) {
+ VLOG(oat) << "ClassLoaderContext check failed: dex files from the context could not be opened";
+ return false;
+ }
+
+ bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext());
+ if (!result) {
+ VLOG(oat) << "ClassLoaderContext check failed. Context was "
+ << file->GetClassLoaderContext()
+ << ". The expected context is " << context->EncodeContextForOatFile(classpath_dir);
+ }
+ return result;
+}
+
bool OatFileAssistant::OatFileInfo::IsExecutable() {
const OatFile* file = GetFile();
return (file != nullptr && file->IsExecutable());
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 5eec943..6dc3c19 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -26,6 +26,7 @@
#include "base/scoped_flock.h"
#include "base/unix_file/fd_file.h"
#include "compiler_filter.h"
+#include "class_loader_context.h"
#include "oat_file.h"
#include "os.h"
@@ -164,7 +165,8 @@
// the oat file in the odex location.
int GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
bool profile_changed = false,
- bool downgrade = false);
+ bool downgrade = false,
+ ClassLoaderContext* context = nullptr);
// Returns true if there is up-to-date code for this dex location,
// irrespective of the compiler filter of the up-to-date code.
@@ -194,7 +196,7 @@
// to a string describing why there was a failure or the update was not
// attempted. error_msg must not be null.
ResultOfAttemptToUpdate MakeUpToDate(bool profile_changed,
- const std::string& class_loader_context,
+ ClassLoaderContext* class_loader_context,
std::string* error_msg);
// Returns an oat file that can be used for loading dex files.
@@ -330,7 +332,8 @@
// compiler filter.
DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
bool profile_changed,
- bool downgrade);
+ bool downgrade,
+ ClassLoaderContext* context);
// Returns the loaded file.
// Loads the file if needed. Returns null if the file failed to load.
@@ -367,6 +370,8 @@
// compiler filter.
bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed, bool downgrade);
+ bool ClassLoaderContextIsOkay(ClassLoaderContext* context);
+
// Release the loaded oat file.
// Returns null if the oat file hasn't been loaded.
//
@@ -404,7 +409,7 @@
// attempted. error_msg must not be null.
ResultOfAttemptToUpdate GenerateOatFileNoChecks(OatFileInfo& info,
CompilerFilter::Filter target,
- const std::string& class_loader_context,
+ const ClassLoaderContext* class_loader_context,
std::string* error_msg);
// Return info for the best oat file.
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index ea665d9..0ad26d6 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -36,6 +36,7 @@
namespace art {
static const std::string kSpecialSharedLibrary = "&";
+static ClassLoaderContext* kSpecialSharedLibraryContext = nullptr;
class OatFileAssistantTest : public DexoptTest {};
@@ -117,7 +118,7 @@
// Trying to make the oat file up to date should not fail or crash.
std::string error_msg;
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg));
// Trying to get the best oat file should fail, but not crash.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -770,7 +771,7 @@
std::string error_msg;
Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) <<
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) <<
error_msg;
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
@@ -951,7 +952,7 @@
// We should get kUpdateSucceeded from MakeUpToDate since there's nothing
// that can be done in this situation.
ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg));
// Verify it didn't create an oat in the default location (dalvik-cache).
OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
@@ -1030,7 +1031,7 @@
std::string error_msg;
Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg));
EXPECT_TRUE(error_msg.empty());
}
@@ -1177,7 +1178,7 @@
std::string error_msg;
Runtime::Current()->AddCompilerOption("--compiler-filter=quicken");
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) <<
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) <<
error_msg;
EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
@@ -1186,7 +1187,7 @@
Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg))
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg))
<< error_msg;
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
@@ -1195,7 +1196,7 @@
Runtime::Current()->AddCompilerOption("--compiler-filter=bogus");
EXPECT_EQ(OatFileAssistant::kUpdateNotAttempted,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg));
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg));
}
TEST(OatFileAssistantUtilsTest, DexLocationToOdexFilename) {
@@ -1255,7 +1256,7 @@
OatFileAssistant::kDefaultCompilerFilterForDexLoading;
std::string error_msg;
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg)) <<
+ oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) <<
error_msg;
EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(default_filter));
@@ -1273,7 +1274,7 @@
const CompilerFilter::Filter default_filter =
OatFileAssistant::kDefaultCompilerFilterForDexLoading;
std::string error_msg;
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibrary, &error_msg);
+ int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(default_filter));
@@ -1295,19 +1296,52 @@
OatFileAssistant::kDefaultCompilerFilterForDexLoading;
std::string error_msg;
std::string context_str = "PCL[" + context_location + "]";
- int status = oat_file_assistant.MakeUpToDate(false, context_str, &error_msg);
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
+ ASSERT_TRUE(context != nullptr);
+ ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
+
+ int status = oat_file_assistant.MakeUpToDate(false, context.get(), &error_msg);
EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(default_filter));
+ oat_file_assistant.GetDexOptNeeded(default_filter, false, false, context.get()));
+
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
EXPECT_NE(nullptr, oat_file.get());
- std::unique_ptr<ClassLoaderContext> context =
- ClassLoaderContext::Create(context_str);
- context->OpenDexFiles(kRuntimeISA, "");
EXPECT_EQ(context->EncodeContextForOatFile(""),
oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
}
+TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string context_location = GetScratchDir() + "/ContextDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+ Copy(GetDexSrc2(), context_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ const CompilerFilter::Filter default_filter =
+ OatFileAssistant::kDefaultCompilerFilterForDexLoading;
+ std::string error_msg;
+ std::string context_str = "PCL[" + context_location + "]";
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
+ ASSERT_TRUE(context != nullptr);
+ ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
+
+ int status = oat_file_assistant.MakeUpToDate(false, context.get(), &error_msg);
+ EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(default_filter, false, false, context.get()));
+
+ // Update the context by overriding the jar file.
+ Copy(GetMultiDexSrc2(), context_location);
+ std::unique_ptr<ClassLoaderContext> updated_context = ClassLoaderContext::Create(context_str);
+ ASSERT_TRUE(updated_context != nullptr);
+ // DexOptNeeded should advise compilation from scratch.
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(
+ default_filter, false, false, updated_context.get()));
+}
+
// TODO: More Tests:
// * Test class linker falls back to unquickened dex for DexNoOat
// * Test class linker falls back to unquickened dex for MultiDexNoOat
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 499f356..9b43a31 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -361,8 +361,7 @@
// If the pat file loading context matches the context used during compilation then we accept
// the oat file without addition checks
- if (context->VerifyClassLoaderContextMatch(
- oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey))) {
+ if (context->VerifyClassLoaderContextMatch(oat_file->GetClassLoaderContext())) {
return false;
}
@@ -426,12 +425,14 @@
// Update the oat file on disk if we can, based on the --compiler-filter
// option derived from the current runtime options.
// This may fail, but that's okay. Best effort is all that matters here.
-
- const std::string& dex2oat_context = context == nullptr
- ? OatFile::kSpecialSharedLibrary
- : context->EncodeContextForDex2oat(/*base_dir*/ "");
- switch (oat_file_assistant.MakeUpToDate(
- /*profile_changed*/false, dex2oat_context, /*out*/ &error_msg)) {
+ // TODO(calin): b/64530081 b/66984396. Pass a null context to verify and compile
+ // secondary dex files in isolation (and avoid to extract/verify the main apk
+ // if it's in the class path). Note this trades correctness for performance
+ // since the resulting slow down is unacceptable in some cases until b/64530081
+ // is fixed.
+ switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/ false,
+ /*class_loader_context*/ nullptr,
+ /*out*/ &error_msg)) {
case OatFileAssistant::kUpdateFailed:
LOG(WARNING) << error_msg;
break;
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 1d524fd..5d2f3b2 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -121,7 +121,7 @@
.WithType<double>().WithRange(0.1, 0.9)
.IntoKey(M::HeapTargetUtilization)
.Define("-XX:ForegroundHeapGrowthMultiplier=_")
- .WithType<double>().WithRange(0.1, 1.0)
+ .WithType<double>().WithRange(0.1, 5.0)
.IntoKey(M::ForegroundHeapGrowthMultiplier)
.Define("-XX:ParallelGCThreads=_")
.WithType<unsigned int>()
@@ -159,6 +159,10 @@
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
.IntoKey(M::DumpNativeStackOnSigQuit)
+ .Define("-XX:MadviseRandomAccess:_")
+ .WithType<bool>()
+ .WithValueMap({{"false", false}, {"true", true}})
+ .IntoKey(M::MadviseRandomAccess)
.Define("-Xusejit:_")
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
@@ -714,6 +718,7 @@
UsageMessage(stream, " -XX:LargeObjectSpace={disabled,map,freelist}\n");
UsageMessage(stream, " -XX:LargeObjectThreshold=N\n");
UsageMessage(stream, " -XX:DumpNativeStackOnSigQuit=booleanvalue\n");
+ UsageMessage(stream, " -XX:MadviseRandomAccess:booleanvalue\n");
UsageMessage(stream, " -XX:SlowDebug={false,true}\n");
UsageMessage(stream, " -Xmethod-trace\n");
UsageMessage(stream, " -Xmethod-trace-file:filename");
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 216edd9..c855906 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -171,6 +171,11 @@
static constexpr double kLowMemoryMaxLoadFactor = 0.8;
static constexpr double kNormalMinLoadFactor = 0.4;
static constexpr double kNormalMaxLoadFactor = 0.7;
+
+// Extra added to the default heap growth multiplier. Used to adjust the GC ergonomics for the read
+// barrier config.
+static constexpr double kExtraDefaultHeapGrowthMultiplier = kUseReadBarrier ? 1.0 : 0.0;
+
Runtime* Runtime::instance_ = nullptr;
struct TraceConfig {
@@ -416,6 +421,12 @@
return;
}
Thread* self = Thread::Current();
+
+ // Dump all threads first and then the aborting thread. While this is counter the logical flow,
+ // it improves the chance of relevant data surviving in the Android logs.
+
+ DumpAllThreads(os, self);
+
if (self == nullptr) {
os << "(Aborting thread was not attached to runtime!)\n";
DumpKernelStack(os, GetTid(), " kernel: ", false);
@@ -431,7 +442,6 @@
}
}
}
- DumpAllThreads(os, self);
}
// No thread-safety analysis as we do explicitly test for holding the mutator lock.
@@ -1130,6 +1140,7 @@
zygote_max_failed_boots_ = runtime_options.GetOrDefault(Opt::ZygoteMaxFailedBoots);
experimental_flags_ = runtime_options.GetOrDefault(Opt::Experimental);
is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode);
+ madvise_random_access_ = runtime_options.GetOrDefault(Opt::MadviseRandomAccess);
plugins_ = runtime_options.ReleaseOrDefault(Opt::Plugins);
agents_ = runtime_options.ReleaseOrDefault(Opt::AgentPath);
@@ -1138,13 +1149,22 @@
// agents_.push_back(lib);
// }
+ float foreground_heap_growth_multiplier;
+ if (is_low_memory_mode_ && !runtime_options.Exists(Opt::ForegroundHeapGrowthMultiplier)) {
+ // If low memory mode, use 1.0 as the multiplier by default.
+ foreground_heap_growth_multiplier = 1.0f;
+ } else {
+ foreground_heap_growth_multiplier =
+ runtime_options.GetOrDefault(Opt::ForegroundHeapGrowthMultiplier) +
+ kExtraDefaultHeapGrowthMultiplier;
+ }
XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
runtime_options.GetOrDefault(Opt::HeapMinFree),
runtime_options.GetOrDefault(Opt::HeapMaxFree),
runtime_options.GetOrDefault(Opt::HeapTargetUtilization),
- runtime_options.GetOrDefault(Opt::ForegroundHeapGrowthMultiplier),
+ foreground_heap_growth_multiplier,
runtime_options.GetOrDefault(Opt::MemoryMaximumSize),
runtime_options.GetOrDefault(Opt::NonMovingSpaceCapacity),
runtime_options.GetOrDefault(Opt::Image),
diff --git a/runtime/runtime.h b/runtime/runtime.h
index c632089..5180acd 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -671,6 +671,12 @@
return result;
}
+ // Whether or not we use MADV_RANDOM on files that are thought to have random access patterns.
+ // This is beneficial for low RAM devices since it reduces page cache thrashing.
+ bool MAdviseRandomAccess() const {
+ return madvise_random_access_;
+ }
+
private:
static void InitPlatformSignalHandlers();
@@ -902,6 +908,10 @@
// Whether or not we are on a low RAM device.
bool is_low_memory_mode_;
+ // Whether or not we use MADV_RANDOM on files that are thought to have random access patterns.
+ // This is beneficial for low RAM devices since it reduces page cache thrashing.
+ bool madvise_random_access_;
+
// Whether the application should run in safe mode, that is, interpreter only.
bool safe_mode_;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 78a60fa..f8671a9 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -70,6 +70,7 @@
RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true)
RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, false)
RUNTIME_OPTIONS_KEY (bool, DumpNativeStackOnSigQuit, true)
+RUNTIME_OPTIONS_KEY (bool, MadviseRandomAccess, false)
RUNTIME_OPTIONS_KEY (unsigned int, JITCompileThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITWarmupThreshold)
RUNTIME_OPTIONS_KEY (unsigned int, JITOsrThreshold)
diff --git a/runtime/signal_catcher.cc b/runtime/signal_catcher.cc
index f0b6ee4..523584e 100644
--- a/runtime/signal_catcher.cc
+++ b/runtime/signal_catcher.cc
@@ -169,7 +169,7 @@
#if defined(ART_TARGET_ANDROID)
if (use_tombstoned_stack_trace_fd_ && !tombstoned_notify_completion(tombstone_fd)) {
- LOG(WARNING) << "Unable to notify tombstoned of dump completion.";
+ PLOG(WARNING) << "Unable to notify tombstoned of dump completion";
}
#endif
}
diff --git a/test/164-resolution-trampoline-dex-cache/expected.txt b/test/164-resolution-trampoline-dex-cache/expected.txt
new file mode 100644
index 0000000..d549cb1
--- /dev/null
+++ b/test/164-resolution-trampoline-dex-cache/expected.txt
@@ -0,0 +1,3 @@
+JNI_OnLoad called
+Base.foo() on MostDerived
+MostDerived.test(.) done.
diff --git a/test/164-resolution-trampoline-dex-cache/info.txt b/test/164-resolution-trampoline-dex-cache/info.txt
new file mode 100644
index 0000000..4e4d82a
--- /dev/null
+++ b/test/164-resolution-trampoline-dex-cache/info.txt
@@ -0,0 +1,3 @@
+Regression test for artQuickResolutionTrampoline() erroneously storing an
+ArtMethod to a DexCache for a MethodId referencing a class missing from the
+associated ClassTable. This discrepancy then led to a crash when JITting.
diff --git a/test/164-resolution-trampoline-dex-cache/profile b/test/164-resolution-trampoline-dex-cache/profile
new file mode 100644
index 0000000..d232ff2
--- /dev/null
+++ b/test/164-resolution-trampoline-dex-cache/profile
@@ -0,0 +1 @@
+LMostDerived;->test(Ljava/lang/Class;)V
diff --git a/test/164-resolution-trampoline-dex-cache/run b/test/164-resolution-trampoline-dex-cache/run
new file mode 100644
index 0000000..5e77cd5
--- /dev/null
+++ b/test/164-resolution-trampoline-dex-cache/run
@@ -0,0 +1,22 @@
+#!/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.
+
+# Use the --secondary switch to add secondary dex file to class path.
+# Make sure we compile the required method using speed-profile compiler filter.
+# Enable JIT through runtime option to avoid the compiler filter set by --jit.
+exec ${RUN} "${@}" --secondary \
+ -Xcompiler-option --compiler-filter=speed-profile --profile \
+ --runtime-option -Xusejit:true
diff --git a/test/164-resolution-trampoline-dex-cache/src-ex/MostDerived.java b/test/164-resolution-trampoline-dex-cache/src-ex/MostDerived.java
new file mode 100644
index 0000000..0c85528
--- /dev/null
+++ b/test/164-resolution-trampoline-dex-cache/src-ex/MostDerived.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+public class MostDerived extends Derived {
+ public static void test(Class main) {
+ // The defining class loader of MostDerived (MDCL) is also the initiating loader of
+ // superclass Derived but delegates the loading to its parent class loader (PCL) which
+ // defines both Derived and Base. Thus Derived.class is recorded in MDCL's ClassTable
+ // but the Base.class is not because the Base's initiating loader is PCL. This is the
+ // case when loading the MostDerived class and remains the case after resolving the
+ // "invoke-super Derived.foo(.)" called from from MostDerived.foo(.). When that
+ // invoke-super is executed from AOT-compiled code, it goes through the .bss ArtMethod*
+ // entry and on first execution goes through the resolution method. After resolving to
+ // the Base.foo(.), the artQuickResolutionTrampoline() used to erroneously fill the
+ // Base.foo(.) entry in the MostDerived's DexCache which is wrong as the referenced
+ // class Base is not in the associated, i.e. MDCL's, ClassTable.
+ new MostDerived().foo(main);
+ try {
+ // This discrepancy then used to crash when resolving the Base.foo(.) method
+ // for JIT compilation of another method.
+ main.getDeclaredMethod("ensureJitCompiled", Class.class, String.class).invoke(
+ null, MostDerived.class, "bar");
+ } catch (Throwable t) {
+ t.printStackTrace(System.out);
+ }
+ System.out.println("MostDerived.test(.) done.");
+ }
+
+ public void foo(Class main) {
+ super.foo(main);
+ }
+
+ public void bar(Class main) {
+ Base b = this;
+ b.foo(main);
+ }
+}
diff --git a/test/164-resolution-trampoline-dex-cache/src/Base.java b/test/164-resolution-trampoline-dex-cache/src/Base.java
new file mode 100644
index 0000000..d4ef78b
--- /dev/null
+++ b/test/164-resolution-trampoline-dex-cache/src/Base.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Base {
+ public void foo(Class main) {
+ System.out.println("Base.foo() on " + getClass().getName());
+ }
+}
diff --git a/test/164-resolution-trampoline-dex-cache/src/Derived.java b/test/164-resolution-trampoline-dex-cache/src/Derived.java
new file mode 100644
index 0000000..acca9ce
--- /dev/null
+++ b/test/164-resolution-trampoline-dex-cache/src/Derived.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+public class Derived extends Base {
+}
diff --git a/test/164-resolution-trampoline-dex-cache/src/Main.java b/test/164-resolution-trampoline-dex-cache/src/Main.java
new file mode 100644
index 0000000..b4731ae
--- /dev/null
+++ b/test/164-resolution-trampoline-dex-cache/src/Main.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+public class Main {
+ public static String TEST_NAME = "164-resolution-trampoline-dex-cache";
+
+ public static void main(String[] args) {
+ // Load the test JNI for ensureJitCompiled(). Note that classes Main loaded
+ // by other class loaders do not have the binding, so we need to pass the
+ // current Main.class around and call ensureJitCompiled() via reflection.
+ System.loadLibrary(args[0]);
+ try {
+ String dex_location = System.getenv("DEX_LOCATION");
+ ClassLoader systemLoader = ClassLoader.getSystemClassLoader().getParent();
+ ClassLoader baseLoader = getClassLoaderFor(dex_location, systemLoader, /* ex */ false);
+ ClassLoader mainLoader = getClassLoaderFor(dex_location, baseLoader, /* ex */ true);
+
+ Class<?> tc = Class.forName("MostDerived", true, mainLoader);
+ Method m = tc.getDeclaredMethod("test", Class.class);
+ m.invoke(null, Main.class);
+ } catch (Throwable t) {
+ t.printStackTrace(System.out);
+ }
+ }
+
+ public static ClassLoader getClassLoaderFor(String location, ClassLoader parent, boolean ex)
+ throws Exception {
+ try {
+ Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
+ Constructor<?> ctor =
+ class_loader_class.getConstructor(String.class, ClassLoader.class);
+ String path = location + "/" + TEST_NAME + (ex ? "-ex.jar" : ".jar");
+ return (ClassLoader)ctor.newInstance(path, parent);
+ } catch (ClassNotFoundException e) {
+ // Running on RI. Use URLClassLoader.
+ String url = "file://" + location + (ex ? "/classes-ex/" : "/classes/");
+ return new java.net.URLClassLoader(
+ new java.net.URL[] { new java.net.URL(url) }, parent);
+ }
+ }
+
+ public static native void ensureJitCompiled(Class<?> klass, String method_name);
+}
diff --git a/test/911-get-stack-trace/src/art/PrintThread.java b/test/911-get-stack-trace/src/art/PrintThread.java
index fee5ba0..d8b3cbc 100644
--- a/test/911-get-stack-trace/src/art/PrintThread.java
+++ b/test/911-get-stack-trace/src/art/PrintThread.java
@@ -42,7 +42,7 @@
// may not exist depending on the environment.
public final static String IGNORE_THREAD_NAME_REGEX =
"Binder:|RenderThread|hwuiTask|Jit thread pool worker|Instr:|JDWP|Profile Saver|main|" +
- "queued-work-looper";
+ "queued-work-looper|InstrumentationConnectionThread";
public final static Matcher IGNORE_THREADS =
Pattern.compile(IGNORE_THREAD_NAME_REGEX).matcher("");
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index 9dcc5f9..7ad5d60 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -56,7 +56,7 @@
boot <- 1+2 (A,B)
[class A, class B, class java.lang.Object]
-[37, 0]
+[35, 0]
B, false
Load: LB; on ClassEvents
diff --git a/test/912-classes/src-art/art/Test912.java b/test/912-classes/src-art/art/Test912.java
index fbf8794..1742276 100644
--- a/test/912-classes/src-art/art/Test912.java
+++ b/test/912-classes/src-art/art/Test912.java
@@ -19,8 +19,10 @@
import java.lang.ref.Reference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Proxy;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Base64;
import java.util.Comparator;
public class Test912 {
@@ -214,8 +216,34 @@
}
}
- private static void testClassVersion() {
- System.out.println(Arrays.toString(getClassVersion(Main.class)));
+ /**
+ * base64 encoded class/dex file for
+ * class Transform {
+ * public void sayHi() {
+ * System.out.println("Goodbye");
+ * }
+ * }
+ */
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCLXSBQ5FiS3f16krSYZFF8xYZtFVp0GRXMAgAAcAAAAHhWNBIAAAAAAAAAACwCAAAO" +
+ "AAAAcAAAAAYAAACoAAAAAgAAAMAAAAABAAAA2AAAAAQAAADgAAAAAQAAAAABAACsAQAAIAEAAGIB" +
+ "AABqAQAAcwEAAIABAACXAQAAqwEAAL8BAADTAQAA4wEAAOYBAADqAQAA/gEAAAMCAAAMAgAAAgAA" +
+ "AAMAAAAEAAAABQAAAAYAAAAIAAAACAAAAAUAAAAAAAAACQAAAAUAAABcAQAABAABAAsAAAAAAAAA" +
+ "AAAAAAAAAAANAAAAAQABAAwAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAB4CAAAA" +
+ "AAAAAQABAAEAAAATAgAABAAAAHAQAwAAAA4AAwABAAIAAAAYAgAACQAAAGIAAAAbAQEAAABuIAIA" +
+ "EAAOAAAAAQAAAAMABjxpbml0PgAHR29vZGJ5ZQALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50" +
+ "U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xh" +
+ "bmcvU3lzdGVtOwAOVHJhbnNmb3JtLmphdmEAAVYAAlZMABJlbWl0dGVyOiBqYWNrLTMuMzYAA291" +
+ "dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAAcOhQAAAAEBAICABKACAQG4Ag0AAAAAAAAAAQAAAAAA" +
+ "AAABAAAADgAAAHAAAAACAAAABgAAAKgAAAADAAAAAgAAAMAAAAAEAAAAAQAAANgAAAAFAAAABAAA" +
+ "AOAAAAAGAAAAAQAAAAABAAABIAAAAgAAACABAAABEAAAAQAAAFwBAAACIAAADgAAAGIBAAADIAAA" +
+ "AgAAABMCAAAAIAAAAQAAAB4CAAAAEAAAAQAAACwCAAA=");
+ private static void testClassVersion() throws Exception {
+ Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
+ Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
+ Class target = ((ClassLoader)ctor.newInstance(
+ ByteBuffer.wrap(DEX_BYTES), Test912.class.getClassLoader())).loadClass("Transform");
+ System.out.println(Arrays.toString(getClassVersion(target)));
}
private static void testClassEvents() throws Exception {
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 69421de..a2502e6 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -105,6 +105,14 @@
"description": ["This test sometimes runs out of memory initializing the boot classpath."]
},
{
+ "tests": "164-resolution-trampoline-dex-cache",
+ "variant": "interp-ac | interpreter",
+ "description": ["This test requires AOT mixed with JIT and enables the JIT by the ",
+ "runtime option -Xusejit:true. This conflicts with -Xint passed for ",
+ "interpreter configurations (interp-ac | interpreter). The 'jit' ",
+ "configuration succeeds even though it does not test anything useful."]
+ },
+ {
"tests": ["908-gc-start-finish",
"913-heaps"],
"variant": "gcstress",