Improve dex location canonicalization-related performance.

Eagerly add canonical dex file locations to the OatFile's
primary lookup map in Setup(). This moves the boot.oat work
from every app startup to the zygote initialization. Since
we always ended up initializing the canonical location map
anyway due to the way that we're loading dex files, the lazy
initialization didn't save anything.

Clean up dex file name canonicalization to make sure we
free() the memory returned by realpath() rather than using
std::unique_ptr<> with the default deleter.

Avoid some unnecessary duplicate OatDexFile lookups.

Bug: 16828525
Bug: 17346103
Change-Id: Id8fbc8992f62996138eb2006a0046c6529747c09
diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc
index 72bf7d3..f192227c 100644
--- a/compiler/elf_patcher.cc
+++ b/compiler/elf_patcher.cc
@@ -200,7 +200,8 @@
     mirror::ArtMethod* target = GetTargetMethod(patch);
     uintptr_t quick_code = reinterpret_cast<uintptr_t>(class_linker->GetQuickOatCodeFor(target));
     DCHECK_NE(quick_code, 0U) << PrettyMethod(target);
-    const OatFile* target_oat = class_linker->FindOpenedOatFileForDexFile(*patch->GetTargetDexFile());
+    const OatFile* target_oat =
+        class_linker->FindOpenedOatDexFileForDexFile(*patch->GetTargetDexFile())->GetOatFile();
     // Get where the data actually starts. if target is this oat_file_ it is oat_data_start_,
     // otherwise it is wherever target_oat is loaded.
     uintptr_t oat_data_addr = GetBaseAddressFor(target_oat);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 563ee93..637fd02 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -756,18 +756,18 @@
   return *oat_file;
 }
 
