Merge "patchoat: DisableAutoClose when patching in place"
diff --git a/build/Android.common_test.mk b/build/Android.common_test.mk
index b536fe4..c750399 100644
--- a/build/Android.common_test.mk
+++ b/build/Android.common_test.mk
@@ -160,6 +160,10 @@
 # $(4): additional dependencies
 # $(5): a make variable used to collate target dependencies, e.g ART_TEST_TARGET_OAT_HelloWorld_DEX
 # $(6): a make variable used to collate host dependencies, e.g ART_TEST_HOST_OAT_HelloWorld_DEX
+#
+# If the input test directory contains a file called main.list, then a
+# multi-dex file is created passing main.list as the --main-dex-list argument
+# to dx.
 define build-art-test-dex
   ifeq ($(ART_BUILD_TARGET),true)
     include $(CLEAR_VARS)
@@ -172,6 +176,9 @@
     LOCAL_JAVA_LIBRARIES := $(TARGET_CORE_JARS)
     LOCAL_MODULE_PATH := $(3)
     LOCAL_DEX_PREOPT_IMAGE_LOCATION := $(TARGET_CORE_IMG_OUT)
+    ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
+      LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
+    endif
     include $(BUILD_JAVA_LIBRARY)
     $(5) := $$(LOCAL_INSTALLED_MODULE)
   endif
@@ -184,6 +191,9 @@
     LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common_test.mk $(4)
     LOCAL_JAVA_LIBRARIES := $(HOST_CORE_JARS)
     LOCAL_DEX_PREOPT_IMAGE := $(HOST_CORE_IMG_LOCATION)
+    ifneq ($(wildcard $(LOCAL_PATH)/$(2)/main.list),)
+      LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(LOCAL_PATH)/$(2)/main.list --minimal-main-dex
+    endif
     include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
     $(6) := $$(LOCAL_INSTALLED_MODULE)
   endif
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 7ab4d64..6967808 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -28,6 +28,7 @@
   GetMethodSignature \
   Interfaces \
   Main \
+  MultiDex \
   MyClass \
   MyClassNatives \
   Nested \
@@ -45,6 +46,19 @@
   $(ART_TARGET_NATIVETEST_OUT),art/build/Android.gtest.mk,ART_TEST_TARGET_GTEST_$(dir)_DEX, \
   ART_TEST_HOST_GTEST_$(dir)_DEX)))
 
+# Create rules for MainStripped, a copy of Main with the classes.dex stripped
+# for the oat file assistant tests.
+ART_TEST_HOST_GTEST_MainStripped_DEX := $(basename $(ART_TEST_HOST_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_HOST_GTEST_Main_DEX))
+ART_TEST_TARGET_GTEST_MainStripped_DEX := $(basename $(ART_TEST_TARGET_GTEST_Main_DEX))Stripped$(suffix $(ART_TEST_TARGET_GTEST_Main_DEX))
+
+$(ART_TEST_HOST_GTEST_MainStripped_DEX): $(ART_TEST_HOST_GTEST_Main_DEX)
+	cp $< $@
+	$(call dexpreopt-remove-classes.dex,$@)
+
+$(ART_TEST_TARGET_GTEST_MainStripped_DEX): $(ART_TEST_TARGET_GTEST_Main_DEX)
+	cp $< $@
+	$(call dexpreopt-remove-classes.dex,$@)
+
 # Dex file dependencies for each gtest.
 ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MyClass Nested Statics StaticsFromCode
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod
@@ -52,6 +66,7 @@
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
 ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
 ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
+ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex Nested
 ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
 ART_GTEST_proxy_test_DEX_DEPS := Interfaces
 ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
@@ -62,6 +77,9 @@
 ART_GTEST_elf_writer_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
 ART_GTEST_elf_writer_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_default_no-pic_64) $(TARGET_CORE_IMAGE_default_no-pic_32)
 
+ART_GTEST_oat_file_assistant_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
+ART_GTEST_oat_file_assistant_test_TARGET_DEPS := $(TARGET_CORE_IMAGE_default_no-pic_64) $(TARGET_CORE_IMAGE_default_no-pic_32)
+
 # TODO: document why this is needed.
 ART_GTEST_proxy_test_HOST_DEPS := $(HOST_CORE_IMAGE_default_no-pic_64) $(HOST_CORE_IMAGE_default_no-pic_32)
 
@@ -141,6 +159,7 @@
   runtime/mirror/object_test.cc \
   runtime/monitor_pool_test.cc \
   runtime/monitor_test.cc \
+  runtime/oat_file_assistant_test.cc \
   runtime/parsed_options_test.cc \
   runtime/reference_table_test.cc \
   runtime/thread_pool_test.cc \
@@ -462,12 +481,12 @@
 endef  # define-art-gtest
 
 ifeq ($(ART_BUILD_TARGET),true)
-  $(foreach file,$(RUNTIME_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),,)))
-  $(foreach file,$(COMPILER_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),art/compiler,libartd-compiler)))
+  $(foreach file,$(RUNTIME_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),,libbacktrace)))
+  $(foreach file,$(COMPILER_GTEST_TARGET_SRC_FILES), $(eval $(call define-art-gtest,target,$(file),art/compiler,libartd-compiler libbacktrace)))
 endif
 ifeq ($(ART_BUILD_HOST),true)
-  $(foreach file,$(RUNTIME_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),,)))
-  $(foreach file,$(COMPILER_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),art/compiler,libartd-compiler)))
+  $(foreach file,$(RUNTIME_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),,libbacktrace)))
+  $(foreach file,$(COMPILER_GTEST_HOST_SRC_FILES), $(eval $(call define-art-gtest,host,$(file),art/compiler,libartd-compiler libbacktrace)))
 endif
 
 # Used outside the art project to get a list of the current tests
@@ -559,6 +578,9 @@
 ART_GTEST_elf_writer_test_TARGET_DEPS :=
 ART_GTEST_jni_compiler_test_DEX_DEPS :=
 ART_GTEST_jni_internal_test_DEX_DEPS :=
+ART_GTEST_oat_file_assistant_test_DEX_DEPS :=
+ART_GTEST_oat_file_assistant_test_HOST_DEPS :=
+ART_GTEST_oat_file_assistant_test_TARGET_DEPS :=
 ART_GTEST_object_test_DEX_DEPS :=
 ART_GTEST_proxy_test_DEX_DEPS :=
 ART_GTEST_reflection_test_DEX_DEPS :=
@@ -567,5 +589,7 @@
 ART_VALGRIND_DEPENDENCIES :=
 $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_TARGET_GTEST_$(dir)_DEX :=))
 $(foreach dir,$(GTEST_DEX_DIRECTORIES), $(eval ART_TEST_HOST_GTEST_$(dir)_DEX :=))
+ART_TEST_HOST_GTEST_MainStripped_DEX :=
+ART_TEST_TARGET_GTEST_MainStripped_DEX :=
 GTEST_DEX_DIRECTORIES :=
 LOCAL_PATH :=
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 130eed2..9f873b3 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -260,6 +260,13 @@
   }
 
   EXPECT_SINGLE_PARSE_FAIL("-verbose:blablabla", CmdlineResult::kUsage);  // invalid verbose opt
+
+  {
+    const char* log_args = "-verbose:oat";
+    LogVerbosity log_verbosity = LogVerbosity();
+    log_verbosity.oat = true;
+    EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
+  }
 }  // TEST_F
 
 // TODO: Enable this b/19274810
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index de99278..03165ed 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -591,6 +591,8 @@
         log_verbosity.jni = true;
       } else if (verbose_options[j] == "monitor") {
         log_verbosity.monitor = true;
+      } else if (verbose_options[j] == "oat") {
+        log_verbosity.oat = true;
       } else if (verbose_options[j] == "profiler") {
         log_verbosity.profiler = true;
       } else if (verbose_options[j] == "signals") {
diff --git a/compiler/dex/quick/mips64/int_mips64.cc b/compiler/dex/quick/mips64/int_mips64.cc
index 9023970..8a57c82 100644
--- a/compiler/dex/quick/mips64/int_mips64.cc
+++ b/compiler/dex/quick/mips64/int_mips64.cc
@@ -260,9 +260,8 @@
     return false;
   }
   RegLocation rl_src_address = info->args[0];     // Long address.
-  rl_src_address = NarrowRegLoc(rl_src_address);  // Ignore high half in info->args[1].
   RegLocation rl_dest = InlineTarget(info);
-  RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+  RegLocation rl_address = LoadValueWide(rl_src_address, kCoreReg);
   RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true);
   DCHECK(size == kSignedByte);
   LoadBaseDisp(rl_address.reg, 0, rl_result.reg, size, kNotVolatile);
@@ -276,9 +275,8 @@
     return false;
   }
   RegLocation rl_src_address = info->args[0];     // Long address.
-  rl_src_address = NarrowRegLoc(rl_src_address);  // Ignore high half in info->args[1].
   RegLocation rl_src_value = info->args[2];       // [size] value.
-  RegLocation rl_address = LoadValue(rl_src_address, kCoreReg);
+  RegLocation rl_address = LoadValueWide(rl_src_address, kCoreReg);
   DCHECK(size == kSignedByte);
   RegLocation rl_value = LoadValue(rl_src_value, kCoreReg);
   StoreBaseDisp(rl_address.reg, 0, rl_value.reg, size, kNotVolatile);
diff --git a/compiler/dex/quick/quick_compiler.cc b/compiler/dex/quick/quick_compiler.cc
index 13a6d9d..02d74a0 100644
--- a/compiler/dex/quick/quick_compiler.cc
+++ b/compiler/dex/quick/quick_compiler.cc
@@ -628,12 +628,13 @@
 
   DCHECK(driver->GetCompilerOptions().IsCompilationEnabled());
 
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Runtime* const runtime = Runtime::Current();
+  ClassLinker* const class_linker = runtime->GetClassLinker();
   InstructionSet instruction_set = driver->GetInstructionSet();
   if (instruction_set == kArm) {
     instruction_set = kThumb2;
   }
-  CompilationUnit cu(driver->GetArenaPool(), instruction_set, driver, class_linker);
+  CompilationUnit cu(runtime->GetArenaPool(), instruction_set, driver, class_linker);
 
   CHECK((cu.instruction_set == kThumb2) ||
         (cu.instruction_set == kArm64) ||
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
index 150bdac..a4df00e 100644
--- a/compiler/dex/verification_results.cc
+++ b/compiler/dex/verification_results.cc
@@ -49,11 +49,6 @@
   DCHECK(method_verifier != NULL);
   MethodReference ref = method_verifier->GetMethodReference();
   bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
-  // TODO: Check also for virtual/interface invokes when DEX-to-DEX supports devirtualization.
-  if (!compile && !method_verifier->HasCheckCasts()) {
-    return true;
-  }
-
   const VerifiedMethod* verified_method = VerifiedMethod::Create(method_verifier, compile);
   if (verified_method == nullptr) {
     // Do not report an error to the verifier. We'll just punt this later.
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index d1291fa..78dd6cc 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2412,8 +2412,9 @@
 
 std::string CompilerDriver::GetMemoryUsageString(bool extended) const {
   std::ostringstream oss;
-  const ArenaPool* arena_pool = GetArenaPool();
-  gc::Heap* heap = Runtime::Current()->GetHeap();
+  Runtime* const runtime = Runtime::Current();
+  const ArenaPool* arena_pool = runtime->GetArenaPool();
+  gc::Heap* const heap = runtime->GetHeap();
   oss << "arena alloc=" << PrettySize(arena_pool->GetBytesAllocated());
   oss << " java alloc=" << PrettySize(heap->GetBytesAllocated());
 #ifdef HAVE_MALLOC_H
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index f949667..28a8245 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -362,12 +362,6 @@
     support_boot_image_fixup_ = support_boot_image_fixup;
   }
 
-  ArenaPool* GetArenaPool() {
-    return &arena_pool_;
-  }
-  const ArenaPool* GetArenaPool() const {
-    return &arena_pool_;
-  }
   SwapAllocator<void>& GetSwapSpaceAllocator() {
     return *swap_space_allocator_.get();
   }
@@ -606,9 +600,6 @@
 
   void* compiler_context_;
 
-  // Arena pool used by the compiler.
-  ArenaPool arena_pool_;
-
   bool support_boot_image_fixup_;
 
   // DeDuplication data structures, these own the corresponding byte arrays.
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 04efa21..beb5755 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -18,6 +18,7 @@
 
 #include "arch/instruction_set.h"
 #include "arch/instruction_set_features.h"
+#include "base/timing_logger.h"
 #include "compiler_callbacks.h"
 #include "dex/pass_manager.h"
 #include "dex/quick_compiler_callbacks.h"
@@ -103,7 +104,8 @@
 }
 
 bool JitCompiler::CompileMethod(Thread* self, mirror::ArtMethod* method) {
-  uint64_t start_time = NanoTime();
+  TimingLogger logger("JIT compiler timing logger", true, VLOG_IS_ON(jit));
+  const uint64_t start_time = NanoTime();
   StackHandleScope<2> hs(self);
   self->AssertNoPendingException();
   Runtime* runtime = Runtime::Current();
@@ -113,14 +115,18 @@
     return true;  // Already compiled
   }
   Handle<mirror::Class> h_class(hs.NewHandle(h_method->GetDeclaringClass()));
-  if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
-    VLOG(jit) << "JIT failed to initialize " << PrettyMethod(h_method.Get());
-    return false;
+  {
+    TimingLogger::ScopedTiming t2("Initializing", &logger);
+    if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
+      VLOG(jit) << "JIT failed to initialize " << PrettyMethod(h_method.Get());
+      return false;
+    }
   }
   const DexFile* dex_file = h_class->GetDexCache()->GetDexFile();
   MethodReference method_ref(dex_file, h_method->GetDexMethodIndex());
   // Only verify if we don't already have verification results.
   if (verification_results_->GetVerifiedMethod(method_ref) == nullptr) {
+    TimingLogger::ScopedTiming t2("Verifying", &logger);
     std::string error;
     if (verifier::MethodVerifier::VerifyMethod(h_method.Get(), true, &error) ==
         verifier::MethodVerifier::kHardFailure) {
@@ -129,7 +135,16 @@
       return false;
     }
   }
-  CompiledMethod* compiled_method(compiler_driver_->CompileMethod(self, h_method.Get()));
+  CompiledMethod* compiled_method = nullptr;
+  {
+    TimingLogger::ScopedTiming t2("Compiling", &logger);
+    compiled_method = compiler_driver_->CompileMethod(self, h_method.Get());
+  }
+  {
+    TimingLogger::ScopedTiming t2("TrimMaps", &logger);
+    // Trim maps to reduce memory usage, TODO: measure how much this increases compile time.
+    runtime->GetArenaPool()->TrimMaps();
+  }
   if (compiled_method == nullptr) {
     return false;
   }
@@ -137,7 +152,7 @@
   // Don't add the method if we are supposed to be deoptimized.
   bool result = false;
   if (!runtime->GetInstrumentation()->AreAllMethodsDeoptimized()) {
-    const void* code = Runtime::Current()->GetClassLinker()->GetOatMethodQuickCodeFor(
+    const void* code = runtime->GetClassLinker()->GetOatMethodQuickCodeFor(
         h_method.Get());
     if (code != nullptr) {
       // Already have some compiled code, just use this instead of linking.
@@ -145,11 +160,13 @@
       h_method->SetEntryPointFromQuickCompiledCode(code);
       result = true;
     } else {
+      TimingLogger::ScopedTiming t2("MakeExecutable", &logger);
       result = MakeExecutable(compiled_method, h_method.Get());
     }
   }
   // Remove the compiled method to save memory.
   compiler_driver_->RemoveCompiledMethod(method_ref);
+  runtime->GetJit()->AddTimingLogger(logger);
   return result;
 }
 
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index a7f1f74..76b9f4f 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -362,6 +362,12 @@
             Primitive::PrettyDescriptor(phi->GetType())));
     }
   }
+  if (phi->GetType() != HPhi::ToPhiType(phi->GetType())) {
+    AddError(StringPrintf("Phi %d in block %d does not have an expected phi type: %s",
+                          phi->GetId(),
+                          phi->GetBlock()->GetBlockId(),
+                          Primitive::PrettyDescriptor(phi->GetType())));
+  }
 }
 
 void SSAChecker::VisitIf(HIf* instruction) {
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index b34957a..e22f7cc 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -124,8 +124,8 @@
     resolved_method->GetAccessFlags(),
     nullptr);
 
-  HGraph* callee_graph =
-      new (graph_->GetArena()) HGraph(graph_->GetArena(), graph_->GetCurrentInstructionId());
+  HGraph* callee_graph = new (graph_->GetArena()) HGraph(
+      graph_->GetArena(), graph_->IsDebuggable(), graph_->GetCurrentInstructionId());
 
   OptimizingCompilerStats inline_stats;
   HGraphBuilder builder(callee_graph,
@@ -155,15 +155,11 @@
   }
 
   // Run simple optimizations on the graph.
-  SsaRedundantPhiElimination redundant_phi(callee_graph);
-  SsaDeadPhiElimination dead_phi(callee_graph);
   HDeadCodeElimination dce(callee_graph);
   HConstantFolding fold(callee_graph);
   InstructionSimplifier simplify(callee_graph, stats_);
 
   HOptimization* optimizations[] = {
-    &redundant_phi,
-    &dead_phi,
     &dce,
     &fold,
     &simplify,
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index 907eff1..0b0cfde 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -388,44 +388,44 @@
   // Make sure we create a preheader of a loop when a header originally has two
   // incoming blocks and one back edge.
   // Bitsets are made of:
-  // (constant0, constant4, constant5, phi in block 8, phi in block 4)
+  // (constant0, constant4, constant5, phi in block 8)
   const char* expected =
     "Block 0\n"
-    "  live in: (00000)\n"
-    "  live out: (11100)\n"
-    "  kill: (11100)\n"
+    "  live in: (0000)\n"
+    "  live out: (1110)\n"
+    "  kill: (1110)\n"
     "Block 1\n"
-    "  live in: (11100)\n"
-    "  live out: (01100)\n"
-    "  kill: (00000)\n"
+    "  live in: (1110)\n"
+    "  live out: (0110)\n"
+    "  kill: (0000)\n"
     "Block 2\n"
-    "  live in: (01000)\n"
-    "  live out: (00000)\n"
-    "  kill: (00000)\n"
+    "  live in: (0100)\n"
+    "  live out: (0000)\n"
+    "  kill: (0000)\n"
     "Block 3\n"
-    "  live in: (00100)\n"
-    "  live out: (00000)\n"
-    "  kill: (00000)\n"
+    "  live in: (0010)\n"
+    "  live out: (0000)\n"
+    "  kill: (0000)\n"
     "Block 4\n"  // loop header
-    "  live in: (00000)\n"
-    "  live out: (00001)\n"
-    "  kill: (00001)\n"
+    "  live in: (0001)\n"
+    "  live out: (0001)\n"
+    "  kill: (0000)\n"
     "Block 5\n"  // back edge
-    "  live in: (00001)\n"
-    "  live out: (00000)\n"
-    "  kill: (00000)\n"
+    "  live in: (0001)\n"
+    "  live out: (0001)\n"
+    "  kill: (0000)\n"
     "Block 6\n"  // return block
-    "  live in: (00001)\n"
-    "  live out: (00000)\n"
-    "  kill: (00000)\n"
+    "  live in: (0001)\n"
+    "  live out: (0000)\n"
+    "  kill: (0000)\n"
     "Block 7\n"  // exit block
-    "  live in: (00000)\n"
-    "  live out: (00000)\n"
-    "  kill: (00000)\n"
+    "  live in: (0000)\n"
+    "  live out: (0000)\n"
+    "  kill: (0000)\n"
     "Block 8\n"  // synthesized pre header
-    "  live in: (00000)\n"
-    "  live out: (00000)\n"
-    "  kill: (00010)\n";
+    "  live in: (0000)\n"
+    "  live out: (0001)\n"
+    "  kill: (0001)\n";
 
   const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
     Instruction::CONST_4 | 0 | 0,
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 8b56166..942aa23 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -103,7 +103,7 @@
 // Control-flow graph of a method. Contains a list of basic blocks.
 class HGraph : public ArenaObject<kArenaAllocMisc> {
  public:
-  HGraph(ArenaAllocator* arena, int start_instruction_id = 0)
+  HGraph(ArenaAllocator* arena, bool debuggable = false, int start_instruction_id = 0)
       : arena_(arena),
         blocks_(arena, kDefaultNumberOfBlocks),
         reverse_post_order_(arena, kDefaultNumberOfBlocks),
@@ -114,6 +114,7 @@
         number_of_in_vregs_(0),
         temporaries_vreg_slots_(0),
         has_array_accesses_(false),
+        debuggable_(debuggable),
         current_instruction_id_(start_instruction_id) {}
 
   ArenaAllocator* GetArena() const { return arena_; }
@@ -208,6 +209,8 @@
     has_array_accesses_ = value;
   }
 
+  bool IsDebuggable() const { return debuggable_; }
+
   HNullConstant* GetNullConstant();
 
  private:
@@ -248,6 +251,11 @@
   // Has array accesses. We can totally skip BCE if it's false.
   bool has_array_accesses_;
 
+  // Indicates whether the graph should be compiled in a way that
+  // ensures full debuggability. If false, we can apply more
+  // aggressive optimizations that may limit the level of debugging.
+  const bool debuggable_;
+
   // The current id to assign to a newly added instruction. See HInstruction.id_.
   int32_t current_instruction_id_;
 
@@ -2498,6 +2506,19 @@
     inputs_.SetSize(number_of_inputs);
   }
 
