diff options
| -rw-r--r-- | compiler/image_writer.cc | 6 | ||||
| -rw-r--r-- | oatdump/oatdump.cc | 12 | ||||
| -rw-r--r-- | runtime/Android.mk | 1 | ||||
| -rw-r--r-- | runtime/base/mutex.cc | 6 | ||||
| -rw-r--r-- | runtime/base/mutex.h | 6 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 382 | ||||
| -rw-r--r-- | runtime/class_linker.h | 43 | ||||
| -rw-r--r-- | runtime/gc/space/image_space.cc | 15 | ||||
| -rw-r--r-- | runtime/gc/space/image_space.h | 5 | ||||
| -rw-r--r-- | runtime/native/dalvik_system_DexFile.cc | 8 | ||||
| -rw-r--r-- | runtime/oat_file.cc | 15 | ||||
| -rw-r--r-- | runtime/oat_file_assistant_test.cc | 20 | ||||
| -rw-r--r-- | runtime/oat_file_manager.cc | 347 | ||||
| -rw-r--r-- | runtime/oat_file_manager.h | 105 | ||||
| -rw-r--r-- | runtime/runtime.cc | 14 | ||||
| -rw-r--r-- | runtime/runtime.h | 9 |
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); |