Snap for 4448085 from 38a95a9cd6cf4829f7b6d3362e83e49a053b731e to oc-m3-release

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