+  // Returns a type equivalent to the given `type`, but that a `HPhi` can hold.
+  static Primitive::Type ToPhiType(Primitive::Type type) {
+    switch (type) {
+      case Primitive::kPrimBoolean:
+      case Primitive::kPrimByte:
+      case Primitive::kPrimShort:
+      case Primitive::kPrimChar:
+        return Primitive::kPrimInt;
+      default:
+        return type;
+    }
+  }
+
   size_t InputCount() const OVERRIDE { return inputs_.Size(); }
 
   void AddInput(HInstruction* input);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index eb98424..3470595 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -298,8 +298,6 @@
                              const DexCompilationUnit& dex_compilation_unit,
                              PassInfoPrinter* pass_info_printer,
                              StackHandleScopeCollection* handles) {
-  SsaRedundantPhiElimination redundant_phi(graph);
-  SsaDeadPhiElimination dead_phi(graph);
   HDeadCodeElimination dce(graph);
   HConstantFolding fold1(graph);
   InstructionSimplifier simplify1(graph, stats);
@@ -317,8 +315,6 @@
   IntrinsicsRecognizer intrinsics(graph, dex_compilation_unit.GetDexFile(), driver);
 
   HOptimization* optimizations[] = {
-    &redundant_phi,
-    &dead_phi,
     &intrinsics,
     &dce,
     &fold1,
@@ -461,7 +457,8 @@
 
   ArenaPool pool;
   ArenaAllocator arena(&pool);
-  HGraph* graph = new (&arena) HGraph(&arena);
+  HGraph* graph = new (&arena) HGraph(
+      &arena, compiler_driver->GetCompilerOptions().GetDebuggable());
 
   // For testing purposes, we put a special marker on method names that should be compiled
   // with this compiler. This makes sure we're not regressing.
diff --git a/compiler/optimizing/primitive_type_propagation.cc b/compiler/optimizing/primitive_type_propagation.cc
index fe23fcf..c20c8a1 100644
--- a/compiler/optimizing/primitive_type_propagation.cc
+++ b/compiler/optimizing/primitive_type_propagation.cc
@@ -33,7 +33,7 @@
       // to merge with a void type, we should use the existing one.
       return new_type == Primitive::kPrimVoid
           ? existing
-          : new_type;
+          : HPhi::ToPhiType(new_type);
   }
 }
 
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 3dc7505..1a8e784 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -22,6 +22,158 @@
 
 namespace art {
 
+/**
+ * A debuggable application may require to reviving phis, to ensure their
+ * associated DEX register is available to a debugger. This class implements
+ * the logic for statement (c) of the SsaBuilder (see ssa_builder.h). It
+ * also makes sure that phis with incompatible input types are not revived
+ * (statement (b) of the SsaBuilder).
+ *
+ * This phase must be run after detecting dead phis through the
+ * DeadPhiElimination phase, and before deleting the dead phis.
+ */
+class DeadPhiHandling : public ValueObject {
+ public:
+  explicit DeadPhiHandling(HGraph* graph)
+      : graph_(graph), worklist_(graph->GetArena(), kDefaultWorklistSize) {}
+
+  void Run();
+
+ private:
+  void VisitBasicBlock(HBasicBlock* block);
+  void ProcessWorklist();
+  void AddToWorklist(HPhi* phi);
+  void AddDependentInstructionsToWorklist(HPhi* phi);
+  bool UpdateType(HPhi* phi);
+
+  HGraph* const graph_;
+  GrowableArray<HPhi*> worklist_;
+
+  static constexpr size_t kDefaultWorklistSize = 8;
+
+  DISALLOW_COPY_AND_ASSIGN(DeadPhiHandling);
+};
+
+bool DeadPhiHandling::UpdateType(HPhi* phi) {
+  Primitive::Type existing = phi->GetType();
+  DCHECK(phi->IsLive());
+
+  bool conflict = false;
+  Primitive::Type new_type = existing;
+  for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
+    HInstruction* input = phi->InputAt(i);
+    if (input->IsPhi() && input->AsPhi()->IsDead()) {
+      // We are doing a reverse post order visit of the graph, reviving
+      // phis that have environment uses and updating their types. If an
+      // input is a phi, and it is dead (because its input types are
+      // conflicting), this phi must be marked dead as well.
+      conflict = true;
+      break;
+    }
+    Primitive::Type input_type = HPhi::ToPhiType(input->GetType());
+
+    // The only acceptable transitions are:
+    // - From void to typed: first time we update the type of this phi.
+    // - From int to reference (or reference to int): the phi has to change
+    //   to reference type. If the integer input cannot be converted to a
+    //   reference input, the phi will remain dead.
+    if (new_type == Primitive::kPrimVoid) {
+      new_type = input_type;
+    } else if (new_type == Primitive::kPrimNot && input_type == Primitive::kPrimInt) {
+      HInstruction* equivalent = SsaBuilder::GetReferenceTypeEquivalent(input);
+      if (equivalent == nullptr) {
+        conflict = true;
+        break;
+      } else {
+        phi->ReplaceInput(equivalent, i);
+        if (equivalent->IsPhi()) {
+          DCHECK_EQ(equivalent->GetType(), Primitive::kPrimNot);
+          // We created a new phi, but that phi has the same inputs as the old phi. We
+          // add it to the worklist to ensure its inputs can also be converted to reference.
+          // If not, it will remain dead, and the algorithm will make the current phi dead
+          // as well.
+          equivalent->AsPhi()->SetLive();
+          AddToWorklist(equivalent->AsPhi());
+        }
+      }
+    } else if (new_type == Primitive::kPrimInt && input_type == Primitive::kPrimNot) {
+      new_type = Primitive::kPrimNot;
+      // Start over, we may request reference equivalents for the inputs of the phi.
+      i = -1;
+    } else if (new_type != input_type) {
+      conflict = true;
+      break;
+    }
+  }
+
+  if (conflict) {
+    phi->SetType(Primitive::kPrimVoid);
+    phi->SetDead();
+    return true;
+  } else {
+    DCHECK(phi->IsLive());
+    phi->SetType(new_type);
+    return existing != new_type;
+  }
+}
+
+void DeadPhiHandling::VisitBasicBlock(HBasicBlock* block) {
+  for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
+    HPhi* phi = it.Current()->AsPhi();
+    if (phi->IsDead() && phi->HasEnvironmentUses()) {
+      phi->SetLive();
+      if (block->IsLoopHeader()) {
+        // Give a type to the loop phi, to guarantee convergence of the algorithm.
+        phi->SetType(phi->InputAt(0)->GetType());
+        AddToWorklist(phi);
+      } else {
+        // Because we are doing a reverse post order visit, all inputs of
+        // this phi have been visited and therefore had their (initial) type set.
+        UpdateType(phi);
+      }
+    }
+  }
+}
+
+void DeadPhiHandling::ProcessWorklist() {
+  while (!worklist_.IsEmpty()) {
+    HPhi* instruction = worklist_.Pop();
+    // Note that the same equivalent phi can be added multiple times in the work list, if
+    // used by multiple phis. The first call to `UpdateType` will know whether the phi is
+    // dead or live.
+    if (instruction->IsLive() && UpdateType(instruction)) {
+      AddDependentInstructionsToWorklist(instruction);
+    }
+  }
+}
+
+void DeadPhiHandling::AddToWorklist(HPhi* instruction) {
+  DCHECK(instruction->IsLive());
+  worklist_.Add(instruction);
+}
+
+void DeadPhiHandling::AddDependentInstructionsToWorklist(HPhi* instruction) {
+  for (HUseIterator<HInstruction*> it(instruction->GetUses()); !it.Done(); it.Advance()) {
+    HPhi* phi = it.Current()->GetUser()->AsPhi();
+    if (phi != nullptr && !phi->IsDead()) {
+      AddToWorklist(phi);
+    }
+  }
+}
+
+void DeadPhiHandling::Run() {
+  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+    VisitBasicBlock(it.Current());
+  }
+  ProcessWorklist();
+}
+
+static bool IsPhiEquivalentOf(HInstruction* instruction, HPhi* phi) {
+  return instruction != nullptr
+      && instruction->IsPhi()
+      && instruction->AsPhi()->GetRegNumber() == phi->GetRegNumber();
+}
+
 void SsaBuilder::BuildSsa() {
   // 1) Visit in reverse post order. We need to have all predecessors of a block visited
   // (with the exception of loops) in order to create the right environment for that
@@ -47,11 +199,9 @@
   // our code generator will complain if the inputs of a phi do not have the same
   // type. The marking allows the type propagation to know which phis it needs
   // to handle. We mark but do not eliminate: the elimination will be done in
-  // step 5).
-  {
-    SsaDeadPhiElimination dead_phis(GetGraph());
-    dead_phis.MarkDeadPhis();
-  }
+  // step 8).
+  SsaDeadPhiElimination dead_phis(GetGraph());
+  dead_phis.MarkDeadPhis();
 
   // 4) Propagate types of phis. At this point, phis are typed void in the general
   // case, or float/double/reference when we created an equivalent phi. So we
@@ -59,17 +209,58 @@
   PrimitiveTypePropagation type_propagation(GetGraph());
   type_propagation.Run();
 
-  // 5) Step 4) changes inputs of phis which may lead to dead phis again. We re-run
-  // the algorithm and this time elimimates them.
-  // TODO: Make this work with debug info and reference liveness. We currently
-  // eagerly remove phis used in environments.
-  {
-    SsaDeadPhiElimination dead_phis(GetGraph());
-    dead_phis.Run();
+  // 5) Now that the graph is correclty typed, we can get rid of redundant phis.
+  // Note that we cannot do this phase before type propagation, otherwise
+  // we could get rid of phi equivalents, whose presence is a requirement for the
+  // type propagation phase. Note that this is to satisfy statement (a) of the
+  // SsaBuilder (see ssa_builder.h).
+  SsaRedundantPhiElimination redundant_phi(GetGraph());
+  redundant_phi.Run();
+
+  // 6) Make sure environments use the right phi "equivalent": a phi marked dead
+  // can have a phi equivalent that is not dead. We must therefore update
+  // all environment uses of the dead phi to use its equivalent. Note that there
+  // can be multiple phis for the same Dex register that are live (for example
+  // when merging constants), in which case it is OK for the environments
+  // to just reference one.
+  for (HReversePostOrderIterator it(*GetGraph()); !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+    for (HInstructionIterator it_phis(block->GetPhis()); !it_phis.Done(); it_phis.Advance()) {
+      HPhi* phi = it_phis.Current()->AsPhi();
+      // If the phi is not dead, or has no environment uses, there is nothing to do.
+      if (!phi->IsDead() || !phi->HasEnvironmentUses()) continue;
+      HInstruction* next = phi->GetNext();
+      if (!IsPhiEquivalentOf(next, phi)) continue;
+      if (next->AsPhi()->IsDead()) {
+        // If the phi equivalent is dead, check if there is another one.
+        next = next->GetNext();
+        if (!IsPhiEquivalentOf(next, phi)) continue;
+        // There can be at most two phi equivalents.
+        DCHECK(!IsPhiEquivalentOf(next->GetNext(), phi));
+        if (next->AsPhi()->IsDead()) continue;
+      }
+      // We found a live phi equivalent. Update the environment uses of `phi` with it.
+      phi->ReplaceWith(next);
+    }
   }
 
-  // 6) Clear locals.
-  // TODO: Move this to a dead code eliminator phase.
+  // 7) Deal with phis to guarantee liveness of phis in case of a debuggable
+  // application. This is for satisfying statement (c) of the SsaBuilder
+  // (see ssa_builder.h).
+  if (GetGraph()->IsDebuggable()) {
+    DeadPhiHandling dead_phi_handler(GetGraph());
+    dead_phi_handler.Run();
+  }
+
+  // 8) Now that the right phis are used for the environments, and we
+  // have potentially revive dead phis in case of a debuggable application,
+  // we can eliminate phis we do not need. Regardless of the debuggable status,
+  // this phase is necessary for statement (b) of the SsaBuilder (see ssa_builder.h),
+  // as well as for the code generation, which does not deal with phis of conflicting
+  // input types.
+  dead_phis.EliminateDeadPhis();
+
+  // 9) Clear locals.
   for (HInstructionIterator it(GetGraph()->GetEntryBlock()->GetInstructions());
        !it.Done();
        it.Advance()) {
@@ -257,12 +448,12 @@
 }
 
 HInstruction* SsaBuilder::GetReferenceTypeEquivalent(HInstruction* value) {
-  if (value->IsIntConstant()) {
-    DCHECK_EQ(value->AsIntConstant()->GetValue(), 0);
+  if (value->IsIntConstant() && value->AsIntConstant()->GetValue() == 0) {
     return value->GetBlock()->GetGraph()->GetNullConstant();
-  } else {
-    DCHECK(value->IsPhi());
+  } else if (value->IsPhi()) {
     return GetFloatDoubleOrReferenceEquivalentOfPhi(value->AsPhi(), Primitive::kPrimNot);
+  } else {
+    return nullptr;
   }
 }
 
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index f50da46..b414fb2 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -24,6 +24,26 @@
 
 static constexpr int kDefaultNumberOfLoops = 2;
 
+/**
+ * Transforms a graph into SSA form. The liveness guarantees of
+ * this transformation are listed below. A DEX register
+ * being killed means its value at a given position in the code
+ * will not be available to its environment uses. A merge in the
+ * following text is materialized as a `HPhi`.
+ *
+ * (a) Dex registers that do not require merging (that is, they do not
+ *     have different values at a join block) are available to all their
+ *     environment uses.
+ *
+ * (b) Dex registers that require merging, and the merging gives
+ *     incompatible types, will be killed for environment uses of that merge.
+ *
+ * (c) When the `debuggable` flag is passed to the compiler, Dex registers
+ *     that require merging and have a proper type after the merge, are
+ *     available to all their environment uses. If the `debuggable` flag
+ *     is not set, values of Dex registers only used by environments
+ *     are killed.
+ */
 class SsaBuilder : public HGraphVisitor {
  public:
   explicit SsaBuilder(HGraph* graph)
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index a05b38c9..00c241b 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -332,8 +332,8 @@
   const char* expected =
     "BasicBlock 0, succ: 1\n"
     "  0: IntConstant 0 [4, 4]\n"
-    "  1: IntConstant 4 [14]\n"
-    "  2: IntConstant 5 [14]\n"
+    "  1: IntConstant 4 [13]\n"
+    "  2: IntConstant 5 [13]\n"
     "  3: Goto\n"
     "BasicBlock 1, pred: 0, succ: 3, 2\n"
     "  4: Equal(0, 0) [5]\n"
@@ -343,18 +343,17 @@
     "BasicBlock 3, pred: 1, succ: 8\n"
     "  7: Goto\n"
     "BasicBlock 4, pred: 8, 5, succ: 6, 5\n"
-    "  8: Phi(14, 8) [8, 12, 9, 9]\n"
-    "  9: Equal(8, 8) [10]\n"
-    "  10: If(9)\n"
+    "  8: Equal(13, 13) [9]\n"
+    "  9: If(8)\n"
     "BasicBlock 5, pred: 4, succ: 4\n"
-    "  11: Goto\n"
+    "  10: Goto\n"
     "BasicBlock 6, pred: 4, succ: 7\n"
-    "  12: Return(8)\n"
+    "  11: Return(13)\n"
     "BasicBlock 7, pred: 6\n"
-    "  13: Exit\n"
+    "  12: Exit\n"
     "BasicBlock 8, pred: 2, 3, succ: 4\n"
-    "  14: Phi(1, 2) [8]\n"
-    "  15: Goto\n";
+    "  13: Phi(1, 2) [8, 8, 11]\n"
+    "  14: Goto\n";
 
   const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
     Instruction::CONST_4 | 0 | 0,
diff --git a/runtime/Android.mk b/runtime/Android.mk
index c5cf890..af0c479 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -135,6 +135,7 @@
   native/sun_misc_Unsafe.cc \
   oat.cc \
   oat_file.cc \
+  oat_file_assistant.cc \
   object_lock.cc \
   offsets.cc \
   os_linux.cc \
@@ -151,7 +152,6 @@
   thread.cc \
   thread_list.cc \
   thread_pool.cc \
-  throw_location.cc \
   trace.cc \
   transaction.cc \
   profiler.cc \
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index ee70fe7..92f4ebe 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -39,7 +39,7 @@
 // impacts where samples will occur. Reducing the count as much as possible improves profiler
 // accuracy in tools like traceview.
 // TODO: get a compiler that can do a proper job of loop optimization and remove this.
-#define SUSPEND_CHECK_INTERVAL 1000
+#define SUSPEND_CHECK_INTERVAL 96
 #endif
 
 #if defined(__cplusplus)
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index e6380bf..70d138d 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -26,8 +26,9 @@
 
 namespace art {
 
-// Memmap is a bit slower than malloc according to my measurements.
-static constexpr bool kUseMemMap = false;
+// Memmap is a bit slower than malloc to allocate, but this is mitigated by the arena pool which
+// only allocates few arenas and recycles them afterwards.
+static constexpr bool kUseMemMap = true;
 static constexpr bool kUseMemSet = true && kUseMemMap;
 static constexpr size_t kValgrindRedZoneBytes = 8;
 constexpr size_t Arena::kDefaultSize;
@@ -129,8 +130,8 @@
       next_(nullptr) {
   if (kUseMemMap) {
     std::string error_msg;
-    map_ = MemMap::MapAnonymous("dalvik-arena", nullptr, size, PROT_READ | PROT_WRITE, false, false,
-                                &error_msg);
+    map_ = MemMap::MapAnonymous("dalvik-LinearAlloc", nullptr, size, PROT_READ | PROT_WRITE, false,
+                                false, &error_msg);
     CHECK(map_ != nullptr) << error_msg;
     memory_ = map_->Begin();
     size_ = map_->Size();
@@ -148,8 +149,15 @@
   }
 }
 
+void Arena::Release() {
+  if (kUseMemMap && bytes_allocated_ > 0) {
+    map_->MadviseDontNeedAndZero();
+    bytes_allocated_ = 0;
+  }
+}
+
 void Arena::Reset() {
-  if (bytes_allocated_) {
+  if (bytes_allocated_ > 0) {
     if (kUseMemSet || !kUseMemMap) {
       memset(Begin(), 0, bytes_allocated_);
     } else {
@@ -162,6 +170,9 @@
 ArenaPool::ArenaPool()
     : lock_("Arena pool lock"),
       free_arenas_(nullptr) {
+  if (kUseMemMap) {
+    MemMap::Init();
+  }
 }
 
 ArenaPool::~ArenaPool() {
@@ -189,6 +200,13 @@
   return ret;
 }
 
+void ArenaPool::TrimMaps() {
+  MutexLock lock(Thread::Current(), lock_);
+  for (auto* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
+    arena->Release();
+  }
+}
+
 size_t ArenaPool::GetBytesAllocated() const {
   size_t total = 0;
   MutexLock lock(Thread::Current(), lock_);
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index 9237391..04ca3ea 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -118,7 +118,10 @@
   static constexpr size_t kDefaultSize = 128 * KB;
   explicit Arena(size_t size = kDefaultSize);
   ~Arena();
+  // Reset is for pre-use and uses memset for performance.
   void Reset();
+  // Release is used inbetween uses and uses madvise for memory usage.
+  void Release();
   uint8_t* Begin() {
     return memory_;
   }
@@ -160,6 +163,9 @@
   Arena* AllocArena(size_t size) LOCKS_EXCLUDED(lock_);
   void FreeArenaChain(Arena* first) LOCKS_EXCLUDED(lock_);
   size_t GetBytesAllocated() const LOCKS_EXCLUDED(lock_);
+  // Trim the maps in arenas by madvising, used by JIT to reduce memory usage. This only works if
+  // kUseMemMap is true.
+  void TrimMaps() LOCKS_EXCLUDED(lock_);
 
  private:
   mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
diff --git a/runtime/base/logging.h b/runtime/base/logging.h
index 3d007ba..014f4ab 100644
--- a/runtime/base/logging.h
+++ b/runtime/base/logging.h
@@ -45,6 +45,7 @@
   bool jit;
   bool jni;
   bool monitor;
+  bool oat;
   bool profiler;
   bool signals;
   bool startup;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f0c8819..700e1ad 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -48,6 +48,7 @@
 #include "leb128.h"
 #include "oat.h"
 #include "oat_file.h"
+#include "oat_file_assistant.h"
 #include "object_lock.h"
 #include "mirror/art_field-inl.h"
 #include "mirror/art_method-inl.h"
@@ -82,8 +83,7 @@
   va_list args;
   va_start(args, fmt);
   Thread* self = Thread::Current();
-  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-  self->ThrowNewExceptionV(throw_location, "Ljava/lang/NoClassDefFoundError;", fmt, args);
+  self->ThrowNewExceptionV("Ljava/lang/NoClassDefFoundError;", fmt, args);
   va_end(args);
 }
 
@@ -105,14 +105,13 @@
     mirror::Throwable* pre_allocated = runtime->GetPreAllocatedNoClassDefFoundError();
     self->SetException(pre_allocated);
   } else {
-    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
     if (c->GetVerifyErrorClass() != NULL) {
       // TODO: change the verifier to store an _instance_, with a useful detail message?
       std::string temp;
-      self->ThrowNewException(throw_location, c->GetVerifyErrorClass()->GetDescriptor(&temp),
+      self->ThrowNewException(c->GetVerifyErrorClass()->GetDescriptor(&temp),
                               PrettyDescriptor(c).c_str());
     } else {
-      self->ThrowNewException(throw_location, "Ljava/lang/NoClassDefFoundError;",
+      self->ThrowNewException("Ljava/lang/NoClassDefFoundError;",
                               PrettyDescriptor(c).c_str());
     }
   }
@@ -141,9 +140,7 @@
 
   // We only wrap non-Error exceptions; an Error can just be used as-is.
   if (!is_error) {
-    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-    self->ThrowNewWrappedException(throw_location, "Ljava/lang/ExceptionInInitializerError;",
-                                   nullptr);
+    self->ThrowNewWrappedException("Ljava/lang/ExceptionInInitializerError;", nullptr);
   }
   VlogClassInitializationFailure(klass);
 }
@@ -662,77 +659,6 @@
   }
 }
 
-bool ClassLinker::GenerateOatFile(const char* dex_filename,
-                                  int oat_fd,
-                                  const char* oat_cache_filename,
-                                  std::string* error_msg) {
-  Locks::mutator_lock_->AssertNotHeld(Thread::Current());  // Avoid starving GC.
-  std::string dex2oat(Runtime::Current()->GetCompilerExecutable());
-
-  gc::Heap* heap = Runtime::Current()->GetHeap();
-  std::string boot_image_option("--boot-image=");
-  if (heap->GetImageSpace() == nullptr) {
-    // TODO If we get a dex2dex compiler working we could maybe use that, OTOH since we are likely
-    // out of space anyway it might not matter.
-    *error_msg = StringPrintf("Cannot create oat file for '%s' because we are running "
-                              "without an image.", dex_filename);
-    return false;
-  }
-  boot_image_option += heap->GetImageSpace()->GetImageLocation();
-
-  std::string dex_file_option("--dex-file=");
-  dex_file_option += dex_filename;
-
-  std::string oat_fd_option("--oat-fd=");
-  StringAppendF(&oat_fd_option, "%d", oat_fd);
-
-  std::string oat_location_option("--oat-location=");
-  oat_location_option += oat_cache_filename;
-
-  std::vector<std::string> argv;
-  argv.push_back(dex2oat);
-  argv.push_back("--runtime-arg");
-  argv.push_back("-classpath");
-  argv.push_back("--runtime-arg");
-  argv.push_back(Runtime::Current()->GetClassPathString());
-
-  Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
-
-  if (!Runtime::Current()->IsVerificationEnabled()) {
-    argv.push_back("--compiler-filter=verify-none");
-  }
-
-  if (Runtime::Current()->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_option);
-  argv.push_back(dex_file_option);
-  argv.push_back(oat_fd_option);
-  argv.push_back(oat_location_option);
-  const std::vector<std::string>& compiler_options = Runtime::Current()->GetCompilerOptions();
-  for (size_t i = 0; i < compiler_options.size(); ++i) {
-    argv.push_back(compiler_options[i].c_str());
-  }
-
-  if (!Exec(argv, error_msg)) {
-    // Manually delete the file. Ensures there is no garbage left over if the process unexpectedly
-    // died. Ignore unlink failure, propagate the original error.
-    TEMP_FAILURE_RETRY(unlink(oat_cache_filename));
-    return false;
-  }
-
-  return true;
-}
-
 const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
   WriterMutexLock mu(Thread::Current(), dex_lock_);
   if (kIsDebugBuild) {
@@ -782,504 +708,81 @@
   return nullptr;
 }
 
+std::vector<std::unique_ptr<const DexFile>> ClassLinker::OpenDexFilesFromOat(
+    const char* dex_location, const char* oat_location,
+    std::vector<std::string>* error_msgs) {
+  CHECK(error_msgs != nullptr);
 
-// Loads all multi dex files from the given oat file returning true on success.
-//
-// Parameters:
-//   oat_file - the oat file to load from
-//   dex_location - the dex location used to generate the oat file
-//   dex_location_checksum - the checksum of the dex_location (may be null for pre-opted files)
-//   generated - whether or not the oat_file existed before or was just (re)generated
-//   error_msgs - any error messages will be appended here
-//   dex_files - the loaded dex_files will be appended here (only if the loading succeeds)
-static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file,
-                                         const char* dex_location,
-                                         const uint32_t* dex_location_checksum,
-                                         bool generated,
-                                         std::vector<std::string>* error_msgs,
-                                         std::vector<std::unique_ptr<const DexFile>>* dex_files) {
-  if (oat_file == nullptr) {
-    return false;
+  // Verify we aren't holding the mutator lock, which could starve GC if we
+  // have to generate or relocate an oat file.
+  Locks::mutator_lock_->AssertNotHeld(Thread::Current());
+
+  OatFileAssistant oat_file_assistant(dex_location, oat_location, kRuntimeISA,
+     !Runtime::Current()->IsAotCompiler());
+
+  // Lock the target oat location to avoid races generating and loading the
+  // oat file.
+  std::string error_msg;
+  if (!oat_file_assistant.Lock(&error_msg)) {
+    // Don't worry too much if this fails. If it does fail, it's unlikely we
+    // can generate an oat file anyway.
+    VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;
   }
 
-  size_t old_size = dex_files->size();  // To rollback on error.
-
-  bool success = true;
-  for (size_t i = 0; success; ++i) {
-    std::string next_name_str = DexFile::GetMultiDexClassesDexName(i, dex_location);
-    const char* next_name = next_name_str.c_str();
-
-    uint32_t next_location_checksum;
-    uint32_t* next_location_checksum_pointer = &next_location_checksum;
-    std::string error_msg;
-    if ((i == 0) && (strcmp(next_name, dex_location) == 0)) {
-      // When i=0 the multidex name should be the same as the location name. We already have the
-      // checksum it so we don't need to recompute it.
-      if (dex_location_checksum == nullptr) {
-        next_location_checksum_pointer = nullptr;
-      } else {
-        next_location_checksum = *dex_location_checksum;
-      }
-    } else if (!DexFile::GetChecksum(next_name, next_location_checksum_pointer, &error_msg)) {
-      DCHECK_EQ(false, i == 0 && generated);
-      next_location_checksum_pointer = nullptr;
-    }
-
-    const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(next_name, nullptr, false);
-
-    if (oat_dex_file == nullptr) {
-      if (i == 0 && generated) {
-        error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out "
-                                 " file'%s'", dex_location, next_location_checksum,
-                                 oat_file->GetLocation().c_str());
-        error_msgs->push_back(error_msg);
-      }
-      break;  // Not found, done.
-    }
-
-    // Checksum test. Test must succeed when generated.
-    success = !generated;
-    if (next_location_checksum_pointer != nullptr) {
-      success = next_location_checksum == oat_dex_file->GetDexFileLocationChecksum();
-    }
-
-    if (success) {
-      std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
-      if (dex_file.get() == nullptr) {
-        success = false;
-        error_msgs->push_back(error_msg);
-      } else {
-        dex_files->push_back(std::move(dex_file));
+  // Check if we already have an up-to-date oat file open.
+  const OatFile* source_oat_file = nullptr;
+  {
+    ReaderMutexLock mu(Thread::Current(), dex_lock_);
+    for (const OatFile* oat_file : oat_files_) {
+      CHECK(oat_file != nullptr);
+      if (oat_file_assistant.GivenOatFileIsUpToDate(*oat_file)) {
+        source_oat_file = oat_file;
+        break;
       }
     }
-
-    // When we generated the file, we expect success, or something is terribly wrong.
-    CHECK_EQ(false, generated && !success)
-        << "dex_location=" << next_name << " oat_location=" << oat_file->GetLocation().c_str()
-        << std::hex << " dex_location_checksum=" << next_location_checksum
-        << " OatDexFile::GetLocationChecksum()=" << oat_dex_file->GetDexFileLocationChecksum();
   }
 
-  if (dex_files->size() == old_size) {
-    success = false;  // We did not even find classes.dex
-  }
-
-  if (success) {
-    return true;
-  } else {
-    dex_files->erase(dex_files->begin() + old_size, dex_files->end());
-    return false;
-  }
-}
-
-// Multidex files make it possible that some, but not all, dex files can be broken/outdated. This
-// complicates the loading process, as we should not use an iterative loading process, because that
-// would register the oat file and dex files that come before the broken one. Instead, check all
-// multidex ahead of time.
-bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
-                                      std::vector<std::string>* error_msgs,
-                                      std::vector<std::unique_ptr<const DexFile>>* dex_files) {
-  // 1) Check whether we have an open oat file.
-  // This requires a dex checksum, use the "primary" one.
-  uint32_t dex_location_checksum;
-  uint32_t* dex_location_checksum_pointer = &dex_location_checksum;
-  bool have_checksum = true;
-  std::string checksum_error_msg;
-  if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) {
-    // This happens for pre-opted files since the corresponding dex files are no longer on disk.
-    dex_location_checksum_pointer = nullptr;
-    have_checksum = false;
-  }
-
-  bool needs_registering = false;
-
-  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.
-
-  // In case the oat file is not open, we play a locking game here so
-  // that if two different processes race to load and register or generate
-  // (or worse, one tries to open a partial generated file) we will be okay.
-  // This is actually common with apps that use DexClassLoader to work
-  // around the dex method reference limit and that have a background
-  // service running in a separate process.
-  ScopedFlock scoped_flock;
-
-  if (open_oat_file.get() == nullptr) {
-    if (oat_location != nullptr) {
-      // Can only do this if we have a checksum, else error.
-      if (!have_checksum) {
-        error_msgs->push_back(checksum_error_msg);
-        return false;
-      }
-
-      std::string error_msg;
-
-      // We are loading or creating one in the future. Time to set up the file lock.
-      if (!scoped_flock.Init(oat_location, &error_msg)) {
-        error_msgs->push_back(error_msg);
-        return false;
-      }
-
-      // TODO Caller specifically asks for this oat_location. We should honor it. Probably?
-      open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum,
-                                                             oat_location, &error_msg));
-
-      if (open_oat_file.get() == nullptr) {
-        std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s",
-                                                dex_location, oat_location, error_msg.c_str());
-        VLOG(class_linker) << compound_msg;
-        error_msgs->push_back(compound_msg);
-      }
-    } else {
-      // TODO: What to lock here?
-      bool obsolete_file_cleanup_failed;
-      open_oat_file.reset(FindOatFileContainingDexFileFromDexLocation(dex_location,
-                                                                      dex_location_checksum_pointer,
-                                                                      kRuntimeISA, error_msgs,
-                                                                      &obsolete_file_cleanup_failed));
-      // There's no point in going forward and eventually try to regenerate the
-      // file if we couldn't remove the obsolete one. Mostly likely we will fail
-      // with the same error when trying to write the new file.
-      // TODO: should we maybe do this only when we get permission issues? (i.e. EACCESS).
-      if (obsolete_file_cleanup_failed) {
-        return false;
-      }
+  // If we didn't have an up-to-date oat file open, try to load one from disk.
+  if (source_oat_file == nullptr) {
+    // Update the oat file on disk if we can. This may fail, but that's okay.
+    // Best effort is all that matters here.
+    if (!oat_file_assistant.MakeUpToDate(&error_msg)) {
+      LOG(WARNING) << error_msg;
     }
-    needs_registering = true;
-  }
 
-  // 3) If we have an oat file, check all contained multidex files for our dex_location.
-  // Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument.
-  bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
-                                              dex_location_checksum_pointer,
-                                              false, error_msgs, dex_files);
-  if (success) {
-    const OatFile* oat_file = open_oat_file.release();  // Avoid deleting it.
-    if (needs_registering) {
-      // We opened the oat file, so we must register it.
-      RegisterOatFile(oat_file);
-    }
-    // If the file isn't executable we failed patchoat but did manage to get the dex files.
-    return oat_file->IsExecutable();
-  } else {
-    if (needs_registering) {
-      // We opened it, delete it.
-      open_oat_file.reset();
-    } else {
-      open_oat_file.release();  // Do not delete open oat files.
+    // Get the oat file on disk.
+    std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+    if (oat_file.get() != nullptr) {
+      source_oat_file = oat_file.release();
+      RegisterOatFile(source_oat_file);
     }
   }
 
-  // 4) If it's not the case (either no oat file or mismatches), regenerate and load.
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
 
-  // Need a checksum, fail else.
-  if (!have_checksum) {
-    error_msgs->push_back(checksum_error_msg);
-    return false;
-  }
-
-  // Look in cache location if no oat_location is given.
-  std::string cache_location;
-  if (oat_location == nullptr) {
-    // Use the dalvik cache.
-    const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA)));
-    cache_location = GetDalvikCacheFilenameOrDie(dex_location, dalvik_cache.c_str());
-    oat_location = cache_location.c_str();
-  }
-
-  bool has_flock = true;
-  // Definitely need to lock now.
-  if (!scoped_flock.HasFile()) {
-    std::string error_msg;
-    if (!scoped_flock.Init(oat_location, &error_msg)) {
-      error_msgs->push_back(error_msg);
-      has_flock = false;
+  // Load the dex files from the oat file.
+  if (source_oat_file != nullptr) {
+    dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
+    if (dex_files.empty()) {
+      error_msgs->push_back("Failed to open dex files from "
+          + source_oat_file->GetLocation());
     }
   }
 
-  if (Runtime::Current()->IsDex2OatEnabled() && has_flock && scoped_flock.HasFile()) {
-    // Create the oat file.
-    open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(),
-                                                    oat_location, error_msgs));
-  }
-
-  // Failed, bail.
-  if (open_oat_file.get() == nullptr) {
-    // dex2oat was disabled or crashed. Add the dex file in the list of dex_files to make progress.
+  // Fall back to running out of the original dex file if we couldn't load any
+  // dex_files from the oat file.
+  if (dex_files.empty()) {
     if (Runtime::Current()->IsDexFileFallbackEnabled()) {
-      std::string error_msg;
-      if (!DexFile::Open(dex_location, dex_location, &error_msg, dex_files)) {
-        error_msgs->push_back(error_msg);
+      if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
+        LOG(WARNING) << error_msg;
+        error_msgs->push_back("Failed to open dex files from "
+            + std::string(dex_location));
       }
     } else {
       error_msgs->push_back("Fallback mode disabled, skipping dex files.");
     }
-    return false;
   }
-
-  // Try to load again, but stronger checks.
-  success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
-                                         dex_location_checksum_pointer,
-                                         true, error_msgs, dex_files);
-  if (success) {
-    RegisterOatFile(open_oat_file.release());
-    return true;
-  } else {
-    return false;
-  }
-}
-
-const OatFile* ClassLinker::FindOatFileInOatLocationForDexFile(const char* dex_location,
-                                                               uint32_t dex_location_checksum,
-                                                               const char* oat_location,
-                                                               std::string* error_msg) {
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
-                                                  !Runtime::Current()->IsAotCompiler(), error_msg));
-  if (oat_file.get() == nullptr) {
-    *error_msg = StringPrintf("Failed to find existing oat file at %s: %s", oat_location,
-                              error_msg->c_str());
-    return nullptr;
-  }
-  Runtime* runtime = Runtime::Current();
-  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
-  if (image_space != nullptr) {
-    const ImageHeader& image_header = image_space->GetImageHeader();
-    uint32_t expected_image_oat_checksum = image_header.GetOatChecksum();
-    uint32_t actual_image_oat_checksum = oat_file->GetOatHeader().GetImageFileLocationOatChecksum();
-    if (expected_image_oat_checksum != actual_image_oat_checksum) {
-      *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat checksum of "
-                                "0x%x, found 0x%x", oat_location, expected_image_oat_checksum,
-                                actual_image_oat_checksum);
-      return nullptr;
-    }
-
-    uintptr_t expected_image_oat_offset = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
-    uint32_t actual_image_oat_offset = oat_file->GetOatHeader().GetImageFileLocationOatDataBegin();
-    if (expected_image_oat_offset != actual_image_oat_offset) {
-      *error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat offset %"
-                                PRIuPTR ", found %ud", oat_location, expected_image_oat_offset,
-                                actual_image_oat_offset);
-      return nullptr;
-    }
-    int32_t expected_patch_delta = image_header.GetPatchDelta();
-    int32_t actual_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
-    if (expected_patch_delta != actual_patch_delta) {
-      *error_msg = StringPrintf("Failed to find oat file at '%s' with expected patch delta %d, "
-                                " found %d", oat_location, expected_patch_delta, actual_patch_delta);
-      return nullptr;
-    }
-  }
-
-  const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
-                                                                    &dex_location_checksum);
-  if (oat_dex_file == nullptr) {
-    *error_msg = StringPrintf("Failed to find oat file at '%s' containing '%s'", oat_location,
-                              dex_location);
-    return nullptr;
-  }
-  uint32_t expected_dex_checksum = dex_location_checksum;
-  uint32_t actual_dex_checksum = oat_dex_file->GetDexFileLocationChecksum();
-  if (expected_dex_checksum != actual_dex_checksum) {
-    *error_msg = StringPrintf("Failed to find oat file at '%s' with expected dex checksum of 0x%x, "
-                              "found 0x%x", oat_location, expected_dex_checksum,
-                              actual_dex_checksum);
-    return nullptr;
-  }
-  std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(error_msg));
-  if (dex_file.get() != nullptr) {
-    return oat_file.release();
-  } else {
-    return nullptr;
-  }
-}
-
-const OatFile* ClassLinker::CreateOatFileForDexLocation(const char* dex_location,
-                                                        int fd, const char* oat_location,
-                                                        std::vector<std::string>* error_msgs) {
-  // Generate the output oat file for the dex file
-  VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location;
-  std::string error_msg;
-  if (!GenerateOatFile(dex_location, fd, oat_location, &error_msg)) {
-    CHECK(!error_msg.empty());
-    error_msgs->push_back(error_msg);
-    return nullptr;
-  }
-  std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
-                                                  !Runtime::Current()->IsAotCompiler(),
-                                                  &error_msg));
-  if (oat_file.get() == nullptr) {
-    std::string compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s",
-                                            oat_location, error_msg.c_str());
-    error_msgs->push_back(compound_msg);
-    return nullptr;
-  }
-
-  return oat_file.release();
-}
-
-bool ClassLinker::VerifyOatImageChecksum(const OatFile* oat_file,
-                                         const InstructionSet instruction_set) {
-  Runtime* runtime = Runtime::Current();
-  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
-  if (image_space == nullptr) {
-    return false;
-  }
-  uint32_t image_oat_checksum = 0;
-  if (instruction_set == kRuntimeISA) {
-    const ImageHeader& image_header = image_space->GetImageHeader();
-    image_oat_checksum = image_header.GetOatChecksum();
-  } else {
-    std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
-        image_space->GetImageLocation().c_str(), instruction_set));
-    image_oat_checksum = image_header->GetOatChecksum();
-  }
-  return oat_file->GetOatHeader().GetImageFileLocationOatChecksum() == image_oat_checksum;
-}
-
-bool ClassLinker::VerifyOatChecksums(const OatFile* oat_file,
-                                     const InstructionSet instruction_set,
-                                     std::string* error_msg) {
-  Runtime* runtime = Runtime::Current();
-  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
-  if (image_space == nullptr) {
-    *error_msg = "No image space for verification against";
-    return false;
-  }
-
-  // If the requested instruction set is the same as the current runtime,
-  // we can use the checksums directly. If it isn't, we'll have to read the
-  // image header from the image for the right instruction set.
-  uint32_t image_oat_checksum = 0;
-  uintptr_t image_oat_data_begin = 0;
-  int32_t image_patch_delta = 0;
-  if (instruction_set == runtime->GetInstructionSet()) {
-    const ImageHeader& image_header = image_space->GetImageHeader();
-    image_oat_checksum = image_header.GetOatChecksum();
-    image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
-    image_patch_delta = image_header.GetPatchDelta();
-  } else {
-    std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
-        image_space->GetImageLocation().c_str(), instruction_set));
-    image_oat_checksum = image_header->GetOatChecksum();
-    image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin());
-    image_patch_delta = image_header->GetPatchDelta();
-  }
-  const OatHeader& oat_header = oat_file->GetOatHeader();
-  bool ret = (oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum);
-
-  // If the oat file is PIC, it doesn't care if/how image was relocated. Ignore these checks.
-  if (!oat_file->IsPic()) {
-    ret = ret && (oat_header.GetImagePatchDelta() == image_patch_delta)
-              && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin);
-  }
-  if (!ret) {
-    *error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d, %d) with (0x%x, %" PRIdPTR ", %d)",
-                              oat_file->GetLocation().c_str(),
-                              oat_file->GetOatHeader().GetImageFileLocationOatChecksum(),
-                              oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(),
-                              oat_file->GetOatHeader().GetImagePatchDelta(),
-                              image_oat_checksum, image_oat_data_begin, image_patch_delta);
-  }
-  return ret;
-}
-
-bool ClassLinker::VerifyOatAndDexFileChecksums(const OatFile* oat_file,
-                                               const char* dex_location,
-                                               uint32_t dex_location_checksum,
-                                               const InstructionSet instruction_set,
-                                               std::string* error_msg) {
-  if (!VerifyOatChecksums(oat_file, instruction_set, error_msg)) {
-    return false;
-  }
-
-  const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
-                                                                    &dex_location_checksum);
-  if (oat_dex_file == nullptr) {
-    *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);
-    for (const OatFile::OatDexFile* oat_dex_file_in : 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_in->GetDexFileLocation().c_str(),
-                                  oat_dex_file_in->GetDexFileLocationChecksum());
-    }
-    return false;
-  }
-
-  DCHECK_EQ(dex_location_checksum, oat_dex_file->GetDexFileLocationChecksum());
-  return true;
-}
-
-bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file,
-                                       const char* dex_location,
-                                       const uint32_t* dex_location_checksum,
-                                       std::string* error_msg) {
-  CHECK(oat_file != nullptr);
-  CHECK(dex_location != nullptr);
-  std::unique_ptr<const DexFile> dex_file;
-  if (dex_location_checksum == nullptr) {
-    // If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is
-    // up-to-date. This is the common case in user builds for jar's and apk's in the /system
-    // directory.
-    const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, nullptr);
-    if (oat_dex_file == nullptr) {
-      *error_msg = StringPrintf("Dex checksum mismatch for location '%s' and failed to find oat "
-                                "dex file '%s': %s", oat_file->GetLocation().c_str(), dex_location,
-                                error_msg->c_str());
-      return false;
-    }
-    dex_file = oat_dex_file->OpenDexFile(error_msg);
-  } else {
-    bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, *dex_location_checksum,
-                                                 kRuntimeISA, error_msg);
-    if (!verified) {
-      return false;
-    }
-    dex_file = oat_file->GetOatDexFile(dex_location,
-                                       dex_location_checksum)->OpenDexFile(error_msg);
-  }
-  return dex_file.get() != nullptr;
-}
-
-const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation(
-    const char* dex_location,
-    const uint32_t* dex_location_checksum,
-    InstructionSet isa,
-    std::vector<std::string>* error_msgs,
-    bool* obsolete_file_cleanup_failed) {
-  *obsolete_file_cleanup_failed = false;
-  bool already_opened = false;
-  std::string dex_location_str(dex_location);
-  std::unique_ptr<const OatFile> oat_file(OpenOatFileFromDexLocation(dex_location_str, isa,
-                                                                     &already_opened,
-                                                                     obsolete_file_cleanup_failed,
-                                                                     error_msgs));
-  std::string error_msg;
-  if (oat_file.get() == nullptr) {
-    error_msgs->push_back(StringPrintf("Failed to open oat file from dex location '%s'",
-                                       dex_location));
-    return nullptr;
-  } else if (oat_file->IsExecutable() &&
-             !VerifyOatWithDexFile(oat_file.get(), dex_location,
-                                   dex_location_checksum, &error_msg)) {
-    error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location "
-                                       "'%s': %s", oat_file->GetLocation().c_str(), dex_location,
-                                       error_msg.c_str()));
-    return nullptr;
-  } else if (!oat_file->IsExecutable() &&
-             Runtime::Current()->GetHeap()->HasImageSpace() &&
-             !VerifyOatImageChecksum(oat_file.get(), isa)) {
-    error_msgs->push_back(StringPrintf("Failed to verify non-executable oat file '%s' found for "
-                                       "dex location '%s'. Image checksum incorrect.",
-                                       oat_file->GetLocation().c_str(), dex_location));
-    return nullptr;
-  } else {
-    return oat_file.release();
-  }
+  return dex_files;
 }
 
 const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
@@ -1294,335 +797,6 @@
   return nullptr;
 }
 
