Create OatFileAssistant class for assisting with oat files.

The oat file assistant is used for determining whether dex2oat or
patchoat is needed, for running dex2oat or patchoat as needed to make
an oat file up to date, and to load dex files associated with a given
dex location.

The introduction of the OatFileAssistant class is meant to clean up and
consolidate code related to the management of oat files that was
duplicated and spread across dalvik_system_DexFile.cc and
class_linker.cc.

Bug: 11301553
Change-Id: I0c16027b9bae4570c2c50faa9c14f581c0cbafb8
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
new file mode 100644
index 0000000..f87fa4f
--- /dev/null
+++ b/runtime/oat_file_assistant.cc
@@ -0,0 +1,952 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "oat_file_assistant.h"
+
+#include <fcntl.h>
+#ifdef __linux__
+#include <sys/sendfile.h>
+#else
+#include <sys/socket.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <set>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "class_linker.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "image.h"
+#include "oat.h"
+#include "os.h"
+#include "profiler.h"
+#include "runtime.h"
+#include "ScopedFd.h"
+#include "utils.h"
+
+namespace art {
+
+OatFileAssistant::OatFileAssistant(const char* dex_location,
+                                   const InstructionSet isa,
+                                   bool load_executable)
+    : OatFileAssistant(dex_location, nullptr, isa, load_executable, nullptr) { }
+
+OatFileAssistant::OatFileAssistant(const char* dex_location,
+                                   const char* oat_location,
+                                   const InstructionSet isa,
+                                   bool load_executable)
+    : OatFileAssistant(dex_location, oat_location, isa, load_executable, nullptr) { }
+
+OatFileAssistant::OatFileAssistant(const char* dex_location,
+                                   const InstructionSet isa,
+                                   bool load_executable,
+                                   const char* package_name)
+    : OatFileAssistant(dex_location, nullptr, isa, load_executable, package_name) { }
+
+OatFileAssistant::OatFileAssistant(const char* dex_location,
+                                   const char* oat_location,
+                                   const InstructionSet isa,
+                                   bool load_executable,
+                                   const char* package_name)
+    : dex_location_(dex_location), isa_(isa),
+      package_name_(package_name), load_executable_(load_executable) {
+  if (load_executable_ && isa != kRuntimeISA) {
+    LOG(WARNING) << "OatFileAssistant: Load executable specified, "
+      << "but isa is not kRuntimeISA. Will not attempt to load executable.";
+    load_executable_ = false;
+  }
+
+  // If the user gave a target oat location, save that as the cached oat
+  // location now so we won't try to construct the default location later.
+  if (oat_location != nullptr) {
+    cached_oat_file_name_ = std::string(oat_location);
+    cached_oat_file_name_attempted_ = true;
+    cached_oat_file_name_found_ = true;
+  }
+
+  // If there is no package name given, we will not be able to find any
+  // profiles associated with this dex location. Preemptively mark that to
+  // be the case, rather than trying to find and load the profiles later.
+  // Similarly, if profiling is disabled.
+  if (package_name == nullptr
+      || !Runtime::Current()->GetProfilerOptions().IsEnabled()) {
+    profile_load_attempted_ = true;
+    profile_load_succeeded_ = false;
+    old_profile_load_attempted_ = true;
+    old_profile_load_succeeded_ = false;
+  }
+}
+
+OatFileAssistant::~OatFileAssistant() {
+  // Clean up the lock file.
+  if (lock_file_.get() != nullptr) {
+    lock_file_->Erase();
+    TEMP_FAILURE_RETRY(unlink(lock_file_->GetPath().c_str()));
+  }
+}
+
+bool OatFileAssistant::IsInBootClassPath() {
+  // Note: We check the current boot class path, regardless of the ISA
+  // specified by the user. This is okay, because the boot class path should
+  // be the same for all ISAs.
+  // TODO: Can we verify the boot class path is the same for all ISAs?
+  Runtime* runtime = Runtime::Current();
+  ClassLinker* class_linker = runtime->GetClassLinker();
+  const auto& boot_class_path = class_linker->GetBootClassPath();
+  for (size_t i = 0; i < boot_class_path.size(); i++) {
+    if (boot_class_path[i]->GetLocation() == std::string(dex_location_)) {
+      VLOG(oat) << "Dex location " << dex_location_ << " is in boot class path";
+      return true;
+    }
+  }
+  return false;
+}
+
+bool OatFileAssistant::Lock(std::string* error_msg) {
+  CHECK(error_msg != nullptr);
+  CHECK(lock_file_.get() == nullptr) << "OatFileAssistant::Lock already acquired";
+
+  if (OatFileName() == nullptr) {
+    *error_msg = "Failed to determine lock file";
+    return false;
+  }
+  std::string lock_file_name = *OatFileName() + ".flock";
+
+  lock_file_.reset(OS::CreateEmptyFile(lock_file_name.c_str()));
+  if (lock_file_.get() == nullptr) {
+    *error_msg = "Failed to create lock file " + lock_file_name;
+    return false;
+  }
+
+  if (!flock_.Init(lock_file_.get(), error_msg)) {
+    TEMP_FAILURE_RETRY(unlink(lock_file_name.c_str()));
+    return false;
+  }
+  return true;
+}
+
+OatFileAssistant::Status OatFileAssistant::GetStatus() {
+  // TODO: If the profiling code is ever restored, it's worth considering
+  // whether we should check to see if the profile is out of date here.
+
+  if (OdexFileIsOutOfDate()) {
+    // The DEX file is not pre-compiled.
+    // TODO: What if the oat file is not out of date? Could we relocate it
+    // from itself?
+    return OatFileIsUpToDate() ? kUpToDate : kOutOfDate;
+  } else {
+    // The DEX file is pre-compiled. If the oat file isn't up to date, we can
+    // patch the pre-compiled version rather than recompiling.
+    if (OatFileIsUpToDate() || OdexFileIsUpToDate()) {
+      return kUpToDate;
+    } else {
+      return kNeedsRelocation;
+    }
+  }
+}
+
+bool OatFileAssistant::MakeUpToDate(std::string* error_msg) {
+  switch (GetStatus()) {
+    case kUpToDate: return true;
+    case kNeedsRelocation: return RelocateOatFile(error_msg);
+    case kOutOfDate: return GenerateOatFile(error_msg);
+  }
+  UNREACHABLE();
+}
+
+std::unique_ptr<OatFile> OatFileAssistant::GetBestOatFile() {
+  if (OatFileIsUpToDate()) {
+    oat_file_released_ = true;
+    return std::move(cached_oat_file_);
+  }
+
+  if (OdexFileIsUpToDate()) {
+    oat_file_released_ = true;
+    return std::move(cached_odex_file_);
+  }
+
+  if (load_executable_) {
+    VLOG(oat) << "Oat File Assistant: No relocated oat file found,"
+      << " attempting to fall back to interpreting oat file instead.";
+
+    if (!OatFileIsOutOfDate()) {
+      load_executable_ = false;
+      ClearOatFileCache();
+      if (!OatFileIsOutOfDate()) {
+        oat_file_released_ = true;
+        return std::move(cached_oat_file_);
+      }
+    }
+
+    if (!OdexFileIsOutOfDate()) {
+      load_executable_ = false;
+      ClearOdexFileCache();
+      if (!OdexFileIsOutOfDate()) {
+        oat_file_released_ = true;
+        return std::move(cached_odex_file_);
+      }
+    }
+  }
+
+  return std::unique_ptr<OatFile>();
+}
+
+std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles(
+    const OatFile& oat_file, const char* dex_location) {
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+
+  // Load the primary dex file.
+  std::string error_msg;
+  const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(
+      dex_location, nullptr, false);
+  if (oat_dex_file == nullptr) {
+    LOG(WARNING) << "Attempt to load out-of-date oat file "
+      << oat_file.GetLocation() << " for dex location " << dex_location;
+    return std::vector<std::unique_ptr<const DexFile>>();
+  }
+
+  std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
+  if (dex_file.get() == nullptr) {
+    LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
+    return std::vector<std::unique_ptr<const DexFile>>();
+  }
+  dex_files.push_back(std::move(dex_file));
+
+  // Load secondary multidex files
+  for (int i = 1; ; i++) {
+    std::string secondary_dex_location = DexFile::GetMultiDexClassesDexName(i, dex_location);
+    oat_dex_file = oat_file.GetOatDexFile(secondary_dex_location.c_str(), nullptr, false);
+    if (oat_dex_file == NULL) {
+      // There are no more secondary dex files to load.
+      break;
+    }
+
+    dex_file = oat_dex_file->OpenDexFile(&error_msg);
+    if (dex_file.get() == nullptr) {
+      LOG(WARNING) << "Failed to open dex file from oat dex file: " << error_msg;
+      return std::vector<std::unique_ptr<const DexFile>>();
+    }
+    dex_files.push_back(std::move(dex_file));
+  }
+  return dex_files;
+}
+
+const std::string* OatFileAssistant::OdexFileName() {
+  if (!cached_odex_file_name_attempted_) {
+    CHECK(dex_location_ != nullptr) << "OatFileAssistant: null dex location";
+    cached_odex_file_name_attempted_ = true;
+
+    std::string error_msg;
+    cached_odex_file_name_found_ = DexFilenameToOdexFilename(
+        dex_location_, isa_, &cached_odex_file_name_, &error_msg);
+    if (!cached_odex_file_name_found_) {
+      // If we can't figure out the odex file, we treat it as if the odex
+      // file was inaccessible.
+      LOG(WARNING) << "Failed to determine odex file name: " << error_msg;
+    }
+  }
+  return cached_odex_file_name_found_ ? &cached_odex_file_name_ : nullptr;
+}
+
+bool OatFileAssistant::OdexFileExists() {
+  return GetOdexFile() != nullptr;
+}
+
+OatFileAssistant::Status OatFileAssistant::OdexFileStatus() {
+  if (OdexFileIsOutOfDate()) {
+    return kOutOfDate;
+  }
+  if (OdexFileIsUpToDate()) {
+    return kUpToDate;
+  }
+  return kNeedsRelocation;
+}
+
+bool OatFileAssistant::OdexFileIsOutOfDate() {
+  if (!odex_file_is_out_of_date_attempted_) {
+    odex_file_is_out_of_date_attempted_ = true;
+    const OatFile* odex_file = GetOdexFile();
+    if (odex_file == nullptr) {
+      cached_odex_file_is_out_of_date_ = true;
+    } else {
+      cached_odex_file_is_out_of_date_ = GivenOatFileIsOutOfDate(*odex_file);
+    }
+  }
+  return cached_odex_file_is_out_of_date_;
+}
+
+bool OatFileAssistant::OdexFileNeedsRelocation() {
+  return OdexFileStatus() == kNeedsRelocation;
+}
+
+bool OatFileAssistant::OdexFileIsUpToDate() {
+  if (!odex_file_is_up_to_date_attempted_) {
+    odex_file_is_up_to_date_attempted_ = true;
+    const OatFile* odex_file = GetOdexFile();
+    if (odex_file == nullptr) {
+      cached_odex_file_is_up_to_date_ = false;
+    } else {
+      cached_odex_file_is_up_to_date_ = GivenOatFileIsUpToDate(*odex_file);
+    }
+  }
+  return cached_odex_file_is_up_to_date_;
+}
+
+const std::string* OatFileAssistant::OatFileName() {
+  if (!cached_oat_file_name_attempted_) {
+    cached_oat_file_name_attempted_ = true;
+
+    // Compute the oat file name from the dex location.
+    CHECK(dex_location_ != nullptr) << "OatFileAssistant: null dex location";
+
+    // TODO: The oat file assistant should be the definitive place for
+    // determining the oat file name from the dex location, not
+    // GetDalvikCacheFilename.
+    std::string cache_dir = StringPrintf("%s%s",
+        DalvikCacheDirectory().c_str(), GetInstructionSetString(isa_));
+    std::string error_msg;
+    cached_oat_file_name_found_ = GetDalvikCacheFilename(dex_location_,
+        cache_dir.c_str(), &cached_oat_file_name_, &error_msg);
+    if (!cached_oat_file_name_found_) {
+      // If we can't determine the oat file name, we treat the oat file as
+      // inaccessible.
+      LOG(WARNING) << "Failed to determine oat file name for dex location "
+        << dex_location_ << ": " << error_msg;
+    }
+  }
+  return cached_oat_file_name_found_ ? &cached_oat_file_name_ : nullptr;
+}
+
+bool OatFileAssistant::OatFileExists() {
+  return GetOatFile() != nullptr;
+}
+
+OatFileAssistant::Status OatFileAssistant::OatFileStatus() {
+  if (OatFileIsOutOfDate()) {
+    return kOutOfDate;
+  }
+  if (OatFileIsUpToDate()) {
+    return kUpToDate;
+  }
+  return kNeedsRelocation;
+}
+
+bool OatFileAssistant::OatFileIsOutOfDate() {
+  if (!oat_file_is_out_of_date_attempted_) {
+    oat_file_is_out_of_date_attempted_ = true;
+    const OatFile* oat_file = GetOatFile();
+    if (oat_file == nullptr) {
+      cached_oat_file_is_out_of_date_ = true;
+    } else {
+      cached_oat_file_is_out_of_date_ = GivenOatFileIsOutOfDate(*oat_file);
+    }
+  }
+  return cached_oat_file_is_out_of_date_;
+}
+
+bool OatFileAssistant::OatFileNeedsRelocation() {
+  return OatFileStatus() == kNeedsRelocation;
+}
+
+bool OatFileAssistant::OatFileIsUpToDate() {
+  if (!oat_file_is_up_to_date_attempted_) {
+    oat_file_is_up_to_date_attempted_ = true;
+    const OatFile* oat_file = GetOatFile();
+    if (oat_file == nullptr) {
+      cached_oat_file_is_up_to_date_ = false;
+    } else {
+      cached_oat_file_is_up_to_date_ = GivenOatFileIsUpToDate(*oat_file);
+    }
+  }
+  return cached_oat_file_is_up_to_date_;
+}
+
+OatFileAssistant::Status OatFileAssistant::GivenOatFileStatus(const OatFile& file) {
+  // TODO: This could cause GivenOatFileIsOutOfDate to be called twice, which
+  // is more work than we need to do. If performance becomes a concern, and
+  // this method is actually called, this should be fixed.
+  if (GivenOatFileIsOutOfDate(file)) {
+    return kOutOfDate;
+  }
+  if (GivenOatFileIsUpToDate(file)) {
+    return kUpToDate;
+  }
+  return kNeedsRelocation;
+}
+
+bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) {
+  // Verify the dex checksum.
+  // Note: GetOatDexFile will return NULL if the dex checksum doesn't match
+  // what we provide, which verifies the primary dex checksum for us.
+  const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum();
+  const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(
+      dex_location_, dex_checksum_pointer, false);
+  if (oat_dex_file == NULL) {
+    return true;
+  }
+
+  // Verify the dex checksums for any secondary multidex files
+  for (int i = 1; ; i++) {
+    std::string secondary_dex_location
+      = DexFile::GetMultiDexClassesDexName(i, dex_location_);
+    const OatFile::OatDexFile* secondary_oat_dex_file
+      = file.GetOatDexFile(secondary_dex_location.c_str(), nullptr, false);
+    if (secondary_oat_dex_file == NULL) {
+      // There are no more secondary dex files to check.
+      break;
+    }
+
+    std::string error_msg;
+    uint32_t expected_secondary_checksum = 0;
+    if (DexFile::GetChecksum(secondary_dex_location.c_str(),
+          &expected_secondary_checksum, &error_msg)) {
+      uint32_t actual_secondary_checksum
+        = secondary_oat_dex_file->GetDexFileLocationChecksum();
+      if (expected_secondary_checksum != actual_secondary_checksum) {
+        VLOG(oat) << "Dex checksum does not match for secondary dex: "
+          << secondary_dex_location
+          << ". Expected: " << expected_secondary_checksum
+          << ", Actual: " << actual_secondary_checksum;
+        return false;
+      }
+    } else {
+      // If we can't get the checksum for the secondary location, we assume
+      // the dex checksum is up to date for this and all other secondary dex
+      // files.
+      break;
+    }
+  }
+
+  // Verify the image checksum
+  const ImageInfo* image_info = GetImageInfo();
+  if (image_info == nullptr) {
+    VLOG(oat) << "No image for oat image checksum to match against.";
+    return true;
+  }
+
+  if (file.GetOatHeader().GetImageFileLocationOatChecksum() != image_info->oat_checksum) {
+    VLOG(oat) << "Oat image checksum does not match image checksum.";
+    return true;
+  }
+
+  // The checksums are all good; the dex file is not out of date.
+  return false;
+}
+
+bool OatFileAssistant::GivenOatFileNeedsRelocation(const OatFile& file) {
+  return GivenOatFileStatus(file) == kNeedsRelocation;
+}
+
+bool OatFileAssistant::GivenOatFileIsUpToDate(const OatFile& file) {
+  if (GivenOatFileIsOutOfDate(file)) {
+    return false;
+  }
+
+  if (file.IsPic()) {
+    return true;
+  }
+
+  const ImageInfo* image_info = GetImageInfo();
+  if (image_info == nullptr) {
+    VLOG(oat) << "No image for to check oat relocation against.";
+    return false;
+  }
+
+  // Verify the oat_data_begin recorded for the image in the oat file matches
+  // the actual oat_data_begin for boot.oat in the image.
+  const OatHeader& oat_header = file.GetOatHeader();
+  uintptr_t oat_data_begin = oat_header.GetImageFileLocationOatDataBegin();
+  if (oat_data_begin != image_info->oat_data_begin) {
+    VLOG(oat) << file.GetLocation() <<
+      ": Oat file image oat_data_begin (" << oat_data_begin << ")"
+      << " does not match actual image oat_data_begin ("
+      << image_info->oat_data_begin << ")";
+    return false;
+  }
+
+  // Verify the oat_patch_delta recorded for the image in the oat file matches
+  // the actual oat_patch_delta for the image.
+  int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
+  if (oat_patch_delta != image_info->patch_delta) {
+    VLOG(oat) << file.GetLocation() <<
+      ": Oat file image patch delta (" << oat_patch_delta << ")"
+      << " does not match actual image patch delta ("
+      << image_info->patch_delta << ")";
+    return false;
+  }
+  return true;
+}
+
+bool OatFileAssistant::ProfileExists() {
+  return GetProfile() != nullptr;
+}
+
+bool OatFileAssistant::OldProfileExists() {
+  return GetOldProfile() != nullptr;
+}
+
+// TODO: The IsProfileChangeSignificant implementation was copied from likely
+// bit-rotted code.
+bool OatFileAssistant::IsProfileChangeSignificant() {
+  ProfileFile* profile = GetProfile();
+  if (profile == nullptr) {
+    return false;
+  }
+
+  ProfileFile* old_profile = GetOldProfile();
+  if (old_profile == nullptr) {
+    return false;
+  }
+
+  // TODO: The following code to compare two profile files should live with
+  // the rest of the profiler code, not the oat file assistant code.
+
+  // A change in profile is considered significant if X% (change_thr property)
+  // of the top K% (compile_thr property) samples has changed.
+  const ProfilerOptions& options = Runtime::Current()->GetProfilerOptions();
+  const double top_k_threshold = options.GetTopKThreshold();
+  const double change_threshold = options.GetTopKChangeThreshold();
+  std::set<std::string> top_k, old_top_k;
+  profile->GetTopKSamples(top_k, top_k_threshold);
+  old_profile->GetTopKSamples(old_top_k, top_k_threshold);
+  std::set<std::string> diff;
+  std::set_difference(top_k.begin(), top_k.end(), old_top_k.begin(),
+      old_top_k.end(), std::inserter(diff, diff.end()));
+
+  // TODO: consider using the usedPercentage instead of the plain diff count.
+  double change_percent = 100.0 * static_cast<double>(diff.size())
+                                / static_cast<double>(top_k.size());
+  std::set<std::string>::iterator end = diff.end();
+  for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) {
+    VLOG(oat) << "Profile new in topK: " << *it;
+  }
+
+  if (change_percent > change_threshold) {
+      VLOG(oat) << "Oat File Assistant: Profile for " << dex_location_
+        << "has changed significantly: (top "
+        << top_k_threshold << "% samples changed in proportion of "
+        << change_percent << "%)";
+      return true;
+  }
+  return false;
+}
+
+// TODO: The CopyProfileFile implementation was copied from likely bit-rotted
+// code.
+void OatFileAssistant::CopyProfileFile() {
+  if (!ProfileExists()) {
+    return;
+  }
+
+  std::string profile_name = ProfileFileName();
+  std::string old_profile_name = OldProfileFileName();
+
+  ScopedFd src(open(old_profile_name.c_str(), O_RDONLY));
+  if (src.get() == -1) {
+    PLOG(WARNING) << "Failed to open profile file " << old_profile_name
+      << ". My uid:gid is " << getuid() << ":" << getgid();
+    return;
+  }
+
+  struct stat stat_src;
+  if (fstat(src.get(), &stat_src) == -1) {
+    PLOG(WARNING) << "Failed to get stats for profile file  " << old_profile_name
+      << ". My uid:gid is " << getuid() << ":" << getgid();
+    return;
+  }
+
+  // Create the copy with rw------- (only accessible by system)
+  ScopedFd dst(open(profile_name.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0600));
+  if (dst.get()  == -1) {
+    PLOG(WARNING) << "Failed to create/write prev profile file " << profile_name
+      << ".  My uid:gid is " << getuid() << ":" << getgid();
+    return;
+  }
+
+#ifdef __linux__
+  if (sendfile(dst.get(), src.get(), nullptr, stat_src.st_size) == -1) {
+#else
+  off_t len;
+  if (sendfile(dst.get(), src.get(), 0, &len, nullptr, 0) == -1) {
+#endif
+    PLOG(WARNING) << "Failed to copy profile file " << old_profile_name
+      << " to " << profile_name << ". My uid:gid is " << getuid()
+      << ":" << getgid();
+  }
+}
+
+bool OatFileAssistant::RelocateOatFile(std::string* error_msg) {
+  CHECK(error_msg != nullptr);
+
+  if (OdexFileName() == nullptr) {
+    *error_msg = "Patching of oat file for dex location "
+      + std::string(dex_location_)
+      + " not attempted because the odex file name could not be determined.";
+    return false;
+  }
+  const std::string& odex_file_name = *OdexFileName();
+
+  if (OatFileName() == nullptr) {
+    *error_msg = "Patching of oat file for dex location "
+      + std::string(dex_location_)
+      + " not attempted because the oat file name could not be determined.";
+    return false;
+  }
+  const std::string& oat_file_name = *OatFileName();
+
+  const ImageInfo* image_info = GetImageInfo();
+  Runtime* runtime = Runtime::Current();
+  if (image_info == nullptr) {
+    *error_msg = "Patching of oat file " + oat_file_name
+      + " not attempted because no image location was found.";
+    return false;
+  }
+
+  if (!runtime->IsDex2OatEnabled()) {
+    *error_msg = "Patching of oat file " + oat_file_name
+      + " not attempted because dex2oat is disabled";
+    return false;
+  }
+
+  std::vector<std::string> argv;
+  argv.push_back(runtime->GetPatchoatExecutable());
+  argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(isa_)));
+  argv.push_back("--input-oat-file=" + odex_file_name);
+  argv.push_back("--output-oat-file=" + oat_file_name);
+  argv.push_back("--patched-image-location=" + image_info->location);
+
+  std::string command_line(Join(argv, ' '));
+  if (!Exec(argv, error_msg)) {
+    // Manually delete the file. This ensures there is no garbage left over if
+    // the process unexpectedly died.
+    TEMP_FAILURE_RETRY(unlink(oat_file_name.c_str()));
+    return false;
+  }
+
+  // Mark that the oat file has changed and we should try to reload.
+  ClearOatFileCache();
+  return true;
+}
+
+bool OatFileAssistant::GenerateOatFile(std::string* error_msg) {
+  CHECK(error_msg != nullptr);
+
+  if (OatFileName() == nullptr) {
+    *error_msg = "Generation of oat file for dex location "
+      + std::string(dex_location_)
+      + " not attempted because the oat file name could not be determined.";
+    return false;
+  }
+  const std::string& oat_file_name = *OatFileName();
+
+  Runtime* runtime = Runtime::Current();
+  if (!runtime->IsDex2OatEnabled()) {
+    *error_msg = "Generation of oat file " + oat_file_name
+      + " not attempted because dex2oat is disabled";
+    return false;
+  }
+
+  std::vector<std::string> args;
+  args.push_back("--dex-file=" + std::string(dex_location_));
+  args.push_back("--oat-file=" + oat_file_name);
+
+  // dex2oat ignores missing dex files and doesn't report an error.
+  // Check explicitly here so we can detect the error properly.
+  // TODO: Why does dex2oat behave that way?
+  if (!OS::FileExists(dex_location_)) {
+    *error_msg = "Dex location " + std::string(dex_location_) + " does not exists.";
+    return false;
+  }
+
+  if (!Dex2Oat(args, error_msg)) {
+    // Manually delete the file. This ensures there is no garbage left over if
+    // the process unexpectedly died.
+    TEMP_FAILURE_RETRY(unlink(oat_file_name.c_str()));
+    return false;
+  }
+
+  // Mark that the oat file has changed and we should try to reload.
+  ClearOatFileCache();
+  return true;
+}
+
+bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args,
+                               std::string* error_msg) {
+  Runtime* runtime = Runtime::Current();
+  std::string image_location = ImageLocation();
+  if (image_location.empty()) {
+    *error_msg = "No image location found for Dex2Oat.";
+    return false;
+  }
+
+  std::vector<std::string> argv;
+  argv.push_back(runtime->GetCompilerExecutable());
+  argv.push_back("--runtime-arg");
+  argv.push_back("-classpath");
+  argv.push_back("--runtime-arg");
+  argv.push_back(runtime->GetClassPathString());
+  runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
+
+  if (!runtime->IsVerificationEnabled()) {
+    argv.push_back("--compiler-filter=verify-none");
+  }
+
+  if (runtime->MustRelocateIfPossible()) {
+    argv.push_back("--runtime-arg");
+    argv.push_back("-Xrelocate");
+  } else {
+    argv.push_back("--runtime-arg");
+    argv.push_back("-Xnorelocate");
+  }
+
+  if (!kIsTargetBuild) {
+    argv.push_back("--host");
+  }
+
+  argv.push_back("--boot-image=" + image_location);
+
+  std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
+  argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
+
+  argv.insert(argv.end(), args.begin(), args.end());
+
+  std::string command_line(Join(argv, ' '));
+  return Exec(argv, error_msg);
+}
+
+bool OatFileAssistant::DexFilenameToOdexFilename(const std::string& location,
+    InstructionSet isa, std::string* odex_filename, std::string* error_msg) {
+  CHECK(odex_filename != nullptr);
+  CHECK(error_msg != nullptr);
+
+  // The odex file name is formed by replacing the dex_location extension with
+  // .odex and inserting an isa directory. For example:
+  //   location = /foo/bar/baz.jar
+  //   odex_location = /foo/bar/<isa>/baz.odex
+
+  // Find the directory portion of the dex location and add the isa directory.
+  size_t pos = location.rfind('/');
+  if (pos == std::string::npos) {
+    *error_msg = "Dex location " + location + " has no directory.";
+    return false;
+  }
+  std::string dir = location.substr(0, pos+1);
+  dir += std::string(GetInstructionSetString(isa));
+
+  // Find the file portion of the dex location.
+  std::string file;
+  if (pos == std::string::npos) {
+    file = location;
+  } else {
+    file = location.substr(pos+1);
+  }
+
+  // Get the base part of the file without the extension.
+  pos = file.rfind('.');
+  if (pos == std::string::npos) {
+    *error_msg = "Dex location " + location + " has no extension.";
+    return false;
+  }
+  std::string base = file.substr(0, pos);
+
+  *odex_filename = dir + "/" + base + ".odex";
+  return true;
+}
+
+std::string OatFileAssistant::DalvikCacheDirectory() {
+  // Note: We don't cache this, because it will only be called once by
+  // OatFileName, and we don't care about the performance of the profiling
+  // code, which isn't used in practice.
+
+  // TODO: The work done in GetDalvikCache is overkill for what we need.
+  // Ideally a new API for getting the DalvikCacheDirectory the way we want
+  // (without existence testing, creation, or death) is provided with the rest
+  // of the GetDalvikCache family of functions. Until such an API is in place,
+  // we use GetDalvikCache to avoid duplicating the logic for determining the
+  // dalvik cache directory.
+  std::string result;
+  bool have_android_data;
+  bool dalvik_cache_exists;
+  bool is_global_cache;
+  GetDalvikCache("", false, &result, &have_android_data, &dalvik_cache_exists, &is_global_cache);
+  return result;
+}
+
+std::string OatFileAssistant::ProfileFileName() {
+  if (package_name_ != nullptr) {
+    return DalvikCacheDirectory() + std::string("profiles/") + package_name_;
+  }
+  return "";
+}
+
+std::string OatFileAssistant::OldProfileFileName() {
+  std::string profile_name = ProfileFileName();
+  if (profile_name.empty()) {
+    return "";
+  }
+  return profile_name + "@old";
+}
+
+std::string OatFileAssistant::ImageLocation() {
+  Runtime* runtime = Runtime::Current();
+  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+  if (image_space == nullptr) {
+    return "";
+  }
+  return image_space->GetImageLocation();
+}
+
+const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
+  if (!required_dex_checksum_attempted) {
+    required_dex_checksum_attempted = true;
+    required_dex_checksum_found = false;
+    std::string error_msg;
+    CHECK(dex_location_ != nullptr) << "OatFileAssistant provided no dex location";
+    if (DexFile::GetChecksum(dex_location_, &cached_required_dex_checksum, &error_msg)) {
+      required_dex_checksum_found = true;
+    } else {
+      // This can happen if the original dex file has been stripped from the
+      // apk.
+      VLOG(oat) << "OatFileAssistant: " << error_msg;
+
+      // Get the checksum from the odex if we can.
+      const OatFile* odex_file = GetOdexFile();
+      if (odex_file != nullptr) {
+        const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(
+            dex_location_, nullptr, false);
+        if (odex_dex_file != nullptr) {
+          cached_required_dex_checksum = odex_dex_file->GetDexFileLocationChecksum();
+          required_dex_checksum_found = true;
+        }
+      }
+    }
+  }
+  return required_dex_checksum_found ? &cached_required_dex_checksum : nullptr;
+}
+
+const OatFile* OatFileAssistant::GetOdexFile() {
+  CHECK(!oat_file_released_) << "OdexFile called after oat file released.";
+  if (!odex_file_load_attempted_) {
+    odex_file_load_attempted_ = true;
+    if (OdexFileName() != nullptr) {
+      const std::string& odex_file_name = *OdexFileName();
+      std::string error_msg;
+      cached_odex_file_.reset(OatFile::Open(odex_file_name.c_str(),
+            odex_file_name.c_str(), nullptr, nullptr, load_executable_,
+            &error_msg));
+      if (cached_odex_file_.get() == nullptr) {
+        VLOG(oat) << "OatFileAssistant test for existing pre-compiled oat file "
+          << odex_file_name << ": " << error_msg;
+      }
+    }
+  }
+  return cached_odex_file_.get();
+}
+
+void OatFileAssistant::ClearOdexFileCache() {
+  odex_file_load_attempted_ = false;
+  cached_odex_file_.reset();
+  odex_file_is_out_of_date_attempted_ = false;
+  odex_file_is_up_to_date_attempted_ = false;
+}
+
+const OatFile* OatFileAssistant::GetOatFile() {
+  CHECK(!oat_file_released_) << "OatFile called after oat file released.";
+  if (!oat_file_load_attempted_) {
+    oat_file_load_attempted_ = true;
+    if (OatFileName() != nullptr) {
+      const std::string& oat_file_name = *OatFileName();
+      std::string error_msg;
+      cached_oat_file_.reset(OatFile::Open(oat_file_name.c_str(),
+            oat_file_name.c_str(), nullptr, nullptr, load_executable_, &error_msg));
+      if (cached_oat_file_.get() == nullptr) {
+        VLOG(oat) << "OatFileAssistant test for existing oat file "
+          << oat_file_name << ": " << error_msg;
+      }
+    }
+  }
+  return cached_oat_file_.get();
+}
+
+void OatFileAssistant::ClearOatFileCache() {
+  oat_file_load_attempted_ = false;
+  cached_oat_file_.reset();
+  oat_file_is_out_of_date_attempted_ = false;
+  oat_file_is_up_to_date_attempted_ = false;
+}
+
+const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() {
+  if (!image_info_load_attempted_) {
+    image_info_load_attempted_ = true;
+
+    Runtime* runtime = Runtime::Current();
+    const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+    if (image_space != nullptr) {
+      cached_image_info_.location = image_space->GetImageLocation();
+
+      if (isa_ == kRuntimeISA) {
+        const ImageHeader& image_header = image_space->GetImageHeader();
+        cached_image_info_.oat_checksum = image_header.GetOatChecksum();
+        cached_image_info_.oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
+        cached_image_info_.patch_delta = image_header.GetPatchDelta();
+      } else {
+        std::unique_ptr<ImageHeader> image_header(
+            gc::space::ImageSpace::ReadImageHeaderOrDie(
+                cached_image_info_.location.c_str(), isa_));
+        cached_image_info_.oat_checksum = image_header->GetOatChecksum();
+        cached_image_info_.oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin());
+        cached_image_info_.patch_delta = image_header->GetPatchDelta();
+      }
+    }
+    image_info_load_succeeded_ = (image_space != nullptr);
+  }
+  return image_info_load_succeeded_ ? &cached_image_info_ : nullptr;
+}
+
+ProfileFile* OatFileAssistant::GetProfile() {
+  if (!profile_load_attempted_) {
+    CHECK(package_name_ != nullptr)
+      << "pakage_name_ is nullptr: "
+      << "profile_load_attempted_ should have been true";
+    profile_load_attempted_ = true;
+    std::string profile_name = ProfileFileName();
+    if (!profile_name.empty()) {
+      profile_load_succeeded_ = cached_profile_.LoadFile(profile_name);
+    }
+  }
+  return profile_load_succeeded_ ? &cached_profile_ : nullptr;
+}
+
+ProfileFile* OatFileAssistant::GetOldProfile() {
+  if (!old_profile_load_attempted_) {
+    CHECK(package_name_ != nullptr)
+      << "pakage_name_ is nullptr: "
+      << "old_profile_load_attempted_ should have been true";
+    old_profile_load_attempted_ = true;
+    std::string old_profile_name = OldProfileFileName();
+    if (!old_profile_name.empty()) {
+      old_profile_load_succeeded_ = cached_old_profile_.LoadFile(old_profile_name);
+    }
+  }
+  return old_profile_load_succeeded_ ? &cached_old_profile_ : nullptr;
+}
+
+}  // namespace art
+