summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/image_writer.cc6
-rw-r--r--oatdump/oatdump.cc12
-rw-r--r--runtime/Android.mk1
-rw-r--r--runtime/base/mutex.cc6
-rw-r--r--runtime/base/mutex.h6
-rw-r--r--runtime/class_linker.cc382
-rw-r--r--runtime/class_linker.h43
-rw-r--r--runtime/gc/space/image_space.cc15
-rw-r--r--runtime/gc/space/image_space.h5
-rw-r--r--runtime/native/dalvik_system_DexFile.cc8
-rw-r--r--runtime/oat_file.cc15
-rw-r--r--runtime/oat_file_assistant_test.cc20
-rw-r--r--runtime/oat_file_manager.cc347
-rw-r--r--runtime/oat_file_manager.h105
-rw-r--r--runtime/runtime.cc14
-rw-r--r--runtime/runtime.h9
16 files changed, 556 insertions, 438 deletions
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index d9f8fcb43a..4310be6464 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -55,6 +55,7 @@
#include "mirror/string-inl.h"
#include "oat.h"
#include "oat_file.h"
+#include "oat_file_manager.h"
#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "handle_scope-inl.h"
@@ -126,8 +127,6 @@ bool ImageWriter::Write(const std::string& image_filename,
const std::string& oat_location) {
CHECK(!image_filename.empty());
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-
std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename.c_str()));
if (oat_file.get() == nullptr) {
PLOG(ERROR) << "Failed to open oat file " << oat_filename << " for " << oat_location;
@@ -141,7 +140,8 @@ bool ImageWriter::Write(const std::string& image_filename,
oat_file->Erase();
return false;
}
- CHECK_EQ(class_linker->RegisterOatFile(oat_file_), oat_file_);
+ Runtime::Current()->GetOatFileManager().RegisterOatFile(
+ std::unique_ptr<const OatFile>(oat_file_));
interpreter_to_interpreter_bridge_offset_ =
oat_file_->GetOatHeader().GetInterpreterToInterpreterBridgeOffset();
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index e2486041af..b9d81a7b2b 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -49,6 +49,7 @@
#include "mirror/object_array-inl.h"
#include "oat.h"
#include "oat_file-inl.h"
+#include "oat_file_manager.h"
#include "os.h"
#include "output_stream.h"
#include "safe_map.h"
@@ -1563,13 +1564,15 @@ class ImageDumper {
}
os << "\n";
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Runtime* const runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
std::string image_filename = image_space_.GetImageFilename();
std::string oat_location = ImageHeader::GetOatLocationFromImageLocation(image_filename);
os << "OAT LOCATION: " << oat_location;
os << "\n";
std::string error_msg;
- const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
+ const OatFile* oat_file = runtime->GetOatFileManager().FindOpenedOatFileFromOatLocation(
+ oat_location);
if (oat_file == nullptr) {
oat_file = OatFile::Open(oat_location, oat_location,
nullptr, nullptr, false, nullptr,
@@ -1594,7 +1597,7 @@ class ImageDumper {
os << "OBJECTS:\n" << std::flush;
// Loop through all the image spaces and dump their objects.
- gc::Heap* heap = Runtime::Current()->GetHeap();
+ gc::Heap* heap = runtime->GetHeap();
const std::vector<gc::space::ContinuousSpace*>& spaces = heap->GetContinuousSpaces();
Thread* self = Thread::Current();
{
@@ -2394,7 +2397,8 @@ static int DumpOatWithRuntime(Runtime* runtime, OatFile* oat_file, OatDumperOpti
// Need to register dex files to get a working dex cache.
ScopedObjectAccess soa(self);
ClassLinker* class_linker = runtime->GetClassLinker();
- class_linker->RegisterOatFile(oat_file);
+ Runtime::Current()->GetOatFileManager().RegisterOatFile(
+ std::unique_ptr<const OatFile>(oat_file));
std::vector<const DexFile*> class_path;
for (const OatFile::OatDexFile* odf : oat_file->GetOatDexFiles()) {
std::string error_msg;
diff --git a/runtime/Android.mk b/runtime/Android.mk
index b8d3e13744..2eb5db104d 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -154,6 +154,7 @@ LIBART_COMMON_SRC_FILES := \
oat.cc \
oat_file.cc \
oat_file_assistant.cc \
+ oat_file_manager.cc \
object_lock.cc \
offsets.cc \
os_linux.cc \
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index b2c567760f..30bfb4a796 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -50,6 +50,7 @@ Mutex* Locks::mem_maps_lock_ = nullptr;
Mutex* Locks::modify_ldt_lock_ = nullptr;
MutatorMutex* Locks::mutator_lock_ = nullptr;
Mutex* Locks::profiler_lock_ = nullptr;
+ReaderWriterMutex* Locks::oat_file_manager_lock_ = nullptr;
Mutex* Locks::reference_processor_lock_ = nullptr;
Mutex* Locks::reference_queue_cleared_references_lock_ = nullptr;
Mutex* Locks::reference_queue_finalizer_references_lock_ = nullptr;
@@ -940,6 +941,7 @@ void Locks::Init() {
DCHECK(classlinker_classes_lock_ != nullptr);
DCHECK(deoptimization_lock_ != nullptr);
DCHECK(heap_bitmap_lock_ != nullptr);
+ DCHECK(oat_file_manager_lock_ != nullptr);
DCHECK(intern_table_lock_ != nullptr);
DCHECK(jni_libraries_lock_ != nullptr);
DCHECK(logging_lock_ != nullptr);
@@ -1028,6 +1030,10 @@ void Locks::Init() {
modify_ldt_lock_ = new Mutex("modify_ldt lock", current_lock_level);
}
+ UPDATE_CURRENT_LOCK_LEVEL(kOatFileManagerLock);
+ DCHECK(oat_file_manager_lock_ == nullptr);
+ oat_file_manager_lock_ = new ReaderWriterMutex("OatFile manager lock", current_lock_level);
+
UPDATE_CURRENT_LOCK_LEVEL(kInternTableLock);
DCHECK(intern_table_lock_ == nullptr);
intern_table_lock_ = new Mutex("InternTable lock", current_lock_level);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 3da806b54d..17f6a039f8 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -83,6 +83,7 @@ enum LockLevel {
kDexFileToMethodInlinerMapLock,
kInternTableLock,
kOatFileSecondaryLookupLock,
+ kOatFileManagerLock,
kTracingUniqueMethodsLock,
kTracingStreamingLock,
kDefaultMutexLevel,
@@ -644,8 +645,11 @@ class Locks {
// Guards modification of the LDT on x86.
static Mutex* modify_ldt_lock_ ACQUIRED_AFTER(allocated_thread_ids_lock_);
+ // Guards opened oat files in OatFileManager.
+ static ReaderWriterMutex* oat_file_manager_lock_ ACQUIRED_AFTER(modify_ldt_lock_);
+
// Guards intern table.
- static Mutex* intern_table_lock_ ACQUIRED_AFTER(modify_ldt_lock_);
+ static Mutex* intern_table_lock_ ACQUIRED_AFTER(oat_file_manager_lock_);
// Guards reference processor.
static Mutex* reference_processor_lock_ ACQUIRED_AFTER(intern_table_lock_);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index acb39c5402..6fa8fc1f66 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -58,6 +58,7 @@
#include "oat_file.h"
#include "oat_file-inl.h"
#include "oat_file_assistant.h"
+#include "oat_file_manager.h"
#include "object_lock.h"
#include "mirror/class.h"
#include "mirror/class-inl.h"
@@ -89,9 +90,6 @@ namespace art {
static constexpr bool kSanityCheckObjects = kIsDebugBuild;
-// For b/21333911.
-static constexpr bool kDuplicateClassesCheck = false;
-
static void ThrowNoClassDefFoundError(const char* fmt, ...)
__attribute__((__format__(__printf__, 1, 2)))
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -696,343 +694,6 @@ void ClassLinker::RunRootClinits() {
}
}
-const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
- WriterMutexLock mu(Thread::Current(), dex_lock_);
- if (kIsDebugBuild) {
- for (size_t i = 0; i < oat_files_.size(); ++i) {
- CHECK_NE(oat_file, oat_files_[i]) << oat_file->GetLocation();
- }
- }
- VLOG(class_linker) << "Registering " << oat_file->GetLocation();
- oat_files_.push_back(oat_file);
- return oat_file;
-}
-
-OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) {
- VLOG(startup) << "ClassLinker::GetImageOatFile entering";
- OatFile* oat_file = space->ReleaseOatFile();
- CHECK_EQ(RegisterOatFile(oat_file), oat_file);
- VLOG(startup) << "ClassLinker::GetImageOatFile exiting";
- return *oat_file;
-}
-
-class DexFileAndClassPair : ValueObject {
- public:
- DexFileAndClassPair(const DexFile* dex_file, size_t current_class_index, bool from_loaded_oat)
- : cached_descriptor_(GetClassDescriptor(dex_file, current_class_index)),
- dex_file_(dex_file),
- current_class_index_(current_class_index),
- from_loaded_oat_(from_loaded_oat) {}
-
- DexFileAndClassPair(const DexFileAndClassPair&) = default;
-
- DexFileAndClassPair& operator=(const DexFileAndClassPair& rhs) {
- cached_descriptor_ = rhs.cached_descriptor_;
- dex_file_ = rhs.dex_file_;
- current_class_index_ = rhs.current_class_index_;
- from_loaded_oat_ = rhs.from_loaded_oat_;
- return *this;
- }
-
- const char* GetCachedDescriptor() const {
- return cached_descriptor_;
- }
-
- bool operator<(const DexFileAndClassPair& rhs) const {
- const char* lhsDescriptor = cached_descriptor_;
- const char* rhsDescriptor = rhs.cached_descriptor_;
- int cmp = strcmp(lhsDescriptor, rhsDescriptor);
- if (cmp != 0) {
- // Note that the order must be reversed. We want to iterate over the classes in dex files.
- // They are sorted lexicographically. Thus, the priority-queue must be a min-queue.
- return cmp > 0;
- }
- return dex_file_ < rhs.dex_file_;
- }
-
- bool DexFileHasMoreClasses() const {
- return current_class_index_ + 1 < dex_file_->NumClassDefs();
- }
-
- DexFileAndClassPair GetNext() const {
- return DexFileAndClassPair(dex_file_, current_class_index_ + 1, from_loaded_oat_);
- }
-
- size_t GetCurrentClassIndex() const {
- return current_class_index_;
- }
-
- bool FromLoadedOat() const {
- return from_loaded_oat_;
- }
-
- const DexFile* GetDexFile() const {
- return dex_file_;
- }
-
- void DeleteDexFile() {
- delete dex_file_;
- dex_file_ = nullptr;
- }
-
- private:
- static const char* GetClassDescriptor(const DexFile* dex_file, size_t index) {
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(static_cast<uint16_t>(index));
- return dex_file->StringByTypeIdx(class_def.class_idx_);
- }
-
- const char* cached_descriptor_;
- const DexFile* dex_file_;
- size_t current_class_index_;
- bool from_loaded_oat_; // We only need to compare mismatches between what we load now
- // and what was loaded before. Any old duplicates must have been
- // OK, and any new "internal" duplicates are as well (they must
- // be from multidex, which resolves correctly).
-};
-
-static void AddDexFilesFromOat(const OatFile* oat_file,
- bool already_loaded,
- std::priority_queue<DexFileAndClassPair>* heap) {
- const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles();
- for (const OatDexFile* oat_dex_file : oat_dex_files) {
- std::string error;
- std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error);
- if (dex_file.get() == nullptr) {
- LOG(WARNING) << "Could not create dex file from oat file: " << error;
- } else {
- if (dex_file->NumClassDefs() > 0U) {
- heap->emplace(dex_file.release(), 0U, already_loaded);
- }
- }
- }
-}
-
-static void AddNext(DexFileAndClassPair* original,
- std::priority_queue<DexFileAndClassPair>* heap) {
- if (original->DexFileHasMoreClasses()) {
- heap->push(original->GetNext());
- } else {
- // Need to delete the dex file.
- original->DeleteDexFile();
- }
-}
-
-static void FreeDexFilesInHeap(std::priority_queue<DexFileAndClassPair>* heap) {
- while (!heap->empty()) {
- delete heap->top().GetDexFile();
- heap->pop();
- }
-}
-
-const OatFile* ClassLinker::GetBootOatFile() {
- gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
- if (image_space == nullptr) {
- return nullptr;
- }
- return image_space->GetOatFile();
-}
-
-const OatFile* ClassLinker::GetPrimaryOatFile() {
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- const OatFile* boot_oat_file = GetBootOatFile();
- if (boot_oat_file != nullptr) {
- for (const OatFile* oat_file : oat_files_) {
- if (oat_file != boot_oat_file) {
- return oat_file;
- }
- }
- }
- return nullptr;
-}
-
-// Check for class-def collisions in dex files.
-//
-// This works by maintaining a heap with one class from each dex file, sorted by the class
-// descriptor. Then a dex-file/class pair is continually removed from the heap and compared
-// against the following top element. If the descriptor is the same, it is now checked whether
-// the two elements agree on whether their dex file was from an already-loaded oat-file or the
-// new oat file. Any disagreement indicates a collision.
-bool ClassLinker::HasCollisions(const OatFile* oat_file, std::string* error_msg) {
- if (!kDuplicateClassesCheck) {
- return false;
- }
-
- // Dex files are registered late - once a class is actually being loaded. We have to compare
- // against the open oat files. Take the dex_lock_ that protects oat_files_ accesses.
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
-
- std::priority_queue<DexFileAndClassPair> queue;
-
- // Add dex files from already loaded oat files, but skip boot.
- {
- const OatFile* boot_oat = GetBootOatFile();
- for (const OatFile* loaded_oat_file : oat_files_) {
- if (loaded_oat_file == boot_oat) {
- continue;
- }
- AddDexFilesFromOat(loaded_oat_file, true, &queue);
- }
- }
-
- if (queue.empty()) {
- // No other oat files, return early.
- return false;
- }
-
- // Add dex files from the oat file to check.
- AddDexFilesFromOat(oat_file, false, &queue);
-
- // Now drain the queue.
- while (!queue.empty()) {
- DexFileAndClassPair compare_pop = queue.top();
- queue.pop();
-
- // Compare against the following elements.
- while (!queue.empty()) {
- DexFileAndClassPair top = queue.top();
-
- if (strcmp(compare_pop.GetCachedDescriptor(), top.GetCachedDescriptor()) == 0) {
- // Same descriptor. Check whether it's crossing old-oat-files to new-oat-files.
- if (compare_pop.FromLoadedOat() != top.FromLoadedOat()) {
- *error_msg =
- StringPrintf("Found duplicated class when checking oat files: '%s' in %s and %s",
- compare_pop.GetCachedDescriptor(),
- compare_pop.GetDexFile()->GetLocation().c_str(),
- top.GetDexFile()->GetLocation().c_str());
- FreeDexFilesInHeap(&queue);
- return true;
- }
- // Pop it.
- queue.pop();
- AddNext(&top, &queue);
- } else {
- // Something else. Done here.
- break;
- }
- }
- AddNext(&compare_pop, &queue);
- }
-
- return false;
-}
-
-std::vector<std::unique_ptr<const DexFile>> ClassLinker::OpenDexFilesFromOat(
- const char* dex_location, const char* oat_location,
- std::vector<std::string>* error_msgs) {
- CHECK(error_msgs != nullptr);
-
- // Verify we aren't holding the mutator lock, which could starve GC if we
- // have to generate or relocate an oat file.
- Locks::mutator_lock_->AssertNotHeld(Thread::Current());
-
- OatFileAssistant oat_file_assistant(dex_location, oat_location, kRuntimeISA,
- !Runtime::Current()->IsAotCompiler());
-
- // Lock the target oat location to avoid races generating and loading the
- // oat file.
- std::string error_msg;
- if (!oat_file_assistant.Lock(&error_msg)) {
- // Don't worry too much if this fails. If it does fail, it's unlikely we
- // can generate an oat file anyway.
- VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;
- }
-
- // Check if we already have an up-to-date oat file open.
- const OatFile* source_oat_file = nullptr;
- {
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- for (const OatFile* oat_file : oat_files_) {
- CHECK(oat_file != nullptr);
- if (oat_file_assistant.GivenOatFileIsUpToDate(*oat_file)) {
- source_oat_file = oat_file;
- break;
- }
- }
- }
-
- // If we didn't have an up-to-date oat file open, try to load one from disk.
- if (source_oat_file == nullptr) {
- // Update the oat file on disk if we can. This may fail, but that's okay.
- // Best effort is all that matters here.
- if (!oat_file_assistant.MakeUpToDate(&error_msg)) {
- LOG(WARNING) << error_msg;
- }
-
- // Get the oat file on disk.
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- if (oat_file.get() != nullptr) {
- // Take the file only if it has no collisions, or we must take it because of preopting.
- bool accept_oat_file = !HasCollisions(oat_file.get(), &error_msg);
- if (!accept_oat_file) {
- // Failed the collision check. Print warning.
- if (Runtime::Current()->IsDexFileFallbackEnabled()) {
- LOG(WARNING) << "Found duplicate classes, falling back to interpreter mode for "
- << dex_location;
- } else {
- LOG(WARNING) << "Found duplicate classes, dex-file-fallback disabled, will be failing to "
- " load classes for " << dex_location;
- }
- LOG(WARNING) << error_msg;
-
- // However, if the app was part of /system and preopted, there is no original dex file
- // available. In that case grudgingly accept the oat file.
- if (!DexFile::MaybeDex(dex_location)) {
- accept_oat_file = true;
- LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. "
- << "Allow oat file use. This is potentially dangerous.";
- }
- }
-
- if (accept_oat_file) {
- source_oat_file = oat_file.release();
- RegisterOatFile(source_oat_file);
- }
- }
- }
-
- std::vector<std::unique_ptr<const DexFile>> dex_files;
-
- // Load the dex files from the oat file.
- if (source_oat_file != nullptr) {
- dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
- if (dex_files.empty()) {
- error_msgs->push_back("Failed to open dex files from "
- + source_oat_file->GetLocation());
- }
- }
-
- // Fall back to running out of the original dex file if we couldn't load any
- // dex_files from the oat file.
- if (dex_files.empty()) {
- if (oat_file_assistant.HasOriginalDexFiles()) {
- if (Runtime::Current()->IsDexFileFallbackEnabled()) {
- if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
- LOG(WARNING) << error_msg;
- error_msgs->push_back("Failed to open dex files from " + std::string(dex_location));
- }
- } else {
- error_msgs->push_back("Fallback mode disabled, skipping dex files.");
- }
- } else {
- error_msgs->push_back("No original dex files found for dex location "
- + std::string(dex_location));
- }
- }
- return dex_files;
-}
-
-const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- for (size_t i = 0; i < oat_files_.size(); i++) {
- const OatFile* oat_file = oat_files_[i];
- DCHECK(oat_file != nullptr);
- if (oat_file->GetLocation() == oat_location) {
- return oat_file;
- }
- }
- return nullptr;
-}
-
static void SanityCheckArtMethod(ArtMethod* m,
mirror::Class* expected_class,
gc::space::ImageSpace* space)
@@ -1169,16 +830,17 @@ void ClassLinker::InitFromImage() {
CHECK(space != nullptr);
image_pointer_size_ = space->GetImageHeader().GetPointerSize();
dex_cache_image_class_lookup_required_ = true;
- OatFile& oat_file = GetImageOatFile(space);
- CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
- CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatDataBegin(), 0U);
- const char* image_file_location = oat_file.GetOatHeader().
+ const OatFile* oat_file = runtime->GetOatFileManager().RegisterImageOatFile(space);
+ DCHECK(oat_file != nullptr);
+ CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
+ CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0U);
+ const char* image_file_location = oat_file->GetOatHeader().
GetStoreValueByKey(OatHeader::kImageLocationKey);
CHECK(image_file_location == nullptr || *image_file_location == 0);
- quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline();
- quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline();
- quick_generic_jni_trampoline_ = oat_file.GetOatHeader().GetQuickGenericJniTrampoline();
- quick_to_interpreter_bridge_trampoline_ = oat_file.GetOatHeader().GetQuickToInterpreterBridge();
+ quick_resolution_trampoline_ = oat_file->GetOatHeader().GetQuickResolutionTrampoline();
+ quick_imt_conflict_trampoline_ = oat_file->GetOatHeader().GetQuickImtConflictTrampoline();
+ quick_generic_jni_trampoline_ = oat_file->GetOatHeader().GetQuickGenericJniTrampoline();
+ quick_to_interpreter_bridge_trampoline_ = oat_file->GetOatHeader().GetQuickToInterpreterBridge();
StackHandleScope<2> hs(self);
mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(
@@ -1200,20 +862,20 @@ void ClassLinker::InitFromImage() {
java_lang_Object->GetObjectSize(),
VoidFunctor()));
- CHECK_EQ(oat_file.GetOatHeader().GetDexFileCount(),
+ CHECK_EQ(oat_file->GetOatHeader().GetDexFileCount(),
static_cast<uint32_t>(dex_caches->GetLength()));
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
StackHandleScope<1> hs2(self);
Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i)));
const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
- const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location.c_str(),
- nullptr);
- CHECK(oat_dex_file != nullptr) << oat_file.GetLocation() << " " << dex_file_location;
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file_location.c_str(),
+ nullptr);
+ CHECK(oat_dex_file != nullptr) << oat_file->GetLocation() << " " << dex_file_location;
std::string error_msg;
std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
- if (dex_file.get() == nullptr) {
+ if (dex_file == nullptr) {
LOG(FATAL) << "Failed to open dex file " << dex_file_location
- << " from within oat file " << oat_file.GetLocation()
+ << " from within oat file " << oat_file->GetLocation()
<< " error '" << error_msg << "'";
UNREACHABLE();
}
@@ -1508,7 +1170,6 @@ ClassLinker::~ClassLinker() {
mirror::IntArray::ResetArrayClass();
mirror::LongArray::ResetArrayClass();
mirror::ShortArray::ResetArrayClass();
- STLDeleteElements(&oat_files_);
Thread* const self = Thread::Current();
JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
for (const ClassLoaderData& data : class_loaders_) {
@@ -6075,7 +5736,8 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) {
}
bool ClassLinker::MayBeCalledWithDirectCodePointer(ArtMethod* m) {
- if (Runtime::Current()->UseJit()) {
+ Runtime* const runtime = Runtime::Current();
+ if (runtime->UseJit()) {
// JIT can have direct code pointers from any method to any other method.
return true;
}
@@ -6097,13 +5759,7 @@ bool ClassLinker::MayBeCalledWithDirectCodePointer(ArtMethod* m) {
} else {
// The method can be called outside its own oat file. Therefore it won't be called using its
// direct code pointer only if all loaded oat files have been compiled in PIC mode.
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- for (const OatFile* oat_file : oat_files_) {
- if (!oat_file->IsPic()) {
- return true;
- }
- }
- return false;
+ return runtime->GetOatFileManager().HaveNonPicOatFile();
}
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 7f3e93806e..76cb0a6fb6 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -325,17 +325,10 @@ class ClassLinker {
REQUIRES(!dex_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
- const OatFile* RegisterOatFile(const OatFile* oat_file)
- REQUIRES(!dex_lock_);
-
const std::vector<const DexFile*>& GetBootClassPath() {
return boot_class_path_;
}
- // Returns the first non-image oat file in the class path.
- const OatFile* GetPrimaryOatFile()
- REQUIRES(!dex_lock_);
-
void VisitClasses(ClassVisitor* visitor)
REQUIRES(!Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -364,26 +357,6 @@ class ClassLinker {
REQUIRES(!dex_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
- // Finds or creates the oat file holding dex_location. Then loads and returns
- // all corresponding dex files (there may be more than one dex file loaded
- // in the case of multidex).
- // This may return the original, unquickened dex files if the oat file could
- // not be generated.
- //
- // Returns an empty vector if the dex files could not be loaded. In this
- // case, there will be at least one error message returned describing why no
- // dex files could not be loaded. The 'error_msgs' argument must not be
- // null, regardless of whether there is an error or not.
- //
- // This method should not be called with the mutator_lock_ held, because it
- // could end up starving GC if we need to generate or relocate any oat
- // files.
- std::vector<std::unique_ptr<const DexFile>> OpenDexFilesFromOat(
- const char* dex_location,
- const char* oat_location,
- std::vector<std::string>* error_msgs)
- REQUIRES(!dex_lock_, !Locks::mutator_lock_);
-
// Allocate an instance of a java.lang.Object.
mirror::Object* AllocObject(Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_)
@@ -581,10 +554,6 @@ class ClassLinker {
REQUIRES(Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
- OatFile& GetImageOatFile(gc::space::ImageSpace* space)
- REQUIRES(!dex_lock_)
- SHARED_REQUIRES(Locks::mutator_lock_);
-
void FinishInit(Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!dex_lock_, !Roles::uninterruptible_);
@@ -758,12 +727,6 @@ class ClassLinker {
return dex_caches_;
}
- const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location)
- REQUIRES(!dex_lock_);
-
- // Returns the boot image oat file.
- const OatFile* GetBootOatFile() SHARED_REQUIRES(dex_lock_);
-
void CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out)
SHARED_REQUIRES(Locks::mutator_lock_);
void CreateProxyMethod(Handle<mirror::Class> klass, ArtMethod* prototype, ArtMethod* out)
@@ -813,9 +776,6 @@ class ClassLinker {
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!dex_lock_);
- // Check for duplicate class definitions of the given oat file against all open oat files.
- bool HasCollisions(const OatFile* oat_file, std::string* error_msg) REQUIRES(!dex_lock_);
-
bool HasInitWithString(Thread* self, const char* descriptor)
SHARED_REQUIRES(Locks::mutator_lock_) REQUIRES(!dex_lock_);
@@ -834,7 +794,6 @@ class ClassLinker {
// JNI weak globals to allow dex caches to get unloaded. We lazily delete weak globals when we
// register new dex files.
std::list<jweak> dex_caches_ GUARDED_BY(dex_lock_);
- std::vector<const OatFile*> oat_files_ GUARDED_BY(dex_lock_);
// This contains the class loaders which have class tables. It is populated by
// InsertClassTableForClassLoader.
@@ -880,8 +839,8 @@ class ClassLinker {
// Image pointer size.
size_t image_pointer_size_;
+ friend class ImageDumper; // for DexLock
friend class ImageWriter; // for GetClassRoots
- friend class ImageDumper; // for FindOpenedOatFileFromOatLocation
friend class JniCompilerTest; // for GetRuntimeQuickGenericJniStub
friend class JniInternalTest; // for GetRuntimeQuickGenericJniStub
ART_FRIEND_TEST(mirror::DexCacheTest, Open); // for AllocDexCache
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 1923d24805..ce64b10364 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -789,10 +789,13 @@ OatFile* ImageSpace::OpenOatFile(const char* image_path, std::string* error_msg)
CHECK(image_header.GetOatDataBegin() != nullptr);
- OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),
+ OatFile* oat_file = OatFile::Open(oat_filename,
+ oat_filename,
+ image_header.GetOatDataBegin(),
image_header.GetOatFileBegin(),
!Runtime::Current()->IsAotCompiler(),
- nullptr, error_msg);
+ nullptr,
+ error_msg);
if (oat_file == nullptr) {
*error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
oat_filename.c_str(), GetName(), error_msg->c_str());
@@ -839,15 +842,13 @@ bool ImageSpace::ValidateOatFile(std::string* error_msg) const {
return true;
}
-
const OatFile* ImageSpace::GetOatFile() const {
return oat_file_non_owned_;
}
-
-OatFile* ImageSpace::ReleaseOatFile() {
- CHECK(oat_file_.get() != nullptr);
- return oat_file_.release();
+std::unique_ptr<const OatFile> ImageSpace::ReleaseOatFile() {
+ CHECK(oat_file_ != nullptr);
+ return std::move(oat_file_);
}
void ImageSpace::Dump(std::ostream& os) const {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 215c18b8d9..99207426a0 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -62,9 +62,8 @@ class ImageSpace : public MemMapSpace {
const OatFile* GetOatFile() const;
// Releases the OatFile from the ImageSpace so it can be transfer to
- // the caller, presumably the ClassLinker.
- OatFile* ReleaseOatFile()
- SHARED_REQUIRES(Locks::mutator_lock_);
+ // the caller, presumably the OatFileManager.
+ std::unique_ptr<const OatFile> ReleaseOatFile();
void VerifyImageAllocations()
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 4aebc2c35f..4850b6fe85 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -27,6 +27,7 @@
#include "mirror/object-inl.h"
#include "mirror/string.h"
#include "oat_file_assistant.h"
+#include "oat_file_manager.h"
#include "os.h"
#include "profiler.h"
#include "runtime.h"
@@ -160,11 +161,14 @@ static jobject DexFile_openDexFileNative(
return 0;
}
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ Runtime* const runtime = Runtime::Current();
+ ClassLinker* linker = runtime->GetClassLinker();
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::vector<std::string> error_msgs;
- dex_files = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs);
+ dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
+ outputName.c_str(),
+ &error_msgs);
if (!dex_files.empty()) {
jlongArray array = ConvertNativeToJavaArray(env, dex_files);
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index a4a159e0da..80f017de5e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -42,6 +42,7 @@
#include "mirror/class.h"
#include "mirror/object-inl.h"
#include "oat_file-inl.h"
+#include "oat_file_manager.h"
#include "os.h"
#include "runtime.h"
#include "utils.h"
@@ -115,7 +116,19 @@ OatFile* OatFile::Open(const std::string& filename,
// TODO: Also try when not executable? The issue here could be re-mapping as writable (as
// !executable is a sign that we may want to patch), which may not be allowed for
// various reasons.
- if (kUseDlopen && (kIsTargetBuild || kUseDlopenOnHost) && executable) {
+ // dlopen always returns the same library if it is already opened on the host. For this reason
+ // we only use dlopen if we are the target or we do not already have the dex file opened. Having
+ // the same library loaded multiple times at different addresses is required for class unloading
+ // and for having dex caches arrays in the .bss section.
+ Runtime* const runtime = Runtime::Current();
+ OatFileManager* const manager = (runtime != nullptr) ? &runtime->GetOatFileManager() : nullptr;
+ if (kUseDlopen &&
+ (kIsTargetBuild ||
+ (kUseDlopenOnHost &&
+ // Manager may be null if we are running without a runtime.
+ manager != nullptr &&
+ manager->FindOpenedOatFileFromOatLocation(location) == nullptr)) &&
+ executable) {
// Try to use dlopen. This may fail for various reasons, outlined below. We try dlopen, as
// this will register the oat file with the linker and allows libunwind to find our info.
ret.reset(OpenDlopen(filename, location, requested_base, abs_dex_location, error_msg));
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 20347a9063..de4e8ec717 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -31,6 +31,7 @@
#include "compiler_callbacks.h"
#include "gc/space/image_space.h"
#include "mem_map.h"
+#include "oat_file_manager.h"
#include "os.h"
#include "scoped_thread_state_change.h"
#include "thread-inl.h"
@@ -958,10 +959,12 @@ class RaceGenerateTask : public Task {
// Load the dex files, and save a pointer to the loaded oat file, so that
// we can verify only one oat file was loaded for the dex location.
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::vector<std::string> error_msgs;
- dex_files = linker->OpenDexFilesFromOat(dex_location_.c_str(), oat_location_.c_str(), &error_msgs);
+ dex_files = Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(
+ dex_location_.c_str(),
+ oat_location_.c_str(),
+ &error_msgs);
CHECK(!dex_files.empty()) << Join(error_msgs, '\n');
CHECK(dex_files[0]->GetOatDexFile() != nullptr) << dex_files[0]->GetLocation();
loaded_oat_file_ = dex_files[0]->GetOatDexFile()->GetOatFile();
@@ -980,8 +983,9 @@ class RaceGenerateTask : public Task {
// Test the case where multiple processes race to generate an oat file.
// This simulates multiple processes using multiple threads.
//
-// We want only one Oat file to be loaded when there is a race to load, to
-// avoid using up the virtual memory address space.
+// We want unique Oat files to be loaded even when there is a race to load.
+// TODO: The test case no longer tests locking the way it was intended since we now get multiple
+// copies of the same Oat files mapped at different locations.
TEST_F(OatFileAssistantTest, RaceToGenerate) {
std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar";
std::string oat_location = GetOdexDir() + "/RaceToGenerate.oat";
@@ -1002,10 +1006,12 @@ TEST_F(OatFileAssistantTest, RaceToGenerate) {
thread_pool.StartWorkers(self);
thread_pool.Wait(self, true, false);
- // Verify every task got the same pointer.
- const OatFile* expected = tasks[0]->GetLoadedOatFile();
+ // Verify every task got a unique oat file.
+ std::set<const OatFile*> oat_files;
for (auto& task : tasks) {
- EXPECT_EQ(expected, task->GetLoadedOatFile());
+ const OatFile* oat_file = task->GetLoadedOatFile();
+ EXPECT_TRUE(oat_files.find(oat_file) == oat_files.end());
+ oat_files.insert(oat_file);
}
}
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
new file mode 100644
index 0000000000..73b065feaa
--- /dev/null
+++ b/runtime/oat_file_manager.cc
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "oat_file_manager.h"
+
+#include <memory>
+#include <queue>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "dex_file.h"
+#include "gc/space/image_space.h"
+#include "oat_file_assistant.h"
+#include "thread-inl.h"
+
+namespace art {
+
+// For b/21333911.
+static constexpr bool kDuplicateClassesCheck = false;
+
+const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file) {
+ ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+ DCHECK(oat_file != nullptr);
+ if (kIsDebugBuild) {
+ for (const std::unique_ptr<const OatFile>& existing : oat_files_) {
+ CHECK_NE(oat_file.get(), existing.get()) << oat_file->GetLocation();
+ // Check that we don't have an oat file with the same address. Copies of the same oat file
+ // should be loaded at different addresses.
+ CHECK_NE(oat_file->Begin(), existing->Begin()) << "Oat file already mapped at that location";
+ }
+ }
+ have_non_pic_oat_file_ = have_non_pic_oat_file_ || !oat_file->IsPic();
+ oat_files_.push_back(std::move(oat_file));
+ return oat_files_.back().get();
+}
+
+const OatFile* OatFileManager::FindOpenedOatFileFromOatLocation(const std::string& oat_location)
+ const {
+ ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+ for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) {
+ if (oat_file->GetLocation() == oat_location) {
+ return oat_file.get();
+ }
+ }
+ return nullptr;
+}
+
+const OatFile* OatFileManager::GetBootOatFile() const {
+ gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
+ if (image_space == nullptr) {
+ return nullptr;
+ }
+ return image_space->GetOatFile();
+}
+
+const OatFile* OatFileManager::GetPrimaryOatFile() const {
+ ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+ const OatFile* boot_oat_file = GetBootOatFile();
+ if (boot_oat_file != nullptr) {
+ for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) {
+ if (oat_file.get() != boot_oat_file) {
+ return oat_file.get();
+ }
+ }
+ }
+ return nullptr;
+}
+
+OatFileManager::~OatFileManager() {
+}
+
+const OatFile* OatFileManager::RegisterImageOatFile(gc::space::ImageSpace* space) {
+ return RegisterOatFile(space->ReleaseOatFile());
+}
+
+class DexFileAndClassPair : ValueObject {
+ public:
+ DexFileAndClassPair(const DexFile* dex_file, size_t current_class_index, bool from_loaded_oat)
+ : cached_descriptor_(GetClassDescriptor(dex_file, current_class_index)),
+ dex_file_(dex_file),
+ current_class_index_(current_class_index),
+ from_loaded_oat_(from_loaded_oat) {}
+
+ DexFileAndClassPair(DexFileAndClassPair&& rhs) {
+ *this = std::move(rhs);
+ }
+
+ DexFileAndClassPair& operator=(DexFileAndClassPair&& rhs) {
+ cached_descriptor_ = rhs.cached_descriptor_;
+ dex_file_ = std::move(rhs.dex_file_);
+ current_class_index_ = rhs.current_class_index_;
+ from_loaded_oat_ = rhs.from_loaded_oat_;
+ return *this;
+ }
+
+ const char* GetCachedDescriptor() const {
+ return cached_descriptor_;
+ }
+
+ bool operator<(const DexFileAndClassPair& rhs) const {
+ const int cmp = strcmp(cached_descriptor_, rhs.cached_descriptor_);
+ if (cmp != 0) {
+ // Note that the order must be reversed. We want to iterate over the classes in dex files.
+ // They are sorted lexicographically. Thus, the priority-queue must be a min-queue.
+ return cmp > 0;
+ }
+ return dex_file_ < rhs.dex_file_;
+ }
+
+ bool DexFileHasMoreClasses() const {
+ return current_class_index_ + 1 < dex_file_->NumClassDefs();
+ }
+
+ void Next() {
+ ++current_class_index_;
+ }
+
+ size_t GetCurrentClassIndex() const {
+ return current_class_index_;
+ }
+
+ bool FromLoadedOat() const {
+ return from_loaded_oat_;
+ }
+
+ const DexFile* GetDexFile() const {
+ return dex_file_.get();
+ }
+
+ private:
+ static const char* GetClassDescriptor(const DexFile* dex_file, size_t index) {
+ DCHECK(IsUint<16>(index));
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(static_cast<uint16_t>(index));
+ return dex_file->StringByTypeIdx(class_def.class_idx_);
+ }
+
+ const char* cached_descriptor_;
+ std::unique_ptr<const DexFile> dex_file_;
+ size_t current_class_index_;
+ bool from_loaded_oat_; // We only need to compare mismatches between what we load now
+ // and what was loaded before. Any old duplicates must have been
+ // OK, and any new "internal" duplicates are as well (they must
+ // be from multidex, which resolves correctly).
+};
+
+static void AddDexFilesFromOat(const OatFile* oat_file,
+ bool already_loaded,
+ /*out*/std::priority_queue<DexFileAndClassPair>* heap) {
+ for (const OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
+ std::string error;
+ std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error);
+ if (dex_file == nullptr) {
+ LOG(WARNING) << "Could not create dex file from oat file: " << error;
+ } else if (dex_file->NumClassDefs() > 0U) {
+ heap->emplace(dex_file.release(), /*current_class_index*/0U, already_loaded);
+ }
+ }
+}
+
+static void AddNext(/*inout*/DexFileAndClassPair* original,
+ /*inout*/std::priority_queue<DexFileAndClassPair>* heap) {
+ if (original->DexFileHasMoreClasses()) {
+ original->Next();
+ heap->push(std::move(*original));
+ }
+}
+
+// Check for class-def collisions in dex files.
+//
+// This works by maintaining a heap with one class from each dex file, sorted by the class
+// descriptor. Then a dex-file/class pair is continually removed from the heap and compared
+// against the following top element. If the descriptor is the same, it is now checked whether
+// the two elements agree on whether their dex file was from an already-loaded oat-file or the
+// new oat file. Any disagreement indicates a collision.
+bool OatFileManager::HasCollisions(const OatFile* oat_file,
+ std::string* error_msg /*out*/) const {
+ DCHECK(oat_file != nullptr);
+ DCHECK(error_msg != nullptr);
+ if (!kDuplicateClassesCheck) {
+ return false;
+ }
+
+ // Dex files are registered late - once a class is actually being loaded. We have to compare
+ // against the open oat files. Take the oat_file_manager_lock_ that protects oat_files_ accesses.
+ ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
+
+ std::priority_queue<DexFileAndClassPair> queue;
+
+ // Add dex files from already loaded oat files, but skip boot.
+ const OatFile* boot_oat = GetBootOatFile();
+ for (const std::unique_ptr<const OatFile>& loaded_oat_file : oat_files_) {
+ if (loaded_oat_file.get() != boot_oat) {
+ AddDexFilesFromOat(loaded_oat_file.get(), /*already_loaded*/true, &queue);
+ }
+ }
+
+ if (queue.empty()) {
+ // No other oat files, return early.
+ return false;
+ }
+
+ // Add dex files from the oat file to check.
+ AddDexFilesFromOat(oat_file, /*already_loaded*/false, &queue);
+
+ // Now drain the queue.
+ while (!queue.empty()) {
+ // Modifying the top element is only safe if we pop right after.
+ DexFileAndClassPair compare_pop(std::move(const_cast<DexFileAndClassPair&>(queue.top())));
+ queue.pop();
+
+ // Compare against the following elements.
+ while (!queue.empty()) {
+ DexFileAndClassPair top(std::move(const_cast<DexFileAndClassPair&>(queue.top())));
+
+ if (strcmp(compare_pop.GetCachedDescriptor(), top.GetCachedDescriptor()) == 0) {
+ // Same descriptor. Check whether it's crossing old-oat-files to new-oat-files.
+ if (compare_pop.FromLoadedOat() != top.FromLoadedOat()) {
+ *error_msg =
+ StringPrintf("Found duplicated class when checking oat files: '%s' in %s and %s",
+ compare_pop.GetCachedDescriptor(),
+ compare_pop.GetDexFile()->GetLocation().c_str(),
+ top.GetDexFile()->GetLocation().c_str());
+ return true;
+ }
+ // Pop it.
+ queue.pop();
+ AddNext(&top, &queue);
+ } else {
+ // Something else. Done here.
+ break;
+ }
+ }
+ AddNext(&compare_pop, &queue);
+ }
+
+ return false;
+}
+
+std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
+ const char* dex_location,
+ const char* oat_location,
+ std::vector<std::string>* error_msgs) {
+ CHECK(dex_location != nullptr);
+ CHECK(error_msgs != nullptr);
+
+ // Verify we aren't holding the mutator lock, which could starve GC if we
+ // have to generate or relocate an oat file.
+ Locks::mutator_lock_->AssertNotHeld(Thread::Current());
+
+ OatFileAssistant oat_file_assistant(dex_location,
+ oat_location,
+ kRuntimeISA,
+ !Runtime::Current()->IsAotCompiler());
+
+ // Lock the target oat location to avoid races generating and loading the
+ // oat file.
+ std::string error_msg;
+ if (!oat_file_assistant.Lock(/*out*/&error_msg)) {
+ // Don't worry too much if this fails. If it does fail, it's unlikely we
+ // can generate an oat file anyway.
+ VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;
+ }
+
+ const OatFile* source_oat_file = nullptr;
+
+ // Update the oat file on disk if we can. This may fail, but that's okay.
+ // Best effort is all that matters here.
+ if (!oat_file_assistant.MakeUpToDate(/*out*/&error_msg)) {
+ LOG(WARNING) << error_msg;
+ }
+
+ // Get the oat file on disk.
+ std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
+ if (oat_file != nullptr) {
+ // Take the file only if it has no collisions, or we must take it because of preopting.
+ bool accept_oat_file = !HasCollisions(oat_file.get(), /*out*/ &error_msg);
+ if (!accept_oat_file) {
+ // Failed the collision check. Print warning.
+ if (Runtime::Current()->IsDexFileFallbackEnabled()) {
+ LOG(WARNING) << "Found duplicate classes, falling back to interpreter mode for "
+ << dex_location;
+ } else {
+ LOG(WARNING) << "Found duplicate classes, dex-file-fallback disabled, will be failing to "
+ " load classes for " << dex_location;
+ }
+ LOG(WARNING) << error_msg;
+
+ // However, if the app was part of /system and preopted, there is no original dex file
+ // available. In that case grudgingly accept the oat file.
+ if (!DexFile::MaybeDex(dex_location)) {
+ accept_oat_file = true;
+ LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. "
+ << "Allow oat file use. This is potentially dangerous.";
+ }
+ }
+
+ if (accept_oat_file) {
+ VLOG(class_linker) << "Registering " << oat_file->GetLocation();
+ source_oat_file = RegisterOatFile(std::move(oat_file));
+ }
+ }
+
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+
+ // Load the dex files from the oat file.
+ if (source_oat_file != nullptr) {
+ dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
+ if (dex_files.empty()) {
+ error_msgs->push_back("Failed to open dex files from " + source_oat_file->GetLocation());
+ }
+ }
+
+ // Fall back to running out of the original dex file if we couldn't load any
+ // dex_files from the oat file.
+ if (dex_files.empty()) {
+ if (oat_file_assistant.HasOriginalDexFiles()) {
+ if (Runtime::Current()->IsDexFileFallbackEnabled()) {
+ if (!DexFile::Open(dex_location, dex_location, /*out*/ &error_msg, &dex_files)) {
+ LOG(WARNING) << error_msg;
+ error_msgs->push_back("Failed to open dex files from " + std::string(dex_location));
+ }
+ } else {
+ error_msgs->push_back("Fallback mode disabled, skipping dex files.");
+ }
+ } else {
+ error_msgs->push_back("No original dex files found for dex location "
+ + std::string(dex_location));
+ }
+ }
+ return dex_files;
+}
+
+} // namespace art
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
new file mode 100644
index 0000000000..3059cb5bb7
--- /dev/null
+++ b/runtime/oat_file_manager.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_OAT_FILE_MANAGER_H_
+#define ART_RUNTIME_OAT_FILE_MANAGER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/mutex.h"
+
+namespace art {
+
+namespace gc {
+namespace space {
+class ImageSpace;
+} // namespace space
+} // namespace gc
+
+class DexFile;
+class OatFile;
+
+// Class for dealing with oat file management.
+//
+// This class knows about all the loaded oat files and provides utility functions. The oat file
+// pointers returned from functions are always valid.
+class OatFileManager {
+ public:
+ OatFileManager() : have_non_pic_oat_file_(false) {}
+ ~OatFileManager();
+
+ // Add an oat file to the internal accounting, std::aborts if there already exists an oat file
+ // with the same base address. Returns the oat file pointer from oat_file.
+ const OatFile* RegisterOatFile(std::unique_ptr<const OatFile> oat_file)
+ REQUIRES(!Locks::oat_file_manager_lock_);
+
+ // Find the first opened oat file with the same location, returns null if there are none.
+ const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location) const
+ REQUIRES(!Locks::oat_file_manager_lock_);
+
+ // Returns true if we have a non pic oat file.
+ bool HaveNonPicOatFile() const {
+ return have_non_pic_oat_file_;
+ }
+
+ // Returns the boot image oat file.
+ const OatFile* GetBootOatFile() const;
+
+ // Returns the first non-image oat file in the class path.
+ const OatFile* GetPrimaryOatFile() const REQUIRES(!Locks::oat_file_manager_lock_);
+
+ // Return the oat file for an image, registers the oat file. Takes ownership of the imagespace's
+ // underlying oat file.
+ const OatFile* RegisterImageOatFile(gc::space::ImageSpace* space)
+ REQUIRES(!Locks::oat_file_manager_lock_);
+
+ // Finds or creates the oat file holding dex_location. Then loads and returns
+ // all corresponding dex files (there may be more than one dex file loaded
+ // in the case of multidex).
+ // This may return the original, unquickened dex files if the oat file could
+ // not be generated.
+ //
+ // Returns an empty vector if the dex files could not be loaded. In this
+ // case, there will be at least one error message returned describing why no
+ // dex files could not be loaded. The 'error_msgs' argument must not be
+ // null, regardless of whether there is an error or not.
+ //
+ // This method should not be called with the mutator_lock_ held, because it
+ // could end up starving GC if we need to generate or relocate any oat
+ // files.
+ std::vector<std::unique_ptr<const DexFile>> OpenDexFilesFromOat(
+ const char* dex_location,
+ const char* oat_location,
+ /*out*/std::vector<std::string>* error_msgs)
+ REQUIRES(!Locks::oat_file_manager_lock_, !Locks::mutator_lock_);
+
+ private:
+ // Check for duplicate class definitions of the given oat file against all open oat files.
+ // Return true if there are any class definition collisions in the oat_file.
+ bool HasCollisions(const OatFile* oat_file, /*out*/std::string* error_msg) const
+ REQUIRES(!Locks::oat_file_manager_lock_);
+
+ std::vector<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_);
+ bool have_non_pic_oat_file_;
+ DISALLOW_COPY_AND_ASSIGN(OatFileManager);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_OAT_FILE_MANAGER_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 9fb21a8425..7a1f0af194 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -115,6 +115,7 @@
#include "native/sun_misc_Unsafe.h"
#include "native_bridge_art_interface.h"
#include "oat_file.h"
+#include "oat_file_manager.h"
#include "os.h"
#include "parsed_options.h"
#include "profiler.h"
@@ -281,6 +282,7 @@ Runtime::~Runtime() {
delete monitor_list_;
delete monitor_pool_;
delete class_linker_;
+ oat_file_manager_.reset();
delete heap_;
delete intern_table_;
delete java_vm_;
@@ -698,7 +700,7 @@ bool Runtime::IsShuttingDown(Thread* self) {
}
bool Runtime::IsDebuggable() const {
- const OatFile* oat_file = GetClassLinker()->GetPrimaryOatFile();
+ const OatFile* oat_file = GetOatFileManager().GetPrimaryOatFile();
return oat_file != nullptr && oat_file->IsDebuggable();
}
@@ -756,9 +758,9 @@ static bool OpenDexFilesFromImage(const std::string& image_location,
if (elf_file.get() == nullptr) {
return false;
}
- std::unique_ptr<OatFile> oat_file(OatFile::OpenWithElfFile(elf_file.release(), oat_location,
- nullptr, &error_msg));
- if (oat_file.get() == nullptr) {
+ std::unique_ptr<const OatFile> oat_file(
+ OatFile::OpenWithElfFile(elf_file.release(), oat_location, nullptr, &error_msg));
+ if (oat_file == nullptr) {
LOG(INFO) << "Unable to use '" << oat_filename << "' because " << error_msg;
return false;
}
@@ -775,7 +777,7 @@ static bool OpenDexFilesFromImage(const std::string& image_location,
dex_files->push_back(std::move(dex_file));
}
}
- Runtime::Current()->GetClassLinker()->RegisterOatFile(oat_file.release());
+ Runtime::Current()->GetOatFileManager().RegisterOatFile(std::move(oat_file));
return true;
}
@@ -831,6 +833,8 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized)
QuasiAtomic::Startup();
+ oat_file_manager_.reset(new OatFileManager);
+
Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold),
runtime_options.GetOrDefault(Opt::HookIsSensitiveThread));
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 6154c34ec5..abccb4409a 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -82,6 +82,7 @@ class LinearAlloc;
class MonitorList;
class MonitorPool;
class NullPointerHandler;
+class OatFileManager;
class SignalCatcher;
class StackOverflowHandler;
class SuspensionHandler;
@@ -573,6 +574,11 @@ class Runtime {
// Create a normal LinearAlloc or low 4gb version if we are 64 bit AOT compiler.
LinearAlloc* CreateLinearAlloc();
+ OatFileManager& GetOatFileManager() const {
+ DCHECK(oat_file_manager_ != nullptr);
+ return *oat_file_manager_.get();
+ }
+
private:
static void InitPlatformSignalHandlers();
@@ -770,6 +776,9 @@ class Runtime {
// Contains the build fingerprint, if given as a parameter.
std::string fingerprint_;
+ // Oat file manager, keeps track of what oat files are open.
+ std::unique_ptr<OatFileManager> oat_file_manager_;
+
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);