-const OatFile* ClassLinker::OpenOatFileFromDexLocation(const std::string& dex_location,
-                                                       InstructionSet isa,
-                                                       bool *already_opened,
-                                                       bool *obsolete_file_cleanup_failed,
-                                                       std::vector<std::string>* error_msgs) {
-  // Find out if we've already opened the file
-  const OatFile* ret = nullptr;
-  std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa));
-  ret = FindOpenedOatFileFromOatLocation(odex_filename);
-  if (ret != nullptr) {
-    *already_opened = true;
-    return ret;
-  }
-
-  std::string dalvik_cache;
-  bool have_android_data = false;
-  bool have_dalvik_cache = false;
-  bool is_global_cache = false;
-  GetDalvikCache(GetInstructionSetString(kRuntimeISA), false, &dalvik_cache,
-                 &have_android_data, &have_dalvik_cache, &is_global_cache);
-  std::string cache_filename;
-  if (have_dalvik_cache) {
-    cache_filename = GetDalvikCacheFilenameOrDie(dex_location.c_str(), dalvik_cache.c_str());
-    ret = FindOpenedOatFileFromOatLocation(cache_filename);
-    if (ret != nullptr) {
-      *already_opened = true;
-      return ret;
-    }
-  } else {
-    // If we need to relocate we should just place odex back where it started.
-    cache_filename = odex_filename;
-  }
-
-  ret = nullptr;
-
-  // We know that neither the odex nor the cache'd version is already in use, if it even exists.
-  //
-  // Now we do the following:
-  // 1) Try and open the odex version
-  // 2) If present, checksum-verified & relocated correctly return it
-  // 3) Close the odex version to free up its address space.
-  // 4) Try and open the cache version
-  // 5) If present, checksum-verified & relocated correctly return it
-  // 6) Close the cache version to free up its address space.
-  // 7) If we should relocate:
-  //   a) If we have opened and checksum-verified the odex version relocate it to
-  //      'cache_filename' and return it
-  //   b) If we have opened and checksum-verified the cache version relocate it in place and return
-  //      it. This should not happen often (I think only the run-test's will hit this case).
-  // 8) If the cache-version was present we should delete it since it must be obsolete if we get to
-  //    this point.
-  // 9) Return nullptr
-
-  *already_opened = false;
-  const Runtime* runtime = Runtime::Current();
-  CHECK(runtime != nullptr);
-  bool executable = !runtime->IsAotCompiler();
-
-  std::string odex_error_msg;
-  bool should_patch_system = false;
-  bool odex_checksum_verified = false;
-  bool have_system_odex = false;
-  {
-    // There is a high probability that both these oat files map similar/the same address
-    // spaces so we must scope them like this so they each gets its turn.
-    std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, nullptr,
-                                                         nullptr,
-                                                         executable, &odex_error_msg));
-    if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, odex_oat_file.get(), isa,
-                                                       &odex_checksum_verified,
-                                                       &odex_error_msg)) {
-      return odex_oat_file.release();
-    } else {
-      if (odex_checksum_verified) {
-        // We can just relocate
-        should_patch_system = true;
-        odex_error_msg = "Image Patches are incorrect";
-      }
-      if (odex_oat_file.get() != nullptr) {
-        have_system_odex = true;
-      }
-    }
-  }
-
-  std::string cache_error_msg;
-  bool should_patch_cache = false;
-  bool cache_checksum_verified = false;
-  if (have_dalvik_cache) {
-    std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, nullptr,
-                                                          nullptr,
-                                                          executable, &cache_error_msg));
-    if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, cache_oat_file.get(), isa,
-                                                        &cache_checksum_verified,
-                                                        &cache_error_msg)) {
-      return cache_oat_file.release();
-    } else if (cache_checksum_verified) {
-      // We can just relocate
-      should_patch_cache = true;
-      cache_error_msg = "Image Patches are incorrect";
-    }
-  } else if (have_android_data) {
-    // dalvik_cache does not exist but android data does. This means we should be able to create
-    // it, so we should try.
-    GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA), true);
-  }
-
-  ret = nullptr;
-  std::string error_msg;
-  if (runtime->CanRelocate()) {
-    // Run relocation
-    gc::space::ImageSpace* space = Runtime::Current()->GetHeap()->GetImageSpace();
-    if (space != nullptr) {
-      const std::string& image_location = space->GetImageLocation();
-      if (odex_checksum_verified && should_patch_system) {
-        ret = PatchAndRetrieveOat(odex_filename, cache_filename, image_location, isa, &error_msg);
-      } else if (cache_checksum_verified && should_patch_cache) {
-        CHECK(have_dalvik_cache);
-        ret = PatchAndRetrieveOat(cache_filename, cache_filename, image_location, isa, &error_msg);
-      }
-    } else if (have_system_odex) {
-      ret = GetInterpretedOnlyOat(odex_filename, isa, &error_msg);
-    }
-  }
-  if (ret == nullptr && have_dalvik_cache && OS::FileExists(cache_filename.c_str())) {
-    // implicitly: were able to fine where the cached version is but we were unable to use it,
-    // either as a destination for relocation or to open a file. We should delete it if it is
-    // there.
-    if (TEMP_FAILURE_RETRY(unlink(cache_filename.c_str())) != 0) {
-      std::string rm_error_msg = StringPrintf("Failed to remove obsolete file from %s when "
-                                              "searching for dex file %s: %s",
-                                              cache_filename.c_str(), dex_location.c_str(),
-                                              strerror(errno));
-      error_msgs->push_back(rm_error_msg);
-      VLOG(class_linker) << rm_error_msg;
-      // Let the caller know that we couldn't remove the obsolete file.
-      // This is a good indication that further writes may fail as well.
-      *obsolete_file_cleanup_failed = true;
-    }
-  }
-  if (ret == nullptr) {
-    VLOG(class_linker) << error_msg;
-    error_msgs->push_back(error_msg);
-    std::string relocation_msg;
-    if (runtime->CanRelocate()) {
-      relocation_msg = StringPrintf(" and relocation failed");
-    }
-    if (have_dalvik_cache && cache_checksum_verified) {
-      error_msg = StringPrintf("Failed to open oat file from %s (error %s) or %s "
-                                "(error %s)%s.", odex_filename.c_str(), odex_error_msg.c_str(),
-                                cache_filename.c_str(), cache_error_msg.c_str(),
-                                relocation_msg.c_str());
-    } else {
-      error_msg = StringPrintf("Failed to open oat file from %s (error %s) (no "
-                               "dalvik_cache availible)%s.", odex_filename.c_str(),
-                               odex_error_msg.c_str(), relocation_msg.c_str());
-    }
-    VLOG(class_linker) << error_msg;
-    error_msgs->push_back(error_msg);
-  }
-  return ret;
-}
-
-const OatFile* ClassLinker::GetInterpretedOnlyOat(const std::string& oat_path,
-                                                  InstructionSet isa,
-                                                  std::string* error_msg) {
-  // We open it non-executable
-  std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, nullptr, false, error_msg));
-  if (output.get() == nullptr) {
-    return nullptr;
-  }
-  if (!Runtime::Current()->GetHeap()->HasImageSpace() ||
-      VerifyOatImageChecksum(output.get(), isa)) {
-    return output.release();
-  } else {
-    *error_msg = StringPrintf("Could not use oat file '%s', image checksum failed to verify.",
-                              oat_path.c_str());
-    return nullptr;
-  }
-}
-
-const OatFile* ClassLinker::PatchAndRetrieveOat(const std::string& input_oat,
-                                                const std::string& output_oat,
-                                                const std::string& image_location,
-                                                InstructionSet isa,
-                                                std::string* error_msg) {
-  Runtime* runtime = Runtime::Current();
-  DCHECK(runtime != nullptr);
-  if (!runtime->GetHeap()->HasImageSpace()) {
-    // We don't have an image space so there is no point in trying to patchoat.
-    LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted because we are "
-                 << "running without an image. Attempting to use oat file for interpretation.";
-    return GetInterpretedOnlyOat(input_oat, isa, error_msg);
-  }
-  if (!runtime->IsDex2OatEnabled()) {
-    // We don't have dex2oat so we can assume we don't have patchoat either. We should just use the
-    // input_oat but make sure we only do interpretation on it's dex files.
-    LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted due to dex2oat being "
-                 << "disabled. Attempting to use oat file for interpretation";
-    return GetInterpretedOnlyOat(input_oat, isa, error_msg);
-  }
-  Locks::mutator_lock_->AssertNotHeld(Thread::Current());  // Avoid starving GC.
-  std::string patchoat(runtime->GetPatchoatExecutable());
-
-  std::string isa_arg("--instruction-set=");
-  isa_arg += GetInstructionSetString(isa);
-  std::string input_oat_filename_arg("--input-oat-file=");
-  input_oat_filename_arg += input_oat;
-  std::string output_oat_filename_arg("--output-oat-file=");
-  output_oat_filename_arg += output_oat;
-  std::string patched_image_arg("--patched-image-location=");
-  patched_image_arg += image_location;
-
-  std::vector<std::string> argv;
-  argv.push_back(patchoat);
-  argv.push_back(isa_arg);
-  argv.push_back(input_oat_filename_arg);
-  argv.push_back(output_oat_filename_arg);
-  argv.push_back(patched_image_arg);
-
-  std::string command_line(Join(argv, ' '));
-  LOG(INFO) << "Relocate Oat File: " << command_line;
-  bool success = Exec(argv, error_msg);
-  if (success) {
-    std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr, nullptr,
-                                                  !runtime->IsAotCompiler(), error_msg));
-    bool checksum_verified = false;
-    if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified,
-                                                error_msg)) {
-      return output.release();
-    } else if (output.get() != nullptr) {
-      *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
-                                "but output file '%s' failed verifcation: %s",
-                                input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
-    } else {
-      *error_msg = StringPrintf("Patching of oat file '%s' succeeded "
-                                "but was unable to open output file '%s': %s",
-                                input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
-    }
-  } else if (!runtime->IsAotCompiler()) {
-    // patchoat failed which means we probably don't have enough room to place the output oat file,
-    // instead of failing we should just run the interpreter from the dex files in the input oat.
-    LOG(WARNING) << "Patching of oat file '" << input_oat << "' failed. Attempting to use oat file "
-                 << "for interpretation. patchoat failure was: " << *error_msg;
-    return GetInterpretedOnlyOat(input_oat, isa, error_msg);
-  } else {
-    *error_msg = StringPrintf("Patching of oat file '%s to '%s' "
-                              "failed: %s", input_oat.c_str(), output_oat.c_str(),
-                              error_msg->c_str());
-  }
-  return nullptr;
-}
-
-bool ClassLinker::CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
-                               bool* checksum_verified,
-                               std::string* error_msg) {
-  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
-  if (image_space == nullptr) {
-    *error_msg = "No image space present";
-    return false;
-  }
-  uint32_t real_image_checksum;
-  void* real_image_oat_offset;
-  int32_t real_patch_delta;
-  if (isa == runtime->GetInstructionSet()) {
-    const ImageHeader& image_header = image_space->GetImageHeader();
-    real_image_checksum = image_header.GetOatChecksum();
-    real_image_oat_offset = image_header.GetOatDataBegin();
-    real_patch_delta = image_header.GetPatchDelta();
-  } else {
-    std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
-        image_space->GetImageLocation().c_str(), isa));
-    real_image_checksum = image_header->GetOatChecksum();
-    real_image_oat_offset = image_header->GetOatDataBegin();
-    real_patch_delta = image_header->GetPatchDelta();
-  }
-
-  const OatHeader& oat_header = oat_file->GetOatHeader();
-  std::string compound_msg;
-
-  uint32_t oat_image_checksum = oat_header.GetImageFileLocationOatChecksum();
-  *checksum_verified = oat_image_checksum == real_image_checksum;
-  if (!*checksum_verified) {
-    StringAppendF(&compound_msg, " Oat Image Checksum Incorrect (expected 0x%x, received 0x%x)",
-                  real_image_checksum, oat_image_checksum);
-  }
-
-  bool offset_verified;
-  bool patch_delta_verified;
-
-  if (!oat_file->IsPic()) {
-    // If an oat file is not PIC, we need to check that the image is at the expected location and
-    // patched in the same way.
-    void* oat_image_oat_offset =
-        reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
-    offset_verified = oat_image_oat_offset == real_image_oat_offset;
-    if (!offset_verified) {
-      StringAppendF(&compound_msg, " Oat Image oat offset incorrect (expected 0x%p, received 0x%p)",
-                    real_image_oat_offset, oat_image_oat_offset);
-    }
-
-    int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
-    patch_delta_verified = oat_patch_delta == real_patch_delta;
-    if (!patch_delta_verified) {
-      StringAppendF(&compound_msg, " Oat image patch delta incorrect (expected 0x%x, "
-                    "received 0x%x)", real_patch_delta, oat_patch_delta);
-    }
-  } else {
-    // If an oat file is PIC, we ignore offset and patching delta.
-    offset_verified = true;
-    patch_delta_verified = true;
-  }
-
-  bool ret = (*checksum_verified && offset_verified && patch_delta_verified);
-  if (!ret) {
-    *error_msg = "Oat file failed to verify:" + compound_msg;
-  }
-  return ret;
-}
-
-const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location,
-                                                       std::string* error_msg) {
-  const OatFile* oat_file = FindOpenedOatFileFromOatLocation(oat_location);
-  if (oat_file != nullptr) {
-    return oat_file;
-  }
-  return OatFile::Open(oat_location, oat_location, nullptr, nullptr,
-                       !Runtime::Current()->IsAotCompiler(), error_msg);
-}
-
 void ClassLinker::InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg) {
   ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
   DCHECK(obj != nullptr);
@@ -2259,8 +1433,8 @@
       return nullptr;
     } else if (result.get() == nullptr) {
       // broken loader - throw NPE to be compatible with Dalvik
-      ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s",
-                                                      class_name_string.c_str()).c_str());
+      ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
+                                             class_name_string.c_str()).c_str());
       return nullptr;
     } else {
       // success, return mirror::Class*
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 6570c5f..75fbdf3 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -313,33 +313,25 @@
       LOCKS_EXCLUDED(dex_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Generate an oat file from a dex file
-  bool GenerateOatFile(const char* dex_filename,
-                       int oat_fd,
-                       const char* oat_cache_filename,
-                       std::string* error_msg)
-      LOCKS_EXCLUDED(Locks::mutator_lock_);
-
-  // Find or create the oat file holding dex_location. Then load all corresponding dex files
-  // (if multidex) into the given vector.
-  bool OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
-                           std::vector<std::string>* error_msgs,
-                           std::vector<std::unique_ptr<const DexFile>>* dex_files)
+  // Finds or creates the oat file holding dex_location. Then loads and returns
+  // all corresponding dex files (there may be more than one dex file loaded
+  // in the case of multidex).
+  // This may return the original, unquickened dex files if the oat file could
+  // not be generated.
+  //
+  // Returns an empty vector if the dex files could not be loaded. In this
+  // case, there will be at least one error message returned describing why no
+  // dex files could not be loaded. The 'error_msgs' argument must not be
+  // null, regardless of whether there is an error or not.
+  //
+  // This method should not be called with the mutator_lock_ held, because it
+  // could end up starving GC if we need to generate or relocate any oat
+  // files.
+  std::vector<std::unique_ptr<const DexFile>>  OpenDexFilesFromOat(
+      const char* dex_location, const char* oat_location,
+      std::vector<std::string>* error_msgs)
       LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
 
-  // Returns true if the given oat file has the same image checksum as the image it is paired with.
-  static bool VerifyOatImageChecksum(const OatFile* oat_file, const InstructionSet instruction_set);
-  // Returns true if the oat file checksums match with the image and the offsets are such that it
-  // could be loaded with it.
-  static bool VerifyOatChecksums(const OatFile* oat_file, const InstructionSet instruction_set,
-                                 std::string* error_msg);
-  // Returns true if oat file contains the dex file with the given location and checksum.
-  static bool VerifyOatAndDexFileChecksums(const OatFile* oat_file,
-                                           const char* dex_location,
-                                           uint32_t dex_location_checksum,
-                                           InstructionSet instruction_set,
-                                           std::string* error_msg);
-
   // Allocate an instance of a java.lang.Object.
   mirror::Object* AllocObject(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -612,73 +604,9 @@
                                                   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.
-  const OatFile* FindOatFileFromOatLocation(const std::string& oat_location,
-                                            std::string* error_msg)
-      LOCKS_EXCLUDED(dex_lock_);
-
   const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location)
       LOCKS_EXCLUDED(dex_lock_);
 
-  const OatFile* OpenOatFileFromDexLocation(const std::string& dex_location,
-                                            InstructionSet isa,
-                                            bool* already_opened,
-                                            bool* obsolete_file_cleanup_failed,
-                                            std::vector<std::string>* error_msg)
-      LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
-
-  const OatFile* GetInterpretedOnlyOat(const std::string& oat_path,
-                                       InstructionSet isa,
-                                       std::string* error_msg);
-
-  const OatFile* PatchAndRetrieveOat(const std::string& input, const std::string& output,
-                                     const std::string& image_location, InstructionSet isa,
-                                     std::string* error_msg)
-      LOCKS_EXCLUDED(Locks::mutator_lock_);
-
-  bool CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
-                    bool* checksum_verified, std::string* error_msg);
-
-  // Note: will not register the oat file.
-  const OatFile* FindOatFileInOatLocationForDexFile(const char* dex_location,
-                                                    uint32_t dex_location_checksum,
-                                                    const char* oat_location,
-                                                    std::string* error_msg)
-      LOCKS_EXCLUDED(dex_lock_);
-
-  // Creates the oat file from the dex_location to the oat_location. Needs a file descriptor for
-  // the file to be written, which is assumed to be under a lock.
-  const OatFile* CreateOatFileForDexLocation(const char* dex_location,
-                                             int fd, const char* oat_location,
-                                             std::vector<std::string>* error_msgs)
-      LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
-
-  // Finds an OatFile that contains a DexFile for the given a DexFile location.
-  //
-  // 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* dex_location,
-                                                             const uint32_t* dex_location_checksum,
-                                                             InstructionSet isa,
-                                                             std::vector<std::string>* error_msgs,
-                                                             bool* obsolete_file_cleanup_failed)
-      LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
-
-  // Verifies:
-  //  - that the oat file contains the dex file (with a matching checksum, which may be null if the
-  // file was pre-opted)
-  //  - the checksums of the oat file (against the image space)
-  //  - the checksum of the dex file against dex_location_checksum
-  //  - that the dex file can be opened
-  // Returns true iff all verification succeed.
-  //
-  // The dex_location is the dex location as stored in the oat file header.
-  // (see DexFile::GetDexCanonicalLocation for a description of location conventions)
-  bool VerifyOatWithDexFile(const OatFile* oat_file, const char* dex_location,
-                            const uint32_t* dex_location_checksum,
-                            std::string* error_msg);
-
   mirror::ArtMethod* CreateProxyConstructor(Thread* self, Handle<mirror::Class> klass,
                                             mirror::Class* proxy_class)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -803,7 +731,6 @@
 
   friend class ImageWriter;  // for GetClassRoots
   friend class ImageDumper;  // for FindOpenedOatFileFromOatLocation
-  friend class ElfPatcher;  // for FindOpenedOatFileForDexFile & FindOpenedOatFileFromOatLocation
   friend class JniCompilerTest;  // for GetRuntimeQuickGenericJniStub
   friend class NoDex2OatTest;  // for FindOpenedOatFileForDexFile
   friend class NoPatchoatTest;  // for FindOpenedOatFileForDexFile
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index b7ffd60..1c8a892 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -36,6 +36,7 @@
 #include "gtest/gtest.h"
 #include "jni_internal.h"
 #include "mirror/class_loader.h"
+#include "mem_map.h"
 #include "noop_compiler_callbacks.h"
 #include "os.h"
 #include "runtime-inl.h"
@@ -194,6 +195,7 @@
 std::unique_ptr<const DexFile> CommonRuntimeTest::LoadExpectSingleDexFile(const char* location) {
   std::vector<std::unique_ptr<const DexFile>> dex_files;
   std::string error_msg;
+  MemMap::Init();
   if (!DexFile::Open(location, location, &error_msg, &dex_files)) {
     LOG(FATAL) << "Could not open .dex file '" << location << "': " << error_msg << "\n";
     UNREACHABLE();
@@ -225,10 +227,12 @@
   options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
   SetUpRuntimeOptions(&options);
 
+  PreRuntimeCreate();
   if (!Runtime::Create(options, false)) {
     LOG(FATAL) << "Failed to create runtime";
     return;
   }
+  PostRuntimeCreate();
   runtime_.reset(Runtime::Current());
   class_linker_ = runtime_->GetClassLinker();
   class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
@@ -335,7 +339,7 @@
 #define ART_TARGET_NATIVETEST_DIR_STRING ""
 #endif
 
-std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTest::OpenTestDexFiles(const char* name) {
+std::string CommonRuntimeTest::GetTestDexFileName(const char* name) {
   CHECK(name != nullptr);
   std::string filename;
   if (IsHost()) {
@@ -347,6 +351,11 @@
   filename += "art-gtest-";
   filename += name;
   filename += ".jar";
+  return filename;
+}
+
+std::vector<std::unique_ptr<const DexFile>> CommonRuntimeTest::OpenTestDexFiles(const char* name) {
+  std::string filename = GetTestDexFileName(name);
   std::string error_msg;
   std::vector<std::unique_ptr<const DexFile>> dex_files;
   bool success = DexFile::Open(filename.c_str(), filename.c_str(), &error_msg, &dex_files);
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 9efea84..cce8485 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -101,11 +101,19 @@
 
   virtual void TearDown();
 
+  // Called before the runtime is created.
+  virtual void PreRuntimeCreate() {}
+
+  // Called after the runtime is created.
+  virtual void PostRuntimeCreate() {}
+
   // Gets the path of the specified dex file for host or target.
   static std::string GetDexFileName(const std::string& jar_prefix);
 
   std::string GetTestAndroidRoot();
 
+  std::string GetTestDexFileName(const char* name);
+
   std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index f5b4354..36de221 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -43,7 +43,7 @@
   }
 }
 
-static void ThrowException(const ThrowLocation* throw_location, const char* exception_descriptor,
+static void ThrowException(const char* exception_descriptor,
                            mirror::Class* referrer, const char* fmt, va_list* args = NULL)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::ostringstream msg;
@@ -56,16 +56,10 @@
   }
   AddReferrerLocation(msg, referrer);
   Thread* self = Thread::Current();
-  if (throw_location == NULL) {
-    ThrowLocation computed_throw_location = self->GetCurrentLocationForThrow();
-    self->ThrowNewException(computed_throw_location, exception_descriptor, msg.str().c_str());
-  } else {
-    self->ThrowNewException(*throw_location, exception_descriptor, msg.str().c_str());
-  }
+  self->ThrowNewException(exception_descriptor, msg.str().c_str());
 }
 
-static void ThrowWrappedException(const ThrowLocation* throw_location,
-                                  const char* exception_descriptor,
+static void ThrowWrappedException(const char* exception_descriptor,
                                   mirror::Class* referrer, const char* fmt, va_list* args = NULL)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::ostringstream msg;
@@ -78,18 +72,13 @@
   }
   AddReferrerLocation(msg, referrer);
   Thread* self = Thread::Current();
-  if (throw_location == NULL) {
-    ThrowLocation computed_throw_location = self->GetCurrentLocationForThrow();
-    self->ThrowNewWrappedException(computed_throw_location, exception_descriptor, msg.str().c_str());
-  } else {
-    self->ThrowNewWrappedException(*throw_location, exception_descriptor, msg.str().c_str());
-  }
+  self->ThrowNewWrappedException(exception_descriptor, msg.str().c_str());
 }
 
 // AbstractMethodError
 
 void ThrowAbstractMethodError(mirror::ArtMethod* method) {
-  ThrowException(NULL, "Ljava/lang/AbstractMethodError;", NULL,
+  ThrowException("Ljava/lang/AbstractMethodError;", NULL,
                  StringPrintf("abstract method \"%s\"",
                               PrettyMethod(method).c_str()).c_str());
 }
@@ -97,20 +86,20 @@
 // ArithmeticException
 
 void ThrowArithmeticExceptionDivideByZero() {
-  ThrowException(NULL, "Ljava/lang/ArithmeticException;", NULL, "divide by zero");
+  ThrowException("Ljava/lang/ArithmeticException;", NULL, "divide by zero");
 }
 
 // ArrayIndexOutOfBoundsException
 
 void ThrowArrayIndexOutOfBoundsException(int index, int length) {
-  ThrowException(NULL, "Ljava/lang/ArrayIndexOutOfBoundsException;", NULL,
+  ThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", NULL,
                  StringPrintf("length=%d; index=%d", length, index).c_str());
 }
 
 // ArrayStoreException
 
 void ThrowArrayStoreException(mirror::Class* element_class, mirror::Class* array_class) {
-  ThrowException(NULL, "Ljava/lang/ArrayStoreException;", NULL,
+  ThrowException("Ljava/lang/ArrayStoreException;", NULL,
                  StringPrintf("%s cannot be stored in an array of type %s",
                               PrettyDescriptor(element_class).c_str(),
                               PrettyDescriptor(array_class).c_str()).c_str());
@@ -119,14 +108,14 @@
 // ClassCastException
 
 void ThrowClassCastException(mirror::Class* dest_type, mirror::Class* src_type) {
-  ThrowException(NULL, "Ljava/lang/ClassCastException;", NULL,
+  ThrowException("Ljava/lang/ClassCastException;", NULL,
                  StringPrintf("%s cannot be cast to %s",
                               PrettyDescriptor(src_type).c_str(),
                               PrettyDescriptor(dest_type).c_str()).c_str());
 }
 
-void ThrowClassCastException(const ThrowLocation* throw_location, const char* msg) {
-  ThrowException(throw_location, "Ljava/lang/ClassCastException;", NULL, msg);
+void ThrowClassCastException(const char* msg) {
+  ThrowException("Ljava/lang/ClassCastException;", NULL, msg);
 }
 
 // ClassCircularityError
@@ -134,7 +123,7 @@
 void ThrowClassCircularityError(mirror::Class* c) {
   std::ostringstream msg;
   msg << PrettyDescriptor(c);
-  ThrowException(NULL, "Ljava/lang/ClassCircularityError;", c, msg.str().c_str());
+  ThrowException("Ljava/lang/ClassCircularityError;", c, msg.str().c_str());
 }
 
 // ClassFormatError
@@ -142,7 +131,7 @@
 void ThrowClassFormatError(mirror::Class* referrer, const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  ThrowException(NULL, "Ljava/lang/ClassFormatError;", referrer, fmt, &args);
+  ThrowException("Ljava/lang/ClassFormatError;", referrer, fmt, &args);
   va_end(args);}
 
 // IllegalAccessError
@@ -151,7 +140,7 @@
   std::ostringstream msg;
   msg << "Illegal class access: '" << PrettyDescriptor(referrer) << "' attempting to access '"
       << PrettyDescriptor(accessed) << "'";
-  ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str());
+  ThrowException("Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str());
 }
 
 void ThrowIllegalAccessErrorClassForMethodDispatch(mirror::Class* referrer, mirror::Class* accessed,
@@ -161,21 +150,21 @@
   msg << "Illegal class access ('" << PrettyDescriptor(referrer) << "' attempting to access '"
       << PrettyDescriptor(accessed) << "') in attempt to invoke " << type
       << " method " << PrettyMethod(called).c_str();
-  ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str());
+  ThrowException("Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str());
 }
 
 void ThrowIllegalAccessErrorMethod(mirror::Class* referrer, mirror::ArtMethod* accessed) {
   std::ostringstream msg;
   msg << "Method '" << PrettyMethod(accessed) << "' is inaccessible to class '"
       << PrettyDescriptor(referrer) << "'";
-  ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str());
+  ThrowException("Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str());
 }
 
 void ThrowIllegalAccessErrorField(mirror::Class* referrer, mirror::ArtField* accessed) {
   std::ostringstream msg;
   msg << "Field '" << PrettyField(accessed, false) << "' is inaccessible to class '"
       << PrettyDescriptor(referrer) << "'";
-  ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str());
+  ThrowException("Ljava/lang/IllegalAccessError;", referrer, msg.str().c_str());
 }
 
 void ThrowIllegalAccessErrorFinalField(mirror::ArtMethod* referrer,
@@ -183,7 +172,7 @@
   std::ostringstream msg;
   msg << "Final field '" << PrettyField(accessed, false) << "' cannot be written to by method '"
       << PrettyMethod(referrer) << "'";
-  ThrowException(NULL, "Ljava/lang/IllegalAccessError;",
+  ThrowException("Ljava/lang/IllegalAccessError;",
                  referrer != NULL ? referrer->GetClass() : NULL,
                  msg.str().c_str());
 }
@@ -191,20 +180,20 @@
 void ThrowIllegalAccessError(mirror::Class* referrer, const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  ThrowException(NULL, "Ljava/lang/IllegalAccessError;", referrer, fmt, &args);
+  ThrowException("Ljava/lang/IllegalAccessError;", referrer, fmt, &args);
   va_end(args);
 }
 
 // IllegalAccessException
 
-void ThrowIllegalAccessException(const ThrowLocation* throw_location, const char* msg) {
-  ThrowException(throw_location, "Ljava/lang/IllegalAccessException;", NULL, msg);
+void ThrowIllegalAccessException(const char* msg) {
+  ThrowException("Ljava/lang/IllegalAccessException;", NULL, msg);
 }
 
 // IllegalArgumentException
 
-void ThrowIllegalArgumentException(const ThrowLocation* throw_location, const char* msg) {
-  ThrowException(throw_location, "Ljava/lang/IllegalArgumentException;", NULL, msg);
+void ThrowIllegalArgumentException(const char* msg) {
+  ThrowException("Ljava/lang/IllegalArgumentException;", NULL, msg);
 }
 
 
@@ -216,7 +205,7 @@
   std::ostringstream msg;
   msg << "The method '" << PrettyMethod(method) << "' was expected to be of type "
       << expected_type << " but instead was found to be of type " << found_type;
-  ThrowException(NULL, "Ljava/lang/IncompatibleClassChangeError;",
+  ThrowException("Ljava/lang/IncompatibleClassChangeError;",
                  referrer != NULL ? referrer->GetClass() : NULL,
                  msg.str().c_str());
 }
@@ -232,7 +221,7 @@
       << "' does not implement interface '"
       << PrettyDescriptor(interface_method->GetDeclaringClass())
       << "' in call to '" << PrettyMethod(interface_method) << "'";
-  ThrowException(NULL, "Ljava/lang/IncompatibleClassChangeError;",
+  ThrowException("Ljava/lang/IncompatibleClassChangeError;",
                  referrer != NULL ? referrer->GetClass() : NULL,
                  msg.str().c_str());
 }
@@ -243,14 +232,14 @@
   msg << "Expected '" << PrettyField(resolved_field) << "' to be a "
       << (is_static ? "static" : "instance") << " field" << " rather than a "
       << (is_static ? "instance" : "static") << " field";
-  ThrowException(NULL, "Ljava/lang/IncompatibleClassChangeError;", referrer->GetClass(),
+  ThrowException("Ljava/lang/IncompatibleClassChangeError;", referrer->GetClass(),
                  msg.str().c_str());
 }
 
 void ThrowIncompatibleClassChangeError(mirror::Class* referrer, const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  ThrowException(NULL, "Ljava/lang/IncompatibleClassChangeError;", referrer, fmt, &args);
+  ThrowException("Ljava/lang/IncompatibleClassChangeError;", referrer, fmt, &args);
   va_end(args);
 }
 
@@ -259,14 +248,14 @@
 void ThrowIOException(const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  ThrowException(NULL, "Ljava/io/IOException;", NULL, fmt, &args);
+  ThrowException("Ljava/io/IOException;", NULL, fmt, &args);
   va_end(args);
 }
 
 void ThrowWrappedIOException(const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  ThrowWrappedException(NULL, "Ljava/io/IOException;", NULL, fmt, &args);
+  ThrowWrappedException("Ljava/io/IOException;", NULL, fmt, &args);
   va_end(args);
 }
 
@@ -275,19 +264,19 @@
 void ThrowLinkageError(mirror::Class* referrer, const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  ThrowException(NULL, "Ljava/lang/LinkageError;", referrer, fmt, &args);
+  ThrowException("Ljava/lang/LinkageError;", referrer, fmt, &args);
   va_end(args);
 }
 
 // NegativeArraySizeException
 
 void ThrowNegativeArraySizeException(int size) {
-  ThrowException(NULL, "Ljava/lang/NegativeArraySizeException;", NULL,
+  ThrowException("Ljava/lang/NegativeArraySizeException;", NULL,
                  StringPrintf("%d", size).c_str());
 }
 
 void ThrowNegativeArraySizeException(const char* msg) {
-  ThrowException(NULL, "Ljava/lang/NegativeArraySizeException;", NULL, msg);
+  ThrowException("Ljava/lang/NegativeArraySizeException;", NULL, msg);
 }
 
 // NoSuchFieldError
@@ -299,7 +288,7 @@
   std::string temp;
   msg << "No " << scope << "field " << name << " of type " << type
       << " in class " << c->GetDescriptor(&temp) << " or its superclasses";
-  ThrowException(NULL, "Ljava/lang/NoSuchFieldError;", c, msg.str().c_str());
+  ThrowException("Ljava/lang/NoSuchFieldError;", c, msg.str().c_str());
 }
 
 // NoSuchMethodError
@@ -310,97 +299,91 @@
   std::string temp;
   msg << "No " << type << " method " << name << signature
       << " in class " << c->GetDescriptor(&temp) << " or its super classes";
-  ThrowException(NULL, "Ljava/lang/NoSuchMethodError;", c, msg.str().c_str());
+  ThrowException("Ljava/lang/NoSuchMethodError;", c, msg.str().c_str());
 }
 
 void ThrowNoSuchMethodError(uint32_t method_idx) {
-  Thread* self = Thread::Current();
-  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-  mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache();
+  mirror::ArtMethod* method = Thread::Current()->GetCurrentMethod(nullptr);
+  mirror::DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
   const DexFile& dex_file = *dex_cache->GetDexFile();
   std::ostringstream msg;
   msg << "No method '" << PrettyMethod(method_idx, dex_file, true) << "'";
-  ThrowException(&throw_location, "Ljava/lang/NoSuchMethodError;",
-                 throw_location.GetMethod()->GetDeclaringClass(), msg.str().c_str());
+  ThrowException("Ljava/lang/NoSuchMethodError;",
+                 method->GetDeclaringClass(), msg.str().c_str());
 }
 
 // NullPointerException
 
-void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location,
-                                             mirror::ArtField* field, bool is_read) {
+void ThrowNullPointerExceptionForFieldAccess(mirror::ArtField* field, bool is_read) {
   std::ostringstream msg;
   msg << "Attempt to " << (is_read ? "read from" : "write to")
       << " field '" << PrettyField(field, true) << "' on a null object reference";
-  ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str());
+  ThrowException("Ljava/lang/NullPointerException;", NULL, msg.str().c_str());
 }
 
-static void ThrowNullPointerExceptionForMethodAccessImpl(const ThrowLocation& throw_location,
-                                                         uint32_t method_idx,
+static void ThrowNullPointerExceptionForMethodAccessImpl(uint32_t method_idx,
                                                          const DexFile& dex_file,
                                                          InvokeType type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::ostringstream msg;
   msg << "Attempt to invoke " << type << " method '"
       << PrettyMethod(method_idx, dex_file, true) << "' on a null object reference";
-  ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL, msg.str().c_str());
+  ThrowException("Ljava/lang/NullPointerException;", NULL, msg.str().c_str());
 }
 
-void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
-                                              uint32_t method_idx,
+void ThrowNullPointerExceptionForMethodAccess(uint32_t method_idx,
                                               InvokeType type) {
-  mirror::DexCache* dex_cache = throw_location.GetMethod()->GetDeclaringClass()->GetDexCache();
+  mirror::DexCache* dex_cache =
+      Thread::Current()->GetCurrentMethod(nullptr)->GetDeclaringClass()->GetDexCache();
   const DexFile& dex_file = *dex_cache->GetDexFile();
-  ThrowNullPointerExceptionForMethodAccessImpl(throw_location, method_idx,
-                                               dex_file, type);
+  ThrowNullPointerExceptionForMethodAccessImpl(method_idx, dex_file, type);
 }
 
-void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
-                                              mirror::ArtMethod* method,
+void ThrowNullPointerExceptionForMethodAccess(mirror::ArtMethod* method,
                                               InvokeType type) {
   mirror::DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
   const DexFile& dex_file = *dex_cache->GetDexFile();
-  ThrowNullPointerExceptionForMethodAccessImpl(throw_location, method->GetDexMethodIndex(),
+  ThrowNullPointerExceptionForMethodAccessImpl(method->GetDexMethodIndex(),
                                                dex_file, type);
 }
 
-void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location) {
-  const DexFile::CodeItem* code = throw_location.GetMethod()->GetCodeItem();
-  uint32_t throw_dex_pc = throw_location.GetDexPc();
+void ThrowNullPointerExceptionFromDexPC() {
+  uint32_t throw_dex_pc;
+  mirror::ArtMethod* method = Thread::Current()->GetCurrentMethod(&throw_dex_pc);
+  const DexFile::CodeItem* code = method->GetCodeItem();
   CHECK_LT(throw_dex_pc, code->insns_size_in_code_units_);
   const Instruction* instr = Instruction::At(&code->insns_[throw_dex_pc]);
   switch (instr->Opcode()) {
     case Instruction::INVOKE_DIRECT:
-      ThrowNullPointerExceptionForMethodAccess(throw_location, instr->VRegB_35c(), kDirect);
+      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kDirect);
       break;
     case Instruction::INVOKE_DIRECT_RANGE:
-      ThrowNullPointerExceptionForMethodAccess(throw_location, instr->VRegB_3rc(), kDirect);
+      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kDirect);
       break;
     case Instruction::INVOKE_VIRTUAL:
-      ThrowNullPointerExceptionForMethodAccess(throw_location, instr->VRegB_35c(), kVirtual);
+      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kVirtual);
       break;
     case Instruction::INVOKE_VIRTUAL_RANGE:
-      ThrowNullPointerExceptionForMethodAccess(throw_location, instr->VRegB_3rc(), kVirtual);
+      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kVirtual);
       break;
     case Instruction::INVOKE_INTERFACE:
-      ThrowNullPointerExceptionForMethodAccess(throw_location, instr->VRegB_35c(), kInterface);
+      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kInterface);
       break;
     case Instruction::INVOKE_INTERFACE_RANGE:
-      ThrowNullPointerExceptionForMethodAccess(throw_location, instr->VRegB_3rc(), kInterface);
+      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kInterface);
       break;
     case Instruction::INVOKE_VIRTUAL_QUICK:
     case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
       // Since we replaced the method index, we ask the verifier to tell us which
       // method is invoked at this location.
-      mirror::ArtMethod* method =
-          verifier::MethodVerifier::FindInvokedMethodAtDexPc(throw_location.GetMethod(),
-                                                             throw_location.GetDexPc());
-      if (method != NULL) {
+      mirror::ArtMethod* invoked_method =
+          verifier::MethodVerifier::FindInvokedMethodAtDexPc(method, throw_dex_pc);
+      if (invoked_method != NULL) {
         // NPE with precise message.
-        ThrowNullPointerExceptionForMethodAccess(throw_location, method, kVirtual);
+        ThrowNullPointerExceptionForMethodAccess(invoked_method, kVirtual);
       } else {
         // NPE with imprecise message.
-        ThrowNullPointerException(&throw_location,
-                                  "Attempt to invoke a virtual method on a null object reference");
+        ThrowNullPointerException("Attempt to invoke a virtual method on a null object reference");
       }
       break;
     }
@@ -412,9 +395,8 @@
     case Instruction::IGET_CHAR:
     case Instruction::IGET_SHORT: {
       mirror::ArtField* field =
-          Runtime::Current()->GetClassLinker()->ResolveField(instr->VRegC_22c(),
-                                                             throw_location.GetMethod(), false);
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */);
+          Runtime::Current()->GetClassLinker()->ResolveField(instr->VRegC_22c(), method, false);
+      ThrowNullPointerExceptionForFieldAccess(field, true /* read */);
       break;
     }
     case Instruction::IGET_QUICK:
@@ -427,15 +409,13 @@
       // Since we replaced the field index, we ask the verifier to tell us which
       // field is accessed at this location.
       mirror::ArtField* field =
-          verifier::MethodVerifier::FindAccessedFieldAtDexPc(throw_location.GetMethod(),
-                                                             throw_location.GetDexPc());
+          verifier::MethodVerifier::FindAccessedFieldAtDexPc(method, throw_dex_pc);
       if (field != NULL) {
         // NPE with precise message.
-        ThrowNullPointerExceptionForFieldAccess(throw_location, field, true /* read */);
+        ThrowNullPointerExceptionForFieldAccess(field, true /* read */);
       } else {
         // NPE with imprecise message.
-        ThrowNullPointerException(&throw_location,
-                                  "Attempt to read from a field on a null object reference");
+        ThrowNullPointerException("Attempt to read from a field on a null object reference");
       }
       break;
     }
