1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "oat_file_manager.h" 18 19 #include <memory> 20 #include <queue> 21 #include <vector> 22 23 #include "android-base/stringprintf.h" 24 25 #include "art_field-inl.h" 26 #include "base/bit_vector-inl.h" 27 #include "base/logging.h" 28 #include "base/stl_util.h" 29 #include "base/systrace.h" 30 #include "class_linker.h" 31 #include "class_loader_context.h" 32 #include "dex_file-inl.h" 33 #include "dex_file_tracking_registrar.h" 34 #include "gc/scoped_gc_critical_section.h" 35 #include "gc/space/image_space.h" 36 #include "handle_scope-inl.h" 37 #include "jni_internal.h" 38 #include "mirror/class_loader.h" 39 #include "mirror/object-inl.h" 40 #include "oat_file_assistant.h" 41 #include "obj_ptr-inl.h" 42 #include "scoped_thread_state_change-inl.h" 43 #include "thread-current-inl.h" 44 #include "thread_list.h" 45 #include "well_known_classes.h" 46 47 namespace art { 48 49 using android::base::StringPrintf; 50 51 // If true, we attempt to load the application image if it exists. 52 static constexpr bool kEnableAppImage = true; 53 54 const OatFile* OatFileManager::RegisterOatFile(std::unique_ptr<const OatFile> oat_file) { 55 WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); 56 DCHECK(oat_file != nullptr); 57 if (kIsDebugBuild) { 58 CHECK(oat_files_.find(oat_file) == oat_files_.end()); 59 for (const std::unique_ptr<const OatFile>& existing : oat_files_) { 60 CHECK_NE(oat_file.get(), existing.get()) << oat_file->GetLocation(); 61 // Check that we don't have an oat file with the same address. Copies of the same oat file 62 // should be loaded at different addresses. 63 CHECK_NE(oat_file->Begin(), existing->Begin()) << "Oat file already mapped at that location"; 64 } 65 } 66 have_non_pic_oat_file_ = have_non_pic_oat_file_ || !oat_file->IsPic(); 67 const OatFile* ret = oat_file.get(); 68 oat_files_.insert(std::move(oat_file)); 69 return ret; 70 } 71 72 void OatFileManager::UnRegisterAndDeleteOatFile(const OatFile* oat_file) { 73 WriterMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); 74 DCHECK(oat_file != nullptr); 75 std::unique_ptr<const OatFile> compare(oat_file); 76 auto it = oat_files_.find(compare); 77 CHECK(it != oat_files_.end()); 78 oat_files_.erase(it); 79 compare.release(); 80 } 81 82 const OatFile* OatFileManager::FindOpenedOatFileFromDexLocation( 83 const std::string& dex_base_location) const { 84 ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); 85 for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) { 86 const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles(); 87 for (const OatDexFile* oat_dex_file : oat_dex_files) { 88 if (DexFile::GetBaseLocation(oat_dex_file->GetDexFileLocation()) == dex_base_location) { 89 return oat_file.get(); 90 } 91 } 92 } 93 return nullptr; 94 } 95 96 const OatFile* OatFileManager::FindOpenedOatFileFromOatLocation(const std::string& oat_location) 97 const { 98 ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); 99 return FindOpenedOatFileFromOatLocationLocked(oat_location); 100 } 101 102 const OatFile* OatFileManager::FindOpenedOatFileFromOatLocationLocked( 103 const std::string& oat_location) const { 104 for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) { 105 if (oat_file->GetLocation() == oat_location) { 106 return oat_file.get(); 107 } 108 } 109 return nullptr; 110 } 111 112 std::vector<const OatFile*> OatFileManager::GetBootOatFiles() const { 113 std::vector<const OatFile*> oat_files; 114 std::vector<gc::space::ImageSpace*> image_spaces = 115 Runtime::Current()->GetHeap()->GetBootImageSpaces(); 116 for (gc::space::ImageSpace* image_space : image_spaces) { 117 oat_files.push_back(image_space->GetOatFile()); 118 } 119 return oat_files; 120 } 121 122 const OatFile* OatFileManager::GetPrimaryOatFile() const { 123 ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); 124 std::vector<const OatFile*> boot_oat_files = GetBootOatFiles(); 125 if (!boot_oat_files.empty()) { 126 for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) { 127 if (std::find(boot_oat_files.begin(), boot_oat_files.end(), oat_file.get()) == 128 boot_oat_files.end()) { 129 return oat_file.get(); 130 } 131 } 132 } 133 return nullptr; 134 } 135 136 OatFileManager::~OatFileManager() { 137 // Explicitly clear oat_files_ since the OatFile destructor calls back into OatFileManager for 138 // UnRegisterOatFileLocation. 139 oat_files_.clear(); 140 } 141 142 std::vector<const OatFile*> OatFileManager::RegisterImageOatFiles( 143 std::vector<gc::space::ImageSpace*> spaces) { 144 std::vector<const OatFile*> oat_files; 145 for (gc::space::ImageSpace* space : spaces) { 146 oat_files.push_back(RegisterOatFile(space->ReleaseOatFile())); 147 } 148 return oat_files; 149 } 150 151 class TypeIndexInfo { 152 public: 153 explicit TypeIndexInfo(const DexFile* dex_file) 154 : type_indexes_(GenerateTypeIndexes(dex_file)), 155 iter_(type_indexes_.Indexes().begin()), 156 end_(type_indexes_.Indexes().end()) { } 157 158 BitVector& GetTypeIndexes() { 159 return type_indexes_; 160 } 161 BitVector::IndexIterator& GetIterator() { 162 return iter_; 163 } 164 BitVector::IndexIterator& GetIteratorEnd() { 165 return end_; 166 } 167 void AdvanceIterator() { 168 iter_++; 169 } 170 171 private: 172 static BitVector GenerateTypeIndexes(const DexFile* dex_file) { 173 BitVector type_indexes(/*start_bits*/0, /*expandable*/true, Allocator::GetMallocAllocator()); 174 for (uint16_t i = 0; i < dex_file->NumClassDefs(); ++i) { 175 const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); 176 uint16_t type_idx = class_def.class_idx_.index_; 177 type_indexes.SetBit(type_idx); 178 } 179 return type_indexes; 180 } 181 182 // BitVector with bits set for the type indexes of all classes in the input dex file. 183 BitVector type_indexes_; 184 BitVector::IndexIterator iter_; 185 BitVector::IndexIterator end_; 186 }; 187 188 class DexFileAndClassPair : ValueObject { 189 public: 190 DexFileAndClassPair(const DexFile* dex_file, TypeIndexInfo* type_info, bool from_loaded_oat) 191 : type_info_(type_info), 192 dex_file_(dex_file), 193 cached_descriptor_(dex_file_->StringByTypeIdx(dex::TypeIndex(*type_info->GetIterator()))), 194 from_loaded_oat_(from_loaded_oat) { 195 type_info_->AdvanceIterator(); 196 } 197 198 DexFileAndClassPair(const DexFileAndClassPair& rhs) = default; 199 200 DexFileAndClassPair& operator=(const DexFileAndClassPair& rhs) = default; 201 202 const char* GetCachedDescriptor() const { 203 return cached_descriptor_; 204 } 205 206 bool operator<(const DexFileAndClassPair& rhs) const { 207 const int cmp = strcmp(cached_descriptor_, rhs.cached_descriptor_); 208 if (cmp != 0) { 209 // Note that the order must be reversed. We want to iterate over the classes in dex files. 210 // They are sorted lexicographically. Thus, the priority-queue must be a min-queue. 211 return cmp > 0; 212 } 213 return dex_file_ < rhs.dex_file_; 214 } 215 216 bool DexFileHasMoreClasses() const { 217 return type_info_->GetIterator() != type_info_->GetIteratorEnd(); 218 } 219 220 void Next() { 221 cached_descriptor_ = dex_file_->StringByTypeIdx(dex::TypeIndex(*type_info_->GetIterator())); 222 type_info_->AdvanceIterator(); 223 } 224 225 bool FromLoadedOat() const { 226 return from_loaded_oat_; 227 } 228 229 const DexFile* GetDexFile() const { 230 return dex_file_; 231 } 232 233 private: 234 TypeIndexInfo* type_info_; 235 const DexFile* dex_file_; 236 const char* cached_descriptor_; 237 bool from_loaded_oat_; // We only need to compare mismatches between what we load now 238 // and what was loaded before. Any old duplicates must have been 239 // OK, and any new "internal" duplicates are as well (they must 240 // be from multidex, which resolves correctly). 241 }; 242 243 static void AddDexFilesFromOat( 244 const OatFile* oat_file, 245 /*out*/std::vector<const DexFile*>* dex_files, 246 std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) { 247 for (const OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { 248 std::string error; 249 std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error); 250 if (dex_file == nullptr) { 251 LOG(WARNING) << "Could not create dex file from oat file: " << error; 252 } else if (dex_file->NumClassDefs() > 0U) { 253 dex_files->push_back(dex_file.get()); 254 opened_dex_files->push_back(std::move(dex_file)); 255 } 256 } 257 } 258 259 static void AddNext(/*inout*/DexFileAndClassPair& original, 260 /*inout*/std::priority_queue<DexFileAndClassPair>& heap) { 261 if (original.DexFileHasMoreClasses()) { 262 original.Next(); 263 heap.push(std::move(original)); 264 } 265 } 266 267 static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded, 268 std::vector<const DexFile*>& dex_files_unloaded, 269 std::string* error_msg /*out*/) { 270 // Generate type index information for each dex file. 271 std::vector<TypeIndexInfo> loaded_types; 272 for (const DexFile* dex_file : dex_files_loaded) { 273 loaded_types.push_back(TypeIndexInfo(dex_file)); 274 } 275 std::vector<TypeIndexInfo> unloaded_types; 276 for (const DexFile* dex_file : dex_files_unloaded) { 277 unloaded_types.push_back(TypeIndexInfo(dex_file)); 278 } 279 280 // Populate the queue of dex file and class pairs with the loaded and unloaded dex files. 281 std::priority_queue<DexFileAndClassPair> queue; 282 for (size_t i = 0; i < dex_files_loaded.size(); ++i) { 283 if (loaded_types[i].GetIterator() != loaded_types[i].GetIteratorEnd()) { 284 queue.emplace(dex_files_loaded[i], &loaded_types[i], /*from_loaded_oat*/true); 285 } 286 } 287 for (size_t i = 0; i < dex_files_unloaded.size(); ++i) { 288 if (unloaded_types[i].GetIterator() != unloaded_types[i].GetIteratorEnd()) { 289 queue.emplace(dex_files_unloaded[i], &unloaded_types[i], /*from_loaded_oat*/false); 290 } 291 } 292 293 // Now drain the queue. 294 bool has_duplicates = false; 295 error_msg->clear(); 296 while (!queue.empty()) { 297 // Modifying the top element is only safe if we pop right after. 298 DexFileAndClassPair compare_pop(queue.top()); 299 queue.pop(); 300 301 // Compare against the following elements. 302 while (!queue.empty()) { 303 DexFileAndClassPair top(queue.top()); 304 if (strcmp(compare_pop.GetCachedDescriptor(), top.GetCachedDescriptor()) == 0) { 305 // Same descriptor. Check whether it's crossing old-oat-files to new-oat-files. 306 if (compare_pop.FromLoadedOat() != top.FromLoadedOat()) { 307 error_msg->append( 308 StringPrintf("Found duplicated class when checking oat files: '%s' in %s and %s\n", 309 compare_pop.GetCachedDescriptor(), 310 compare_pop.GetDexFile()->GetLocation().c_str(), 311 top.GetDexFile()->GetLocation().c_str())); 312 if (!VLOG_IS_ON(oat)) { 313 return true; 314 } 315 has_duplicates = true; 316 } 317 queue.pop(); 318 AddNext(top, queue); 319 } else { 320 // Something else. Done here. 321 break; 322 } 323 } 324 AddNext(compare_pop, queue); 325 } 326 327 return has_duplicates; 328 } 329 330 // Check for class-def collisions in dex files. 331 // 332 // This first walks the class loader chain present in the given context, getting all the dex files 333 // from the class loader. 334 // 335 // If the context is null (which means the initial class loader was null or unsupported) 336 // this returns false. b/37777332. 337 // 338 // This first checks whether all class loaders in the context have the same type and 339 // classpath. If so, we exit early. Otherwise, we do the collision check. 340 // 341 // The collision check works by maintaining a heap with one class from each dex file, sorted by the 342 // class descriptor. Then a dex-file/class pair is continually removed from the heap and compared 343 // against the following top element. If the descriptor is the same, it is now checked whether 344 // the two elements agree on whether their dex file was from an already-loaded oat-file or the 345 // new oat file. Any disagreement indicates a collision. 346 bool OatFileManager::HasCollisions(const OatFile* oat_file, 347 const ClassLoaderContext* context, 348 std::string* error_msg /*out*/) const { 349 DCHECK(oat_file != nullptr); 350 DCHECK(error_msg != nullptr); 351 352 // The context might be null if there are unrecognized class loaders in the chain or they 353 // don't meet sensible sanity conditions. In this case we assume that the app knows what it's 354 // doing and accept the oat file. 355 // Note that this has correctness implications as we cannot guarantee that the class resolution 356 // used during compilation is OK (b/37777332). 357 if (context == nullptr) { 358 LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader"; 359 return false; 360 } 361 362 // If the pat file loading context matches the context used during compilation then we accept 363 // the oat file without addition checks 364 if (context->VerifyClassLoaderContextMatch(oat_file->GetClassLoaderContext())) { 365 return false; 366 } 367 368 // The class loader context does not match. Perform a full duplicate classes check. 369 370 std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles(); 371 372 // Vector that holds the newly opened dex files live, this is done to prevent leaks. 373 std::vector<std::unique_ptr<const DexFile>> opened_dex_files; 374 375 ScopedTrace st("Collision check"); 376 // Add dex files from the oat file to check. 377 std::vector<const DexFile*> dex_files_unloaded; 378 AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files); 379 return CollisionCheck(dex_files_loaded, dex_files_unloaded, error_msg); 380 } 381 382 std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( 383 const char* dex_location, 384 jobject class_loader, 385 jobjectArray dex_elements, 386 const OatFile** out_oat_file, 387 std::vector<std::string>* error_msgs) { 388 ScopedTrace trace(__FUNCTION__); 389 CHECK(dex_location != nullptr); 390 CHECK(error_msgs != nullptr); 391 392 // Verify we aren't holding the mutator lock, which could starve GC if we 393 // have to generate or relocate an oat file. 394 Thread* const self = Thread::Current(); 395 Locks::mutator_lock_->AssertNotHeld(self); 396 Runtime* const runtime = Runtime::Current(); 397 398 std::unique_ptr<ClassLoaderContext> context; 399 // If the class_loader is null there's not much we can do. This happens if a dex files is loaded 400 // directly with DexFile APIs instead of using class loaders. 401 if (class_loader == nullptr) { 402 LOG(WARNING) << "Opening an oat file without a class loader. " 403 << "Are you using the deprecated DexFile APIs?"; 404 context = nullptr; 405 } else { 406 context = ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements); 407 } 408 409 OatFileAssistant oat_file_assistant(dex_location, 410 kRuntimeISA, 411 !runtime->IsAotCompiler()); 412 413 // Lock the target oat location to avoid races generating and loading the 414 // oat file. 415 std::string error_msg; 416 if (!oat_file_assistant.Lock(/*out*/&error_msg)) { 417 // Don't worry too much if this fails. If it does fail, it's unlikely we 418 // can generate an oat file anyway. 419 VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg; 420 } 421 422 const OatFile* source_oat_file = nullptr; 423 424 if (!oat_file_assistant.IsUpToDate()) { 425 // Update the oat file on disk if we can, based on the --compiler-filter 426 // option derived from the current runtime options. 427 // This may fail, but that's okay. Best effort is all that matters here. 428 // TODO(calin): b/64530081 b/66984396. Pass a null context to verify and compile 429 // secondary dex files in isolation (and avoid to extract/verify the main apk 430 // if it's in the class path). Note this trades correctness for performance 431 // since the resulting slow down is unacceptable in some cases until b/64530081 432 // is fixed. 433 switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/ false, 434 /*class_loader_context*/ nullptr, 435 /*out*/ &error_msg)) { 436 case OatFileAssistant::kUpdateFailed: 437 LOG(WARNING) << error_msg; 438 break; 439 440 case OatFileAssistant::kUpdateNotAttempted: 441 // Avoid spamming the logs if we decided not to attempt making the oat 442 // file up to date. 443 VLOG(oat) << error_msg; 444 break; 445 446 case OatFileAssistant::kUpdateSucceeded: 447 // Nothing to do. 448 break; 449 } 450 } 451 452 // Get the oat file on disk. 453 std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release()); 454 455 // Prevent oat files from being loaded if no class_loader or dex_elements are provided. 456 // This can happen when the deprecated DexFile.<init>(String) is called directly, and it 457 // could load oat files without checking the classpath, which would be incorrect. 458 if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) { 459 // Take the file only if it has no collisions, or we must take it because of preopting. 460 bool accept_oat_file = 461 !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg); 462 if (!accept_oat_file) { 463 // Failed the collision check. Print warning. 464 if (Runtime::Current()->IsDexFileFallbackEnabled()) { 465 if (!oat_file_assistant.HasOriginalDexFiles()) { 466 // We need to fallback but don't have original dex files. We have to 467 // fallback to opening the existing oat file. This is potentially 468 // unsafe so we warn about it. 469 accept_oat_file = true; 470 471 LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. " 472 << "Allow oat file use. This is potentially dangerous."; 473 } else { 474 // We have to fallback and found original dex files - extract them from an APK. 475 // Also warn about this operation because it's potentially wasteful. 476 LOG(WARNING) << "Found duplicate classes, falling back to extracting from APK : " 477 << dex_location; 478 LOG(WARNING) << "NOTE: This wastes RAM and hurts startup performance."; 479 } 480 } else { 481 // TODO: We should remove this. The fact that we're here implies -Xno-dex-file-fallback 482 // was set, which means that we should never fallback. If we don't have original dex 483 // files, we should just fail resolution as the flag intended. 484 if (!oat_file_assistant.HasOriginalDexFiles()) { 485 accept_oat_file = true; 486 } 487 488 LOG(WARNING) << "Found duplicate classes, dex-file-fallback disabled, will be failing to " 489 " load classes for " << dex_location; 490 } 491 492 LOG(WARNING) << error_msg; 493 } 494 495 if (accept_oat_file) { 496 VLOG(class_linker) << "Registering " << oat_file->GetLocation(); 497 source_oat_file = RegisterOatFile(std::move(oat_file)); 498 *out_oat_file = source_oat_file; 499 } 500 } 501 502 std::vector<std::unique_ptr<const DexFile>> dex_files; 503 504 // Load the dex files from the oat file. 505 if (source_oat_file != nullptr) { 506 bool added_image_space = false; 507 if (source_oat_file->IsExecutable()) { 508 std::unique_ptr<gc::space::ImageSpace> image_space = 509 kEnableAppImage ? oat_file_assistant.OpenImageSpace(source_oat_file) : nullptr; 510 if (image_space != nullptr) { 511 ScopedObjectAccess soa(self); 512 StackHandleScope<1> hs(self); 513 Handle<mirror::ClassLoader> h_loader( 514 hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader))); 515 // Can not load app image without class loader. 516 if (h_loader != nullptr) { 517 std::string temp_error_msg; 518 // Add image space has a race condition since other threads could be reading from the 519 // spaces array. 520 { 521 ScopedThreadSuspension sts(self, kSuspended); 522 gc::ScopedGCCriticalSection gcs(self, 523 gc::kGcCauseAddRemoveAppImageSpace, 524 gc::kCollectorTypeAddRemoveAppImageSpace); 525 ScopedSuspendAll ssa("Add image space"); 526 runtime->GetHeap()->AddSpace(image_space.get()); 527 } 528 { 529 ScopedTrace trace2(StringPrintf("Adding image space for location %s", dex_location)); 530 added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(), 531 h_loader, 532 dex_elements, 533 dex_location, 534 /*out*/&dex_files, 535 /*out*/&temp_error_msg); 536 } 537 if (added_image_space) { 538 // Successfully added image space to heap, release the map so that it does not get 539 // freed. 540 image_space.release(); 541 542 // Register for tracking. 543 for (const auto& dex_file : dex_files) { 544 dex::tracking::RegisterDexFile(dex_file.get()); 545 } 546 } else { 547 LOG(INFO) << "Failed to add image file " << temp_error_msg; 548 dex_files.clear(); 549 { 550 ScopedThreadSuspension sts(self, kSuspended); 551 gc::ScopedGCCriticalSection gcs(self, 552 gc::kGcCauseAddRemoveAppImageSpace, 553 gc::kCollectorTypeAddRemoveAppImageSpace); 554 ScopedSuspendAll ssa("Remove image space"); 555 runtime->GetHeap()->RemoveSpace(image_space.get()); 556 } 557 // Non-fatal, don't update error_msg. 558 } 559 } 560 } 561 } 562 if (!added_image_space) { 563 DCHECK(dex_files.empty()); 564 dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location); 565 566 // Register for tracking. 567 for (const auto& dex_file : dex_files) { 568 dex::tracking::RegisterDexFile(dex_file.get()); 569 } 570 } 571 if (dex_files.empty()) { 572 error_msgs->push_back("Failed to open dex files from " + source_oat_file->GetLocation()); 573 } else { 574 // Opened dex files from an oat file, madvise them to their loaded state. 575 for (const std::unique_ptr<const DexFile>& dex_file : dex_files) { 576 OatDexFile::MadviseDexFile(*dex_file, MadviseState::kMadviseStateAtLoad); 577 } 578 } 579 } 580 581 // Fall back to running out of the original dex file if we couldn't load any 582 // dex_files from the oat file. 583 if (dex_files.empty()) { 584 if (oat_file_assistant.HasOriginalDexFiles()) { 585 if (Runtime::Current()->IsDexFileFallbackEnabled()) { 586 static constexpr bool kVerifyChecksum = true; 587 if (!DexFile::Open( 588 dex_location, dex_location, kVerifyChecksum, /*out*/ &error_msg, &dex_files)) { 589 LOG(WARNING) << error_msg; 590 error_msgs->push_back("Failed to open dex files from " + std::string(dex_location) 591 + " because: " + error_msg); 592 } 593 } else { 594 error_msgs->push_back("Fallback mode disabled, skipping dex files."); 595 } 596 } else { 597 error_msgs->push_back("No original dex files found for dex location " 598 + std::string(dex_location)); 599 } 600 } 601 602 return dex_files; 603 } 604 605 void OatFileManager::DumpForSigQuit(std::ostream& os) { 606 ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_); 607 std::vector<const OatFile*> boot_oat_files = GetBootOatFiles(); 608 for (const std::unique_ptr<const OatFile>& oat_file : oat_files_) { 609 if (ContainsElement(boot_oat_files, oat_file.get())) { 610 continue; 611 } 612 os << oat_file->GetLocation() << ": " << oat_file->GetCompilerFilter() << "\n"; 613 } 614 } 615 616 } // namespace art 617