-const OatFile* ClassLinker::FindOpenedOatFileForDexFile(const DexFile& dex_file) {
+const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFileForDexFile(const DexFile& dex_file) {
   const char* dex_location = dex_file.GetLocation().c_str();
   uint32_t dex_location_checksum = dex_file.GetLocationChecksum();
-  return FindOpenedOatFile(nullptr, dex_location, &dex_location_checksum);
+  return FindOpenedOatDexFile(nullptr, dex_location, &dex_location_checksum);
 }
 
-const OatFile* ClassLinker::FindOpenedOatFile(const char* oat_location, const char* dex_location,
-                                              const uint32_t* const dex_location_checksum) {
+const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFile(const char* oat_location,
+                                                             const char* dex_location,
+                                                             const uint32_t* dex_location_checksum) {
   ReaderMutexLock mu(Thread::Current(), dex_lock_);
-  for (size_t i = 0; i < oat_files_.size(); i++) {
-    const OatFile* oat_file = oat_files_[i];
-    DCHECK(oat_file != NULL);
+  for (const OatFile* oat_file : oat_files_) {
+    DCHECK(oat_file != nullptr);
 
     if (oat_location != nullptr) {
       if (oat_file->GetLocation() != oat_location) {
@@ -778,11 +778,11 @@
     const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
                                                                       dex_location_checksum,
                                                                       false);
-    if (oat_dex_file != NULL) {
-      return oat_file;
+    if (oat_dex_file != nullptr) {
+      return oat_dex_file;
     }
   }
-  return NULL;
+  return nullptr;
 }
 
 
@@ -904,8 +904,10 @@
 
   bool needs_registering = false;
 
-  std::unique_ptr<const OatFile> open_oat_file(FindOpenedOatFile(oat_location, dex_location,
-                                                                 dex_location_checksum_pointer));
+  const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFile(oat_location, dex_location,
+                                                                 dex_location_checksum_pointer);
+  std::unique_ptr<const OatFile> open_oat_file(
+      oat_dex_file != nullptr ? oat_dex_file->GetOatFile() : nullptr);
 
   // 2) If we do not have an open one, maybe there's one on disk already.
 
@@ -1026,7 +1028,8 @@
   }
 
   // Try to load again, but stronger checks.
-  success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location, dex_location_checksum_pointer,
+  success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
+                                         dex_location_checksum_pointer,
                                          true, error_msgs, dex_files);
   if (success) {
     RegisterOatFile(open_oat_file.release());
@@ -1201,12 +1204,11 @@
   if (oat_dex_file == NULL) {
     *error_msg = StringPrintf("oat file '%s' does not contain contents for '%s' with checksum 0x%x",
                               oat_file->GetLocation().c_str(), dex_location, dex_location_checksum);
-    std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file->GetOatDexFiles();
-    for (size_t i = 0; i < oat_dex_files.size(); i++) {
-      const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
-      *error_msg  += StringPrintf("\noat file '%s' contains contents for '%s'",
+    for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
+      *error_msg  += StringPrintf("\noat file '%s' contains contents for '%s' with checksum 0x%x",
                                   oat_file->GetLocation().c_str(),
-                                  oat_dex_file->GetDexFileLocation().c_str());
+                                  oat_dex_file->GetDexFileLocation().c_str(),
+                                  oat_dex_file->GetDexFileLocationChecksum());
     }
     return false;
   }
@@ -1248,7 +1250,7 @@
 
 const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation(
     const char* dex_location,
-    const uint32_t* const dex_location_checksum,
+    const uint32_t* dex_location_checksum,
     InstructionSet isa,
     std::vector<std::string>* error_msgs,
     bool* obsolete_file_cleanup_failed) {
@@ -2400,15 +2402,11 @@
 OatFile::OatClass ClassLinker::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx,
                                             bool* found) {
   DCHECK_NE(class_def_idx, DexFile::kDexNoIndex16);
-  const OatFile* oat_file = FindOpenedOatFileForDexFile(dex_file);
-  if (oat_file == nullptr) {
+  const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFileForDexFile(dex_file);
+  if (oat_dex_file == nullptr) {
     *found = false;
     return OatFile::OatClass::Invalid();
   }
-  uint dex_location_checksum = dex_file.GetLocationChecksum();
-  const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(),
-                                                                    &dex_location_checksum);
-  CHECK(oat_dex_file != NULL) << dex_file.GetLocation();
   *found = true;
   return oat_dex_file->GetOatClass(class_def_idx);
 }
@@ -3681,17 +3679,13 @@
     }
   }
 
-  const OatFile* oat_file = FindOpenedOatFileForDexFile(dex_file);
+  const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFileForDexFile(dex_file);
   // Make this work with gtests, which do not set up the image properly.
   // TODO: we should clean up gtests to set up the image path properly.
-  if (Runtime::Current()->IsCompiler() || (oat_file == NULL)) {
+  if (Runtime::Current()->IsCompiler() || (oat_dex_file == nullptr)) {
     return false;
   }
 
-  CHECK(oat_file != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass);
-  uint dex_location_checksum = dex_file.GetLocationChecksum();
-  const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file.GetLocation().c_str(),
-                                                                    &dex_location_checksum);
   CHECK(oat_dex_file != NULL) << dex_file.GetLocation() << " " << PrettyClass(klass);
   uint16_t class_def_index = klass->GetDexClassDefIndex();
   oat_file_class_status = oat_dex_file->GetOatClass(class_def_index).GetStatus();
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index d1f5aa0..0248a21 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -549,14 +549,15 @@
   }
   mirror::DexCache* GetDexCache(size_t idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_, dex_lock_);
 
-  const OatFile* FindOpenedOatFileForDexFile(const DexFile& dex_file)
+  const OatFile::OatDexFile* FindOpenedOatDexFileForDexFile(const DexFile& dex_file)
       LOCKS_EXCLUDED(dex_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Find an opened oat file that contains dex_location. If oat_location is not nullptr, the file
-  // must have that location, else any oat location is accepted.
-  const OatFile* FindOpenedOatFile(const char* oat_location, const char* dex_location,
-                                   const uint32_t* const dex_location_checksum)
+  // Find an opened oat dex file that contains dex_location. If oat_location is not nullptr,
+  // the file must have that location, else any oat location is accepted.
+  const OatFile::OatDexFile* FindOpenedOatDexFile(const char* oat_location,
+                                                  const char* dex_location,
+                                                  const uint32_t* dex_location_checksum)
       LOCKS_EXCLUDED(dex_lock_);
 
   // Will open the oat file directly without relocating, even if we could/should do relocation.
@@ -606,8 +607,8 @@
   // Note 1: this will not check open oat files, which are assumed to be stale when this is run.
   // Note 2: Does not register the oat file. It is the caller's job to register if the file is to
   //         be kept.
-  const OatFile* FindOatFileContainingDexFileFromDexLocation(const char* location,
-                                                             const uint32_t* const location_checksum,
+  const OatFile* FindOatFileContainingDexFileFromDexLocation(const char* dex_location,
+                                                             const uint32_t* dex_location_checksum,
                                                              InstructionSet isa,
                                                              std::vector<std::string>* error_msgs,
                                                              bool* obsolete_file_cleanup_failed)
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index ed3592c..38e2e68 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -77,14 +77,13 @@
   // Strip ":...", which is the location
   const char* zip_entry_name = kClassesDex;
   const char* file_part = filename;
-  std::unique_ptr<const char> file_part_ptr;
+  std::string file_part_storage;
 
-
-  if (IsMultiDexLocation(filename)) {
-    std::pair<const char*, const char*> pair = SplitMultiDexLocation(filename);
-    file_part_ptr.reset(pair.first);
-    file_part = pair.first;
-    zip_entry_name = pair.second;
+  if (DexFile::IsMultiDexLocation(filename)) {
+    file_part_storage = GetBaseLocation(filename);
+    file_part = file_part_storage.c_str();
+    zip_entry_name = filename + file_part_storage.size() + 1;
+    DCHECK_EQ(zip_entry_name[-1], kMultiDexSeparator);
   }
 
   ScopedFd fd(OpenAndReadMagic(file_part, &magic, error_msg));
@@ -303,7 +302,7 @@
 
     while (i < 100) {
       std::string name = StringPrintf("classes%zu.dex", i);
-      std::string fake_location = location + ":" + name;
+      std::string fake_location = location + kMultiDexSeparator + name;
       std::unique_ptr<const DexFile> next_dex_file(Open(zip_archive, name.c_str(), fake_location,
                                                         error_msg, &error_code));
       if (next_dex_file.get() == nullptr) {
@@ -951,21 +950,6 @@
   return strrchr(location, kMultiDexSeparator) != nullptr;
 }
 
-std::pair<const char*, const char*> DexFile::SplitMultiDexLocation(
-    const char* location) {
-  const char* colon_ptr = strrchr(location, kMultiDexSeparator);
-
-  // Check it's synthetic.
-  CHECK_NE(colon_ptr, static_cast<const char*>(nullptr));
-
-  size_t colon_index = colon_ptr - location;
-  char* tmp = new char[colon_index + 1];
-  strncpy(tmp, location, colon_index);
-  tmp[colon_index] = 0;
-
-  return std::make_pair(tmp, colon_ptr + 1);
-}
-
 std::string DexFile::GetMultiDexClassesDexName(size_t number, const char* dex_location) {
   if (number == 0) {
     return dex_location;
@@ -976,26 +960,17 @@
 
 std::string DexFile::GetDexCanonicalLocation(const char* dex_location) {
   CHECK_NE(dex_location, static_cast<const char*>(nullptr));
-  char* path = nullptr;
-  if (!IsMultiDexLocation(dex_location)) {
-    path = realpath(dex_location, nullptr);
+  std::string base_location = GetBaseLocation(dex_location);
+  const char* suffix = dex_location + base_location.size();
+  DCHECK(suffix[0] == 0 || suffix[0] == kMultiDexSeparator);
+  UniqueCPtr<const char[]> path(realpath(base_location.c_str(), nullptr));
+  if (path != nullptr && path.get() != base_location) {
+    return std::string(path.get()) + suffix;
+  } else if (suffix[0] == 0) {
+    return base_location;
   } else {
-    std::pair<const char*, const char*> pair = DexFile::SplitMultiDexLocation(dex_location);
-    const char* dex_real_location(realpath(pair.first, nullptr));
-    delete pair.first;
-    if (dex_real_location != nullptr) {
-      int length = strlen(dex_real_location) + strlen(pair.second) + strlen(kMultiDexSeparatorString) + 1;
-      char* multidex_canonical_location = reinterpret_cast<char*>(malloc(sizeof(char) * length));
-      snprintf(multidex_canonical_location, length, "%s" kMultiDexSeparatorString "%s", dex_real_location, pair.second);
-      free(const_cast<char*>(dex_real_location));
-      path = multidex_canonical_location;
-    }
+    return dex_location;
   }
-
-  // If realpath fails then we just copy the argument.
-  std::string result(path == nullptr ? dex_location : path);
-  free(path);
-  return result;
 }
 
 std::ostream& operator<<(std::ostream& os, const DexFile& dex_file) {
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 118bd80..1b46a12 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -389,14 +389,21 @@
   // For normal dex files, location and base location coincide. If a dex file is part of a multidex
   // archive, the base location is the name of the originating jar/apk, stripped of any internal
   // classes*.dex path.
-  const std::string GetBaseLocation() const {
-    if (IsMultiDexLocation(location_.c_str())) {
-      std::pair<const char*, const char*> pair = SplitMultiDexLocation(location_.c_str());
-      std::string res(pair.first);
-      delete[] pair.first;
-      return res;
+  static std::string GetBaseLocation(const char* location) {
+    const char* pos = strrchr(location, kMultiDexSeparator);
+    if (pos == nullptr) {
+      return location;
     } else {
+      return std::string(location, pos - location);
+    }
+  }
+
+  std::string GetBaseLocation() const {
+    size_t pos = location_.rfind(kMultiDexSeparator);
+    if (pos == std::string::npos) {
       return location_;
+    } else {
+      return location_.substr(0, pos);
     }
   }
 
@@ -918,13 +925,6 @@
   // whether the string contains the separator character.
   static bool IsMultiDexLocation(const char* location);
 
-  // Splits a multidex location at the last separator character. The second component is a pointer
-  // to the character after the separator. The first is a copy of the substring up to the separator.
-  //
-  // Note: It's the caller's job to free the first component of the returned pair.
-  // Bug 15313523: gcc/libc++ don't allow a unique_ptr for the first component
-  static std::pair<const char*, const char*> SplitMultiDexLocation(const char* location);
-
 
   // The base address of the memory mapping.
   const byte* const begin_;
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 330d045..d0c5603 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -355,8 +355,8 @@
 
 TEST_F(DexFileTest, GetDexCanonicalLocation) {
   ScratchFile file;
-  char* dex_location_real = realpath(file.GetFilename().c_str(), nullptr);
-  std::string dex_location(dex_location_real);
+  UniqueCPtr<const char[]> dex_location_real(realpath(file.GetFilename().c_str(), nullptr));
+  std::string dex_location(dex_location_real.get());
 
   ASSERT_EQ(dex_location, DexFile::GetDexCanonicalLocation(dex_location.c_str()));
   std::string multidex_location = DexFile::GetMultiDexClassesDexName(1, dex_location.c_str());
@@ -371,8 +371,6 @@
   ASSERT_EQ(multidex_location, DexFile::GetDexCanonicalLocation(multidex_location_sym.c_str()));
 
   ASSERT_EQ(0, unlink(dex_location_sym.c_str()));
-
-  free(dex_location_real);
 }
 
 }  // namespace art
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index d179a96..cf1c6e1 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -140,7 +140,7 @@
 }
 
 OatFile::~OatFile() {
-  STLDeleteValues(&oat_dex_files_);
+  STLDeleteElements(&oat_dex_files_storage_);
   if (dlopen_handle_ != NULL) {
     dlclose(dlopen_handle_);
   }
@@ -238,7 +238,9 @@
     return false;
   }
 
-  for (size_t i = 0; i < GetOatHeader().GetDexFileCount(); i++) {
+  uint32_t dex_file_count = GetOatHeader().GetDexFileCount();
+  oat_dex_files_storage_.reserve(dex_file_count);
+  for (size_t i = 0; i < dex_file_count; i++) {
     uint32_t dex_file_location_size = *reinterpret_cast<const uint32_t*>(oat);
     if (UNLIKELY(dex_file_location_size == 0U)) {
       *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd with empty location name",
@@ -315,14 +317,24 @@
       return false;
     }
 
-    // Create the OatDexFile and add it to the owning map indexed by the dex file location.
+    std::string canonical_location = DexFile::GetDexCanonicalLocation(dex_file_location.c_str());
+
+    // Create the OatDexFile and add it to the owning container.
     OatDexFile* oat_dex_file = new OatDexFile(this,
                                               dex_file_location,
+                                              canonical_location,
                                               dex_file_checksum,
                                               dex_file_pointer,
                                               methods_offsets_pointer);
+    oat_dex_files_storage_.push_back(oat_dex_file);
+
+    // Add the location and canonical location (if different) to the oat_dex_files_ table.
     StringPiece key(oat_dex_file->GetDexFileLocation());
     oat_dex_files_.Put(key, oat_dex_file);
+    if (canonical_location != dex_file_location) {
+      StringPiece canonical_key(oat_dex_file->GetCanonicalDexFileLocation());
+      oat_dex_files_.Put(canonical_key, oat_dex_file);
+    }
   }
   return true;
 }
@@ -370,20 +382,13 @@
       oat_dex_file = secondary_lb->second;  // May be nullptr.
     } else {
       // We haven't seen this dex_location before, we must check the canonical location.
-      if (UNLIKELY(oat_dex_files_by_canonical_location_.empty())) {
-        // Lazily fill in the oat_dex_files_by_canonical_location_.
-        for (const auto& entry : oat_dex_files_) {
-          const std::string& dex_location = entry.second->GetDexFileLocation();
-          string_cache_.emplace_back(DexFile::GetDexCanonicalLocation(dex_location.c_str()));
-          StringPiece canonical_location_key(string_cache_.back());
-          oat_dex_files_by_canonical_location_.Put(canonical_location_key, entry.second);
-        }
-      }
       std::string dex_canonical_location = DexFile::GetDexCanonicalLocation(dex_location);
-      StringPiece canonical_key(dex_canonical_location);
-      auto canonical_it = oat_dex_files_by_canonical_location_.find(canonical_key);
-      if (canonical_it != oat_dex_files_by_canonical_location_.end()) {
-        oat_dex_file = canonical_it->second;
+      if (dex_canonical_location != dex_location) {
+        StringPiece canonical_key(dex_canonical_location);
+        auto canonical_it = oat_dex_files_.find(canonical_key);
+        if (canonical_it != oat_dex_files_.end()) {
+          oat_dex_file = canonical_it->second;
+        }  // else keep nullptr.
       }  // else keep nullptr.
 
       // Copy the key to the string_cache_ and store the result in secondary map.
@@ -408,11 +413,11 @@
                  << " ( canonical path " << dex_canonical_location << ")"
                  << " with checksum " << checksum << " in OatFile " << GetLocation();
     if (kIsDebugBuild) {
-      for (Table::const_iterator it = oat_dex_files_.begin(); it != oat_dex_files_.end(); ++it) {
+      for (const OatDexFile* odf : oat_dex_files_storage_) {
         LOG(WARNING) << "OatFile " << GetLocation()
-                     << " contains OatDexFile " << it->second->GetDexFileLocation()
-                     << " (canonical path " << it->first << ")"
-                     << " with checksum 0x" << std::hex << it->second->GetDexFileLocationChecksum();
+                     << " contains OatDexFile " << odf->GetDexFileLocation()
+                     << " (canonical path " << odf->GetCanonicalDexFileLocation() << ")"
+                     << " with checksum 0x" << std::hex << odf->GetDexFileLocationChecksum();
       }
     }
   }
@@ -420,21 +425,15 @@
   return NULL;
 }
 
-std::vector<const OatFile::OatDexFile*> OatFile::GetOatDexFiles() const {
-  std::vector<const OatFile::OatDexFile*> result;
-  for (Table::const_iterator it = oat_dex_files_.begin(); it != oat_dex_files_.end(); ++it) {
-    result.push_back(it->second);
-  }
-  return result;
-}
-
 OatFile::OatDexFile::OatDexFile(const OatFile* oat_file,
                                 const std::string& dex_file_location,
+                                const std::string& canonical_dex_file_location,
                                 uint32_t dex_file_location_checksum,
                                 const byte* dex_file_pointer,
                                 const uint32_t* oat_class_offsets_pointer)
     : oat_file_(oat_file),
       dex_file_location_(dex_file_location),
+      canonical_dex_file_location_(canonical_dex_file_location),
       dex_file_location_checksum_(dex_file_location_checksum),
       dex_file_pointer_(dex_file_pointer),
       oat_class_offsets_pointer_(oat_class_offsets_pointer) {}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 8cb47e2..2fd4f4c 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -214,6 +214,10 @@
     // Opens the DexFile referred to by this OatDexFile from within the containing OatFile.
     const DexFile* OpenDexFile(std::string* error_msg) const;
 
+    const OatFile* GetOatFile() const {
+      return oat_file_;
+    }
+
     // Returns the size of the DexFile refered to by this OatDexFile.
     size_t FileSize() const;
 
@@ -222,6 +226,11 @@
       return dex_file_location_;
     }
 
+    // Returns the canonical location of DexFile that was the source of this OatDexFile.
+    const std::string& GetCanonicalDexFileLocation() const {
+      return canonical_dex_file_location_;
+    }
+
     // Returns checksum of original DexFile that was the source of this OatDexFile;
     uint32_t GetDexFileLocationChecksum() const {
       return dex_file_location_checksum_;
@@ -235,12 +244,14 @@
    private:
     OatDexFile(const OatFile* oat_file,
                const std::string& dex_file_location,
+               const std::string& canonical_dex_file_location,
                uint32_t dex_file_checksum,
                const byte* dex_file_pointer,
                const uint32_t* oat_class_offsets_pointer);
 
     const OatFile* const oat_file_;
     const std::string dex_file_location_;
+    const std::string canonical_dex_file_location_;
     const uint32_t dex_file_location_checksum_;
     const byte* const dex_file_pointer_;
     const uint32_t* const oat_class_offsets_pointer_;
@@ -254,7 +265,9 @@
                                   bool exception_if_not_found = true) const
       LOCKS_EXCLUDED(secondary_lookup_lock_);
 
-  std::vector<const OatDexFile*> GetOatDexFiles() const;
+  const std::vector<const OatDexFile*>& GetOatDexFiles() const {
+    return oat_dex_files_storage_;
+  }
 
   size_t Size() const {
     return End() - Begin();
@@ -307,6 +320,9 @@
   // dlopen handle during runtime.
   void* dlopen_handle_;
 
+  // Owning storage for the OatDexFile objects.
+  std::vector<const OatDexFile*> oat_dex_files_storage_;
+
   // NOTE: We use a StringPiece as the key type to avoid a memory allocation on every
   // lookup with a const char* key. The StringPiece doesn't own its backing storage,
   // therefore we're using the OatDexFile::dex_file_location_ as the backing storage
@@ -314,11 +330,11 @@
   // of keys in secondary_oat_dex_files_ and oat_dex_files_by_canonical_location_.
   typedef AllocationTrackingSafeMap<StringPiece, const OatDexFile*, kAllocatorTagOatFile> Table;
 
-  // Map each plain dex file location retrieved from the oat file to its OatDexFile.
-  // This map doesn't change after it's constructed in Setup() and therefore doesn't
-  // need any locking and provides the cheapest dex file lookup for GetOatDexFile()
-  // for a very frequent use case. Never contains a nullptr value.
-  Table oat_dex_files_;                         // Owns the OatDexFile* values.
+  // Map each location and canonical location (if different) retrieved from the
+  // oat file to its OatDexFile. This map doesn't change after it's constructed in Setup()
+  // and therefore doesn't need any locking and provides the cheapest dex file lookup
+  // for GetOatDexFile() for a very frequent use case. Never contains a nullptr value.
+  Table oat_dex_files_;
 
   // Lock guarding all members needed for secondary lookup in GetOatDexFile().
   mutable Mutex secondary_lookup_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
@@ -329,10 +345,6 @@
   // location and use oat_dex_files_by_canonical_location_.
   mutable Table secondary_oat_dex_files_ GUARDED_BY(secondary_lookup_lock_);
 
-  // Map the canonical location to an OatDexFile. This lazily constructed map is used
-  // when we're doing the secondary lookup for a given location for the first time.
-  mutable Table oat_dex_files_by_canonical_location_ GUARDED_BY(secondary_lookup_lock_);
-
   // Cache of strings. Contains the backing storage for keys in the secondary_oat_dex_files_
   // and the lazily initialized oat_dex_files_by_canonical_location_.
   // NOTE: We're keeping references to contained strings in form of StringPiece and adding
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 89ad505..33b09a3 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -599,9 +599,7 @@
     return false;
   }
 
-  std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file->GetOatDexFiles();
-  for (size_t i = 0; i < oat_dex_files.size(); i++) {
-    const OatFile::OatDexFile* oat_dex_file = oat_dex_files[i];
+  for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
     if (oat_dex_file == nullptr) {
       *failures += 1;
       continue;
diff --git a/runtime/utils.h b/runtime/utils.h
index 7fb5bbd..50462b1 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -20,6 +20,7 @@
 #include <pthread.h>
 
 #include <limits>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -502,6 +503,18 @@
 void EncodeUnsignedLeb128(uint32_t data, std::vector<uint8_t>* buf);
 void EncodeSignedLeb128(int32_t data, std::vector<uint8_t>* buf);
 
+// Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below.
+struct FreeDelete {
+  // NOTE: Deleting a const object is valid but free() takes a non-const pointer.
+  void operator()(const void* ptr) const {
+    free(const_cast<void*>(ptr));
+  }
+};
+
+// Alias for std::unique_ptr<> that uses the C function free() to delete objects.
+template <typename T>
+using UniqueCPtr = std::unique_ptr<T, FreeDelete>;
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_UTILS_H_
diff --git a/test/116-nodex2oat/nodex2oat.cc b/test/116-nodex2oat/nodex2oat.cc
index 4326db0..04cac45 100644
--- a/test/116-nodex2oat/nodex2oat.cc
+++ b/test/116-nodex2oat/nodex2oat.cc
@@ -28,9 +28,9 @@
     ScopedObjectAccess soa(Thread::Current());
     mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
     const DexFile& dex_file = klass->GetDexFile();
-    const OatFile* oat_file =
-        Runtime::Current()->GetClassLinker()->FindOpenedOatFileForDexFile(dex_file);
-    return oat_file != nullptr;
+    const OatFile::OatDexFile* oat_dex_file =
+        Runtime::Current()->GetClassLinker()->FindOpenedOatDexFileForDexFile(dex_file);
+    return oat_dex_file != nullptr;
   }
 };
 
diff --git a/test/117-nopatchoat/nopatchoat.cc b/test/117-nopatchoat/nopatchoat.cc
index ced7f6e..5994653 100644
--- a/test/117-nopatchoat/nopatchoat.cc
+++ b/test/117-nopatchoat/nopatchoat.cc
@@ -28,9 +28,9 @@
     ScopedObjectAccess soa(Thread::Current());
     mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
     const DexFile& dex_file = klass->GetDexFile();
-    const OatFile* oat_file =
-        Runtime::Current()->GetClassLinker()->FindOpenedOatFileForDexFile(dex_file);
-    return oat_file != nullptr && oat_file->IsExecutable();
+    const OatFile::OatDexFile* oat_dex_file =
+        Runtime::Current()->GetClassLinker()->FindOpenedOatDexFileForDexFile(dex_file);
+    return oat_dex_file != nullptr && oat_dex_file->GetOatFile()->IsExecutable();
   }
 };
 
diff --git a/test/118-noimage-dex2oat/noimage-dex2oat.cc b/test/118-noimage-dex2oat/noimage-dex2oat.cc
index 4a3d33c..7340d9e 100644
--- a/test/118-noimage-dex2oat/noimage-dex2oat.cc
+++ b/test/118-noimage-dex2oat/noimage-dex2oat.cc
@@ -28,9 +28,9 @@
     ScopedObjectAccess soa(Thread::Current());
     mirror::Class* klass = soa.Decode<mirror::Class*>(cls);
     const DexFile& dex_file = klass->GetDexFile();
-    const OatFile* oat_file =
-        Runtime::Current()->GetClassLinker()->FindOpenedOatFileForDexFile(dex_file);
-    return oat_file != nullptr;
+    const OatFile::OatDexFile* oat_dex_file =
+        Runtime::Current()->GetClassLinker()->FindOpenedOatDexFileForDexFile(dex_file);
+    return oat_dex_file != nullptr;
   }
 };