@@ -447,9 +427,8 @@
     case Instruction::IPUT_CHAR:
     case Instruction::IPUT_SHORT: {
       mirror::ArtField* field =
-          Runtime::Current()->GetClassLinker()->ResolveField(instr->VRegC_22c(),
-                                                             throw_location.GetMethod(), false);
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */);
+          Runtime::Current()->GetClassLinker()->ResolveField(instr->VRegC_22c(), method, false);
+      ThrowNullPointerExceptionForFieldAccess(field, false /* write */);
       break;
     }
     case Instruction::IPUT_QUICK:
@@ -462,15 +441,13 @@
       // Since we replaced the field index, we ask the verifier to tell us which
       // field is accessed at this location.
       mirror::ArtField* field =
-          verifier::MethodVerifier::FindAccessedFieldAtDexPc(throw_location.GetMethod(),
-                                                             throw_location.GetDexPc());
+          verifier::MethodVerifier::FindAccessedFieldAtDexPc(method, throw_dex_pc);
       if (field != NULL) {
         // NPE with precise message.
-        ThrowNullPointerExceptionForFieldAccess(throw_location, field, false /* write */);
+        ThrowNullPointerExceptionForFieldAccess(field, false /* write */);
       } else {
         // NPE with imprecise message.
-        ThrowNullPointerException(&throw_location,
-                                  "Attempt to write to a field on a null object reference");
+        ThrowNullPointerException("Attempt to write to a field on a null object reference");
       }
       break;
     }
@@ -481,7 +458,7 @@
     case Instruction::AGET_BYTE:
     case Instruction::AGET_CHAR:
     case Instruction::AGET_SHORT:
-      ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL,
+      ThrowException("Ljava/lang/NullPointerException;", NULL,
                      "Attempt to read from null array");
       break;
     case Instruction::APUT:
@@ -491,28 +468,28 @@
     case Instruction::APUT_BYTE:
     case Instruction::APUT_CHAR:
     case Instruction::APUT_SHORT:
-      ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL,
+      ThrowException("Ljava/lang/NullPointerException;", NULL,
                      "Attempt to write to null array");
       break;
     case Instruction::ARRAY_LENGTH:
-      ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL,
+      ThrowException("Ljava/lang/NullPointerException;", NULL,
                      "Attempt to get length of null array");
       break;
     default: {
       // TODO: We should have covered all the cases where we expect a NPE above, this
       //       message/logging is so we can improve any cases we've missed in the future.
-      const DexFile& dex_file =
-          *throw_location.GetMethod()->GetDeclaringClass()->GetDexCache()->GetDexFile();
-      ThrowException(&throw_location, "Ljava/lang/NullPointerException;", NULL,
+      const DexFile* dex_file =
+          method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+      ThrowException("Ljava/lang/NullPointerException;", NULL,
                      StringPrintf("Null pointer exception during instruction '%s'",
-                                  instr->DumpString(&dex_file).c_str()).c_str());
+                                  instr->DumpString(dex_file).c_str()).c_str());
       break;
     }
   }
 }
 
-void ThrowNullPointerException(const ThrowLocation* throw_location, const char* msg) {
-  ThrowException(throw_location, "Ljava/lang/NullPointerException;", NULL, msg);
+void ThrowNullPointerException(const char* msg) {
+  ThrowException("Ljava/lang/NullPointerException;", NULL, msg);
 }
 
 // RuntimeException
@@ -520,7 +497,7 @@
 void ThrowRuntimeException(const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  ThrowException(NULL, "Ljava/lang/RuntimeException;", NULL, fmt, &args);
+  ThrowException("Ljava/lang/RuntimeException;", NULL, fmt, &args);
   va_end(args);
 }
 
@@ -529,7 +506,7 @@
 void ThrowVerifyError(mirror::Class* referrer, const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  ThrowException(NULL, "Ljava/lang/VerifyError;", referrer, fmt, &args);
+  ThrowException("Ljava/lang/VerifyError;", referrer, fmt, &args);
   va_end(args);
 }
 
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index ebedae0..9e749e3 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -29,7 +29,6 @@
 }  // namespace mirror
 class Signature;
 class StringPiece;
-class ThrowLocation;
 
 // AbstractMethodError
 
@@ -60,7 +59,7 @@
 void ThrowClassCastException(mirror::Class* dest_type, mirror::Class* src_type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
 
-void ThrowClassCastException(const ThrowLocation* throw_location, const char* msg)
+void ThrowClassCastException(const char* msg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
 
 // ClassFormatError
@@ -94,12 +93,12 @@
 
 // IllegalAccessException
 
-void ThrowIllegalAccessException(const ThrowLocation* throw_location, const char* msg)
+void ThrowIllegalAccessException(const char* msg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
 
 // IllegalArgumentException
 
-void ThrowIllegalArgumentException(const ThrowLocation* throw_location, const char* msg)
+void ThrowIllegalArgumentException(const char* msg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
 
 // IncompatibleClassChangeError
@@ -161,25 +160,22 @@
 
 // NullPointerException
 
-void ThrowNullPointerExceptionForFieldAccess(const ThrowLocation& throw_location,
-                                             mirror::ArtField* field,
+void ThrowNullPointerExceptionForFieldAccess(mirror::ArtField* field,
                                              bool is_read)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
 
-void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
-                                              uint32_t method_idx,
+void ThrowNullPointerExceptionForMethodAccess(uint32_t method_idx,
                                               InvokeType type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
 
-void ThrowNullPointerExceptionForMethodAccess(const ThrowLocation& throw_location,
-                                              mirror::ArtMethod* method,
+void ThrowNullPointerExceptionForMethodAccess(mirror::ArtMethod* method,
                                               InvokeType type)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
 
-void ThrowNullPointerExceptionFromDexPC(const ThrowLocation& throw_location)
+void ThrowNullPointerExceptionFromDexPC()
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
 
-void ThrowNullPointerException(const ThrowLocation* throw_location, const char* msg)
+void ThrowNullPointerException(const char* msg)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
 
 // RuntimeException
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 964e84c..9f2a09b 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -47,7 +47,6 @@
 #include "ScopedPrimitiveArray.h"
 #include "handle_scope-inl.h"
 #include "thread_list.h"
-#include "throw_location.h"
 #include "utf.h"
 #include "verifier/method_verifier-inl.h"
 #include "well_known_classes.h"
@@ -347,26 +346,9 @@
 static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
 
 void DebugInvokeReq::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
-  if (receiver != nullptr) {
-    callback(&receiver, arg, root_info);
-  }
-  if (thread != nullptr) {
-    callback(&thread, arg, root_info);
-  }
-  if (klass != nullptr) {
-    callback(reinterpret_cast<mirror::Object**>(&klass), arg, root_info);
-  }
-  if (method != nullptr) {
-    callback(reinterpret_cast<mirror::Object**>(&method), arg, root_info);
-  }
-}
-
-void DebugInvokeReq::Clear() {
-  invoke_needed = false;
-  receiver = nullptr;
-  thread = nullptr;
-  klass = nullptr;
-  method = nullptr;
+  receiver.VisitRootIfNonNull(callback, arg, root_info);  // null for static method call.
+  klass.VisitRoot(callback, arg, root_info);
+  method.VisitRoot(callback, arg, root_info);
 }
 
 void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
@@ -3517,10 +3499,14 @@
   };
 
   // Allocate single step.
-  SingleStepControl* single_step_control = new SingleStepControl(step_size, step_depth,
-                                                                 visitor.stack_depth,
-                                                                 visitor.method);
-  CHECK(single_step_control != nullptr) << "Failed to allocate SingleStepControl";
+  SingleStepControl* single_step_control =
+      new (std::nothrow) SingleStepControl(step_size, step_depth,
+                                           visitor.stack_depth, visitor.method);
+  if (single_step_control == nullptr) {
+    LOG(ERROR) << "Failed to allocate SingleStepControl";
+    return JDWP::ERR_OUT_OF_MEMORY;
+  }
+
   mirror::ArtMethod* m = single_step_control->GetMethod();
   const int32_t line_number = visitor.line_number;
   if (!m->IsNative()) {
@@ -3597,7 +3583,7 @@
   ThreadList* thread_list = Runtime::Current()->GetThreadList();
 
   Thread* targetThread = nullptr;
-  DebugInvokeReq* req = nullptr;
+  std::unique_ptr<DebugInvokeReq> req;
   Thread* self = Thread::Current();
   {
     ScopedObjectAccessUnchecked soa(self);
@@ -3608,8 +3594,13 @@
       LOG(ERROR) << "InvokeMethod request for invalid thread id " << thread_id;
       return error;
     }
-    req = targetThread->GetInvokeReq();
-    if (!req->ready) {
+    if (targetThread->GetInvokeReq() != nullptr) {
+      // Thread is already invoking a method on behalf of the debugger.
+      LOG(ERROR) << "InvokeMethod request for thread already invoking a method: " << *targetThread;
+      return JDWP::ERR_ALREADY_INVOKING;
+    }
+    if (!targetThread->IsReadyForDebugInvoke()) {
+      // Thread is not suspended by an event so it cannot invoke a method.
       LOG(ERROR) << "InvokeMethod request for thread not stopped by event: " << *targetThread;
       return JDWP::ERR_INVALID_THREAD;
     }
@@ -3643,11 +3634,10 @@
       return JDWP::ERR_INVALID_OBJECT;
     }
 
-    mirror::Object* thread = gRegistry->Get<mirror::Object*>(thread_id, &error);
+    gRegistry->Get<mirror::Object*>(thread_id, &error);
     if (error != JDWP::ERR_NONE) {
       return JDWP::ERR_INVALID_OBJECT;
     }
-    // TODO: check that 'thread' is actually a java.lang.Thread!
 
     mirror::Class* c = DecodeClass(class_id, &error);
     if (c == nullptr) {
@@ -3705,14 +3695,17 @@
       }
     }
 
-    req->receiver = receiver;
-    req->thread = thread;
-    req->klass = c;
-    req->method = m;
-    req->arg_count = arg_count;
-    req->arg_values = arg_values;
-    req->options = options;
-    req->invoke_needed = true;
+    // Allocates a DebugInvokeReq.
+    req.reset(new (std::nothrow) DebugInvokeReq(receiver, c, m, options, arg_values, arg_count));
+    if (req.get() == nullptr) {
+      LOG(ERROR) << "Failed to allocate DebugInvokeReq";
+      return JDWP::ERR_OUT_OF_MEMORY;
+    }
+
+    // Attach the DebugInvokeReq to the target thread so it executes the method when
+    // it is resumed. Once the invocation completes, it will detach it and signal us
+    // before suspending itself.
+    targetThread->SetDebugInvokeReq(req.get());
   }
 
   // The fact that we've released the thread list lock is a bit risky --- if the thread goes
@@ -3746,7 +3739,7 @@
       gJdwpState->ReleaseJdwpTokenForCommand();
 
       // Wait for the request to finish executing.
-      while (req->invoke_needed) {
+      while (targetThread->GetInvokeReq() != nullptr) {
         req->cond.Wait(self);
       }
     }
@@ -3779,11 +3772,7 @@
 
   // Copy the result.
   *pResultTag = req->result_tag;
-  if (IsPrimitiveTag(req->result_tag)) {
-    *pResultValue = req->result_value.GetJ();
-  } else {
-    *pResultValue = gRegistry->Add(req->result_value.GetL());
-  }
+  *pResultValue = req->result_value;
   *pExceptionId = req->exception;
   return req->error;
 }
@@ -3793,60 +3782,55 @@
 
   // We can be called while an exception is pending. We need
   // to preserve that across the method invocation.
-  StackHandleScope<2> hs(soa.Self());
-  auto old_exception = hs.NewHandle<mirror::Throwable>(nullptr);
-  {
-    ThrowLocation old_throw_location;
-    mirror::Throwable* old_exception_obj = soa.Self()->GetException();
-    old_exception.Assign(old_exception_obj);
-    soa.Self()->ClearException();
-  }
+  StackHandleScope<4> hs(soa.Self());
+  auto old_exception = hs.NewHandle<mirror::Throwable>(soa.Self()->GetException());
+  soa.Self()->ClearException();
 
   // Translate the method through the vtable, unless the debugger wants to suppress it.
-  MutableHandle<mirror::ArtMethod> m(hs.NewHandle(pReq->method));
-  if ((pReq->options & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver != nullptr) {
-    mirror::ArtMethod* actual_method = pReq->klass->FindVirtualMethodForVirtualOrInterface(m.Get());
+  MutableHandle<mirror::ArtMethod> m(hs.NewHandle(pReq->method.Read()));
+  if ((pReq->options & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver.Read() != nullptr) {
+    mirror::ArtMethod* actual_method = pReq->klass.Read()->FindVirtualMethodForVirtualOrInterface(m.Get());
     if (actual_method != m.Get()) {
-      VLOG(jdwp) << "ExecuteMethod translated " << PrettyMethod(m.Get()) << " to " << PrettyMethod(actual_method);
+      VLOG(jdwp) << "ExecuteMethod translated " << PrettyMethod(m.Get())
+                 << " to " << PrettyMethod(actual_method);
       m.Assign(actual_method);
     }
   }
   VLOG(jdwp) << "ExecuteMethod " << PrettyMethod(m.Get())
-             << " receiver=" << pReq->receiver
+             << " receiver=" << pReq->receiver.Read()
              << " arg_count=" << pReq->arg_count;
   CHECK(m.Get() != nullptr);
 
   CHECK_EQ(sizeof(jvalue), sizeof(uint64_t));
 
-  pReq->result_value = InvokeWithJValues(soa, pReq->receiver, soa.EncodeMethod(m.Get()),
-                                         reinterpret_cast<jvalue*>(pReq->arg_values));
+  JValue result = InvokeWithJValues(soa, pReq->receiver.Read(), soa.EncodeMethod(m.Get()),
+                                    reinterpret_cast<jvalue*>(pReq->arg_values));
 
-  mirror::Throwable* exception = soa.Self()->GetException();
-  soa.Self()->ClearException();
-  pReq->exception = gRegistry->Add(exception);
   pReq->result_tag = BasicTagFromDescriptor(m.Get()->GetShorty());
+  const bool is_object_result = (pReq->result_tag == JDWP::JT_OBJECT);
+  Handle<mirror::Object> object_result = hs.NewHandle(is_object_result ? result.GetL() : nullptr);
+  Handle<mirror::Throwable> exception = hs.NewHandle(soa.Self()->GetException());
+  soa.Self()->ClearException();
+  pReq->exception = gRegistry->Add(exception.Get());
   if (pReq->exception != 0) {
-    VLOG(jdwp) << "  JDWP invocation returning with exception=" << exception
-        << " " << exception->Dump();
-    pReq->result_value.SetJ(0);
-  } else if (pReq->result_tag == JDWP::JT_OBJECT) {
+    VLOG(jdwp) << "  JDWP invocation returning with exception=" << exception.Get()
+               << " " << exception->Dump();
+    pReq->result_value = 0;
+  } else if (is_object_result) {
     /* if no exception thrown, examine object result more closely */
-    JDWP::JdwpTag new_tag = TagFromObject(soa, pReq->result_value.GetL());
+    JDWP::JdwpTag new_tag = TagFromObject(soa, object_result.Get());
     if (new_tag != pReq->result_tag) {
       VLOG(jdwp) << "  JDWP promoted result from " << pReq->result_tag << " to " << new_tag;
       pReq->result_tag = new_tag;
     }
 
-    /*
-     * Register the object.  We don't actually need an ObjectId yet,
-     * but we do need to be sure that the GC won't move or discard the
-     * object when we switch out of RUNNING.  The ObjectId conversion
-     * will add the object to the "do not touch" list.
-     *
-     * We can't use the "tracked allocation" mechanism here because
-     * the object is going to be handed off to a different thread.
-     */
-    gRegistry->Add(pReq->result_value.GetL());
+    // Register the object in the registry and reference its ObjectId. This ensures
+    // GC safety and prevents from accessing stale reference if the object is moved.
+    pReq->result_value = gRegistry->Add(object_result.Get());
+  } else {
+    // Primitive result.
+    DCHECK(IsPrimitiveTag(pReq->result_tag));
+    pReq->result_value = result.GetJ();
   }
 
   if (old_exception.Get() != nullptr) {
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 428ded7..01c9d5d 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -48,40 +48,33 @@
 class ScopedObjectAccessUnchecked;
 class StackVisitor;
 class Thread;
-class ThrowLocation;
 
 /*
  * Invoke-during-breakpoint support.
  */
 struct DebugInvokeReq {
-  DebugInvokeReq()
-      : ready(false), invoke_needed(false),
-        receiver(NULL), thread(NULL), klass(NULL), method(NULL),
-        arg_count(0), arg_values(NULL), options(0), error(JDWP::ERR_NONE),
-        result_tag(JDWP::JT_VOID), exception(0),
+  DebugInvokeReq(mirror::Object* invoke_receiver, mirror::Class* invoke_class,
+                 mirror::ArtMethod* invoke_method, uint32_t invoke_options,
+                 uint64_t* args, uint32_t args_count)
+      : receiver(invoke_receiver), klass(invoke_class), method(invoke_method),
+        arg_count(args_count), arg_values(args), options(invoke_options),
+        error(JDWP::ERR_NONE), result_tag(JDWP::JT_VOID), result_value(0), exception(0),
         lock("a DebugInvokeReq lock", kBreakpointInvokeLock),
         cond("a DebugInvokeReq condition variable", lock) {
   }
 
-  /* boolean; only set when we're in the tail end of an event handler */
-  bool ready;
-
-  /* boolean; set if the JDWP thread wants this thread to do work */
-  bool invoke_needed;
-
   /* request */
-  mirror::Object* receiver;      /* not used for ClassType.InvokeMethod */
-  mirror::Object* thread;
-  mirror::Class* klass;
-  mirror::ArtMethod* method;
-  uint32_t arg_count;
-  uint64_t* arg_values;   /* will be NULL if arg_count_ == 0 */
-  uint32_t options;
+  GcRoot<mirror::Object> receiver;      // not used for ClassType.InvokeMethod
+  GcRoot<mirror::Class> klass;
+  GcRoot<mirror::ArtMethod> method;
+  const uint32_t arg_count;
+  uint64_t* const arg_values;   // will be NULL if arg_count_ == 0
+  const uint32_t options;
 
   /* result */
   JDWP::JdwpError error;
   JDWP::JdwpTag result_tag;
-  JValue result_value;
+  uint64_t result_value;        // either a primitive value or an ObjectId
   JDWP::ObjectId exception;
 
   /* condition variable to wait on while the method executes */
@@ -91,8 +84,6 @@
   void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void Clear();
-
  private:
   DISALLOW_COPY_AND_ASSIGN(DebugInvokeReq);
 };
@@ -581,6 +572,8 @@
       LOCKS_EXCLUDED(Locks::thread_list_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Invoke support for commands ClassType.InvokeMethod, ClassType.NewInstance and
+  // ObjectReference.InvokeMethod.
   static JDWP::JdwpError InvokeMethod(JDWP::ObjectId thread_id, JDWP::ObjectId object_id,
                                       JDWP::RefTypeId class_id, JDWP::MethodId method_id,
                                       uint32_t arg_count, uint64_t* arg_values,
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 149c6b4..8a13d34 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -53,9 +53,7 @@
   }
   if (kAccessCheck) {
     if (UNLIKELY(!klass->IsInstantiable())) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      self->ThrowNewException(throw_location, "Ljava/lang/InstantiationError;",
-                              PrettyDescriptor(klass).c_str());
+      self->ThrowNewException("Ljava/lang/InstantiationError;", PrettyDescriptor(klass).c_str());
       *slow_path = true;
       return nullptr;  // Failure
     }
@@ -294,9 +292,7 @@
     } else {
       if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive ||
                    resolved_field->FieldSize() != expected_size)) {
-        ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-        DCHECK(throw_location.GetMethod() == referrer);
-        self->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
+        self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
                                  "Attempted read of %zd-bit %s on field '%s'",
                                  expected_size * (32 / sizeof(int32_t)),
                                  is_primitive ? "primitive" : "non-primitive",
@@ -367,9 +363,7 @@
   } else if (UNLIKELY(*this_object == nullptr && type != kStatic)) {
     // Maintain interpreter-like semantics where NullPointerException is thrown
     // after potential NoSuchMethodError from class linker.
-    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-    DCHECK_EQ(*referrer, throw_location.GetMethod());
-    ThrowNullPointerExceptionForMethodAccess(throw_location, method_idx, type);
+    ThrowNullPointerExceptionForMethodAccess(method_idx, type);
     return nullptr;  // Failure.
   } else if (access_check) {
     // Incompatible class change should have been handled in resolve method.
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index af528b7..70e2851 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -55,10 +55,8 @@
       ThrowRuntimeException("Bad filled array request for type %s",
                             PrettyDescriptor(klass).c_str());
     } else {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      DCHECK(throw_location.GetMethod() == referrer);
       self->ThrowNewExceptionF(
-          throw_location, "Ljava/lang/InternalError;",
+          "Ljava/lang/InternalError;",
           "Found type %s; filled-new-array not implemented for anything but 'int'",
           PrettyDescriptor(klass).c_str());
     }
@@ -281,18 +279,8 @@
       // This can cause thread suspension.
       mirror::Class* result_type = h_interface_method->GetReturnType();
       mirror::Object* result_ref = soa.Decode<mirror::Object*>(result);
-      mirror::Object* rcvr = soa.Decode<mirror::Object*>(rcvr_jobj);
-      mirror::ArtMethod* proxy_method;
-      if (h_interface_method->GetDeclaringClass()->IsInterface()) {
-        proxy_method = rcvr->GetClass()->FindVirtualMethodForInterface(h_interface_method.Get());
-      } else {
-        // Proxy dispatch to a method defined in Object.
-        DCHECK(h_interface_method->GetDeclaringClass()->IsObjectClass());
-        proxy_method = h_interface_method.Get();
-      }
-      ThrowLocation throw_location(rcvr, proxy_method, -1);
       JValue result_unboxed;
-      if (!UnboxPrimitiveForResult(throw_location, result_ref, result_type, &result_unboxed)) {
+      if (!UnboxPrimitiveForResult(result_ref, result_type, &result_unboxed)) {
         DCHECK(soa.Self()->IsExceptionPending());
         return zero;
       }
@@ -327,9 +315,7 @@
         declares_exception = declared_exception->IsAssignableFrom(exception_class);
       }
       if (!declares_exception) {
-        ThrowLocation throw_location(rcvr, proxy_method, -1);
-        soa.Self()->ThrowNewWrappedException(throw_location,
-                                             "Ljava/lang/reflect/UndeclaredThrowableException;",
+        soa.Self()->ThrowNewWrappedException("Ljava/lang/reflect/UndeclaredThrowableException;",
                                              NULL);
       }
     }
@@ -340,16 +326,14 @@
 bool FillArrayData(mirror::Object* obj, const Instruction::ArrayDataPayload* payload) {
   DCHECK_EQ(payload->ident, static_cast<uint16_t>(Instruction::kArrayDataSignature));
   if (UNLIKELY(obj == nullptr)) {
-    ThrowNullPointerException(nullptr, "null array in FILL_ARRAY_DATA");
+    ThrowNullPointerException("null array in FILL_ARRAY_DATA");
     return false;
   }
   mirror::Array* array = obj->AsArray();
   DCHECK(!array->IsObjectArray());
   if (UNLIKELY(static_cast<int32_t>(payload->element_count) > array->GetLength())) {
     Thread* self = Thread::Current();
-    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-    self->ThrowNewExceptionF(throw_location,
-                             "Ljava/lang/ArrayIndexOutOfBoundsException;",
+    self->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
                              "failed FILL_ARRAY_DATA; length=%d, index=%d",
                              array->GetLength(), payload->element_count);
     return false;
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index 7326fcf..22bf939 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -155,8 +155,7 @@
                                                          sizeof(int8_t));
   if (LIKELY(field != nullptr)) {
     if (UNLIKELY(obj == nullptr)) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+      ThrowNullPointerExceptionForFieldAccess(field, true);
     } else {
       return field->GetByte(obj);
     }
@@ -177,8 +176,7 @@
                                                          sizeof(int8_t));
   if (LIKELY(field != nullptr)) {
     if (UNLIKELY(obj == nullptr)) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+      ThrowNullPointerExceptionForFieldAccess(field, true);
     } else {
       return field->GetBoolean(obj);
     }
@@ -198,8 +196,7 @@
                                                          sizeof(int16_t));
   if (LIKELY(field != nullptr)) {
     if (UNLIKELY(obj == nullptr)) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+      ThrowNullPointerExceptionForFieldAccess(field, true);
     } else {
       return field->GetShort(obj);
     }
@@ -220,8 +217,7 @@
                                                          sizeof(int16_t));
   if (LIKELY(field != nullptr)) {
     if (UNLIKELY(obj == nullptr)) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+      ThrowNullPointerExceptionForFieldAccess(field, true);
     } else {
       return field->GetChar(obj);
     }
@@ -242,8 +238,7 @@
                                                          sizeof(int32_t));
   if (LIKELY(field != nullptr)) {
     if (UNLIKELY(obj == nullptr)) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+      ThrowNullPointerExceptionForFieldAccess(field, true);
     } else {
       return field->Get32(obj);
     }
@@ -264,8 +259,7 @@
                                                          sizeof(int64_t));
   if (LIKELY(field != nullptr)) {
     if (UNLIKELY(obj == nullptr)) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+      ThrowNullPointerExceptionForFieldAccess(field, true);
     } else {
       return field->Get64(obj);
     }
@@ -287,8 +281,7 @@
                                                       sizeof(mirror::HeapReference<mirror::Object>));
   if (LIKELY(field != nullptr)) {
     if (UNLIKELY(obj == nullptr)) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, true);
+      ThrowNullPointerExceptionForFieldAccess(field, true);
     } else {
       return field->GetObj(obj);
     }
@@ -448,8 +441,7 @@
   }
   if (LIKELY(field != nullptr)) {
     if (UNLIKELY(obj == nullptr)) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
+      ThrowNullPointerExceptionForFieldAccess(field, false);
     } else {
       Primitive::Type type = field->GetTypeAsPrimitiveType();
       // Compiled code can't use transactional mode.
@@ -489,8 +481,7 @@
   }
   if (LIKELY(field != nullptr)) {
     if (UNLIKELY(obj == nullptr)) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
+      ThrowNullPointerExceptionForFieldAccess(field, false);
     } else {
       Primitive::Type type = field->GetTypeAsPrimitiveType();
       // Compiled code can't use transactional mode.
@@ -525,8 +516,7 @@
   }
   if (LIKELY(field != nullptr)) {
     if (UNLIKELY(obj == nullptr)) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
+      ThrowNullPointerExceptionForFieldAccess(field, false);
     } else {
       // Compiled code can't use transactional mode.
       field->Set32<false>(obj, new_value);
@@ -551,8 +541,7 @@
                                                           sizeof(int64_t));
   if (LIKELY(field != nullptr)) {
     if (UNLIKELY(obj == nullptr)) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
+      ThrowNullPointerExceptionForFieldAccess(field, false);
     } else {
       // Compiled code can't use transactional mode.
       field->Set64<false>(obj, new_value);
@@ -578,8 +567,7 @@
                                                        sizeof(mirror::HeapReference<mirror::Object>));
   if (LIKELY(field != nullptr)) {
     if (UNLIKELY(obj == nullptr)) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      ThrowNullPointerExceptionForFieldAccess(throw_location, field, false);
+      ThrowNullPointerExceptionForFieldAccess(field, false);
     } else {
       // Compiled code can't use transactional mode.
       field->SetObj<false>(obj, new_value);
diff --git a/runtime/entrypoints/quick/quick_lock_entrypoints.cc b/runtime/entrypoints/quick/quick_lock_entrypoints.cc
index 8ceac97..4423c08 100644
--- a/runtime/entrypoints/quick/quick_lock_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_lock_entrypoints.cc
@@ -25,9 +25,7 @@
     NO_THREAD_SAFETY_ANALYSIS /* EXCLUSIVE_LOCK_FUNCTION(Monitor::monitor_lock_) */ {
   ScopedQuickEntrypointChecks sqec(self);
   if (UNLIKELY(obj == nullptr)) {
-    ThrowLocation throw_location(self->GetCurrentLocationForThrow());
-    ThrowNullPointerException(&throw_location,
-                              "Null reference used for synchronization (monitor-enter)");
+    ThrowNullPointerException("Null reference used for synchronization (monitor-enter)");
     return -1;  // Failure.
   } else {
     if (kIsDebugBuild) {
@@ -47,9 +45,7 @@
     NO_THREAD_SAFETY_ANALYSIS /* UNLOCK_FUNCTION(Monitor::monitor_lock_) */ {
   ScopedQuickEntrypointChecks sqec(self);
   if (UNLIKELY(obj == nullptr)) {
-    ThrowLocation throw_location(self->GetCurrentLocationForThrow());
-    ThrowNullPointerException(&throw_location,
-                              "Null reference used for synchronization (monitor-exit)");
+    ThrowNullPointerException("Null reference used for synchronization (monitor-exit)");
     return -1;  // Failure.
   } else {
     // MonitorExit may throw exception.
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index 127f9e0..70317bb 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -42,9 +42,7 @@
    */
   ScopedQuickEntrypointChecks sqec(self);
   if (exception == nullptr) {
-    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-    self->ThrowNewException(throw_location, "Ljava/lang/NullPointerException;",
-                            "throw with null exception");
+    self->ThrowNewException("Ljava/lang/NullPointerException;", "throw with null exception");
   } else {
     self->SetException(exception);
   }
@@ -56,8 +54,7 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   ScopedQuickEntrypointChecks sqec(self);
   self->NoteSignalBeingHandled();
-  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-  ThrowNullPointerExceptionFromDexPC(throw_location);
+  ThrowNullPointerExceptionFromDexPC();
   self->NoteSignalHandlerDone();
   self->QuickDeliverException();
 }
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index e3f18f1..8972f3a 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -38,7 +38,6 @@
 }  // namespace mirror
 union JValue;
 class Thread;
-class ThrowLocation;
 
 namespace instrumentation {
 
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index e6e647c..2a9c0d4 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -23,8 +23,8 @@
 namespace art {
 namespace interpreter {
 
-void ThrowNullPointerExceptionFromInterpreter(const ShadowFrame& shadow_frame) {
-  ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+void ThrowNullPointerExceptionFromInterpreter() {
+  ThrowNullPointerExceptionFromDexPC();
 }
 
 template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
@@ -44,7 +44,7 @@
   } else {
     obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
     if (UNLIKELY(obj == nullptr)) {
-      ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(), f, true);
+      ThrowNullPointerExceptionForFieldAccess(f, true);
       return false;
     }
   }
@@ -126,7 +126,7 @@
   if (UNLIKELY(obj == nullptr)) {
     // We lost the reference to the field index so we cannot get a more
     // precised exception message.
-    ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+    ThrowNullPointerExceptionFromDexPC();
     return false;
   }
   MemberOffset field_offset(inst->VRegC_22c());
@@ -238,8 +238,7 @@
   } else {
     obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
     if (UNLIKELY(obj == nullptr)) {
-      ThrowNullPointerExceptionForFieldAccess(shadow_frame.GetCurrentLocationForThrow(),
-                                              f, false);
+      ThrowNullPointerExceptionForFieldAccess(f, false);
       return false;
     }
   }
@@ -289,8 +288,7 @@
         if (!reg->VerifierInstanceOf(field_class)) {
           // This should never happen.
           std::string temp1, temp2, temp3;
-          self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
-                                   "Ljava/lang/VirtualMachineError;",
+          self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
                                    "Put '%s' that is not instance of field '%s' in '%s'",
                                    reg->GetClass()->GetDescriptor(&temp1),
                                    field_class->GetDescriptor(&temp2),
@@ -346,7 +344,7 @@
   if (UNLIKELY(obj == nullptr)) {
     // We lost the reference to the field index so we cannot get a more
     // precised exception message.
-    ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+    ThrowNullPointerExceptionFromDexPC();
     return false;
   }
   MemberOffset field_offset(inst->VRegC_22c());
@@ -555,8 +553,7 @@
             if (!o->VerifierInstanceOf(arg_type)) {
               // This should never happen.
               std::string temp1, temp2;
-              self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
-                                       "Ljava/lang/VirtualMachineError;",
+              self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
                                        "Invoking %s with bad arg %d, type '%s' not instance of '%s'",
                                        new_shadow_frame->GetMethod()->GetName(), shorty_pos,
                                        o->GetClass()->GetDescriptor(&temp1),
@@ -658,8 +655,7 @@
       ThrowRuntimeException("Bad filled array request for type %s",
                             PrettyDescriptor(componentClass).c_str());
     } else {
-      self->ThrowNewExceptionF(shadow_frame.GetCurrentLocationForThrow(),
-                               "Ljava/lang/InternalError;",
+      self->ThrowNewExceptionF("Ljava/lang/InternalError;",
                                "Found type %s; filled-new-array not implemented for anything but 'int'",
                                PrettyDescriptor(componentClass).c_str());
     }
@@ -776,8 +772,7 @@
     // If it is not an InternalError, wrap it.
     std::string type(PrettyTypeOf(self->GetException()));
     if (type != "java.lang.InternalError") {
-      self->ThrowNewWrappedException(self->GetCurrentLocationForThrow(),
-                                     "Ljava/lang/ClassNotFoundException;",
+      self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;",
                                      "ClassNotFoundException");
     }
   }
@@ -856,7 +851,7 @@
           ok = true;
         }
       } else {
-        self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), "Ljava/lang/InternalError;",
+        self->ThrowNewExceptionF("Ljava/lang/InternalError;",
                                  "Could not find default constructor for '%s'",
                                  PrettyClass(h_klass.Get()).c_str());
       }
@@ -865,9 +860,7 @@
       std::string error_msg = StringPrintf("Failed in Class.newInstance for '%s' with %s",
                                            PrettyClass(h_klass.Get()).c_str(),
                                            PrettyTypeOf(self->GetException()).c_str());
-      self->ThrowNewWrappedException(self->GetCurrentLocationForThrow(),
-                                     "Ljava/lang/InternalError;",
-                                     error_msg.c_str());
+      self->ThrowNewWrappedException("Ljava/lang/InternalError;", error_msg.c_str());
     }
   } else if (name == "java.lang.reflect.Field java.lang.Class.getDeclaredField(java.lang.String)") {
     // Special managed code cut-out to allow field lookup in a un-started runtime that'd fail
@@ -938,7 +931,7 @@
         dst->Set(dstPos + i, src->Get(srcPos + i));
       }
     } else {
-      self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(), "Ljava/lang/InternalError;",
+      self->ThrowNewExceptionF("Ljava/lang/InternalError;",
                                "Unimplemented System.arraycopy for type '%s'",
                                PrettyDescriptor(ctype).c_str());
     }
@@ -1005,8 +998,7 @@
     }
 
     if (!ok) {
-      self->ThrowNewException(self->GetCurrentLocationForThrow(), "Ljava/lang/InternalError;",
-                              "Unimplemented ThreadLocal.get");
+      self->ThrowNewException("Ljava/lang/InternalError;", "Unimplemented ThreadLocal.get");
     }
   } else {
     // Not special, continue with regular interpreter execution.
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 06b809f..3095316 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -77,7 +77,7 @@
 extern JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item,
                               ShadowFrame& shadow_frame, JValue result_register);
 
-void ThrowNullPointerExceptionFromInterpreter(const ShadowFrame& shadow_frame)
+void ThrowNullPointerExceptionFromInterpreter()
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 static inline void DoMonitorEnter(Thread* self, Object* ref) NO_THREAD_SAFETY_ANALYSIS {
@@ -138,7 +138,7 @@
   if (UNLIKELY(receiver == nullptr)) {
     // We lost the reference to the method index so we cannot get a more
     // precised exception message.
-    ThrowNullPointerExceptionFromDexPC(shadow_frame.GetCurrentLocationForThrow());
+    ThrowNullPointerExceptionFromDexPC();
     return false;
   }
   const uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index f1ab747..5f97f94 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -341,8 +341,7 @@
       if (!obj_result->VerifierInstanceOf(return_type)) {
         // This should never happen.
         std::string temp1, temp2;
-        self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
-                                 "Ljava/lang/VirtualMachineError;",
+        self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
                                  "Returning '%s' that is not instance of return type '%s'",
                                  obj_result->GetClass()->GetDescriptor(&temp1),
                                  return_type->GetDescriptor(&temp2));
@@ -465,7 +464,7 @@
   HANDLE_INSTRUCTION_START(MONITOR_ENTER) {
     Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
     if (UNLIKELY(obj == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       DoMonitorEnter(self, obj);
@@ -477,7 +476,7 @@
   HANDLE_INSTRUCTION_START(MONITOR_EXIT) {
     Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
     if (UNLIKELY(obj == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       DoMonitorExit(self, obj);
@@ -519,7 +518,7 @@
   HANDLE_INSTRUCTION_START(ARRAY_LENGTH) {
     Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
     if (UNLIKELY(array == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength());
@@ -596,12 +595,11 @@
   HANDLE_INSTRUCTION_START(THROW) {
     Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
     if (UNLIKELY(exception == NULL)) {
-      ThrowNullPointerException(NULL, "throw with null exception");
+      ThrowNullPointerException("throw with null exception");
     } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
       // This should never happen.
       std::string temp;
-      self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
-                               "Ljava/lang/VirtualMachineError;",
+      self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
                                "Throwing '%s' that is not instance of Throwable",
                                exception->GetClass()->GetDescriptor(&temp));
     } else {
@@ -972,7 +970,7 @@
   HANDLE_INSTRUCTION_START(AGET_BOOLEAN) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
@@ -990,7 +988,7 @@
   HANDLE_INSTRUCTION_START(AGET_BYTE) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
@@ -1008,7 +1006,7 @@
   HANDLE_INSTRUCTION_START(AGET_CHAR) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
@@ -1026,7 +1024,7 @@
   HANDLE_INSTRUCTION_START(AGET_SHORT) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
@@ -1044,7 +1042,7 @@
   HANDLE_INSTRUCTION_START(AGET) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
@@ -1062,7 +1060,7 @@
   HANDLE_INSTRUCTION_START(AGET_WIDE)  {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
@@ -1080,7 +1078,7 @@
   HANDLE_INSTRUCTION_START(AGET_OBJECT) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
@@ -1098,7 +1096,7 @@
   HANDLE_INSTRUCTION_START(APUT_BOOLEAN) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       uint8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
@@ -1117,7 +1115,7 @@
   HANDLE_INSTRUCTION_START(APUT_BYTE) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       int8_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
@@ -1136,7 +1134,7 @@
   HANDLE_INSTRUCTION_START(APUT_CHAR) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       uint16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
@@ -1155,7 +1153,7 @@
   HANDLE_INSTRUCTION_START(APUT_SHORT) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       int16_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
@@ -1174,7 +1172,7 @@
   HANDLE_INSTRUCTION_START(APUT) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       int32_t val = shadow_frame.GetVReg(inst->VRegA_23x(inst_data));
@@ -1193,7 +1191,7 @@
   HANDLE_INSTRUCTION_START(APUT_WIDE) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       int64_t val = shadow_frame.GetVRegLong(inst->VRegA_23x(inst_data));
@@ -1212,7 +1210,7 @@
   HANDLE_INSTRUCTION_START(APUT_OBJECT) {
     Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
     if (UNLIKELY(a == NULL)) {
-      ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+      ThrowNullPointerExceptionFromInterpreter();
       HANDLE_PENDING_EXCEPTION();
     } else {
       int32_t index = shadow_frame.GetVReg(inst->VRegC_23x());
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index dceed47..9313c75 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -248,8 +248,7 @@
           if (!obj_result->VerifierInstanceOf(return_type)) {
             // This should never happen.
             std::string temp1, temp2;
-            self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
-                                     "Ljava/lang/VirtualMachineError;",
+            self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
                                      "Returning '%s' that is not instance of return type '%s'",
                                      obj_result->GetClass()->GetDescriptor(&temp1),
                                      return_type->GetDescriptor(&temp2));
@@ -370,7 +369,7 @@
         PREAMBLE();
         Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
         if (UNLIKELY(obj == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
         } else {
           DoMonitorEnter(self, obj);
@@ -382,7 +381,7 @@
         PREAMBLE();
         Object* obj = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
         if (UNLIKELY(obj == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
         } else {
           DoMonitorExit(self, obj);
@@ -424,7 +423,7 @@
         PREAMBLE();
         Object* array = shadow_frame.GetVRegReference(inst->VRegB_12x(inst_data));
         if (UNLIKELY(array == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
         } else {
           shadow_frame.SetVReg(inst->VRegA_12x(inst_data), array->AsArray()->GetLength());
@@ -506,12 +505,11 @@
         PREAMBLE();
         Object* exception = shadow_frame.GetVRegReference(inst->VRegA_11x(inst_data));
         if (UNLIKELY(exception == NULL)) {
-          ThrowNullPointerException(NULL, "throw with null exception");
+          ThrowNullPointerException("throw with null exception");
         } else if (do_assignability_check && !exception->GetClass()->IsThrowableClass()) {
           // This should never happen.
           std::string temp;
-          self->ThrowNewExceptionF(self->GetCurrentLocationForThrow(),
-                                   "Ljava/lang/VirtualMachineError;",
+          self->ThrowNewExceptionF("Ljava/lang/VirtualMachineError;",
                                    "Throwing '%s' that is not instance of Throwable",
                                    exception->GetClass()->GetDescriptor(&temp));
         } else {
@@ -817,7 +815,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -835,7 +833,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -853,7 +851,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -871,7 +869,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -889,7 +887,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -907,7 +905,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -925,7 +923,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -943,7 +941,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -962,7 +960,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -981,7 +979,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -1000,7 +998,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -1019,7 +1017,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -1038,7 +1036,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
@@ -1057,7 +1055,7 @@
         PREAMBLE();
         Object* a = shadow_frame.GetVRegReference(inst->VRegB_23x());
         if (UNLIKELY(a == NULL)) {
-          ThrowNullPointerExceptionFromInterpreter(shadow_frame);
+          ThrowNullPointerExceptionFromInterpreter();
           HANDLE_PENDING_EXCEPTION();
           break;
         }
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 08332d3..e68616f 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -741,8 +741,7 @@
   }
   // Throwing can cause libraries_lock to be reacquired.
   if (native_method == nullptr) {
-    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-    self->ThrowNewException(throw_location, "Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
+    self->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;", detail.c_str());
   }
   return native_method;
 }
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index fc08d23..4bf7142 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -596,17 +596,15 @@
     return;
   }
 
-  DebugInvokeReq* pReq = Dbg::GetInvokeReq();
   while (true) {
-    pReq->ready = true;
     Dbg::SuspendSelf();
-    pReq->ready = false;
 
     /*
      * The JDWP thread has told us (and possibly all other threads) to
      * resume.  See if it has left anything in our DebugInvokeReq mailbox.
      */
-    if (!pReq->invoke_needed) {
+    DebugInvokeReq* const pReq = Dbg::GetInvokeReq();
+    if (pReq == nullptr) {
       /*LOGD("SuspendByPolicy: no invoke needed");*/
       break;
     }
@@ -614,10 +612,7 @@
     /* grab this before posting/suspending again */
     AcquireJdwpTokenForEvent(thread_self_id);
 
-    /* leave pReq->invoke_needed_ raised so we can check reentrancy */
     Dbg::ExecuteMethod(pReq);
-
-    pReq->error = ERR_NONE;
   }
 }
 
@@ -650,7 +645,7 @@
  */
 bool JdwpState::InvokeInProgress() {
   DebugInvokeReq* pReq = Dbg::GetInvokeReq();
-  return pReq->invoke_needed;
+  return pReq != nullptr;
 }
 
 void JdwpState::AcquireJdwpTokenForCommand() {
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 0ce4de7..c7083dc 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -91,9 +91,9 @@
  * If "is_constructor" is set, this returns "object_id" rather than the
  * expected-to-be-void return value of the called function.
  */
-static JdwpError FinishInvoke(JdwpState*, Request* request, ExpandBuf* pReply,
-                              ObjectId thread_id, ObjectId object_id,
-                              RefTypeId class_id, MethodId method_id, bool is_constructor)
+static JdwpError RequestInvoke(JdwpState*, Request* request, ExpandBuf* pReply,
+                               ObjectId thread_id, ObjectId object_id,
+                               RefTypeId class_id, MethodId method_id, bool is_constructor)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   CHECK(!is_constructor || object_id != 0);
 
@@ -131,37 +131,35 @@
     return err;
   }
 
-  if (err == ERR_NONE) {
-    if (is_constructor) {
-      // If we invoked a constructor (which actually returns void), return the receiver,
-      // unless we threw, in which case we return NULL.
-      resultTag = JT_OBJECT;
-      resultValue = (exceptObjId == 0) ? object_id : 0;
-    }
+  if (is_constructor) {
+    // If we invoked a constructor (which actually returns void), return the receiver,
+    // unless we threw, in which case we return NULL.
+    resultTag = JT_OBJECT;
+    resultValue = (exceptObjId == 0) ? object_id : 0;
+  }
 
-    size_t width = Dbg::GetTagWidth(resultTag);
-    expandBufAdd1(pReply, resultTag);
-    if (width != 0) {
-      WriteValue(pReply, width, resultValue);
-    }
-    expandBufAdd1(pReply, JT_OBJECT);
-    expandBufAddObjectId(pReply, exceptObjId);
+  size_t width = Dbg::GetTagWidth(resultTag);
+  expandBufAdd1(pReply, resultTag);
+  if (width != 0) {
+    WriteValue(pReply, width, resultValue);
+  }
+  expandBufAdd1(pReply, JT_OBJECT);
+  expandBufAddObjectId(pReply, exceptObjId);
 
-    VLOG(jdwp) << "  --> returned " << resultTag
-        << StringPrintf(" %#" PRIx64 " (except=%#" PRIx64 ")", resultValue, exceptObjId);
+  VLOG(jdwp) << "  --> returned " << resultTag
+      << StringPrintf(" %#" PRIx64 " (except=%#" PRIx64 ")", resultValue, exceptObjId);
 
-    /* show detailed debug output */
-    if (resultTag == JT_STRING && exceptObjId == 0) {
-      if (resultValue != 0) {
-        if (VLOG_IS_ON(jdwp)) {
-          std::string result_string;
-          JDWP::JdwpError error = Dbg::StringToUtf8(resultValue, &result_string);
-          CHECK_EQ(error, JDWP::ERR_NONE);
-          VLOG(jdwp) << "      string '" << result_string << "'";
-        }
-      } else {
-        VLOG(jdwp) << "      string (null)";
+  /* show detailed debug output */
+  if (resultTag == JT_STRING && exceptObjId == 0) {
+    if (resultValue != 0) {
+      if (VLOG_IS_ON(jdwp)) {
+        std::string result_string;
+        JDWP::JdwpError error = Dbg::StringToUtf8(resultValue, &result_string);
+        CHECK_EQ(error, JDWP::ERR_NONE);
+        VLOG(jdwp) << "      string '" << result_string << "'";
       }
+    } else {
+      VLOG(jdwp) << "      string (null)";
     }
   }
 
@@ -693,7 +691,7 @@
   ObjectId thread_id = request->ReadThreadId();
   MethodId method_id = request->ReadMethodId();
 
-  return FinishInvoke(state, request, pReply, thread_id, 0, class_id, method_id, false);
+  return RequestInvoke(state, request, pReply, thread_id, 0, class_id, method_id, false);
 }
 
 /*
@@ -717,7 +715,7 @@
   if (object_id == 0) {
     return ERR_OUT_OF_MEMORY;
   }
-  return FinishInvoke(state, request, pReply, thread_id, object_id, class_id, method_id, true);
+  return RequestInvoke(state, request, pReply, thread_id, object_id, class_id, method_id, true);
 }
 
 /*
@@ -879,7 +877,7 @@
   RefTypeId class_id = request->ReadRefTypeId();
   MethodId method_id = request->ReadMethodId();
 
-  return FinishInvoke(state, request, pReply, thread_id, object_id, class_id, method_id, false);
+  return RequestInvoke(state, request, pReply, thread_id, object_id, class_id, method_id, false);
 }
 
 static JdwpError OR_DisableCollection(JdwpState*, Request* request, ExpandBuf*)
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 539c181..9b89459 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -40,16 +40,32 @@
       options.GetOrDefault(RuntimeArgumentMap::JITCodeCacheCapacity);
   jit_options->compile_threshold_ =
       options.GetOrDefault(RuntimeArgumentMap::JITCompileThreshold);
+  jit_options->dump_info_on_shutdown_ =
+      options.Exists(RuntimeArgumentMap::DumpJITInfoOnShutdown);
   return jit_options;
 }
 
+void Jit::DumpInfo(std::ostream& os) {
+  os << "Code cache size=" << PrettySize(code_cache_->CodeCacheSize())
+     << " data cache size=" << PrettySize(code_cache_->DataCacheSize())
+     << " num methods=" << code_cache_->NumMethods()
+     << "\n";
+  cumulative_timings_.Dump(os);
+}
+
+void Jit::AddTimingLogger(const TimingLogger& logger) {
+  cumulative_timings_.AddLogger(logger);
+}
+
 Jit::Jit()
     : jit_library_handle_(nullptr), jit_compiler_handle_(nullptr), jit_load_(nullptr),
-      jit_compile_method_(nullptr) {
+      jit_compile_method_(nullptr), dump_info_on_shutdown_(false),
+      cumulative_timings_("JIT timings") {
 }
 
 Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
   std::unique_ptr<Jit> jit(new Jit);
+  jit->dump_info_on_shutdown_ = options->DumpJitInfoOnShutdown();
   if (!jit->LoadCompiler(error_msg)) {
     return nullptr;
   }
@@ -133,6 +149,9 @@
 }
 
 Jit::~Jit() {
+  if (dump_info_on_shutdown_) {
+    DumpInfo(LOG(INFO));
+  }
   DeleteThreadPool();
   if (jit_compiler_handle_ != nullptr) {
     jit_unload_(jit_compiler_handle_);
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index b80015f..6b206d1 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -24,6 +24,7 @@
 #include "atomic.h"
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "base/timing_logger.h"
 #include "gc_root.h"
 #include "jni.h"
 #include "object_callbacks.h"
@@ -61,6 +62,11 @@
     return code_cache_.get();
   }
   void DeleteThreadPool();
+  // Dump interesting info: #methods compiled, code vs data size, compile / verify cumulative
+  // loggers.
+  void DumpInfo(std::ostream& os);
+  // Add a timing logger to cumulative_timings_.
+  void AddTimingLogger(const TimingLogger& logger);
 
  private:
   Jit();
@@ -73,6 +79,10 @@
   void (*jit_unload_)(void*);
   bool (*jit_compile_method_)(void*, mirror::ArtMethod*, Thread*);
 
+  // Performance monitoring.
+  bool dump_info_on_shutdown_;
+  CumulativeLogger cumulative_timings_;
+
   std::unique_ptr<jit::JitInstrumentationCache> instrumentation_cache_;
   std::unique_ptr<jit::JitCodeCache> code_cache_;
   CompilerCallbacks* compiler_callbacks_;  // Owned by the jit compiler.
@@ -87,12 +97,16 @@
   size_t GetCodeCacheCapacity() const {
     return code_cache_capacity_;
   }
+  bool DumpJitInfoOnShutdown() const {
+    return dump_info_on_shutdown_;
+  }
 
  private:
   size_t code_cache_capacity_;
   size_t compile_threshold_;
+  bool dump_info_on_shutdown_;
 
-  JitOptions() : code_cache_capacity_(0), compile_threshold_(0) {
+  JitOptions() : code_cache_capacity_(0), compile_threshold_(0), dump_info_on_shutdown_(false) {
   }
 };
 
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 4ae4d57..4d367e0 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -77,6 +77,7 @@
   if (size > CodeCacheRemain()) {
     return nullptr;
   }
+  ++num_methods_;  // TODO: This is hacky but works since each method has exactly one code region.
   code_cache_ptr_ += size;
   return code_cache_ptr_ - size;
 }
diff --git a/runtime/jit/jit_instrumentation.h b/runtime/jit/jit_instrumentation.h
index 9d122e0..425d2d3 100644
--- a/runtime/jit/jit_instrumentation.h
+++ b/runtime/jit/jit_instrumentation.h
@@ -39,7 +39,6 @@
 }  // namespace mirror
 union JValue;
 class Thread;
-class ThrowLocation;
 
 namespace jit {
 
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 213de6f..6063e1e 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -45,7 +45,6 @@
 #include "mirror/object_array-inl.h"
 #include "mirror/string-inl.h"
 #include "mirror/throwable.h"
-#include "nativebridge/native_bridge.h"
 #include "parsed_options.h"
 #include "reflection.h"
 #include "runtime.h"
@@ -89,9 +88,8 @@
 static void ThrowNoSuchMethodError(ScopedObjectAccess& soa, mirror::Class* c,
                                    const char* name, const char* sig, const char* kind)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
   std::string temp;
-  soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchMethodError;",
+  soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
                                  "no %s method \"%s.%s%s\"",
                                  kind, c->GetDescriptor(&temp), name, sig);
 }
@@ -102,8 +100,7 @@
   LOG(return_errors ? ERROR : FATAL) << "Failed to register native method in "
       << PrettyDescriptor(c) << " in " << c->GetDexCache()->GetLocation()->ToModifiedUtf8()
       << ": " << kind << " is null at index " << idx;
-  ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
-  soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchMethodError;",
+  soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchMethodError;",
                                  "%s is null at index %d", kind, idx);
 }
 
@@ -200,8 +197,7 @@
     Handle<mirror::Throwable> cause(hs2.NewHandle(soa.Self()->GetException()));
     soa.Self()->ClearException();
     std::string temp;
-    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
-    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
                                    "no type \"%s\" found and so no field \"%s\" "
                                    "could be found in class \"%s\" or its superclasses", sig, name,
                                    c->GetDescriptor(&temp));
@@ -216,8 +212,7 @@
     field = c->FindInstanceField(name, field_type->GetDescriptor(&temp));
   }
   if (field == nullptr) {
-    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
-    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/NoSuchFieldError;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
                                    "no \"%s\" field \"%s\" in class \"%s\" or its superclasses",
                                    sig, name, c->GetDescriptor(&temp));
     return nullptr;
@@ -229,8 +224,7 @@
                         jsize length, const char* identifier)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::string type(PrettyTypeOf(array));
-  ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
-  soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayIndexOutOfBoundsException;",
+  soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
                                  "%s offset=%d length=%d %s.length=%d",
                                  type.c_str(), start, length, identifier, array->GetLength());
 }
@@ -238,8 +232,7 @@
 static void ThrowSIOOBE(ScopedObjectAccess& soa, jsize start, jsize length,
                         jsize array_length)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
-  soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/StringIndexOutOfBoundsException;",
+  soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
                                  "offset=%d length=%d string.length()=%d", start, length,
                                  array_length);
 }
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index 96d426b..80d5135 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -233,9 +233,8 @@
     std::string actualSrcType(PrettyTypeOf(o));
     std::string dstType(PrettyTypeOf(this));
     Thread* self = Thread::Current();
-    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
     if (throw_exception) {
-      self->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayStoreException;",
+      self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
                                "source[%d] of type %s cannot be stored in destination array of type %s",
                                src_pos + i, actualSrcType.c_str(), dstType.c_str());
     } else {
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index dce8bac..d41d37e 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -300,8 +300,7 @@
   va_list args;
   va_start(args, fmt);
   Thread* self = Thread::Current();
-  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-  self->ThrowNewExceptionV(throw_location, "Ljava/lang/IllegalMonitorStateException;", fmt, args);
+  self->ThrowNewExceptionV("Ljava/lang/IllegalMonitorStateException;", fmt, args);
   if (!Runtime::Current()->IsStarted() || VLOG_IS_ON(monitor)) {
     std::ostringstream ss;
     self->Dump(ss);
@@ -428,8 +427,7 @@
   // Enforce the timeout range.
   if (ms < 0 || ns < 0 || ns > 999999) {
     monitor_lock_.Unlock(self);
-    ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-    self->ThrowNewExceptionF(throw_location, "Ljava/lang/IllegalArgumentException;",
+    self->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
                              "timeout arguments out of range: ms=%" PRId64 " ns=%d", ms, ns);
     return;
   }
@@ -540,8 +538,7 @@
       self->SetInterruptedLocked(false);
     }
     if (interruptShouldThrow) {
-      ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-      self->ThrowNewException(throw_location, "Ljava/lang/InterruptedException;", NULL);
+      self->ThrowNewException("Ljava/lang/InterruptedException;", NULL);
     }
   }
 }
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index e1fe3eb..c182a4d 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -16,31 +16,17 @@
 
 #include "dalvik_system_DexFile.h"
 
-#include <algorithm>
-#include <set>
-#include <fcntl.h>
-#ifdef __linux__
-#include <sys/sendfile.h>
-#else
-#include <sys/socket.h>
-#endif
-#include <sys/stat.h>
-#include <unistd.h>
-
 #include "base/logging.h"
 #include "base/stl_util.h"
 #include "base/stringprintf.h"
 #include "class_linker.h"
 #include "common_throws.h"
 #include "dex_file-inl.h"
-#include "gc/space/image_space.h"
-#include "gc/space/space-inl.h"
-#include "image.h"
 #include "jni_internal.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
 #include "mirror/string.h"
-#include "oat.h"
+#include "oat_file_assistant.h"
 #include "os.h"
 #include "profiler.h"
 #include "runtime.h"
@@ -51,11 +37,6 @@
 #include "well_known_classes.h"
 #include "zip_archive.h"
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wshadow"
-#include "ScopedFd.h"
-#pragma GCC diagnostic pop
-
 namespace art {
 
 static std::unique_ptr<std::vector<const DexFile*>>
@@ -182,10 +163,9 @@
   std::vector<std::unique_ptr<const DexFile>> dex_files;
   std::vector<std::string> error_msgs;
 
-  bool success = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs,
-                                             &dex_files);
+  dex_files = linker->OpenDexFilesFromOat(sourceName.c_str(), outputName.c_str(), &error_msgs);
 
-  if (success || !dex_files.empty()) {
+  if (!dex_files.empty()) {
     jlongArray array = ConvertNativeToJavaArray(env, dex_files);
     if (array == nullptr) {
       ScopedObjectAccess soa(env);
@@ -197,9 +177,6 @@
     }
     return array;
   } else {
-    // The vector should be empty after a failed loading attempt.
-    DCHECK_EQ(0U, dex_files.size());
-
     ScopedObjectAccess soa(env);
     CHECK(!error_msgs.empty());
     // The most important message is at the end. So set up nesting by going forward, which will
@@ -320,40 +297,6 @@
   return result;
 }
 
-static void CopyProfileFile(const char* oldfile, const char* newfile) {
-  ScopedFd src(open(oldfile, O_RDONLY));
-  if (src.get() == -1) {
-    PLOG(ERROR) << "Failed to open profile file " << oldfile
-      << ". My uid:gid is " << getuid() << ":" << getgid();
-    return;
-  }
-
-  struct stat stat_src;
-  if (fstat(src.get(), &stat_src) == -1) {
-    PLOG(ERROR) << "Failed to get stats for profile file  " << oldfile
-      << ". My uid:gid is " << getuid() << ":" << getgid();
-    return;
-  }
-
-  // Create the copy with rw------- (only accessible by system)
-  ScopedFd dst(open(newfile, O_WRONLY|O_CREAT|O_TRUNC, 0600));
-  if (dst.get()  == -1) {
-    PLOG(ERROR) << "Failed to create/write prev profile file " << newfile
-      << ".  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(ERROR) << "Failed to copy profile file " << oldfile << " to " << newfile
-      << ". My uid:gid is " << getuid() << ":" << getgid();
-  }
-}
-
 // Java: dalvik.system.DexFile.UP_TO_DATE
 static const jbyte kUpToDate = 0;
 // Java: dalvik.system.DexFile.DEXOPT_NEEDED
@@ -361,102 +304,8 @@
 // Java: dalvik.system.DexFile.PATCHOAT_NEEDED
 static const jbyte kDexoptNeeded = 2;
 
-template <const bool kVerboseLogging, const bool kReasonLogging>
-static jbyte IsDexOptNeededForFile(const std::string& oat_filename, const char* filename,
-                                   InstructionSet target_instruction_set,
-                                   bool* oat_is_pic) {
-  std::string error_msg;
-  std::unique_ptr<const OatFile> oat_file(OatFile::Open(oat_filename, oat_filename, nullptr,
-                                                        nullptr,
-                                                        false, &error_msg));
-  if (oat_file.get() == nullptr) {
-    // Note that even though this is kDexoptNeeded, we use
-    // kVerboseLogging instead of the usual kReasonLogging since it is
-    // the common case on first boot and very spammy.
-    if (kVerboseLogging) {
-      LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << oat_filename
-          << "' for file location '" << filename << "': " << error_msg;
-    }
-    error_msg.clear();
-    return kDexoptNeeded;
-  }
-
-  // Pass-up the information about if this is PIC.
-  // TODO: Refactor this function to be less complicated.
-  *oat_is_pic = oat_file->IsPic();
-
-  bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate();
-  uint32_t location_checksum = 0;
-  const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename, nullptr,
-                                                                          kReasonLogging);
-  if (oat_dex_file != nullptr) {
-    // If its not possible to read the classes.dex assume up-to-date as we won't be able to
-    // compile it anyway.
-    if (!DexFile::GetChecksum(filename, &location_checksum, &error_msg)) {
-      if (kVerboseLogging) {
-        LOG(INFO) << "DexFile_isDexOptNeeded found precompiled stripped file: "
-            << filename << " for " << oat_filename << ": " << error_msg;
-      }
-      if (ClassLinker::VerifyOatChecksums(oat_file.get(), target_instruction_set, &error_msg)) {
-        if (kVerboseLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
-                    << " is up-to-date for " << filename;
-        }
-        return kUpToDate;
-      } else if (should_relocate_if_possible &&
-                  ClassLinker::VerifyOatImageChecksum(oat_file.get(), target_instruction_set)) {
-        if (kReasonLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
-                    << " needs to be relocated for " << filename;
-        }
-        return kPatchoatNeeded;
-      } else {
-        if (kReasonLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
-                    << " is out of date for " << filename;
-        }
-        return kDexoptNeeded;
-      }
-      // If we get here the file is out of date and we should use the system one to relocate.
-    } else {
-      if (ClassLinker::VerifyOatAndDexFileChecksums(oat_file.get(), filename, location_checksum,
-                                                    target_instruction_set, &error_msg)) {
-        if (kVerboseLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
-                    << " is up-to-date for " << filename;
-        }
-        return kUpToDate;
-      } else if (location_checksum == oat_dex_file->GetDexFileLocationChecksum()
-                  && should_relocate_if_possible
-                  && ClassLinker::VerifyOatImageChecksum(oat_file.get(), target_instruction_set)) {
-        if (kReasonLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
-                    << " needs to be relocated for " << filename;
-        }
-        return kPatchoatNeeded;
-      } else {
-        if (kReasonLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
-                    << " is out of date for " << filename;
-        }
-        return kDexoptNeeded;
-      }
-    }
-  } else {
-    if (kReasonLogging) {
-      LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
-                << " does not contain " << filename;
-    }
-    return kDexoptNeeded;
-  }
-}
-
 static jbyte IsDexOptNeededInternal(JNIEnv* env, const char* filename,
     const char* pkgname, const char* instruction_set, const jboolean defer) {
-  // Spammy logging for kUpToDate
-  const bool kVerboseLogging = false;
-  // Logging of reason for returning kDexoptNeeded or kPatchoatNeeded.
-  const bool kReasonLogging = true;
 
   if ((filename == nullptr) || !OS::FileExists(filename)) {
     LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename << "' does not exist";
@@ -466,117 +315,6 @@
     return kUpToDate;
   }
 
-  // Always treat elements of the bootclasspath as up-to-date.  The
-  // fact that code is running at all means that this should be true.
-  Runtime* runtime = Runtime::Current();
-  ClassLinker* class_linker = runtime->GetClassLinker();
-  // TODO: We're assuming that the 64 and 32 bit runtimes have identical
-  // class paths. isDexOptNeeded will not necessarily be called on a runtime
-  // that has the same instruction set as the file being dexopted.
-  const std::vector<const DexFile*>& boot_class_path = class_linker->GetBootClassPath();
-  for (size_t i = 0; i < boot_class_path.size(); i++) {
-    if (boot_class_path[i]->GetLocation() == filename) {
-      if (kVerboseLogging) {
-        LOG(INFO) << "DexFile_isDexOptNeeded ignoring boot class path file: " << filename;
-      }
-      return kUpToDate;
-    }
-  }
-
-  bool force_system_only = false;
-  bool require_system_version = false;
-
-  // Check the profile file.  We need to rerun dex2oat if the profile has changed significantly
-  // since the last time, or it's new.
-  // If the 'defer' argument is true then this will be retried later.  In this case we
-  // need to make sure that the profile file copy is not made so that we will get the
-  // same result second time.
-  std::string profile_file;
-  std::string prev_profile_file;
-  bool should_copy_profile = false;
-  if (Runtime::Current()->GetProfilerOptions().IsEnabled() && (pkgname != nullptr)) {
-    profile_file = GetDalvikCacheOrDie("profiles", false /* create_if_absent */)
-        + std::string("/") + pkgname;
-    prev_profile_file = profile_file + std::string("@old");
-
-    struct stat profstat, prevstat;
-    int e1 = stat(profile_file.c_str(), &profstat);
-    int e1_errno = errno;
-    int e2 = stat(prev_profile_file.c_str(), &prevstat);
-    int e2_errno = errno;
-    if (e1 < 0) {
-      if (e1_errno != EACCES) {
-        // No profile file, need to run dex2oat, unless we find a file in system
-        if (kReasonLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeededInternal profile file " << profile_file << " doesn't exist. "
-                    << "Will check odex to see if we can find a working version.";
-        }
-        // Force it to only accept system files/files with versions in system.
-        require_system_version = true;
-      } else {
-        LOG(INFO) << "DexFile_isDexOptNeededInternal recieved EACCES trying to stat profile file "
-                  << profile_file;
-      }
-    } else if (e2 == 0) {
-      // There is a previous profile file.  Check if the profile has changed significantly.
-      // A change in profile is considered significant if X% (change_thr property) of the top K%
-      // (compile_thr property) samples has changed.
-      double top_k_threshold = Runtime::Current()->GetProfilerOptions().GetTopKThreshold();
-      double change_threshold = Runtime::Current()->GetProfilerOptions().GetTopKChangeThreshold();
-      double change_percent = 0.0;
-      ProfileFile new_profile, old_profile;
-      bool new_ok = new_profile.LoadFile(profile_file);
-      bool old_ok = old_profile.LoadFile(prev_profile_file);
-      if (!new_ok || !old_ok) {
-        if (kVerboseLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeededInternal Ignoring invalid profiles: "
-                    << (new_ok ?  "" : profile_file) << " " << (old_ok ? "" : prev_profile_file);
-        }
-      } else {
-        std::set<std::string> new_top_k, old_top_k;
-        new_profile.GetTopKSamples(new_top_k, top_k_threshold);
-        old_profile.GetTopKSamples(old_top_k, top_k_threshold);
-        if (new_top_k.empty()) {
-          if (kVerboseLogging) {
-            LOG(INFO) << "DexFile_isDexOptNeededInternal empty profile: " << profile_file;
-          }
-          // If the new topK is empty we shouldn't optimize so we leave the change_percent at 0.0.
-        } else {
-          std::set<std::string> diff;
-          std::set_difference(new_top_k.begin(), new_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.
-          change_percent = 100.0 * static_cast<double>(diff.size()) / static_cast<double>(new_top_k.size());
-          if (kVerboseLogging) {
-            std::set<std::string>::iterator end = diff.end();
-            for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) {
-              LOG(INFO) << "DexFile_isDexOptNeededInternal new in topK: " << *it;
-            }
-          }
-        }
-      }
-
-      if (change_percent > change_threshold) {
-        if (kReasonLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeededInternal size of new profile file " << profile_file <<
-          " is significantly different from old profile file " << prev_profile_file << " (top "
-          << top_k_threshold << "% samples changed in proportion of " << change_percent << "%)";
-        }
-        should_copy_profile = !defer;
-        // Force us to only accept system files.
-        force_system_only = true;
-      }
-    } else if (e2_errno == ENOENT) {
-      // Previous profile does not exist.  Make a copy of the current one.
-      if (kVerboseLogging) {
-        LOG(INFO) << "DexFile_isDexOptNeededInternal previous profile doesn't exist: " << prev_profile_file;
-      }
-      should_copy_profile = !defer;
-    } else {
-      PLOG(INFO) << "Unable to stat previous profile file " << prev_profile_file;
-    }
-  }
-
   const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
   if (target_instruction_set == kNone) {
     ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
@@ -585,75 +323,43 @@
     return 0;
   }
 
-  // Get the filename for odex file next to the dex file.
-  std::string odex_filename(DexFilenameToOdexFilename(filename, target_instruction_set));
-  // Get the filename for the dalvik-cache file
-  std::string cache_dir;
-  bool have_android_data = false;
-  bool dalvik_cache_exists = false;
-  bool is_global_cache = false;
-  GetDalvikCache(instruction_set, false, &cache_dir, &have_android_data, &dalvik_cache_exists,
-                 &is_global_cache);
-  std::string cache_filename;  // was cache_location
-  bool have_cache_filename = false;
-  if (dalvik_cache_exists) {
-    std::string error_msg;
-    have_cache_filename = GetDalvikCacheFilename(filename, cache_dir.c_str(), &cache_filename,
-                                                 &error_msg);
-    if (!have_cache_filename && kVerboseLogging) {
-      LOG(INFO) << "DexFile_isDexOptNeededInternal failed to find cache file for dex file " << filename
-                << ": " << error_msg;
+  // TODO: Verify the dex location is well formed, and throw an IOException if
+  // not?
+
+  OatFileAssistant oat_file_assistant(filename, target_instruction_set, false, pkgname);
+
+  // Always treat elements of the bootclasspath as up-to-date.
+  if (oat_file_assistant.IsInBootClassPath()) {
+    return kUpToDate;
+  }
+
+  // TODO: Checking the profile should probably be done in the GetStatus()
+  // function. We have it here because GetStatus() should not be copying
+  // profile files. But who should be copying profile files?
+  if (oat_file_assistant.OdexFileIsOutOfDate()) {
+    // Needs recompile if profile has changed significantly.
+    if (Runtime::Current()->GetProfilerOptions().IsEnabled()) {
+      if (oat_file_assistant.IsProfileChangeSignificant()) {
+        if (!defer) {
+          oat_file_assistant.CopyProfileFile();
+        }
+        return kDexoptNeeded;
+      } else if (oat_file_assistant.ProfileExists()
+          && !oat_file_assistant.OldProfileExists()) {
+        if (!defer) {
+          oat_file_assistant.CopyProfileFile();
+        }
+      }
     }
   }
 
-  bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate();
-
-  jbyte dalvik_cache_decision = -1;
-  // Lets try the cache first (since we want to load from there since thats where the relocated
-  // versions will be).
-  if (have_cache_filename && !force_system_only) {
-    bool oat_is_pic;
-    // We can use the dalvik-cache if we find a good file.
-    dalvik_cache_decision =
-        IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(cache_filename, filename,
-                                                               target_instruction_set, &oat_is_pic);
-
-    // Apps that are compiled with --compile-pic never need to be patchoat-d
-    if (oat_is_pic && dalvik_cache_decision == kPatchoatNeeded) {
-      dalvik_cache_decision = kUpToDate;
-    }
-    // We will only return DexOptNeeded if both the cache and system return it.
-    if (dalvik_cache_decision != kDexoptNeeded && !require_system_version) {
-      CHECK(!(dalvik_cache_decision == kPatchoatNeeded && !should_relocate_if_possible))
-          << "May not return PatchoatNeeded when patching is disabled.";
-      return dalvik_cache_decision;
-    }
-    // We couldn't find one thats easy. We should now try the system.
+  OatFileAssistant::Status status = oat_file_assistant.GetStatus();
+  switch (status) {
+    case OatFileAssistant::kUpToDate: return kUpToDate;
+    case OatFileAssistant::kNeedsRelocation: return kPatchoatNeeded;
+    case OatFileAssistant::kOutOfDate: return kDexoptNeeded;
   }
-
-  bool oat_is_pic;
-  jbyte system_decision =
-      IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(odex_filename, filename,
-                                                             target_instruction_set, &oat_is_pic);
-  CHECK(!(system_decision == kPatchoatNeeded && !should_relocate_if_possible))
-      << "May not return PatchoatNeeded when patching is disabled.";
-
-  // Apps that are compiled with --compile-pic never need to be patchoat-d
-  if (oat_is_pic && system_decision == kPatchoatNeeded) {
-    system_decision = kUpToDate;
-  }
-
-  if (require_system_version && system_decision == kPatchoatNeeded
-                             && dalvik_cache_decision == kUpToDate) {
-    // We have a version from system relocated to the cache. Return it.
-    return dalvik_cache_decision;
-  }
-
-  if (should_copy_profile && system_decision == kDexoptNeeded) {
-    CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str());
-  }
-
-  return system_decision;
+  UNREACHABLE();
 }
 
 static jbyte DexFile_isDexOptNeededInternal(JNIEnv* env, jclass, jstring javaFilename,
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 6c82eb2..57ca2b1 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -93,8 +93,7 @@
   int fd = dup(originalFd);
   if (fd < 0) {
     ScopedObjectAccess soa(env);
-    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
-    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/RuntimeException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
                                    "dup(%d) failed: %s", originalFd, strerror(errno));
     return;
   }
@@ -148,8 +147,7 @@
 
 static void ThrowUnsupportedOperationException(JNIEnv* env) {
   ScopedObjectAccess soa(env);
-  ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
-  soa.Self()->ThrowNewException(throw_location, "Ljava/lang/UnsupportedOperationException;", NULL);
+  soa.Self()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", NULL);
 }
 
 static void VMDebug_startInstructionCounting(JNIEnv* env, jclass) {
@@ -196,7 +194,7 @@
   // Only one of these may be NULL.
   if (javaFilename == NULL && javaFd == NULL) {
     ScopedObjectAccess soa(env);
-    ThrowNullPointerException(NULL, "fileName == null && fd == null");
+    ThrowNullPointerException("fileName == null && fd == null");
     return;
   }
 
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 599d97f..6e3f1bc 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -72,7 +72,7 @@
   }
   mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
   if (UNLIKELY(element_class == nullptr)) {
-    ThrowNullPointerException(NULL, "element class == null");
+    ThrowNullPointerException("element class == null");
     return nullptr;
   }
   Runtime* runtime = Runtime::Current();
@@ -97,7 +97,7 @@
   }
   mirror::Class* element_class = soa.Decode<mirror::Class*>(javaElementClass);
   if (UNLIKELY(element_class == nullptr)) {
-    ThrowNullPointerException(NULL, "element class == null");
+    ThrowNullPointerException("element class == null");
     return nullptr;
   }
   Runtime* runtime = Runtime::Current();
@@ -120,7 +120,7 @@
   ScopedFastNativeObjectAccess soa(env);
   mirror::Array* array = soa.Decode<mirror::Array*>(javaArray);
   if (!array->IsArrayInstance()) {
-    ThrowIllegalArgumentException(NULL, "not an array");
+    ThrowIllegalArgumentException("not an array");
     return 0;
   }
   if (Runtime::Current()->GetHeap()->IsMovableObject(array)) {
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 1ea75f3..60d14e9 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -55,8 +55,7 @@
   // is especially handy for array types, since we want to avoid
   // auto-generating bogus array classes.
   if (!IsValidBinaryClassName(name.c_str())) {
-    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
-    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ClassNotFoundException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;",
                                    "Invalid name: %s", name.c_str());
     return nullptr;
   }
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index 4ea2546..6afe83b 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -29,7 +29,7 @@
 static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) {
   ScopedFastNativeObjectAccess soa(env);
   if (UNLIKELY(javaRhs == NULL)) {
-    ThrowNullPointerException(NULL, "rhs == null");
+    ThrowNullPointerException("rhs == null");
     return -1;
   } else {
     return soa.Decode<mirror::String*>(javaThis)->CompareTo(soa.Decode<mirror::String*>(javaRhs));
diff --git a/runtime/native/java_lang_System.cc b/runtime/native/java_lang_System.cc
index f79be56..736b42b 100644
--- a/runtime/native/java_lang_System.cc
+++ b/runtime/native/java_lang_System.cc
@@ -39,8 +39,7 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   std::string actualType(PrettyTypeOf(array));
   Thread* self = Thread::Current();
-  ThrowLocation throw_location = self->GetCurrentLocationForThrow();
-  self->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayStoreException;",
+  self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
                            "%s of type %s is not an array", identifier, actualType.c_str());
 }
 
@@ -52,11 +51,11 @@
 
   // Null pointer checks.
   if (UNLIKELY(javaSrc == nullptr)) {
-    ThrowNullPointerException(nullptr, "src == null");
+    ThrowNullPointerException("src == null");
     return;
   }
   if (UNLIKELY(javaDst == nullptr)) {
-    ThrowNullPointerException(nullptr, "dst == null");
+    ThrowNullPointerException("dst == null");
     return;
   }
 
@@ -78,8 +77,7 @@
   if (UNLIKELY(srcPos < 0) || UNLIKELY(dstPos < 0) || UNLIKELY(count < 0) ||
       UNLIKELY(srcPos > srcArray->GetLength() - count) ||
       UNLIKELY(dstPos > dstArray->GetLength() - count)) {
-    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
-    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayIndexOutOfBoundsException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
                                    "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
                                    srcArray->GetLength(), srcPos, dstArray->GetLength(), dstPos,
                                    count);
@@ -132,8 +130,7 @@
                srcComponentType->IsPrimitive())) {
     std::string srcType(PrettyTypeOf(srcArray));
     std::string dstType(PrettyTypeOf(dstArray));
-    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
-    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/ArrayStoreException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
                                    "Incompatible types: src=%s, dst=%s",
                                    srcType.c_str(), dstType.c_str());
     return;
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index e4b8db1..d3b52ba 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -100,7 +100,7 @@
   ScopedObjectAccess soa(env);
   mirror::Object* object = soa.Decode<mirror::Object*>(java_object);
   if (object == NULL) {
-    ThrowNullPointerException(NULL, "object == null");
+    ThrowNullPointerException("object == null");
     return JNI_FALSE;
   }
   MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 3121a90..765f548 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -42,8 +42,7 @@
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::Class> c(hs.NewHandle(m->GetDeclaringClass()));
   if (UNLIKELY(c->IsAbstract())) {
-    ThrowLocation throw_location = soa.Self()->GetCurrentLocationForThrow();
-    soa.Self()->ThrowNewExceptionF(throw_location, "Ljava/lang/InstantiationException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
                                    "Can't instantiate %s %s",
                                    c->IsInterface() ? "interface" : "abstract class",
                                    PrettyDescriptor(c.Get()).c_str());
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 2cebf02..9c5bde9 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -34,7 +34,7 @@
                                                    mirror::Object* obj)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
   if (kIsSet && field->IsFinal()) {
-    ThrowIllegalAccessException(nullptr,
+    ThrowIllegalAccessException(
             StringPrintf("Cannot set %s field %s of class %s",
                 PrettyJavaAccessFlags(field->GetAccessFlags()).c_str(),
                 PrettyField(field).c_str(),
@@ -45,7 +45,7 @@
   mirror::Class* calling_class = nullptr;
   if (!VerifyAccess(self, obj, field->GetDeclaringClass(), field->GetAccessFlags(),
                     &calling_class)) {
-    ThrowIllegalAccessException(nullptr,
+    ThrowIllegalAccessException(
             StringPrintf("Class %s cannot access %s field %s of class %s",
                 calling_class == nullptr ? "null" : PrettyClass(calling_class).c_str(),
                 PrettyJavaAccessFlags(field->GetAccessFlags()).c_str(),
@@ -98,8 +98,8 @@
       // Never okay.
       break;
   }
-  ThrowIllegalArgumentException(nullptr, StringPrintf("Not a primitive field: %s",
-                                                      PrettyField(f).c_str()).c_str());
+  ThrowIllegalArgumentException(StringPrintf("Not a primitive field: %s",
+                                             PrettyField(f).c_str()).c_str());
   return false;
 }
 
@@ -190,7 +190,7 @@
   }
   // Widen it if necessary (and possible).
   JValue wide_value;
-  if (!ConvertPrimitiveValue(nullptr, false, field_type, kPrimitiveType, field_value,
+  if (!ConvertPrimitiveValue(false, field_type, kPrimitiveType, field_value,
                              &wide_value)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return JValue();
@@ -270,8 +270,8 @@
     FALLTHROUGH_INTENDED;
   case Primitive::kPrimVoid:
     // Never okay.
-    ThrowIllegalArgumentException(nullptr, StringPrintf("Not a primitive field: %s",
-                                                        PrettyField(f).c_str()).c_str());
+    ThrowIllegalArgumentException(StringPrintf("Not a primitive field: %s",
+                                               PrettyField(f).c_str()).c_str());
     return;
   }
 }
@@ -329,14 +329,14 @@
   }
   Primitive::Type field_type = f->GetTypeAsPrimitiveType();
   if (UNLIKELY(field_type == Primitive::kPrimNot)) {
-    ThrowIllegalArgumentException(nullptr, StringPrintf("Not a primitive field: %s",
-                                                        PrettyField(f).c_str()).c_str());
+    ThrowIllegalArgumentException(StringPrintf("Not a primitive field: %s",
+                                               PrettyField(f).c_str()).c_str());
     return;
   }
 
   // Widen the value if necessary (and possible).
   JValue wide_value;
-  if (!ConvertPrimitiveValue(nullptr, false, kPrimitiveType, field_type, new_value, &wide_value)) {
+  if (!ConvertPrimitiveValue(false, kPrimitiveType, field_type, new_value, &wide_value)) {
     DCHECK(soa.Self()->IsExceptionPending());
     return;
   }
diff --git a/runtime/oat.h b/runtime/oat.h
index 5540ade..79cb024 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '0', '5', '7', '\0' };
+  static constexpr uint8_t kOatVersion[] = { '0', '5', '8', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
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
+
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
new file mode 100644
index 0000000..958b440
--- /dev/null
+++ b/runtime/oat_file_assistant.h
@@ -0,0 +1,431 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_OAT_FILE_ASSISTANT_H_
+#define ART_RUNTIME_OAT_FILE_ASSISTANT_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "arch/instruction_set.h"
+#include "base/scoped_flock.h"
+#include "base/unix_file/fd_file.h"
+#include "oat_file.h"
+#include "os.h"
+#include "profiler.h"
+
+namespace art {
+
+// Class for assisting with oat file management.
+//
+// This class collects common utilities for determining the status of an oat
+// file on the device, updating the oat file, and loading the oat file.
+//
+// The oat file assistant is intended to be used with dex locations not on the
+// boot class path. See the IsInBootClassPath method for a way to check if the
+// dex location is in the boot class path.
+//
+// TODO: All the profiling related code is old and untested. It should either
+// be restored and tested, or removed.
+class OatFileAssistant {
+ public:
+  enum Status {
+    // kOutOfDate - An oat file is said to be out of date if the file does not
+    // exist, or is out of date with respect to the dex file or boot image.
+    kOutOfDate,
+
+    // kNeedsRelocation - An oat file is said to need relocation if the code
+    // is up to date, but not yet properly relocated for address space layout
+    // randomization (ASLR). In this case, the oat file is neither "out of
+    // date" nor "up to date".
+    kNeedsRelocation,
+
+    // kUpToDate - An oat file is said to be up to date if it is not out of
+    // date and has been properly relocated for the purposes of ASLR.
+    kUpToDate,
+  };
+
+  // Constructs an OatFileAssistant object to assist the oat file
+  // corresponding to the given dex location with the target instruction set.
+  //
+  // The dex_location must not be NULL and should remain available and
+  // unchanged for the duration of the lifetime of the OatFileAssistant object.
+  // Typically the dex_location is the absolute path to the original,
+  // un-optimized dex file.
+  //
+  //
+  // Note: Currently the dex_location must have an extension.
+  // TODO: Relax this restriction?
+  //
+  // The isa should be either the 32 bit or 64 bit variant for the current
+  // device. For example, on an arm device, use arm or arm64. An oat file can
+  // be loaded executable only if the ISA matches the current runtime.
+  OatFileAssistant(const char* dex_location, const InstructionSet isa,
+                   bool load_executable);
+
+  // Constructs an OatFileAssistant, providing an explicit target oat_location
+  // to use instead of the standard oat location.
+  OatFileAssistant(const char* dex_location, const char* oat_location,
+                   const InstructionSet isa, bool load_executable);
+
+  // Constructs an OatFileAssistant, providing an additional package_name used
+  // solely for the purpose of locating profile files.
+  //
+  // TODO: Why is the name of the profile file based on the package name and
+  // not the dex location? If there is no technical reason the dex_location
+  // can't be used, we should prefer that instead.
+  OatFileAssistant(const char* dex_location, const InstructionSet isa,
+                   bool load_executable, const char* package_name);
+
+  // Constructs an OatFileAssistant with user specified oat location and a
+  // package name.
+  OatFileAssistant(const char* dex_location, const char* oat_location,
+                   const InstructionSet isa, bool load_executable,
+                   const char* package_name);
+
+  ~OatFileAssistant();
+
+  // Returns true if the dex location refers to an element of the boot class
+  // path.
+  bool IsInBootClassPath();
+
+  // Obtains a lock on the target oat file.
+  // Only one OatFileAssistant object can hold the lock for a target oat file
+  // at a time. The Lock is released automatically when the OatFileAssistant
+  // object goes out of scope. The Lock() method must not be called if the
+  // lock has already been acquired.
+  //
+  // Returns true on success.
+  // Returns false on error, in which case error_msg will contain more
+  // information on the error.
+  //
+  // The 'error_msg' argument must not be null.
+  //
+  // This is intended to be used to avoid race conditions when multiple
+  // processes generate oat files, such as when a foreground Activity and
+  // a background Service both use DexClassLoaders pointing to the same dex
+  // file.
+  bool Lock(std::string* error_msg);
+
+  // Returns the overall compilation status for the given dex location.
+  Status GetStatus();
+
+  // Attempts to generate or relocate the oat file as needed to make it up to
+  // date.
+  // Returns true on success.
+  //
+  // If there is a failure, the value of error_msg will be set to a string
+  // describing why there was failure. error_msg must not be nullptr.
+  bool MakeUpToDate(std::string* error_msg);
+
+  // Returns an oat file that can be used for loading dex files.
+  // Returns nullptr if no suitable oat file was found.
+  //
+  // After this call, no other methods of the OatFileAssistant should be
+  // called, because access to the loaded oat file has been taken away from
+  // the OatFileAssistant object.
+  std::unique_ptr<OatFile> GetBestOatFile();
+
+  // Loads the dex files in the given oat file for the given dex location.
+  // The oat file should be up to date for the given dex location.
+  // This loads multiple dex files in the case of multidex.
+  // Returns an empty vector if no dex files for that location could be loaded
+  // from the oat file.
+  //
+  // The caller is responsible for freeing the dex_files returned, if any. The
+  // dex_files will only remain valid as long as the oat_file is valid.
+  static std::vector<std::unique_ptr<const DexFile>> LoadDexFiles(
+      const OatFile& oat_file, const char* dex_location);
+
+  // If the dex file has been pre-compiled on the host, the compiled oat file
+  // will have the extension .odex, and is referred to as the odex file.
+  // It is called odex for legacy reasons; the file is really an oat file. The
+  // odex file will typically have a patch delta of 0 and need to be relocated
+  // before use for the purposes of ASLR.
+  // These methods return the location and status of the odex file for the dex
+  // location.
+  // Notes:
+  //  * OdexFileName may return null if the odex file name could not be
+  //    determined.
+  const std::string* OdexFileName();
+  bool OdexFileExists();
+  Status OdexFileStatus();
+  bool OdexFileIsOutOfDate();
+  bool OdexFileNeedsRelocation();
+  bool OdexFileIsUpToDate();
+
+  // When the dex files is compiled on the target device, the oat file is the
+  // result. The oat file will have been relocated to some
+  // (possibly-out-of-date) offset for ASLR.
+  // These methods return the location and status of the target oat file for
+  // the dex location.
+  //
+  // Notes:
+  //  * To get the overall status of the compiled code for this dex_location,
+  //    use the GetStatus() method, not the OatFileStatus() method.
+  //  * OatFileName may return null if the oat file name could not be
+  //    determined.
+  const std::string* OatFileName();
+  bool OatFileExists();
+  Status OatFileStatus();
+  bool OatFileIsOutOfDate();
+  bool OatFileNeedsRelocation();
+  bool OatFileIsUpToDate();
+
+  // These methods return the status for a given opened oat file with respect
+  // to the dex location.
+  Status GivenOatFileStatus(const OatFile& file);
+  bool GivenOatFileIsOutOfDate(const OatFile& file);
+  bool GivenOatFileNeedsRelocation(const OatFile& file);
+  bool GivenOatFileIsUpToDate(const OatFile& file);
+
+  // Returns true if there is an accessible profile associated with the dex
+  // location.
+  // This returns false if profiling is disabled.
+  bool ProfileExists();
+
+  // The old profile is a file containing a previous snapshot of profiling
+  // information associated with the dex file code. This is used to track how
+  // the profiling information has changed over time.
+  //
+  // Returns true if there is an accessible old profile associated with the
+  // dex location.
+  // This returns false if profiling is disabled.
+  bool OldProfileExists();
+
+  // Returns true if there has been a significant change between the old
+  // profile and the current profile.
+  // This returns false if profiling is disabled.
+  bool IsProfileChangeSignificant();
+
+  // Copy the current profile to the old profile location.
+  void CopyProfileFile();
+
+  // Generates the oat file by relocation from the odex file.
+  // This does not check the current status before attempting to relocate the
+  // oat file.
+  // Returns true on success.
+  // This will fail if dex2oat is not enabled in the current runtime.
+  //
+  // If there is a failure, the value of error_msg will be set to a string
+  // describing why there was failure. error_msg must not be nullptr.
+  bool RelocateOatFile(std::string* error_msg);
+
+  // Generate the oat file from the dex file.
+  // This does not check the current status before attempting to generate the
+  // oat file.
+  // Returns true on success.
+  // This will fail if dex2oat is not enabled in the current runtime.
+  //
+  // If there is a failure, the value of error_msg will be set to a string
+  // describing why there was failure. error_msg must not be nullptr.
+  bool GenerateOatFile(std::string* error_msg);
+
+  // Executes dex2oat using the current runtime configuration overridden with
+  // the given arguments. This does not check to see if dex2oat is enabled in
+  // the runtime configuration.
+  // Returns true on success.
+  //
+  // If there is a failure, the value of error_msg will be set to a string
+  // describing why there was failure. error_msg must not be nullptr.
+  //
+  // TODO: The OatFileAssistant probably isn't the right place to have this
+  // function.
+  static bool Dex2Oat(const std::vector<std::string>& args, std::string* error_msg);
+
+  // Constructs the odex file name for the given dex location.
+  // Returns true on success, in which case odex_filename is set to the odex
+  // file name.
+  // Returns false on error, in which case error_msg describes the error.
+  // Neither odex_filename nor error_msg may be null.
+  static bool DexFilenameToOdexFilename(const std::string& location,
+      InstructionSet isa, std::string* odex_filename, std::string* error_msg);
+
+ private:
+  struct ImageInfo {
+    uint32_t oat_checksum = 0;
+    uintptr_t oat_data_begin = 0;
+    int32_t patch_delta = 0;
+    std::string location;
+  };
+
+  // Returns the path to the dalvik cache directory.
+  // Does not check existence of the cache or try to create it.
+  // Includes the trailing slash.
+  // Returns an empty string if we can't get the dalvik cache directory path.
+  std::string DalvikCacheDirectory();
+
+  // Constructs the filename for the profile file.
+  // Returns an empty string if we do not have the necessary information to
+  // construct the filename.
+  std::string ProfileFileName();
+
+  // Constructs the filename for the old profile file.
+  // Returns an empty string if we do not have the necessary information to
+  // construct the filename.
+  std::string OldProfileFileName();
+
+  // Returns the current image location.
+  // Returns an empty string if the image location could not be retrieved.
+  //
+  // TODO: This method should belong with an image file manager, not
+  // the oat file assistant.
+  static std::string ImageLocation();
+
+  // Gets the dex checksum required for an up-to-date oat file.
+  // Returns dex_checksum if a required checksum was located. Returns
+  // nullptr if the required checksum was not found.
+  // The caller shouldn't clean up or free the returned pointer.
+  const uint32_t* GetRequiredDexChecksum();
+
+  // Returns the loaded odex file.
+  // Loads the file if needed. Returns nullptr if the file failed to load.
+  // The caller shouldn't clean up or free the returned pointer.
+  const OatFile* GetOdexFile();
+
+  // Clear any cached information about the odex file that depends on the
+  // contents of the file.
+  void ClearOdexFileCache();
+
+  // Returns the loaded oat file.
+  // Loads the file if needed. Returns nullptr if the file failed to load.
+  // The caller shouldn't clean up or free the returned pointer.
+  const OatFile* GetOatFile();
+
+  // Clear any cached information about the oat file that depends on the
+  // contents of the file.
+  void ClearOatFileCache();
+
+  // Returns the loaded image info.
+  // Loads the image info if needed. Returns nullptr if the image info failed
+  // to load.
+  // The caller shouldn't clean up or free the returned pointer.
+  const ImageInfo* GetImageInfo();
+
+  // Returns the loaded profile.
+  // Loads the profile if needed. Returns nullptr if the profile failed
+  // to load.
+  // The caller shouldn't clean up or free the returned pointer.
+  ProfileFile* GetProfile();
+
+  // Returns the loaded old profile.
+  // Loads the old profile if needed. Returns nullptr if the old profile
+  // failed to load.
+  // The caller shouldn't clean up or free the returned pointer.
+  ProfileFile* GetOldProfile();
+
+  // To implement Lock(), we lock a dummy file where the oat file would go
+  // (adding ".flock" to the target file name) and retain the lock for the
+  // remaining lifetime of the OatFileAssistant object.
+  std::unique_ptr<File> lock_file_;
+  ScopedFlock flock_;
+
+  // In a properly constructed OatFileAssistant object, dex_location_ should
+  // never be nullptr.
+  const char* dex_location_ = nullptr;
+
+  // In a properly constructed OatFileAssistant object, isa_ should be either
+  // the 32 or 64 bit variant for the current device.
+  const InstructionSet isa_ = kNone;
+
+  // The package name, used solely to find the profile file.
+  // This may be nullptr in a properly constructed object. In this case,
+  // profile_load_attempted_ and old_profile_load_attempted_ will be true, and
+  // profile_load_succeeded_ and old_profile_load_succeeded_ will be false.
+  const char* package_name_ = nullptr;
+
+  // Whether we will attempt to load oat files executable.
+  bool load_executable_ = false;
+
+  // Cached value of the required dex checksum.
+  // This should be accessed only by the GetRequiredDexChecksum() method.
+  uint32_t cached_required_dex_checksum;
+  bool required_dex_checksum_attempted = false;
+  bool required_dex_checksum_found;
+
+  // Cached value of the odex file name.
+  // This should be accessed only by the OdexFileName() method.
+  bool cached_odex_file_name_attempted_ = false;
+  bool cached_odex_file_name_found_;
+  std::string cached_odex_file_name_;
+
+  // Cached value of the loaded odex file.
+  // Use the GetOdexFile method rather than accessing this directly, unless you
+  // know the odex file isn't out of date.
+  bool odex_file_load_attempted_ = false;
+  std::unique_ptr<OatFile> cached_odex_file_;
+
+  // Cached results for OdexFileIsOutOfDate
+  bool odex_file_is_out_of_date_attempted_ = false;
+  bool cached_odex_file_is_out_of_date_;
+
+  // Cached results for OdexFileIsUpToDate
+  bool odex_file_is_up_to_date_attempted_ = false;
+  bool cached_odex_file_is_up_to_date_;
+
+  // Cached value of the oat file name.
+  // This should be accessed only by the OatFileName() method.
+  bool cached_oat_file_name_attempted_ = false;
+  bool cached_oat_file_name_found_;
+  std::string cached_oat_file_name_;
+
+  // Cached value of the loaded odex file.
+  // Use the GetOatFile method rather than accessing this directly, unless you
+  // know the odex file isn't out of date.
+  bool oat_file_load_attempted_ = false;
+  std::unique_ptr<OatFile> cached_oat_file_;
+
+  // Cached results for OatFileIsOutOfDate
+  bool oat_file_is_out_of_date_attempted_ = false;
+  bool cached_oat_file_is_out_of_date_;
+
+  // Cached results for OatFileIsUpToDate
+  bool oat_file_is_up_to_date_attempted_ = false;
+  bool cached_oat_file_is_up_to_date_;
+
+  // Cached value of the image info.
+  // Use the GetImageInfo method rather than accessing these directly.
+  // TODO: The image info should probably be moved out of the oat file
+  // assistant to an image file manager.
+  bool image_info_load_attempted_ = false;
+  bool image_info_load_succeeded_ = false;
+  ImageInfo cached_image_info_;
+
+  // Cached value of the profile file.
+  // Use the GetProfile method rather than accessing these directly.
+  bool profile_load_attempted_ = false;
+  bool profile_load_succeeded_ = false;
+  ProfileFile cached_profile_;
+
+  // Cached value of the profile file.
+  // Use the GetOldProfile method rather than accessing these directly.
+  bool old_profile_load_attempted_ = false;
+  bool old_profile_load_succeeded_ = false;
+  ProfileFile cached_old_profile_;
+
+  // For debugging only.
+  // If this flag is set, the oat or odex file has been released to the user
+  // of the OatFileAssistant object and the OatFileAssistant object is in a
+  // bad state and should no longer be used.
+  bool oat_file_released_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(OatFileAssistant);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_OAT_FILE_ASSISTANT_H_
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
new file mode 100644
index 0000000..71679ae
--- /dev/null
+++ b/runtime/oat_file_assistant_test.cc
@@ -0,0 +1,878 @@
+/*
+ * 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 <algorithm>
+#include <fstream>
+#include <string>
+#include <vector>
+#include <sys/param.h>
+
+#include <backtrace/BacktraceMap.h>
+#include <gtest/gtest.h>
+
+#include "class_linker.h"
+#include "common_runtime_test.h"
+#include "mem_map.h"
+#include "os.h"
+#include "thread-inl.h"
+#include "utils.h"
+
+namespace art {
+
+class OatFileAssistantTest : public CommonRuntimeTest {
+ public:
+  virtual void SetUp() {
+    ReserveImageSpace();
+    CommonRuntimeTest::SetUp();
+
+    // Create a scratch directory to work from.
+    scratch_dir_ = android_data_ + "/OatFileAssistantTest";
+    ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700));
+
+    // Create a subdirectory in scratch for the current isa.
+    // This is the location that will be used for odex files in the tests.
+    isa_dir_ = scratch_dir_ + "/" + GetInstructionSetString(kRuntimeISA);
+    ASSERT_EQ(0, mkdir(isa_dir_.c_str(), 0700));
+
+    // Verify the environment is as we expect
+    uint32_t checksum;
+    std::string error_msg;
+    ASSERT_TRUE(OS::FileExists(GetImageFile().c_str()))
+      << "Expected pre-compiled boot image to be at: " << GetImageFile();
+    ASSERT_TRUE(OS::FileExists(GetDexSrc1().c_str()))
+      << "Expected dex file to be at: " << GetDexSrc1();
+    ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str()))
+      << "Expected stripped dex file to be at: " << GetStrippedDexSrc1();
+    ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg))
+      << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1();
+    ASSERT_TRUE(OS::FileExists(GetMultiDexSrc1().c_str()))
+      << "Expected multidex file to be at: " << GetMultiDexSrc1();
+    ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
+      << "Expected dex file to be at: " << GetDexSrc2();
+  }
+
+  virtual void SetUpRuntimeOptions(RuntimeOptions* options) {
+    // options->push_back(std::make_pair("-verbose:oat", nullptr));
+
+    // Set up the image location.
+    options->push_back(std::make_pair("-Ximage:" + GetImageLocation(),
+          nullptr));
+    // Make sure compilercallbacks are not set so that relocation will be
+    // enabled.
+    for (std::pair<std::string, const void*>& pair : *options) {
+      if (pair.first == "compilercallbacks") {
+        pair.second = nullptr;
+      }
+    }
+  }
+
+  virtual void PreRuntimeCreate() {
+    UnreserveImageSpace();
+  }
+
+  virtual void PostRuntimeCreate() {
+    ReserveImageSpace();
+  }
+
+  virtual void TearDown() {
+    ClearDirectory(isa_dir_.c_str());
+    ASSERT_EQ(0, rmdir(isa_dir_.c_str()));
+
+    ClearDirectory(scratch_dir_.c_str());
+    ASSERT_EQ(0, rmdir(scratch_dir_.c_str()));
+
+    CommonRuntimeTest::TearDown();
+  }
+
+  void Copy(std::string src, std::string dst) {
+    std::ifstream  src_stream(src, std::ios::binary);
+    std::ofstream  dst_stream(dst, std::ios::binary);
+
+    dst_stream << src_stream.rdbuf();
+  }
+
+  // Returns the directory where the pre-compiled core.art can be found.
+  // TODO: We should factor out this into common tests somewhere rather than
+  // re-hardcoding it here (This was copied originally from the elf writer
+  // test).
+  std::string GetImageDirectory() {
+    if (IsHost()) {
+      const char* host_dir = getenv("ANDROID_HOST_OUT");
+      CHECK(host_dir != NULL);
+      return std::string(host_dir) + "/framework";
+    } else {
+      return std::string("/data/art-test");
+    }
+  }
+
+  std::string GetImageLocation() {
+    return GetImageDirectory() + "/core.art";
+  }
+
+  std::string GetImageFile() {
+    return GetImageDirectory() + "/" + GetInstructionSetString(kRuntimeISA)
+      + "/core.art";
+  }
+
+  std::string GetDexSrc1() {
+    return GetTestDexFileName("Main");
+  }
+
+  // Returns the path to a dex file equivalent to GetDexSrc1, but with the dex
+  // file stripped.
+  std::string GetStrippedDexSrc1() {
+    return GetTestDexFileName("MainStripped");
+  }
+
+  std::string GetMultiDexSrc1() {
+    return GetTestDexFileName("MultiDex");
+  }
+
+  std::string GetDexSrc2() {
+    return GetTestDexFileName("Nested");
+  }
+
+  // Scratch directory, for dex and odex files (oat files will go in the
+  // dalvik cache).
+  std::string GetScratchDir() {
+    return scratch_dir_;
+  }
+
+  // ISA directory is the subdirectory in the scratch directory where odex
+  // files should be located.
+  std::string GetISADir() {
+    return isa_dir_;
+  }
+
+  // Generate an odex file for the purposes of test.
+  // If pic is true, generates a PIC odex.
+  void GenerateOdexForTest(const std::string& dex_location,
+                           const std::string& odex_location,
+                           bool pic = false) {
+    // For this operation, we temporarily redirect the dalvik cache so dex2oat
+    // doesn't find the relocated image file.
+    std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp";
+    setenv("ANDROID_DATA", android_data_tmp.c_str(), 1);
+    std::vector<std::string> args;
+    args.push_back("--dex-file=" + dex_location);
+    args.push_back("--oat-file=" + odex_location);
+    if (pic) {
+      args.push_back("--compile-pic");
+    } else {
+      args.push_back("--include-patch-information");
+
+      // We need to use the quick compiler to generate non-PIC code, because
+      // the optimizing compiler always generates PIC.
+      args.push_back("--compiler-backend=Quick");
+    }
+    args.push_back("--runtime-arg");
+    args.push_back("-Xnorelocate");
+    std::string error_msg;
+    ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+    setenv("ANDROID_DATA", android_data_.c_str(), 1);
+  }
+
+  void GeneratePicOdexForTest(const std::string& dex_location,
+                              const std::string& odex_location) {
+    GenerateOdexForTest(dex_location, odex_location, true);
+  }
+
+ private:
+  // Reserve memory around where the image will be loaded so other memory
+  // won't conflict when it comes time to load the image.
+  // This can be called with an already loaded image to reserve the space
+  // around it.
+  void ReserveImageSpace() {
+    MemMap::Init();
+
+    // Ensure a chunk of memory is reserved for the image space.
+    uintptr_t reservation_start = ART_BASE_ADDRESS + ART_BASE_ADDRESS_MIN_DELTA;
+    uintptr_t reservation_end = ART_BASE_ADDRESS + ART_BASE_ADDRESS_MAX_DELTA
+      + 100 * 1024 * 1024;
+
+    std::string error_msg;
+    std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
+    ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map";
+    for (BacktraceMap::const_iterator it = map->begin();
+        reservation_start < reservation_end && it != map->end(); ++it) {
+      if (it->end <= reservation_start) {
+        continue;
+      }
+
+      if (it->start < reservation_start) {
+        reservation_start = std::min(reservation_end, it->end);
+      }
+
+      image_reservation_.push_back(std::unique_ptr<MemMap>(
+          MemMap::MapAnonymous("image reservation",
+              reinterpret_cast<uint8_t*>(reservation_start),
+              std::min(it->start, reservation_end) - reservation_start,
+              PROT_NONE, false, false, &error_msg)));
+      ASSERT_TRUE(image_reservation_.back().get() != nullptr) << error_msg;
+      LOG(INFO) << "Reserved space for image " <<
+        reinterpret_cast<void*>(image_reservation_.back()->Begin()) << "-" <<
+        reinterpret_cast<void*>(image_reservation_.back()->End());
+      reservation_start = it->end;
+    }
+  }
+
+
+  // Unreserve any memory reserved by ReserveImageSpace. This should be called
+  // before the image is loaded.
+  void UnreserveImageSpace() {
+    image_reservation_.clear();
+  }
+
+  std::string scratch_dir_;
+  std::string isa_dir_;
+  std::vector<std::unique_ptr<MemMap>> image_reservation_;
+};
+
+class OatFileAssistantNoDex2OatTest : public OatFileAssistantTest {
+ public:
+  virtual void SetUpRuntimeOptions(RuntimeOptions* options) {
+    OatFileAssistantTest::SetUpRuntimeOptions(options);
+    options->push_back(std::make_pair("-Xnodex2oat", nullptr));
+  }
+};
+
+// Generate an oat file for the purposes of test, as opposed to testing
+// generation of oat files.
+static void GenerateOatForTest(const char* dex_location) {
+  OatFileAssistant oat_file_assistant(dex_location, kRuntimeISA, false);
+
+  std::string error_msg;
+  ASSERT_TRUE(oat_file_assistant.GenerateOatFile(&error_msg)) << error_msg;
+}
+
+// Case: We have a DEX file, but no OAT file for it.
+// Expect: The oat file status is kOutOfDate.
+TEST_F(OatFileAssistantTest, DexNoOat) {
+  std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
+  Copy(GetDexSrc1(), dex_location);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+  EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.OatFileStatus());
+}
+
+// Case: We have no DEX file and no OAT file.
+// Expect: Status is out of date. Loading should fail, but not crash.
+TEST_F(OatFileAssistantTest, NoDexNoOat) {
+  std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+  EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  EXPECT_EQ(nullptr, oat_file.get());
+}
+
+// Case: We have a DEX file and up-to-date OAT file for it.
+// Expect: The oat file status is kUpToDate.
+TEST_F(OatFileAssistantTest, OatUpToDate) {
+  std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str());
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+  EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.GetStatus());
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.OatFileExists());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.OatFileStatus());
+}
+
+// Case: We have a MultiDEX file and up-to-date OAT file for it.
+// Expect: The oat file status is kUpToDate.
+TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) {
+  std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar";
+  Copy(GetMultiDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str());
+
+  // Verify we can load both dex files.
+  OatFileAssistant executable_oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  std::unique_ptr<OatFile> oat_file = executable_oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(oat_file.get() != nullptr);
+  EXPECT_TRUE(oat_file->IsExecutable());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  dex_files = executable_oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+  EXPECT_EQ(2u, dex_files.size());
+}
+
+// Case: We have a DEX file and out of date OAT file.
+// Expect: The oat file status is kOutOfDate.
+TEST_F(OatFileAssistantTest, OatOutOfDate) {
+  std::string dex_location = GetScratchDir() + "/OatOutOfDate.jar";
+
+  // We create a dex, generate an oat for it, then overwrite the dex with a
+  // different dex to make the oat out of date.
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str());
+  Copy(GetDexSrc2(), dex_location);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+}
+
+// Case: We have a DEX file and an ODEX file, but no OAT file.
+// Expect: The oat file status is kNeedsRelocation.
+TEST_F(OatFileAssistantTest, DexOdexNoOat) {
+  std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
+  std::string odex_location = GetISADir() + "/DexOdexNoOat.odex";
+
+  // Create the dex and odex files
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOdexForTest(dex_location, odex_location);
+
+  // Verify the status.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+  EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.GetStatus());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
+  EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.OdexFileNeedsRelocation());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+}
+
+// Case: We have a stripped DEX file and an ODEX file, but no OAT file.
+// Expect: The oat file status is kNeedsRelocation.
+TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) {
+  std::string dex_location = GetScratchDir() + "/StrippedDexOdexNoOat.jar";
+  std::string odex_location = GetISADir() + "/StrippedDexOdexNoOat.odex";
+
+  // Create the dex and odex files
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOdexForTest(dex_location, odex_location);
+
+  // Strip the dex file
+  Copy(GetStrippedDexSrc1(), dex_location);
+
+  // Verify the status.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+  EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.GetStatus());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+
+  // Make the oat file up to date.
+  std::string error_msg;
+  ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+
+  EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.GetStatus());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.OatFileExists());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+
+  // Verify we can load the dex files from it.
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(oat_file.get() != nullptr);
+  EXPECT_TRUE(oat_file->IsExecutable());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+  EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a stripped DEX file, an ODEX file, and an out of date OAT file.
+// Expect: The oat file status is kNeedsRelocation.
+TEST_F(OatFileAssistantTest, StrippedDexOdexOat) {
+  std::string dex_location = GetScratchDir() + "/StrippedDexOdexOat.jar";
+  std::string odex_location = GetISADir() + "/StrippedDexOdexOat.odex";
+
+  // Create the oat file from a different dex file so it looks out of date.
+  Copy(GetDexSrc2(), dex_location);
+  GenerateOatForTest(dex_location.c_str());
+
+  // Create the odex file
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOdexForTest(dex_location, odex_location);
+
+  // Strip the dex file.
+  Copy(GetStrippedDexSrc1(), dex_location);
+
+  // Verify the status.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+  EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.GetStatus());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+
+  // Make the oat file up to date.
+  std::string error_msg;
+  ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+
+  EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.GetStatus());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_TRUE(oat_file_assistant.OdexFileNeedsRelocation());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.OatFileExists());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+
+  // Verify we can load the dex files from it.
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(oat_file.get() != nullptr);
+  EXPECT_TRUE(oat_file->IsExecutable());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+  EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and
+// OAT files both have patch delta of 0.
+// Expect: It shouldn't crash.
+TEST_F(OatFileAssistantTest, OdexOatOverlap) {
+  std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
+  std::string odex_location = GetISADir() + "/OdexOatOverlap.odex";
+  std::string oat_location = GetISADir() + "/OdexOatOverlap.oat";
+
+  // Create the dex and odex files
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOdexForTest(dex_location, odex_location);
+
+  // Create the oat file by copying the odex so they are located in the same
+  // place in memory.
+  Copy(odex_location, oat_location);
+
+  // Verify things don't go bad.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+      oat_location.c_str(), kRuntimeISA, true);
+
+  EXPECT_EQ(OatFileAssistant::kNeedsRelocation, oat_file_assistant.GetStatus());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.OatFileExists());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+
+  // Things aren't relocated, so it should fall back to interpreted.
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(oat_file.get() != nullptr);
+  EXPECT_FALSE(oat_file->IsExecutable());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+  EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a DEX file and a PIC ODEX file, but no OAT file.
+// Expect: The oat file status is kUpToDate, because PIC needs no relocation.
+TEST_F(OatFileAssistantTest, DexPicOdexNoOat) {
+  std::string dex_location = GetScratchDir() + "/DexPicOdexNoOat.jar";
+  std::string odex_location = GetISADir() + "/DexPicOdexNoOat.odex";
+
+  // Create the dex and odex files
+  Copy(GetDexSrc1(), dex_location);
+  GeneratePicOdexForTest(dex_location, odex_location);
+
+  // Verify the status.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+  EXPECT_EQ(OatFileAssistant::kUpToDate, oat_file_assistant.GetStatus());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_TRUE(oat_file_assistant.OdexFileExists());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+}
+
+// Case: We have a DEX file and up-to-date OAT file for it.
+// Expect: We should load an executable dex file.
+TEST_F(OatFileAssistantTest, LoadOatUpToDate) {
+  std::string dex_location = GetScratchDir() + "/LoadOatUpToDate.jar";
+
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str());
+
+  // Load the oat using an oat file assistant.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(oat_file.get() != nullptr);
+  EXPECT_TRUE(oat_file->IsExecutable());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+  EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a DEX file and up-to-date OAT file for it.
+// Expect: Loading non-executable should load the oat non-executable.
+TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) {
+  std::string dex_location = GetScratchDir() + "/LoadNoExecOatUpToDate.jar";
+
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOatForTest(dex_location.c_str());
+
+  // Load the oat using an oat file assistant.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(oat_file.get() != nullptr);
+  EXPECT_FALSE(oat_file->IsExecutable());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+  EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a DEX file.
+// Expect: We should load an executable dex file from an alternative oat
+// location.
+TEST_F(OatFileAssistantTest, LoadDexNoAlternateOat) {
+  std::string dex_location = GetScratchDir() + "/LoadDexNoAlternateOat.jar";
+  std::string oat_location = GetScratchDir() + "/LoadDexNoAlternateOat.oat";
+
+  Copy(GetDexSrc1(), dex_location);
+
+  OatFileAssistant oat_file_assistant(
+      dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+  std::string error_msg;
+  ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(oat_file.get() != nullptr);
+  EXPECT_TRUE(oat_file->IsExecutable());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+  EXPECT_EQ(1u, dex_files.size());
+
+  EXPECT_TRUE(OS::FileExists(oat_location.c_str()));
+
+  // Verify it didn't create an oat in the default location.
+  OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
+  EXPECT_FALSE(ofm.OatFileExists());
+}
+
+// Case: Non-existent Dex location.
+// Expect: The dex code is out of date, and trying to update it fails.
+TEST_F(OatFileAssistantTest, NonExsistentDexLocation) {
+  std::string dex_location = GetScratchDir() + "/BadDexLocation.jar";
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+
+  std::string error_msg;
+  EXPECT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
+  EXPECT_FALSE(error_msg.empty());
+}
+
+// Turn an absolute path into a path relative to the current working
+// directory.
+static std::string MakePathRelative(std::string target) {
+  char buf[MAXPATHLEN];
+  std::string cwd = getcwd(buf, MAXPATHLEN);
+
+  // Split the target and cwd paths into components.
+  std::vector<std::string> target_path;
+  std::vector<std::string> cwd_path;
+  Split(target, '/', &target_path);
+  Split(cwd, '/', &cwd_path);
+
+  // Reverse the path components, so we can use pop_back().
+  std::reverse(target_path.begin(), target_path.end());
+  std::reverse(cwd_path.begin(), cwd_path.end());
+
+  // Drop the common prefix of the paths. Because we reversed the path
+  // components, this becomes the common suffix of target_path and cwd_path.
+  while (!target_path.empty() && !cwd_path.empty()
+      && target_path.back() == cwd_path.back()) {
+    target_path.pop_back();
+    cwd_path.pop_back();
+  }
+
+  // For each element of the remaining cwd_path, add '..' to the beginning
+  // of the target path. Because we reversed the path components, we add to
+  // the end of target_path.
+  for (unsigned int i = 0; i < cwd_path.size(); i++) {
+    target_path.push_back("..");
+  }
+
+  // Reverse again to get the right path order, and join to get the result.
+  std::reverse(target_path.begin(), target_path.end());
+  return Join(target_path, '/');
+}
+
+// Case: Non-absolute path to Dex location.
+// Expect: Not sure, but it shouldn't crash.
+TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) {
+  std::string abs_dex_location = GetScratchDir() + "/NonAbsoluteDexLocation.jar";
+  Copy(GetDexSrc1(), abs_dex_location);
+
+  std::string dex_location = MakePathRelative(abs_dex_location);
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+}
+
+// Case: Very short, non-existent Dex location.
+// Expect: Dex code is out of date, and trying to update it fails.
+TEST_F(OatFileAssistantTest, ShortDexLocation) {
+  std::string dex_location = "/xx";
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+
+  std::string error_msg;
+  EXPECT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
+  EXPECT_FALSE(error_msg.empty());
+}
+
+// Case: Non-standard extension for dex file.
+// Expect: The oat file status is kOutOfDate.
+TEST_F(OatFileAssistantTest, LongDexExtension) {
+  std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
+  Copy(GetDexSrc1(), dex_location);
+
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+  EXPECT_EQ(OatFileAssistant::kOutOfDate, oat_file_assistant.GetStatus());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+}
+
+// A task to generate a dex location. Used by the RaceToGenerate test.
+class RaceGenerateTask : public Task {
+ public:
+  explicit RaceGenerateTask(const std::string& dex_location, const std::string& oat_location)
+    : dex_location_(dex_location), oat_location_(oat_location),
+      loaded_oat_file_(nullptr)
+  {}
+
+  void Run(Thread* self) {
+    UNUSED(self);
+
+    // Load the dex files, and save a pointer to the loaded oat file, so that
+    // we can verify only one oat file was loaded for the dex location.
+    ClassLinker* linker = Runtime::Current()->GetClassLinker();
+    std::vector<std::unique_ptr<const DexFile>> dex_files;
+    std::vector<std::string> error_msgs;
+    dex_files = linker->OpenDexFilesFromOat(dex_location_.c_str(), oat_location_.c_str(), &error_msgs);
+    CHECK(!dex_files.empty()) << Join(error_msgs, '\n');
+    loaded_oat_file_ = dex_files[0]->GetOatFile();
+  }
+
+  const OatFile* GetLoadedOatFile() const {
+    return loaded_oat_file_;
+  }
+
+ private:
+  std::string dex_location_;
+  std::string oat_location_;
+  const OatFile* loaded_oat_file_;
+};
+
+// Test the case where multiple processes race to generate an oat file.
+// This simulates multiple processes using multiple threads.
+//
+// We want only one Oat file to be loaded when there is a race to load, to
+// avoid using up the virtual memory address space.
+TEST_F(OatFileAssistantTest, RaceToGenerate) {
+  std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar";
+  std::string oat_location = GetISADir() + "/RaceToGenerate.oat";
+
+  // We use the lib core dex file, because it's large, and hopefully should
+  // take a while to generate.
+  Copy(GetLibCoreDexFileName(), dex_location);
+
+  const int kNumThreads = 32;
+  Thread* self = Thread::Current();
+  ThreadPool thread_pool("Oat file assistant test thread pool", kNumThreads);
+  std::vector<std::unique_ptr<RaceGenerateTask>> tasks;
+  for (int i = 0; i < kNumThreads; i++) {
+    std::unique_ptr<RaceGenerateTask> task(new RaceGenerateTask(dex_location, oat_location));
+    thread_pool.AddTask(self, task.get());
+    tasks.push_back(std::move(task));
+  }
+  thread_pool.StartWorkers(self);
+  thread_pool.Wait(self, true, false);
+
+  // Verify every task got the same pointer.
+  const OatFile* expected = tasks[0]->GetLoadedOatFile();
+  for (auto& task : tasks) {
+    EXPECT_EQ(expected, task->GetLoadedOatFile());
+  }
+}
+
+// Case: We have a DEX file and an ODEX file, no OAT file, and dex2oat is
+// disabled.
+// Expect: We should load the odex file non-executable.
+TEST_F(OatFileAssistantNoDex2OatTest, LoadDexOdexNoOat) {
+  std::string dex_location = GetScratchDir() + "/LoadDexOdexNoOat.jar";
+  std::string odex_location = GetISADir() + "/LoadDexOdexNoOat.odex";
+
+  // Create the dex and odex files
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOdexForTest(dex_location, odex_location);
+
+  // Load the oat using an executable oat file assistant.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(oat_file.get() != nullptr);
+  EXPECT_FALSE(oat_file->IsExecutable());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+  EXPECT_EQ(1u, dex_files.size());
+}
+
+// Case: We have a MultiDEX file and an ODEX file, no OAT file, and dex2oat is
+// disabled.
+// Expect: We should load the odex file non-executable.
+TEST_F(OatFileAssistantNoDex2OatTest, LoadMultiDexOdexNoOat) {
+  std::string dex_location = GetScratchDir() + "/LoadMultiDexOdexNoOat.jar";
+  std::string odex_location = GetISADir() + "/LoadMultiDexOdexNoOat.odex";
+
+  // Create the dex and odex files
+  Copy(GetMultiDexSrc1(), dex_location);
+  GenerateOdexForTest(dex_location, odex_location);
+
+  // Load the oat using an executable oat file assistant.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(oat_file.get() != nullptr);
+  EXPECT_FALSE(oat_file->IsExecutable());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+  EXPECT_EQ(2u, dex_files.size());
+}
+
+TEST(OatFileAssistantUtilsTest, DexFilenameToOdexFilename) {
+  std::string error_msg;
+  std::string odex_file;
+
+  EXPECT_TRUE(OatFileAssistant::DexFilenameToOdexFilename(
+        "/foo/bar/baz.jar", kArm, &odex_file, &error_msg)) << error_msg;
+  EXPECT_EQ("/foo/bar/arm/baz.odex", odex_file);
+
+  EXPECT_TRUE(OatFileAssistant::DexFilenameToOdexFilename(
+        "/foo/bar/baz.funnyext", kArm, &odex_file, &error_msg)) << error_msg;
+  EXPECT_EQ("/foo/bar/arm/baz.odex", odex_file);
+
+  EXPECT_FALSE(OatFileAssistant::DexFilenameToOdexFilename(
+        "nopath.jar", kArm, &odex_file, &error_msg));
+  EXPECT_FALSE(OatFileAssistant::DexFilenameToOdexFilename(
+        "/foo/bar/baz_noext", kArm, &odex_file, &error_msg));
+}
+
+
+// TODO: More Tests:
+//  * Test class linker falls back to unquickened dex for DexNoOat
+//  * Test class linker falls back to unquickened dex for MultiDexNoOat
+//  * Test multidex files:
+//     - Multidex with only classes2.dex out of date should have status
+//       kOutOfDate
+//  * Test using secondary isa
+//  * Test with profiling info?
+//  * Test for status of oat while oat is being generated (how?)
+//  * Test case where 32 and 64 bit boot class paths differ,
+//      and we ask IsInBootClassPath for a class in exactly one of the 32 or
+//      64 bit boot class paths.
+//  * Test unexpected scenarios (?):
+//    - Dex is stripped, don't have odex.
+//    - Oat file corrupted after status check, before reload unexecutable
+//    because it's unrelocated and no dex2oat
+
+}  // namespace art
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 607569a..a53aeaa 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -136,6 +136,8 @@
           .IntoKey(M::LongGCLogThreshold)
       .Define("-XX:DumpGCPerformanceOnShutdown")
           .IntoKey(M::DumpGCPerformanceOnShutdown)
+      .Define("-XX:DumpJITInfoOnShutdown")
+          .IntoKey(M::DumpJITInfoOnShutdown)
       .Define("-XX:IgnoreMaxFootprint")
           .IntoKey(M::IgnoreMaxFootprint)
       .Define("-XX:LowMemoryMode")
@@ -620,6 +622,7 @@
   UsageMessage(stream, "  -XX:LongPauseLogThreshold=integervalue\n");
   UsageMessage(stream, "  -XX:LongGCLogThreshold=integervalue\n");
   UsageMessage(stream, "  -XX:DumpGCPerformanceOnShutdown\n");
+  UsageMessage(stream, "  -XX:DumpJITInfoOnShutdown\n");
   UsageMessage(stream, "  -XX:IgnoreMaxFootprint\n");
   UsageMessage(stream, "  -XX:UseTLAB\n");
   UsageMessage(stream, "  -XX:BackgroundGC=none\n");
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index 08619fa..8cccec8 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -30,7 +30,6 @@
 }  // namespace mirror
 class Context;
 class Thread;
-class ThrowLocation;
 class ShadowFrame;
 
 // Manages exception delivery for Quick backend.
diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h
index be4d560..f21c1a0 100644
--- a/runtime/reflection-inl.h
+++ b/runtime/reflection-inl.h
@@ -27,7 +27,7 @@
 
 namespace art {
 
-inline bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
+inline bool ConvertPrimitiveValue(bool unbox_for_result,
                                   Primitive::Type srcType, Primitive::Type dstType,
                                   const JValue& src, JValue* dst) {
   DCHECK(srcType != Primitive::kPrimNot && dstType != Primitive::kPrimNot);
@@ -88,13 +88,11 @@
     break;
   }
   if (!unbox_for_result) {
-    ThrowIllegalArgumentException(throw_location,
-                                  StringPrintf("Invalid primitive conversion from %s to %s",
+    ThrowIllegalArgumentException(StringPrintf("Invalid primitive conversion from %s to %s",
                                                PrettyDescriptor(srcType).c_str(),
                                                PrettyDescriptor(dstType).c_str()).c_str());
   } else {
-    ThrowClassCastException(throw_location,
-                            StringPrintf("Couldn't convert result of type %s to %s",
+    ThrowClassCastException(StringPrintf("Couldn't convert result of type %s to %s",
                                          PrettyDescriptor(srcType).c_str(),
                                          PrettyDescriptor(dstType).c_str()).c_str());
   }
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 4acd07f..a54a39d 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -207,7 +207,7 @@
   static void ThrowIllegalPrimitiveArgumentException(const char* expected,
                                                      const char* found_descriptor)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    ThrowIllegalArgumentException(nullptr,
+    ThrowIllegalArgumentException(
         StringPrintf("Invalid primitive conversion from %s to %s", expected,
                      PrettyDescriptor(found_descriptor).c_str()).c_str());
   }
@@ -227,7 +227,7 @@
         mirror::Class* dst_class =
             h_m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_, true);
         if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
-          ThrowIllegalArgumentException(nullptr,
+          ThrowIllegalArgumentException(
               StringPrintf("method %s argument %zd has type %s, got %s",
                   PrettyMethod(h_m.Get(), false).c_str(),
                   args_offset + 1,  // Humans don't count from 0.
@@ -255,7 +255,7 @@
               ThrowIllegalPrimitiveArgumentException(expected, \
                                                      arg->GetClass<>()->GetDescriptor(&temp)); \
             } else { \
-              ThrowIllegalArgumentException(nullptr, \
+              ThrowIllegalArgumentException(\
                   StringPrintf("method %s argument %zd has type %s, got %s", \
                       PrettyMethod(h_m.Get(), false).c_str(), \
                       args_offset + 1, \
@@ -580,8 +580,7 @@
   uint32_t classes_size = (classes == nullptr) ? 0 : classes->Size();
   uint32_t arg_count = (objects != nullptr) ? objects->GetLength() : 0;
   if (arg_count != classes_size) {
-    ThrowIllegalArgumentException(nullptr,
-                                  StringPrintf("Wrong number of arguments; expected %d, got %d",
+    ThrowIllegalArgumentException(StringPrintf("Wrong number of arguments; expected %d, got %d",
                                                classes_size, arg_count).c_str());
     return nullptr;
   }
@@ -590,7 +589,7 @@
   mirror::Class* calling_class = nullptr;
   if (!accessible && !VerifyAccess(soa.Self(), receiver, declaring_class, m->GetAccessFlags(),
                                    &calling_class)) {
-    ThrowIllegalAccessException(nullptr,
+    ThrowIllegalAccessException(
         StringPrintf("Class %s cannot access %s method %s of class %s",
             calling_class == nullptr ? "null" : PrettyClass(calling_class).c_str(),
             PrettyJavaAccessFlags(m->GetAccessFlags()).c_str(),
@@ -631,13 +630,12 @@
 
 bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) {
   if (o == nullptr) {
-    ThrowNullPointerException(nullptr, "null receiver");
+    ThrowNullPointerException("null receiver");
     return false;
   } else if (!o->InstanceOf(c)) {
     std::string expected_class_name(PrettyDescriptor(c));
     std::string actual_class_name(PrettyTypeOf(o));
-    ThrowIllegalArgumentException(nullptr,
-                                  StringPrintf("Expected receiver of type %s, but got %s",
+    ThrowIllegalArgumentException(StringPrintf("Expected receiver of type %s, but got %s",
                                                expected_class_name.c_str(),
                                                actual_class_name.c_str()).c_str());
     return false;
@@ -718,7 +716,7 @@
   return "result";
 }
 
-static bool UnboxPrimitive(const ThrowLocation* throw_location, mirror::Object* o,
+static bool UnboxPrimitive(mirror::Object* o,
                            mirror::Class* dst_class, mirror::ArtField* f,
                            JValue* unboxed_value)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -726,14 +724,12 @@
   if (!dst_class->IsPrimitive()) {
     if (UNLIKELY(o != nullptr && !o->InstanceOf(dst_class))) {
       if (!unbox_for_result) {
-        ThrowIllegalArgumentException(throw_location,
-                                      StringPrintf("%s has type %s, got %s",
+        ThrowIllegalArgumentException(StringPrintf("%s has type %s, got %s",
                                                    UnboxingFailureKind(f).c_str(),
                                                    PrettyDescriptor(dst_class).c_str(),
                                                    PrettyTypeOf(o).c_str()).c_str());
       } else {
-        ThrowClassCastException(throw_location,
-                                StringPrintf("Couldn't convert result of type %s to %s",
+        ThrowClassCastException(StringPrintf("Couldn't convert result of type %s to %s",
                                              PrettyTypeOf(o).c_str(),
                                              PrettyDescriptor(dst_class).c_str()).c_str());
       }
@@ -743,20 +739,17 @@
     return true;
   }
   if (UNLIKELY(dst_class->GetPrimitiveType() == Primitive::kPrimVoid)) {
-    ThrowIllegalArgumentException(throw_location,
-                                  StringPrintf("Can't unbox %s to void",
+    ThrowIllegalArgumentException(StringPrintf("Can't unbox %s to void",
                                                UnboxingFailureKind(f).c_str()).c_str());
     return false;
   }
   if (UNLIKELY(o == nullptr)) {
     if (!unbox_for_result) {
-      ThrowIllegalArgumentException(throw_location,
-                                    StringPrintf("%s has type %s, got null",
+      ThrowIllegalArgumentException(StringPrintf("%s has type %s, got null",
                                                  UnboxingFailureKind(f).c_str(),
                                                  PrettyDescriptor(dst_class).c_str()).c_str());
     } else {
-      ThrowNullPointerException(throw_location,
-                                StringPrintf("Expected to unbox a '%s' primitive type but was returned null",
+      ThrowNullPointerException(StringPrintf("Expected to unbox a '%s' primitive type but was returned null",
                                              PrettyDescriptor(dst_class).c_str()).c_str());
     }
     return false;
@@ -793,14 +786,14 @@
     boxed_value.SetS(primitive_field->GetShort(o));
   } else {
     std::string temp;
-    ThrowIllegalArgumentException(throw_location,
+    ThrowIllegalArgumentException(
         StringPrintf("%s has type %s, got %s", UnboxingFailureKind(f).c_str(),
             PrettyDescriptor(dst_class).c_str(),
             PrettyDescriptor(o->GetClass()->GetDescriptor(&temp)).c_str()).c_str());
     return false;
   }
 
-  return ConvertPrimitiveValue(throw_location, unbox_for_result,
+  return ConvertPrimitiveValue(unbox_for_result,
                                src_class->GetPrimitiveType(), dst_class->GetPrimitiveType(),
                                boxed_value, unboxed_value);
 }
@@ -808,12 +801,12 @@
 bool UnboxPrimitiveForField(mirror::Object* o, mirror::Class* dst_class, mirror::ArtField* f,
                             JValue* unboxed_value) {
   DCHECK(f != nullptr);
-  return UnboxPrimitive(nullptr, o, dst_class, f, unboxed_value);
+  return UnboxPrimitive(o, dst_class, f, unboxed_value);
 }
 
-bool UnboxPrimitiveForResult(const ThrowLocation& throw_location, mirror::Object* o,
+bool UnboxPrimitiveForResult(mirror::Object* o,
                              mirror::Class* dst_class, JValue* unboxed_value) {
-  return UnboxPrimitive(&throw_location, o, dst_class, nullptr, unboxed_value);
+  return UnboxPrimitive(o, dst_class, nullptr, unboxed_value);
 }
 
 bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class,
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 1a64871..857d63b 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -31,18 +31,16 @@
 union JValue;
 class ScopedObjectAccessAlreadyRunnable;
 class ShadowFrame;
-class ThrowLocation;
 
 mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 bool UnboxPrimitiveForField(mirror::Object* o, mirror::Class* dst_class, mirror::ArtField* f,
                             JValue* unboxed_value)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-bool UnboxPrimitiveForResult(const ThrowLocation& throw_location, mirror::Object* o,
-                             mirror::Class* dst_class, JValue* unboxed_value)
+bool UnboxPrimitiveForResult(mirror::Object* o, mirror::Class* dst_class, JValue* unboxed_value)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-ALWAYS_INLINE bool ConvertPrimitiveValue(const ThrowLocation* throw_location, bool unbox_for_result,
+ALWAYS_INLINE bool ConvertPrimitiveValue(bool unbox_for_result,
                                          Primitive::Type src_class, Primitive::Type dst_class,
                                          const JValue& src, JValue* dst)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 70de0db..61798c3 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -251,6 +251,7 @@
     VLOG(jit) << "Deleting jit";
     jit_.reset(nullptr);
   }
+  arena_pool_.reset();
 
   // Shutdown the fault manager if it was initialized.
   fault_manager.Shutdown();
@@ -787,6 +788,7 @@
   max_spins_before_thin_lock_inflation_ =
       runtime_options.GetOrDefault(Opt::MaxSpinsBeforeThinLockInflation);
 
+  arena_pool_.reset(new ArenaPool);
   monitor_list_ = new MonitorList;
   monitor_pool_ = MonitorPool::Create();
   thread_list_ = new ThreadList;
@@ -1014,7 +1016,7 @@
   }
 
   // Pre-allocate an OutOfMemoryError for the double-OOME case.
-  self->ThrowNewException(ThrowLocation(), "Ljava/lang/OutOfMemoryError;",
+  self->ThrowNewException("Ljava/lang/OutOfMemoryError;",
                           "OutOfMemoryError thrown while trying to throw OutOfMemoryError; "
                           "no stack trace available");
   pre_allocated_OutOfMemoryError_ = GcRoot<mirror::Throwable>(self->GetException());
@@ -1022,7 +1024,7 @@
 
   // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class
   // ahead of checking the application's class loader.
-  self->ThrowNewException(ThrowLocation(), "Ljava/lang/NoClassDefFoundError;",
+  self->ThrowNewException("Ljava/lang/NoClassDefFoundError;",
                           "Class not found using the boot class loader; no stack trace available");
   pre_allocated_NoClassDefFoundError_ = GcRoot<mirror::Throwable>(self->GetException());
   self->ClearException();
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 5078b7f..4cddb5c 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -28,6 +28,7 @@
 
 #include "arch/instruction_set.h"
 #include "base/allocator.h"
+#include "base/arena_allocator.h"
 #include "base/macros.h"
 #include "compiler_callbacks.h"
 #include "gc_root.h"
@@ -545,6 +546,13 @@
 
   void CreateJit();
 
+  ArenaPool* GetArenaPool() {
+    return arena_pool_.get();
+  }
+  const ArenaPool* GetArenaPool() const {
+    return arena_pool_.get();
+  }
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -608,6 +616,8 @@
 
   gc::Heap* heap_;
 
+  std::unique_ptr<ArenaPool> arena_pool_;
+
   // The number of spins that are done before thread suspension is used to forcibly inflate.
   size_t max_spins_before_thin_lock_inflation_;
   MonitorList* monitor_list_;
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index d072ffa..8775f8d 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -59,6 +59,7 @@
 RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
                                           LongGCLogThreshold,             gc::Heap::kDefaultLongGCLogThreshold)
 RUNTIME_OPTIONS_KEY (Unit,                DumpGCPerformanceOnShutdown)
+RUNTIME_OPTIONS_KEY (Unit,                DumpJITInfoOnShutdown)
 RUNTIME_OPTIONS_KEY (Unit,                IgnoreMaxFootprint)
 RUNTIME_OPTIONS_KEY (Unit,                LowMemoryMode)
 RUNTIME_OPTIONS_KEY (bool,                UseTLAB,                        false)
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 97a8d01..48becf6 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -28,7 +28,6 @@
 #include "runtime.h"
 #include "thread.h"
 #include "thread_list.h"
-#include "throw_location.h"
 #include "verify_object-inl.h"
 #include "vmap_table.h"
 
@@ -57,10 +56,6 @@
   }
 }
 
-ThrowLocation ShadowFrame::GetCurrentLocationForThrow() const {
-  return ThrowLocation(GetThisObject(), GetMethod(), GetDexPC());
-}
-
 size_t ManagedStack::NumJniShadowFrameReferences() const {
   size_t count = 0;
   for (const ManagedStack* current_fragment = this; current_fragment != NULL;
diff --git a/runtime/stack.h b/runtime/stack.h
index b495f03..13bd47f 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -25,7 +25,6 @@
 #include "gc_root.h"
 #include "mirror/object_reference.h"
 #include "read_barrier.h"
-#include "throw_location.h"
 #include "utils.h"
 #include "verify_object.h"
 
@@ -40,6 +39,7 @@
 class ShadowFrame;
 class HandleScope;
 class ScopedObjectAccess;
+class StackVisitor;
 class Thread;
 
 // The kind of vreg being accessed in calls to Set/GetVReg.
@@ -258,8 +258,6 @@
 
   mirror::Object* GetThisObject(uint16_t num_ins) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  ThrowLocation GetCurrentLocationForThrow() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   bool Contains(StackReference<mirror::Object>* shadow_frame_entry_obj) const {
     if (HasReferenceArray()) {
       return ((&References()[0] <= shadow_frame_entry_obj) &&
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 94d1059..e8e9355 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1138,8 +1138,6 @@
 Thread::Thread(bool daemon) : tls32_(daemon), wait_monitor_(nullptr), interrupted_(false) {
   wait_mutex_ = new Mutex("a thread wait mutex");
   wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
-  tlsPtr_.debug_invoke_req = new DebugInvokeReq;
-  tlsPtr_.single_step_control = nullptr;
   tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
   tlsPtr_.name = new std::string(kThreadNameDuringStartup);
   tlsPtr_.nested_signal_state = static_cast<jmp_buf*>(malloc(sizeof(jmp_buf)));
@@ -1291,7 +1289,6 @@
     CleanupCpu();
   }
 
-  delete tlsPtr_.debug_invoke_req;
   if (tlsPtr_.single_step_control != nullptr) {
     delete tlsPtr_.single_step_control;
   }
@@ -1705,29 +1702,25 @@
   return result;
 }
 
-void Thread::ThrowNewExceptionF(const ThrowLocation& throw_location,
-                                const char* exception_class_descriptor, const char* fmt, ...) {
+void Thread::ThrowNewExceptionF(const char* exception_class_descriptor, const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
-  ThrowNewExceptionV(throw_location, exception_class_descriptor,
-                     fmt, args);
+  ThrowNewExceptionV(exception_class_descriptor, fmt, args);
   va_end(args);
 }
 
-void Thread::ThrowNewExceptionV(const ThrowLocation& throw_location,
-                                const char* exception_class_descriptor,
+void Thread::ThrowNewExceptionV(const char* exception_class_descriptor,
                                 const char* fmt, va_list ap) {
   std::string msg;
   StringAppendV(&msg, fmt, ap);
-  ThrowNewException(throw_location, exception_class_descriptor, msg.c_str());
+  ThrowNewException(exception_class_descriptor, msg.c_str());
 }
 
-void Thread::ThrowNewException(const ThrowLocation& throw_location,
-                               const char* exception_class_descriptor,
+void Thread::ThrowNewException(const char* exception_class_descriptor,
                                const char* msg) {
   // Callers should either clear or call ThrowNewWrappedException.
   AssertNoPendingExceptionForNewException(msg);
-  ThrowNewWrappedException(throw_location, exception_class_descriptor, msg);
+  ThrowNewWrappedException(exception_class_descriptor, msg);
 }
 
 static mirror::ClassLoader* GetCurrentClassLoader(Thread* self)
@@ -1738,8 +1731,7 @@
       : nullptr;
 }
 
-void Thread::ThrowNewWrappedException(const ThrowLocation& throw_location ATTRIBUTE_UNUSED,
-                                      const char* exception_class_descriptor,
+void Thread::ThrowNewWrappedException(const char* exception_class_descriptor,
                                       const char* msg) {
   DCHECK_EQ(this, Thread::Current());
   ScopedObjectAccessUnchecked soa(this);
@@ -1843,7 +1835,7 @@
       msg, (tls32_.throwing_OutOfMemoryError ? " (recursive case)" : ""));
   if (!tls32_.throwing_OutOfMemoryError) {
     tls32_.throwing_OutOfMemoryError = true;
-    ThrowNewException(GetCurrentLocationForThrow(), "Ljava/lang/OutOfMemoryError;", msg);
+    ThrowNewException("Ljava/lang/OutOfMemoryError;", msg);
     tls32_.throwing_OutOfMemoryError = false;
   } else {
     Dump(LOG(WARNING));  // The pre-allocated OOME has no stack, so help out and log one.
@@ -2074,14 +2066,6 @@
   return visitor.method_;
 }
 
-ThrowLocation Thread::GetCurrentLocationForThrow() {
-  Context* context = GetLongJumpContext();
-  CurrentMethodVisitor visitor(this, context, true);
-  visitor.WalkStack(false);
-  ReleaseLongJumpContext(context);
-  return ThrowLocation(visitor.this_object_, visitor.method_, visitor.dex_pc_);
-}
-
 bool Thread::HoldsLock(mirror::Object* object) const {
   if (object == nullptr) {
     return false;
@@ -2416,4 +2400,21 @@
   delete ssc;
 }
 
+void Thread::SetDebugInvokeReq(DebugInvokeReq* req) {
+  CHECK(Dbg::IsDebuggerActive());
+  CHECK(GetInvokeReq() == nullptr) << "Debug invoke req already active in thread " << *this;
+  CHECK(Thread::Current() != this) << "Debug invoke can't be dispatched by the thread itself";
+  CHECK(req != nullptr);
+  tlsPtr_.debug_invoke_req = req;
+}
+
+void Thread::ClearDebugInvokeReq() {
+  CHECK(Dbg::IsDebuggerActive());
+  CHECK(GetInvokeReq() != nullptr) << "Debug invoke req not active in thread " << *this;
+  CHECK(Thread::Current() == this) << "Debug invoke must be finished by the thread itself";
+  // We do not own the DebugInvokeReq* so we must not delete it, it is the responsibility of
+  // the owner (the JDWP thread).
+  tlsPtr_.debug_invoke_req = nullptr;
+}
+
 }  // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index af02dc7..2e9ae3c 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -41,7 +41,6 @@
 #include "runtime_stats.h"
 #include "stack.h"
 #include "thread_state.h"
-#include "throw_location.h"
 
 namespace art {
 
@@ -364,8 +363,6 @@
   bool IsExceptionThrownByCurrentMethod(mirror::Throwable* exception) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  ThrowLocation GetCurrentLocationForThrow() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
   void SetTopOfStack(StackReference<mirror::ArtMethod>* top_method) {
     tlsPtr_.managed_stack.SetTopQuickFrame(top_method);
   }
@@ -380,24 +377,19 @@
   }
 
   // If 'msg' is NULL, no detail message is set.
-  void ThrowNewException(const ThrowLocation& throw_location,
-                         const char* exception_class_descriptor, const char* msg)
+  void ThrowNewException(const char* exception_class_descriptor, const char* msg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // If 'msg' is NULL, no detail message is set. An exception must be pending, and will be
   // used as the new exception's cause.
-  void ThrowNewWrappedException(const ThrowLocation& throw_location,
-                                const char* exception_class_descriptor,
-                                const char* msg)
+  void ThrowNewWrappedException(const char* exception_class_descriptor, const char* msg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void ThrowNewExceptionF(const ThrowLocation& throw_location,
-                          const char* exception_class_descriptor, const char* fmt, ...)
-      __attribute__((format(printf, 4, 5)))
+  void ThrowNewExceptionF(const char* exception_class_descriptor, const char* fmt, ...)
+      __attribute__((format(printf, 3, 4)))
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void ThrowNewExceptionV(const ThrowLocation& throw_location,
-                          const char* exception_class_descriptor, const char* fmt, va_list ap)
+  void ThrowNewExceptionV(const char* exception_class_descriptor, const char* fmt, va_list ap)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   // OutOfMemoryError is special, because we need to pre-allocate an instance.
@@ -707,6 +699,16 @@
     return tlsPtr_.single_step_control;
   }
 
+  // Indicates whether this thread is ready to invoke a method for debugging. This
+  // is only true if the thread has been suspended by a debug event.
+  bool IsReadyForDebugInvoke() const {
+    return tls32_.ready_for_debug_invoke;
+  }
+
+  void SetReadyForDebugInvoke(bool ready) {
+    tls32_.ready_for_debug_invoke = ready;
+  }
+
   // Activates single step control for debugging. The thread takes the
   // ownership of the given SingleStepControl*. It is deleted by a call
   // to DeactivateSingleStepControl or upon thread destruction.
@@ -715,6 +717,17 @@
   // Deactivates single step control for debugging.
   void DeactivateSingleStepControl();
 
+  // Sets debug invoke request for debugging. When the thread is resumed,
+  // it executes the method described by this request then suspends itself.
+  // The thread does not take ownership of the given DebugInvokeReq*, it is
+  // owned by the JDWP thread which is waiting for the execution of the
+  // method.
+  void SetDebugInvokeReq(DebugInvokeReq* req);
+
+  // Clears debug invoke request for debugging. When the thread completes
+  // method invocation, it clears its debug invoke request, signals the
+  // JDWP thread and suspends itself.
+  void ClearDebugInvokeReq();
 
   // Returns the fake exception used to activate deoptimization.
   static mirror::Throwable* GetDeoptimizationException() {
@@ -966,7 +979,8 @@
     explicit tls_32bit_sized_values(bool is_daemon) :
       suspend_count(0), debug_suspend_count(0), thin_lock_thread_id(0), tid(0),
       daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0),
-      thread_exit_check_count(0), handling_signal_(false), suspended_at_suspend_check(false) {
+      thread_exit_check_count(0), handling_signal_(false), suspended_at_suspend_check(false),
+      ready_for_debug_invoke(false) {
     }
 
     union StateAndFlags state_and_flags;
@@ -1010,6 +1024,11 @@
     // used to distinguish runnable threads that are suspended due to
     // a normal suspend check from other threads.
     bool32_t suspended_at_suspend_check;
+
+    // True if the thread has been suspended by a debugger event. This is
+    // used to invoke method from the debugger which is only allowed when
+    // the thread is suspended by an event.
+    bool32_t ready_for_debug_invoke;
   } tls32_;
 
   struct PACKED(8) tls_64bit_sized_values {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index d4c1e8c..ddfbebd 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -860,7 +860,8 @@
 }
 
 void ThreadList::SuspendSelfForDebugger() {
-  Thread* self = Thread::Current();
+  Thread* const self = Thread::Current();
+  self->SetReadyForDebugInvoke(true);
 
   // The debugger thread must not suspend itself due to debugger activity!
   Thread* debug_thread = Dbg::GetDebugThread();
@@ -881,11 +882,10 @@
   VLOG(threads) << *self << " self-suspending (debugger)";
 
   // Tell JDWP we've completed invocation and are ready to suspend.
-  DebugInvokeReq* pReq = self->GetInvokeReq();
-  DCHECK(pReq != NULL);
-  if (pReq->invoke_needed) {
-    // Clear this before signaling.
-    pReq->Clear();
+  DebugInvokeReq* const pReq = self->GetInvokeReq();
+  if (pReq != nullptr) {
+    // Clear debug invoke request before signaling.
+    self->ClearDebugInvokeReq();
 
     VLOG(jdwp) << "invoke complete, signaling";
     MutexLock mu(self, pReq->lock);
@@ -916,6 +916,7 @@
     CHECK_EQ(self->GetSuspendCount(), 0);
   }
 
+  self->SetReadyForDebugInvoke(false);
   VLOG(threads) << *self << " self-reviving (debugger)";
 }
 
diff --git a/runtime/throw_location.cc b/runtime/throw_location.cc
deleted file mode 100644
index 4d2aec0..0000000
--- a/runtime/throw_location.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2013 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 "throw_location.h"
-
-#include "mirror/art_method-inl.h"
-#include "mirror/class-inl.h"
-#include "mirror/object-inl.h"
-#include "utils.h"
-
-namespace art {
-
-std::string ThrowLocation::Dump() const {
-  if (method_ != nullptr) {
-    return StringPrintf("%s:%d", PrettyMethod(method_).c_str(),
-                        method_->GetLineNumFromDexPC(dex_pc_));
-  } else {
-    return "unknown throw location";
-  }
-}
-
-void ThrowLocation::VisitRoots(RootCallback* visitor, void* arg) {
-  if (this_object_ != nullptr) {
-    visitor(&this_object_, arg, RootInfo(kRootVMInternal));
-    DCHECK(this_object_ != nullptr);
-  }
-  if (method_ != nullptr) {
-    visitor(reinterpret_cast<mirror::Object**>(&method_), arg, RootInfo(kRootVMInternal));
-    DCHECK(method_ != nullptr);
-  }
-}
-
-}  // namespace art
diff --git a/runtime/throw_location.h b/runtime/throw_location.h
deleted file mode 100644
index bec0da4..0000000
--- a/runtime/throw_location.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_THROW_LOCATION_H_
-#define ART_RUNTIME_THROW_LOCATION_H_
-
-#include "object_callbacks.h"
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "gc_root.h"
-
-#include <stdint.h>
-#include <string>
-
-namespace art {
-
-namespace mirror {
-class ArtMethod;
-class Object;
-}  // mirror
-
-class PACKED(4) ThrowLocation {
- public:
-  ThrowLocation() {
-    Clear();
-  }
-
-  ThrowLocation(mirror::Object* throw_this_object, mirror::ArtMethod* throw_method,
-                uint32_t throw_dex_pc) :
-      this_object_(throw_this_object),
-      method_(throw_method),
-      dex_pc_(throw_dex_pc)
-#ifdef __LP64__
-      , pad_(0)
-#endif
-
-  {
-#ifdef __LP64__
-    UNUSED(pad_);
-#endif
-  }
-
-  mirror::Object* GetThis() const {
-    return this_object_;
-  }
-
-  mirror::ArtMethod* GetMethod() const {
-    return method_;
-  }
-
-  uint32_t GetDexPc() const {
-    return dex_pc_;
-  }
-
-  void Clear() {
-    this_object_ = NULL;
-    method_ = NULL;
-    dex_pc_ = -1;
-  }
-
-  std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  void VisitRoots(RootCallback* visitor, void* arg);
-
- private:
-  // The 'this' reference of the throwing method.
-  mirror::Object* this_object_;
-  // The throwing method.
-  mirror::ArtMethod* method_;
-  // The instruction within the throwing method.
-  uint32_t dex_pc_;
-  // Ensure 8byte alignment on 64bit.
-#ifdef __LP64__
-  uint32_t pad_;
-#endif
-};
-
-}  // namespace art
-
-#endif  // ART_RUNTIME_THROW_LOCATION_H_
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index c0fd7a5..2199021 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -75,8 +75,7 @@
     CHECK(IsAborted()) << "Rethrow InternalError while transaction is not aborted";
   }
   std::string abort_msg(GetAbortMessage());
-  self->ThrowNewException(self->GetCurrentLocationForThrow(), "Ljava/lang/InternalError;",
-                          abort_msg.c_str());
+  self->ThrowNewException("Ljava/lang/InternalError;", abort_msg.c_str());
 }
 
 bool Transaction::IsAborted() {
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 851eceb..8a23ff7 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1508,23 +1508,6 @@
   return filename;
 }
 
-std::string DexFilenameToOdexFilename(const std::string& location, const InstructionSet isa) {
-  // location = /foo/bar/baz.jar
-  // odex_location = /foo/bar/<isa>/baz.odex
-  std::string odex_location(location);
-  InsertIsaDirectory(isa, &odex_location);
-  size_t dot_index = odex_location.rfind('.');
-
-  // The location must have an extension, otherwise it's not clear what we
-  // should return.
-  CHECK_NE(dot_index, std::string::npos) << odex_location;
-  CHECK_EQ(std::string::npos, odex_location.find('/', dot_index)) << odex_location;
-
-  odex_location.resize(dot_index + 1);
-  odex_location += "odex";
-  return odex_location;
-}
-
 bool IsZipMagic(uint32_t magic) {
   return (('P' == ((magic >> 0) & 0xff)) &&
           ('K' == ((magic >> 8) & 0xff)));
diff --git a/runtime/utils.h b/runtime/utils.h
index 9d04d35..d294f4b 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -516,12 +516,6 @@
 // Returns the system location for an image
 std::string GetSystemImageFilename(const char* location, InstructionSet isa);
 
-// Returns an .odex file name adjacent to the dex location.
-// For example, for "/foo/bar/baz.jar", return "/foo/bar/<isa>/baz.odex".
-// The dex location must include a directory component and have an extension.
-// Note: does not support multidex location strings.
-std::string DexFilenameToOdexFilename(const std::string& location, InstructionSet isa);
-
 // Check whether the given magic matches a known file type.
 bool IsZipMagic(uint32_t magic);
 bool IsDexMagic(uint32_t magic);
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 5465762..6b36c19 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -371,13 +371,6 @@
                GetSystemImageFilename("/system/framework/boot.art", kArm).c_str());
 }
 
-TEST_F(UtilsTest, DexFilenameToOdexFilename) {
-  EXPECT_STREQ("/foo/bar/arm/baz.odex",
-               DexFilenameToOdexFilename("/foo/bar/baz.jar", kArm).c_str());
-  EXPECT_STREQ("/foo/bar/arm/baz.odex",
-               DexFilenameToOdexFilename("/foo/bar/baz.funnyext", kArm).c_str());
-}
-
 TEST_F(UtilsTest, ExecSuccess) {
   std::vector<std::string> command;
   if (kIsTargetBuild) {
diff --git a/test/457-regs/expected.txt b/test/457-regs/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/457-regs/expected.txt
diff --git a/test/457-regs/info.txt b/test/457-regs/info.txt
new file mode 100644
index 0000000..d950003
--- /dev/null
+++ b/test/457-regs/info.txt
@@ -0,0 +1 @@
+Tests debuggability of DEX registers.
diff --git a/test/457-regs/regs_jni.cc b/test/457-regs/regs_jni.cc
new file mode 100644
index 0000000..ce701e8
--- /dev/null
+++ b/test/457-regs/regs_jni.cc
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "arch/context.h"
+#include "jni.h"
+#include "mirror/art_method-inl.h"
+#include "scoped_thread_state_change.h"
+#include "stack.h"
+#include "thread.h"
+
+namespace art {
+
+namespace {
+
+class TestVisitor : public StackVisitor {
+ public:
+  TestVisitor(Thread* thread, Context* context)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : StackVisitor(thread, context) {}
+
+  bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::ArtMethod* m = GetMethod();
+    std::string m_name(m->GetName());
+
+    if (m_name.compare("mergeOk") == 0) {
+      uint32_t value = 0;
+
+      CHECK(GetVReg(m, 0, kIntVReg, &value));
+      CHECK_EQ(value, 0u);
+
+      CHECK(GetVReg(m, 1, kIntVReg, &value));
+      CHECK_EQ(value, 1u);
+
+      CHECK(GetVReg(m, 2, kIntVReg, &value));
+      CHECK_EQ(value, 2u);
+
+      CHECK(GetVReg(m, 3, kIntVReg, &value));
+      CHECK_EQ(value, 1u);
+
+      CHECK(GetVReg(m, 4, kIntVReg, &value));
+      CHECK_EQ(value, 2u);
+      did_check_ = true;
+    } else if (m_name.compare("mergeNotOk") == 0) {
+      uint32_t value = 0;
+
+      CHECK(GetVReg(m, 0, kIntVReg, &value));
+      CHECK_EQ(value, 0u);
+
+      CHECK(GetVReg(m, 1, kIntVReg, &value));
+      CHECK_EQ(value, 1u);
+
+      bool success = GetVReg(m, 2, kIntVReg, &value);
+      if (m->IsOptimized(sizeof(void*))) CHECK(!success);
+
+      CHECK(GetVReg(m, 3, kReferenceVReg, &value));
+      CHECK_EQ(value, 1u);
+
+      CHECK(GetVReg(m, 4, kFloatVReg, &value));
+      uint32_t cast = bit_cast<float, uint32_t>(4.0f);
+      CHECK_EQ(value, cast);
+      did_check_ = true;
+    } else if (m_name.compare("phiEquivalent") == 0) {
+      uint32_t value = 0;
+
+      CHECK(GetVReg(m, 0, kIntVReg, &value));
+      // Quick doesn't like this one on x64.
+      CHECK_EQ(value, 0u);
+
+      CHECK(GetVReg(m, 1, kIntVReg, &value));
+      CHECK_EQ(value, 1u);
+
+      CHECK(GetVReg(m, 2, kFloatVReg, &value));
+      CHECK_EQ(value, 1u);
+
+      did_check_ = true;
+    } else if (m_name.compare("mergeReferences") == 0) {
+      uint32_t value = 0;
+
+      CHECK(GetVReg(m, 0, kIntVReg, &value));
+      CHECK_EQ(value, 0u);
+
+      CHECK(GetVReg(m, 1, kIntVReg, &value));
+      CHECK_EQ(value, 1u);
+
+      CHECK(GetVReg(m, 2, kReferenceVReg, &value));
+      CHECK_EQ(value, 0u);
+
+      CHECK(GetVReg(m, 3, kReferenceVReg, &value));
+      CHECK_NE(value, 0u);
+
+      did_check_ = true;
+    } else if (m_name.compare("phiAllEquivalents") == 0) {
+      uint32_t value = 0;
+
+      CHECK(GetVReg(m, 0, kIntVReg, &value));
+      CHECK_EQ(value, 0u);
+
+      CHECK(GetVReg(m, 1, kIntVReg, &value));
+      CHECK_EQ(value, 1u);
+
+      CHECK(GetVReg(m, 2, kReferenceVReg, &value));
+      CHECK_EQ(value, 0u);
+
+      did_check_ = true;
+    }
+
+    return true;
+  }
+
+  bool did_check_ = false;
+};
+
+extern "C" JNIEXPORT void JNICALL Java_PhiLiveness_regsNativeCall(
+    JNIEnv*, jclass value ATTRIBUTE_UNUSED) {
+  ScopedObjectAccess soa(Thread::Current());
+  std::unique_ptr<Context> context(Context::Create());
+  TestVisitor visitor(soa.Self(), context.get());
+  visitor.WalkStack();
+  CHECK(visitor.did_check_);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_PhiLiveness_regsNativeCallWithParameters(
+    JNIEnv*, jclass value ATTRIBUTE_UNUSED, jobject main, jint int_value, jfloat float_value) {
+  ScopedObjectAccess soa(Thread::Current());
+  std::unique_ptr<Context> context(Context::Create());
+  CHECK(soa.Decode<mirror::Object*>(main) == nullptr);
+  CHECK_EQ(int_value, 0);
+  int32_t cast = bit_cast<float, int32_t>(float_value);
+  CHECK_EQ(cast, 0);
+  TestVisitor visitor(soa.Self(), context.get());
+  visitor.WalkStack();
+  CHECK(visitor.did_check_);
+}
+
+}  // namespace
+
+}  // namespace art
diff --git a/test/457-regs/smali/PhiLiveness.smali b/test/457-regs/smali/PhiLiveness.smali
new file mode 100644
index 0000000..c8a6773
--- /dev/null
+++ b/test/457-regs/smali/PhiLiveness.smali
@@ -0,0 +1,82 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LPhiLiveness;
+
+.super Ljava/lang/Object;
+
+.method public static mergeOk(ZB)V
+   .registers 5
+   const/4 v0, 0x0
+   const/4 v1, 0x1
+   move v2, v3
+   if-eq v1, v0, :else
+   move v2, v4
+   :else
+   invoke-static {}, LPhiLiveness;->regsNativeCall()V
+   return-void
+.end method
+
+.method public static mergeNotOk(ZF)V
+   .registers 5
+   const/4 v0, 0x0
+   const/4 v1, 0x1
+   move v2, v3
+   if-eq v1, v0, :else
+   move v2, v4
+   :else
+   invoke-static {}, LPhiLiveness;->regsNativeCall()V
+   return-void
+.end method
+
+.method public static mergeReferences(LMain;)V
+   .registers 4
+   const/4 v0, 0x0
+   const/4 v1, 0x1
+   move-object v2, p0
+   if-eq v1, v0, :else
+   move v2, v0
+   :else
+   invoke-static {}, LPhiLiveness;->regsNativeCall()V
+   return-void
+.end method
+
+.method public static phiEquivalent()F
+   .registers 5
+   const/4 v0, 0x0
+   const/4 v1, 0x1
+   move v2, v0
+   if-eq v1, v0, :else
+   move v2, v1
+   :else
+   invoke-static {}, LPhiLiveness;->regsNativeCall()V
+   return v2
+.end method
+
+.method public static phiAllEquivalents(LMain;)V
+   .registers 4
+   const/4 v0, 0x0
+   const/4 v1, 0x1
+   move v2, v0
+   if-eq v1, v0, :else
+   move v2, v0
+   :else
+   invoke-static {v2, v2, v2}, LPhiLiveness;->regsNativeCallWithParameters(LMain;IF)V
+   return-void
+.end method
+
+.method public static native regsNativeCall()V
+.end method
+.method public static native regsNativeCallWithParameters(LMain;IF)V
+.end method
diff --git a/test/457-regs/src/Main.java b/test/457-regs/src/Main.java
new file mode 100644
index 0000000..0d82033
--- /dev/null
+++ b/test/457-regs/src/Main.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+
+  // Workaround for b/18051191.
+  class InnerClass {}
+
+  public static void main(String[] args) throws Exception {
+    Class<?> c = Class.forName("PhiLiveness");
+    Method m = c.getMethod("mergeOk", boolean.class, byte.class);
+    m.invoke(null, new Boolean(true), new Byte((byte)2));
+
+    m = c.getMethod("mergeNotOk", boolean.class, float.class);
+    m.invoke(null, new Boolean(true), new Float(4.0f));
+
+    m = c.getMethod("mergeReferences", Main.class);
+    m.invoke(null, new Main());
+
+    m = c.getMethod("phiEquivalent");
+    m.invoke(null);
+
+    m = c.getMethod("phiAllEquivalents", Main.class);
+    m.invoke(null, new Main());
+  }
+
+  static {
+    System.loadLibrary("arttest");
+  }
+}
diff --git a/test/703-floating-point-div/src/Main.java b/test/703-floating-point-div/src/Main.java
index 9990a54..2303702 100644
--- a/test/703-floating-point-div/src/Main.java
+++ b/test/703-floating-point-div/src/Main.java
@@ -41,7 +41,7 @@
         double d7 = -0.0;
         double d8 = Double.MAX_VALUE;
         double d9 = Double.MIN_VALUE;
-        double d0 = Double.NaN;
+        double dNaN = Double.NaN;
 
         expectEquals(Double.doubleToRawLongBits(dPi/d1), 0x1921fb54442d18L);
         expectEquals(Double.doubleToRawLongBits(dPi/d2), 0xbff921fb54442d18L);
@@ -53,7 +53,10 @@
 
         expectEquals(Double.doubleToRawLongBits(dPi/d8), 0xc90fdaa22168cL);
         expectEquals(Double.doubleToRawLongBits(dPi/d9), 0x7ff0000000000000L);
-        expectEquals(Double.doubleToRawLongBits(dPi/d0), 0x7ff8000000000000L);
+
+        // Not-a-number computation. Use doubleToLongBits to get canonical NaN. The literal value
+        // is the canonical NaN (see Double.doubleToLongBits).
+        expectEquals(Double.doubleToLongBits(dPi/dNaN), 0x7ff8000000000000L);
     }
 
     public static void divFloatTest() {
@@ -66,7 +69,7 @@
         float f7 = -0.0f;
         float f8 = Float.MAX_VALUE;
         float f9 = Float.MIN_VALUE;
-        float f0 = Float.NaN;
+        float fNaN = Float.NaN;
 
         expectEquals(Float.floatToRawIntBits(fPi/f1), 0xc90fdb);
         expectEquals(Float.floatToRawIntBits(fPi/f2), 0xbfc90fdb);
@@ -78,7 +81,10 @@
 
         expectEquals(Float.floatToRawIntBits(fPi/f8), 0x6487ee);
         expectEquals(Float.floatToRawIntBits(fPi/f9), 0x7f800000);
-        expectEquals(Float.floatToRawIntBits(fPi/f0), 0x7fc00000);
+
+        // Not-a-number computation. Use floatToIntBits to get canonical NaN. The literal value
+        // is the canonical NaN (see Float.floatToIntBits).
+        expectEquals(Float.floatToIntBits(fPi/fNaN), 0x7fc00000);
     }
 
     public static void main(String[] args) {
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index 75c5d72..f4bab3f 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -29,7 +29,8 @@
   117-nopatchoat/nopatchoat.cc \
   118-noimage-dex2oat/noimage-dex2oat.cc \
   454-get-vreg/get_vreg_jni.cc \
-  455-set-vreg/set_vreg_jni.cc
+  455-set-vreg/set_vreg_jni.cc \
+  457-regs/regs_jni.cc
 
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
 ifdef TARGET_2ND_ARCH
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 63d5984..b85ece8 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -311,6 +311,7 @@
   131-structural-change \
   454-get-vreg \
   455-set-vreg \
+  457-regs \
 
 ifneq (,$(filter ndebug,$(RUN_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREBUILD_TYPES), \
@@ -321,7 +322,8 @@
 TEST_ART_BROKEN_NDEBUG_TESTS :=
 
 # Known broken tests for the default compiler (Quick).
-TEST_ART_BROKEN_DEFAULT_RUN_TESTS :=
+TEST_ART_BROKEN_DEFAULT_RUN_TESTS := \
+  457-regs
 
 ifneq (,$(filter default,$(COMPILER_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -377,6 +379,18 @@
 
 TEST_ART_BROKEN_OPTIMIZING_RUN_TESTS :=
 
+# Tests that should fail when the optimizing compiler compiles them non-debuggable.
+TEST_ART_BROKEN_OPTIMIZING_NONDEBUGGABLE_RUN_TESTS := \
+  457-regs \
+
+ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      optimizing,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),nondebuggable,$(TEST_ART_BROKEN_OPTIMIZING_NONDEBUGGABLE_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_OPTIMIZING_NONDEBUGGABLE_RUN_TESTS :=
+
 
 # Clear variables ahead of appending to them when defining tests.
 $(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=))
diff --git a/test/MultiDex/Main.java b/test/MultiDex/Main.java
new file mode 100644
index 0000000..659dba9
--- /dev/null
+++ b/test/MultiDex/Main.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+class Main {
+    public static void main(String args[]) {
+      Second second = new Second();
+      System.out.println(second.getSecond());
+    }
+}
diff --git a/test/MultiDex/Second.java b/test/MultiDex/Second.java
new file mode 100644
index 0000000..540aedb
--- /dev/null
+++ b/test/MultiDex/Second.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+class Second {
+  public String getSecond() {
+    return "I Second That.";
+  }
+}
diff --git a/test/MultiDex/main.list b/test/MultiDex/main.list
new file mode 100644
index 0000000..44ba78e
--- /dev/null
+++ b/test/MultiDex/main.list
@@ -0,0 +1 @@
+Main.class
diff --git a/tools/analyze-init-failures.py b/tools/analyze-init-failures.py
index f803ea3..cca05e1 100755
--- a/tools/analyze-init-failures.py
+++ b/tools/analyze-init-failures.py
@@ -40,6 +40,8 @@
 
   class_fail_class = {}
   class_fail_method = {}
+  class_fail_load_library = {}
+  class_fail_get_property = {}
   root_failures = set()
   root_errors = {}
 
@@ -82,6 +84,8 @@
       immediate_class = root_err_class
       immediate_method = root_method_name
       root_errors[root_err_class] = error
+      was_load_library = False
+      was_get_property = False
       # Now go "up" the stack.
       while True:
         raw_line = it.next()
@@ -95,8 +99,19 @@
           # A class initializer is on the stack...
           class_fail_class[err_class] = immediate_class
           class_fail_method[err_class] = immediate_method
+          class_fail_load_library[err_class] = was_load_library
           immediate_class = err_class
           immediate_method = err_method_name
+          class_fail_get_property[err_class] = was_get_property
+          was_get_property = False
+        was_load_library = err_method_name == "loadLibrary"
+        was_get_property = was_get_property or err_method_name == "getProperty"
+      failed_clazz_norm = re.sub(r"^L", "", failed_clazz)
+      failed_clazz_norm = re.sub(r";$", "", failed_clazz_norm)
+      failed_clazz_norm = re.sub(r"/", "", failed_clazz_norm)
+      if immediate_class != failed_clazz_norm:
+        class_fail_class[failed_clazz_norm] = immediate_class
+        class_fail_method[failed_clazz_norm] = immediate_method
     except StopIteration:
       # print('Done')
       break  # Done
@@ -114,7 +129,11 @@
   for (r_class, r_id) in class_index.items():
     error_string = ''
     if r_class in root_failures:
-      error_string = ',color=Red,tooltip="' + root_errors[r_class] + '",URL="' + root_errors[r_class] + '"'
+      error_string = ',style=filled,fillcolor=Red,tooltip="' + root_errors[r_class] + '",URL="' + root_errors[r_class] + '"'
+    elif r_class in class_fail_load_library and class_fail_load_library[r_class] == True:
+      error_string = error_string + ',style=filled,fillcolor=Bisque'
+    elif r_class in class_fail_get_property and class_fail_get_property[r_class] == True:
+      error_string = error_string + ',style=filled,fillcolor=Darkseagreen'
     print('  n%d [shape=box,label="%s"%s];' % (r_id, r_class, error_string))
 
   # Some space.