Merge "ART: Generalize "x >> (s & m)" simplification."
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 10b3fe1..1218586 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -669,10 +669,10 @@
 #endif
 #ifdef ART_ENABLE_CODEGEN_mips
     case kMips: {
-      mips::PcRelativeFixups* pc_relative_fixups =
-          new (arena) mips::PcRelativeFixups(graph, codegen, stats);
       SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
       GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects, "GVN$after_arch");
+      mips::PcRelativeFixups* pc_relative_fixups =
+          new (arena) mips::PcRelativeFixups(graph, codegen, stats);
       HOptimization* mips_optimizations[] = {
           side_effects,
           gvn,
@@ -696,11 +696,15 @@
 #endif
 #ifdef ART_ENABLE_CODEGEN_x86
     case kX86: {
+      SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
+      GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects, "GVN$after_arch");
       x86::PcRelativeFixups* pc_relative_fixups =
           new (arena) x86::PcRelativeFixups(graph, codegen, stats);
       x86::X86MemoryOperandGeneration* memory_gen =
           new (arena) x86::X86MemoryOperandGeneration(graph, codegen, stats);
       HOptimization* x86_optimizations[] = {
+          side_effects,
+          gvn,
           pc_relative_fixups,
           memory_gen
       };
@@ -710,9 +714,13 @@
 #endif
 #ifdef ART_ENABLE_CODEGEN_x86_64
     case kX86_64: {
+      SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
+      GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects, "GVN$after_arch");
       x86::X86MemoryOperandGeneration* memory_gen =
           new (arena) x86::X86MemoryOperandGeneration(graph, codegen, stats);
       HOptimization* x86_64_optimizations[] = {
+          side_effects,
+          gvn,
           memory_gen
       };
       RunOptimizations(x86_64_optimizations, arraysize(x86_64_optimizations), pass_observer);
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 7ddf1c1..1f644c1 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -311,7 +311,11 @@
   {
     ProfileCompilationInfo profile;
     VisitLibcoreDexes([&profile](MethodReference ref) {
-      EXPECT_TRUE(profile.AddMethodIndex(ProfileCompilationInfo::MethodHotness::kFlagHot, ref));
+      uint32_t flags = ProfileCompilationInfo::MethodHotness::kFlagHot |
+          ProfileCompilationInfo::MethodHotness::kFlagStartup;
+      EXPECT_TRUE(profile.AddMethodIndex(
+          static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags),
+          ref));
     }, [&profile](TypeReference ref) {
       EXPECT_TRUE(profile.AddClassForDex(ref));
     }, kMethodFrequency, kTypeFrequency);
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index a80dbf6..dfbe31a 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -961,8 +961,8 @@
       if (method_hotness.IsHot() &&
               !method_hotness.IsStartup() && !method_hotness.IsPostStartup()) {
         std::string name = method_reference.PrettyMethod();
-        LOG(WARNING) << "Method " << name << " had a Hot method that wasn't marked "
-                     << "either start-up or post-startup. Possible corrupted profile?";
+        LOG(FATAL) << "Method " << name << " had a Hot method that wasn't marked "
+                   << "either start-up or post-startup. Possible corrupted profile?";
         // This is not fatal, so only warn.
       }
     }
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 7f841fc..f05977a 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -86,20 +86,38 @@
 
 TiMethodCallback gMethodCallback;
 
+// TODO We should make this much more selective in the future so we only return true when we
+// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though
+// we can just assume that we care we are loaded at all.
+//
+// Even if we don't keep track of this at the method level we might want to keep track of it at the
+// level of enabled capabilities.
+struct TiMethodInspectionCallback : public art::MethodInspectionCallback {
+  bool IsMethodBeingInspected(art::ArtMethod* method ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+    return true;
+  }
+};
+
+TiMethodInspectionCallback gMethodInspectionCallback;
+
 void MethodUtil::Register(EventHandler* handler) {
   gMethodCallback.event_handler = handler;
   art::ScopedThreadStateChange stsc(art::Thread::Current(),
                                     art::ThreadState::kWaitingForDebuggerToAttach);
   art::ScopedSuspendAll ssa("Add method callback");
-  art::Runtime::Current()->GetRuntimeCallbacks()->AddMethodCallback(&gMethodCallback);
+  art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
+  callbacks->AddMethodCallback(&gMethodCallback);
+  callbacks->AddMethodInspectionCallback(&gMethodInspectionCallback);
 }
 
 void MethodUtil::Unregister() {
   art::ScopedThreadStateChange stsc(art::Thread::Current(),
                                     art::ThreadState::kWaitingForDebuggerToAttach);
   art::ScopedSuspendAll ssa("Remove method callback");
-  art::Runtime* runtime = art::Runtime::Current();
-  runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
+  art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
+  callbacks->RemoveMethodCallback(&gMethodCallback);
+  callbacks->AddMethodInspectionCallback(&gMethodInspectionCallback);
 }
 
 jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
diff --git a/runtime/Android.bp b/runtime/Android.bp
index a3a45e1..711bc65 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -387,7 +387,7 @@
             ],
             shared_libs: [
                 "libziparchive",
-                "libz-host",
+                "libz",
             ],
         },
     },
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 6daec72..b021ff1 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -324,6 +324,7 @@
 bool Dbg::gDebuggerActive = false;
 bool Dbg::gDisposed = false;
 ObjectRegistry* Dbg::gRegistry = nullptr;
+DebuggerActiveMethodInspectionCallback Dbg::gDebugActiveCallback;
 
 // Deoptimization support.
 std::vector<DeoptimizationRequest> Dbg::deoptimization_requests_;
@@ -341,6 +342,10 @@
 Dbg::DbgThreadLifecycleCallback Dbg::thread_lifecycle_callback_;
 Dbg::DbgClassLoadCallback Dbg::class_load_callback_;
 
+bool DebuggerActiveMethodInspectionCallback::IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) {
+  return Dbg::IsDebuggerActive();
+}
+
 // Breakpoints.
 static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
 
@@ -652,6 +657,7 @@
   }
   instrumentation_events_ = 0;
   gDebuggerActive = true;
+  Runtime::Current()->GetRuntimeCallbacks()->AddMethodInspectionCallback(&gDebugActiveCallback);
   LOG(INFO) << "Debugger is active";
 }
 
@@ -689,6 +695,8 @@
         runtime->GetInstrumentation()->DisableDeoptimization(kDbgInstrumentationKey);
       }
       gDebuggerActive = false;
+      Runtime::Current()->GetRuntimeCallbacks()->RemoveMethodInspectionCallback(
+          &gDebugActiveCallback);
     }
   }
 
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 0be46d6..18126b1 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -34,6 +34,7 @@
 #include "jni.h"
 #include "jvalue.h"
 #include "obj_ptr.h"
+#include "runtime_callbacks.h"
 #include "thread.h"
 #include "thread_state.h"
 
@@ -51,6 +52,12 @@
 class StackVisitor;
 class Thread;
 
+struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback {
+  bool IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED)
+      OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+};
+
+
 /*
  * Invoke-during-breakpoint support.
  */
@@ -773,6 +780,8 @@
   // Indicates whether the debugger is making requests.
   static bool gDebuggerActive;
 
+  static DebuggerActiveMethodInspectionCallback gDebugActiveCallback;
+
   // Indicates whether we should drop the JDWP connection because the runtime stops or the
   // debugger called VirtualMachine.Dispose.
   static bool gDisposed;
diff --git a/runtime/dex_instruction_iterator.h b/runtime/dex_instruction_iterator.h
index 6585875..280746e 100644
--- a/runtime/dex_instruction_iterator.h
+++ b/runtime/dex_instruction_iterator.h
@@ -26,8 +26,8 @@
 
 class DexInstructionIterator : public std::iterator<std::forward_iterator_tag, Instruction> {
  public:
-  using Type = std::iterator<std::forward_iterator_tag, Instruction>::value_type;
-  using difference_type = typename std::iterator<std::forward_iterator_tag, Type>::difference_type;
+  using value_type = std::iterator<std::forward_iterator_tag, Instruction>::value_type;
+  using difference_type = std::iterator<std::forward_iterator_tag, value_type>::difference_type;
 
   DexInstructionIterator() = default;
   DexInstructionIterator(const DexInstructionIterator&) = default;
@@ -35,12 +35,8 @@
   DexInstructionIterator& operator=(const DexInstructionIterator&) = default;
   DexInstructionIterator& operator=(DexInstructionIterator&&) = default;
 
-  explicit DexInstructionIterator(const Type* inst) : inst_(inst) {}
-  explicit DexInstructionIterator(const uint16_t* inst) : inst_(Type::At(inst)) {}
-
-  ALWAYS_INLINE bool operator==(const DexInstructionIterator& other) const {
-    return inst_ == other.inst_ || inst_ == nullptr || other.inst_ == nullptr;
-  }
+  explicit DexInstructionIterator(const value_type* inst) : inst_(inst) {}
+  explicit DexInstructionIterator(const uint16_t* inst) : inst_(value_type::At(inst)) {}
 
   // Value after modification.
   DexInstructionIterator& operator++() {
@@ -55,11 +51,11 @@
     return temp;
   }
 
-  const Type& operator*() const {
+  const value_type& operator*() const {
     return *inst_;
   }
 
-  const Type* operator->() const {
+  const value_type* operator->() const {
     return &**this;
   }
 
@@ -69,14 +65,19 @@
         reinterpret_cast<const uint16_t*>(code_item_begin.inst_);
   }
 
-  const Type* Inst() const {
+  const value_type* Inst() const {
     return inst_;
   }
 
  private:
-  const Type* inst_ = nullptr;
+  const value_type* inst_ = nullptr;
 };
 
+static ALWAYS_INLINE inline bool operator==(const DexInstructionIterator& lhs,
+                                            const DexInstructionIterator& rhs) {
+  return lhs.Inst() == rhs.Inst();
+}
+
 static inline bool operator!=(const DexInstructionIterator& lhs,
                               const DexInstructionIterator& rhs) {
   return !(lhs == rhs);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index eef2773..813a264 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1119,8 +1119,8 @@
       const DexFile::CodeItem* code;
       code = caller->GetCodeItem();
       CHECK_LT(dex_pc, code->insns_size_in_code_units_);
-      const Instruction* instr = &code->InstructionAt(dex_pc);
-      Instruction::Code instr_code = instr->Opcode();
+      const Instruction& instr = code->InstructionAt(dex_pc);
+      Instruction::Code instr_code = instr.Opcode();
       bool is_range;
       switch (instr_code) {
         case Instruction::INVOKE_DIRECT:
@@ -1164,10 +1164,10 @@
           is_range = true;
           break;
         default:
-          LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(nullptr);
+          LOG(FATAL) << "Unexpected call into trampoline: " << instr.DumpString(nullptr);
           UNREACHABLE();
       }
-      called_method.index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
+      called_method.index = (is_range) ? instr.VRegB_3rc() : instr.VRegB_35c();
       // Check that the invoke matches what we expected, note that this path only happens for debug
       // builds.
       if (found_stack_map) {
@@ -2484,16 +2484,16 @@
     uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
     const DexFile::CodeItem* code_item = caller_method->GetCodeItem();
     DCHECK_LT(dex_pc, code_item->insns_size_in_code_units_);
-    const Instruction* instr = &code_item->InstructionAt(dex_pc);
-    Instruction::Code instr_code = instr->Opcode();
+    const Instruction& instr = code_item->InstructionAt(dex_pc);
+    Instruction::Code instr_code = instr.Opcode();
     DCHECK(instr_code == Instruction::INVOKE_INTERFACE ||
            instr_code == Instruction::INVOKE_INTERFACE_RANGE)
-        << "Unexpected call into interface trampoline: " << instr->DumpString(nullptr);
+        << "Unexpected call into interface trampoline: " << instr.DumpString(nullptr);
     if (instr_code == Instruction::INVOKE_INTERFACE) {
-      dex_method_idx = instr->VRegB_35c();
+      dex_method_idx = instr.VRegB_35c();
     } else {
       DCHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
-      dex_method_idx = instr->VRegB_3rc();
+      dex_method_idx = instr.VRegB_3rc();
     }
 
     const DexFile& dex_file = caller_method->GetDeclaringClass()->GetDexFile();
@@ -2600,11 +2600,11 @@
   ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
   uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
   const DexFile::CodeItem* code = caller_method->GetCodeItem();
-  const Instruction* inst = &code->InstructionAt(dex_pc);
-  DCHECK(inst->Opcode() == Instruction::INVOKE_POLYMORPHIC ||
-         inst->Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
+  const Instruction& inst = code->InstructionAt(dex_pc);
+  DCHECK(inst.Opcode() == Instruction::INVOKE_POLYMORPHIC ||
+         inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
   const DexFile* dex_file = caller_method->GetDexFile();
-  const uint32_t proto_idx = inst->VRegH();
+  const uint32_t proto_idx = inst.VRegH();
   const char* shorty = dex_file->GetShorty(proto_idx);
   const size_t shorty_length = strlen(shorty);
   static const bool kMethodIsStatic = false;  // invoke() and invokeExact() are not static.
@@ -2621,7 +2621,7 @@
   // Resolve method - it's either MethodHandle.invoke() or MethodHandle.invokeExact().
   ClassLinker* linker = Runtime::Current()->GetClassLinker();
   ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
-      self, inst->VRegB(), caller_method, kVirtual);
+      self, inst.VRegB(), caller_method, kVirtual);
   DCHECK((resolved_method ==
           jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact)) ||
          (resolved_method ==
@@ -2642,15 +2642,15 @@
     return static_cast<uintptr_t>('V');
   }
 
-  DCHECK_EQ(ArtMethod::NumArgRegisters(shorty) + 1u, (uint32_t)inst->VRegA());
+  DCHECK_EQ(ArtMethod::NumArgRegisters(shorty) + 1u, (uint32_t)inst.VRegA());
   DCHECK_EQ(resolved_method->IsStatic(), kMethodIsStatic);
 
   // Fix references before constructing the shadow frame.
   gc_visitor.FixupReferences();
 
   // Construct shadow frame placing arguments consecutively from |first_arg|.
-  const bool is_range = (inst->Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
-  const size_t num_vregs = is_range ? inst->VRegA_4rcc() : inst->VRegA_45cc();
+  const bool is_range = (inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
+  const size_t num_vregs = is_range ? inst.VRegA_4rcc() : inst.VRegA_45cc();
   const size_t first_arg = 0;
   ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
       CREATE_SHADOW_FRAME(num_vregs, /* link */ nullptr, resolved_method, dex_pc);
diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index 6903af2..80fdadb 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -188,9 +188,7 @@
       const uint32_t* vreg_ptr = &vregs_[i];
       ref = reinterpret_cast<const StackReference<mirror::Object>*>(vreg_ptr)->AsMirrorPtr();
     }
-    if (kUseReadBarrier) {
-      ReadBarrier::AssertToSpaceInvariant(ref);
-    }
+    ReadBarrier::MaybeAssertToSpaceInvariant(ref);
     if (kVerifyFlags & kVerifyReads) {
       VerifyObject(ref);
     }
@@ -256,9 +254,7 @@
     if (kVerifyFlags & kVerifyWrites) {
       VerifyObject(val);
     }
-    if (kUseReadBarrier) {
-      ReadBarrier::AssertToSpaceInvariant(val);
-    }
+    ReadBarrier::MaybeAssertToSpaceInvariant(val);
     uint32_t* vreg = &vregs_[i];
     reinterpret_cast<StackReference<mirror::Object>*>(vreg)->Assign(val);
     if (HasReferenceArray()) {
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 8c27bfe..97a3b71 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -490,10 +490,10 @@
       return false;
     }
 
-    // Before allowing the jump, make sure the debugger is not active to avoid jumping from
-    // interpreter to OSR while e.g. single stepping. Note that we could selectively disable
-    // OSR when single stepping, but that's currently hard to know at this point.
-    if (Dbg::IsDebuggerActive()) {
+    // Before allowing the jump, make sure no code is actively inspecting the method to avoid
+    // jumping from interpreter to OSR while e.g. single stepping. Note that we could selectively
+    // disable OSR when single stepping, but that's currently hard to know at this point.
+    if (Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method)) {
       return false;
     }
 
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 1ed7889..5601317 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -1704,7 +1704,10 @@
       if (m < (number_of_methods / kFavorSplit)) {
         method_idx %= kFavorFirstN;
       }
-      info.AddMethodIndex(MethodHotness::kFlagHot,
+      // Alternate between startup and post startup.
+      uint32_t flags = MethodHotness::kFlagHot;
+      flags |= ((m & 1) != 0) ? MethodHotness::kFlagPostStartup : MethodHotness::kFlagStartup;
+      info.AddMethodIndex(static_cast<MethodHotness::Flag>(flags),
                           profile_key,
                           /*method_idx*/ 0,
                           method_idx,
@@ -1761,8 +1764,13 @@
       if (number_of_methods - i == methods_required_in_profile ||
           std::rand() % (number_of_methods - i - methods_required_in_profile) == 0) {
         uint32_t method_index = (method_start_index + i) % number_of_methods;
-        info.AddMethodIndex(MethodHotness::kFlagHot, MethodReference(dex_file.get(),
-                                                                     method_index));
+        // Alternate between startup and post startup.
+        uint32_t flags = MethodHotness::kFlagHot;
+        flags |= ((method_index & 1) != 0)
+            ? MethodHotness::kFlagPostStartup
+            : MethodHotness::kFlagStartup;
+        info.AddMethodIndex(static_cast<MethodHotness::Flag>(flags),
+                            MethodReference(dex_file.get(), method_index));
         methods_required_in_profile--;
       }
     }
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index b2f6c03..9a42c29 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -635,29 +635,35 @@
   }
 
   if (boot_image_tables != nullptr) {
-    // Map boot image tables into the .bss. The reserved size must match size of the tables.
-    size_t reserved_size = static_cast<size_t>(boot_image_tables_end - boot_image_tables);
-    size_t tables_size = 0u;
-    for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
-      tables_size += space->GetImageHeader().GetBootImageConstantTablesSize();
-      DCHECK_ALIGNED(tables_size, kPageSize);
-    }
-    if (tables_size != reserved_size) {
-      *error_msg = StringPrintf("In oat file '%s' found unexpected boot image table sizes, "
-                                    " %zu bytes, should be %zu.",
-                                GetLocation().c_str(),
-                                reserved_size,
-                                tables_size);
-      return false;
-    }
-    for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
-      uint32_t current_tables_size = space->GetImageHeader().GetBootImageConstantTablesSize();
-      if (current_tables_size != 0u && !MapConstantTables(space, boot_image_tables)) {
+    Runtime* runtime = Runtime::Current();
+    if (UNLIKELY(runtime == nullptr)) {
+      // This must be oatdump without boot image. Make sure the .bss is inaccessible.
+      mprotect(const_cast<uint8_t*>(BssBegin()), BssSize(), PROT_NONE);
+    } else {
+      // Map boot image tables into the .bss. The reserved size must match size of the tables.
+      size_t reserved_size = static_cast<size_t>(boot_image_tables_end - boot_image_tables);
+      size_t tables_size = 0u;
+      for (gc::space::ImageSpace* space : runtime->GetHeap()->GetBootImageSpaces()) {
+        tables_size += space->GetImageHeader().GetBootImageConstantTablesSize();
+        DCHECK_ALIGNED(tables_size, kPageSize);
+      }
+      if (tables_size != reserved_size) {
+        *error_msg = StringPrintf("In oat file '%s' found unexpected boot image table sizes, "
+                                      " %zu bytes, should be %zu.",
+                                  GetLocation().c_str(),
+                                  reserved_size,
+                                  tables_size);
         return false;
       }
-      boot_image_tables += current_tables_size;
+      for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
+        uint32_t current_tables_size = space->GetImageHeader().GetBootImageConstantTablesSize();
+        if (current_tables_size != 0u && !MapConstantTables(space, boot_image_tables)) {
+          return false;
+        }
+        boot_image_tables += current_tables_size;
+      }
+      DCHECK(boot_image_tables == boot_image_tables_end);
     }
-    DCHECK(boot_image_tables == boot_image_tables_end);
   }
   return true;
 }
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index d8b6237..f94923e 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -166,10 +166,9 @@
                 << line_number << ")";
     }
   }
-  if (clear_exception_) {
-    // Exception was cleared as part of delivery.
-    DCHECK(!self_->IsExceptionPending());
-  } else {
+  // Exception was cleared as part of delivery.
+  DCHECK(!self_->IsExceptionPending());
+  if (!clear_exception_) {
     // Put exception back in root set with clear throw location.
     self_->SetException(exception_ref.Get());
   }
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index 8090f9b..12b63c9 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -112,6 +112,10 @@
     handler_dex_pc_ = dex_pc;
   }
 
+  bool GetClearException() const {
+    return clear_exception_;
+  }
+
   void SetClearException(bool clear_exception) {
     clear_exception_ = clear_exception;
   }
diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h
index 45e78bc..00674b2 100644
--- a/runtime/read_barrier.h
+++ b/runtime/read_barrier.h
@@ -89,6 +89,14 @@
   static void AssertToSpaceInvariant(GcRootSource* gc_root_source, mirror::Object* ref)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Without the holder object, and only with the read barrier configuration (no-op otherwise).
+  static void MaybeAssertToSpaceInvariant(mirror::Object* ref)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (kUseReadBarrier) {
+      AssertToSpaceInvariant(ref);
+    }
+  }
+
   // ALWAYS_INLINE on this caused a performance regression b/26744236.
   static mirror::Object* Mark(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 88d3f28..f164f7c 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -26,10 +26,6 @@
 
 namespace art {
 
-void RuntimeCallbacks::AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
-  thread_callbacks_.push_back(cb);
-}
-
 template <typename T>
 ALWAYS_INLINE
 static inline void Remove(T* cb, std::vector<T*>* data) {
@@ -39,6 +35,27 @@
   }
 }
 
+void RuntimeCallbacks::AddMethodInspectionCallback(MethodInspectionCallback* cb) {
+  method_inspection_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveMethodInspectionCallback(MethodInspectionCallback* cb) {
+  Remove(cb, &method_inspection_callbacks_);
+}
+
+bool RuntimeCallbacks::IsMethodBeingInspected(ArtMethod* m) {
+  for (MethodInspectionCallback* cb : method_inspection_callbacks_) {
+    if (cb->IsMethodBeingInspected(m)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void RuntimeCallbacks::AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
+  thread_callbacks_.push_back(cb);
+}
+
 void RuntimeCallbacks::MonitorContendedLocking(Monitor* m) {
   for (MonitorCallback* cb : monitor_callbacks_) {
     cb->MonitorContendedLocking(m);
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index fa686d3..c936049 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -94,6 +94,18 @@
   virtual ~MonitorCallback() {}
 };
 
+// A callback to let parts of the runtime note that they are currently relying on a particular
+// method remaining in it's current state. Users should not rely on always being called. If multiple
+// callbacks are added the runtime will short-circuit when the first one returns 'true'.
+class MethodInspectionCallback {
+ public:
+  virtual ~MethodInspectionCallback() {}
+
+  // Returns true if the method is being inspected currently and the runtime should not modify it in
+  // potentially dangerous ways (i.e. replace with compiled version, JIT it, etc).
+  virtual bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
 class RuntimeCallbacks {
  public:
   void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_);
@@ -151,6 +163,15 @@
   void AddMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
   void RemoveMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Returns true if some MethodInspectionCallback indicates the method is being inspected/depended
+  // on by some code.
+  bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
+  void AddMethodInspectionCallback(MethodInspectionCallback* cb)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  void RemoveMethodInspectionCallback(MethodInspectionCallback* cb)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   std::vector<ThreadLifecycleCallback*> thread_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
@@ -164,6 +185,8 @@
       GUARDED_BY(Locks::mutator_lock_);
   std::vector<MonitorCallback*> monitor_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
+  std::vector<MethodInspectionCallback*> method_inspection_callbacks_
+      GUARDED_BY(Locks::mutator_lock_);
 };
 
 }  // namespace art
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 968a23b..524e73d 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2159,11 +2159,11 @@
     ScopedObjectAccess soa(self);
     // We may need to call user-supplied managed code, do this before final clean-up.
     HandleUncaughtExceptions(soa);
+    RemoveFromThreadGroup(soa);
     Runtime* runtime = Runtime::Current();
     if (runtime != nullptr) {
       runtime->GetRuntimeCallbacks()->ThreadDeath(self);
     }
-    RemoveFromThreadGroup(soa);
 
     // this.nativePeer = 0;
     if (Runtime::Current()->IsActiveTransaction()) {
@@ -3080,6 +3080,8 @@
     UNREACHABLE();
   }
 
+  ReadBarrier::MaybeAssertToSpaceInvariant(exception.Ptr());
+
   // This is a real exception: let the instrumentation know about it.
   instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
   if (instrumentation->HasExceptionThrownListeners() &&
@@ -3121,6 +3123,15 @@
   QuickExceptionHandler exception_handler(this, false);
   exception_handler.FindCatch(exception);
   exception_handler.UpdateInstrumentationStack();
+  if (exception_handler.GetClearException()) {
+    // Exception was cleared as part of delivery.
+    DCHECK(!IsExceptionPending());
+  } else {
+    // Exception was put back with a throw location.
+    DCHECK(IsExceptionPending());
+    // Check the to-space invariant on the re-installed exception (if applicable).
+    ReadBarrier::MaybeAssertToSpaceInvariant(GetException());
+  }
   exception_handler.DoLongJump();
 }
 
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index b789ac6..cfdf20d 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -1955,7 +1955,7 @@
     if (dead_start >= 0) {
       LogVerifyInfo()
           << "dead code " << reinterpret_cast<void*>(dead_start)
-          << "-" << reinterpret_cast<void*>(instructions.end().GetDexPC(instructions.begin()));
+          << "-" << reinterpret_cast<void*>(instructions.end().GetDexPC(instructions.begin()) - 1);
     }
     // To dump the state of the verify after a method, do something like:
     // if (dex_file_->PrettyMethod(dex_method_idx_) ==
@@ -2637,7 +2637,7 @@
         break;
       }
 
-      const Instruction* instance_of_inst = &code_item_->InstructionAt(instance_of_idx);
+      const Instruction& instance_of_inst = code_item_->InstructionAt(instance_of_idx);
 
       /* Check for peep-hole pattern of:
        *    ...;
@@ -2652,9 +2652,9 @@
        *  - when vX == vY.
        */
       if (!CurrentInsnFlags()->IsBranchTarget() &&
-          (Instruction::INSTANCE_OF == instance_of_inst->Opcode()) &&
-          (inst->VRegA_21t() == instance_of_inst->VRegA_22c()) &&
-          (instance_of_inst->VRegA_22c() != instance_of_inst->VRegB_22c())) {
+          (Instruction::INSTANCE_OF == instance_of_inst.Opcode()) &&
+          (inst->VRegA_21t() == instance_of_inst.VRegA_22c()) &&
+          (instance_of_inst.VRegA_22c() != instance_of_inst.VRegB_22c())) {
         // Check the type of the instance-of is different than that of registers type, as if they
         // are the same there is no work to be done here. Check that the conversion is not to or
         // from an unresolved type as type information is imprecise. If the instance-of is to an
@@ -2665,9 +2665,9 @@
         // type is assignable to the original then allow optimization. This check is performed to
         // ensure that subsequent merges don't lose type information - such as becoming an
         // interface from a class that would lose information relevant to field checks.
-        const RegType& orig_type = work_line_->GetRegisterType(this, instance_of_inst->VRegB_22c());
+        const RegType& orig_type = work_line_->GetRegisterType(this, instance_of_inst.VRegB_22c());
         const RegType& cast_type = ResolveClass<CheckAccess::kYes>(
-            dex::TypeIndex(instance_of_inst->VRegC_22c()));
+            dex::TypeIndex(instance_of_inst.VRegC_22c()));
 
         if (!orig_type.Equals(cast_type) &&
             !cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() &&
@@ -2684,7 +2684,7 @@
           }
           update_line->CopyFromLine(work_line_.get());
           update_line->SetRegisterType<LockOp::kKeep>(this,
-                                                      instance_of_inst->VRegB_22c(),
+                                                      instance_of_inst.VRegB_22c(),
                                                       cast_type);
           if (!GetInstructionFlags(instance_of_idx).IsBranchTarget() && 0 != instance_of_idx) {
             // See if instance-of was preceded by a move-object operation, common due to the small
@@ -2699,26 +2699,26 @@
                             work_insn_idx_)) {
               break;
             }
-            const Instruction* move_inst = &code_item_->InstructionAt(move_idx);
-            switch (move_inst->Opcode()) {
+            const Instruction& move_inst = code_item_->InstructionAt(move_idx);
+            switch (move_inst.Opcode()) {
               case Instruction::MOVE_OBJECT:
-                if (move_inst->VRegA_12x() == instance_of_inst->VRegB_22c()) {
+                if (move_inst.VRegA_12x() == instance_of_inst.VRegB_22c()) {
                   update_line->SetRegisterType<LockOp::kKeep>(this,
-                                                              move_inst->VRegB_12x(),
+                                                              move_inst.VRegB_12x(),
                                                               cast_type);
                 }
                 break;
               case Instruction::MOVE_OBJECT_FROM16:
-                if (move_inst->VRegA_22x() == instance_of_inst->VRegB_22c()) {
+                if (move_inst.VRegA_22x() == instance_of_inst.VRegB_22c()) {
                   update_line->SetRegisterType<LockOp::kKeep>(this,
-                                                              move_inst->VRegB_22x(),
+                                                              move_inst.VRegB_22x(),
                                                               cast_type);
                 }
                 break;
               case Instruction::MOVE_OBJECT_16:
-                if (move_inst->VRegA_32x() == instance_of_inst->VRegB_22c()) {
+                if (move_inst.VRegA_32x() == instance_of_inst.VRegB_22c()) {
                   update_line->SetRegisterType<LockOp::kKeep>(this,
-                                                              move_inst->VRegB_32x(),
+                                                              move_inst.VRegB_32x(),
                                                               cast_type);
                 }
                 break;
diff --git a/test/1923-frame-pop/src/art/Trace.java b/test/1923-frame-pop/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/1923-frame-pop/src/art/Trace.java
+++ b/test/1923-frame-pop/src/art/Trace.java
@@ -53,4 +53,16 @@
   public static native void watchFieldModification(Field f);
   public static native void watchAllFieldAccesses();
   public static native void watchAllFieldModifications();
+
+  // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+  // need to add to the bottom and not modify old ones to maintain compat.
+  public static native void enableTracing2(Class<?> methodClass,
+                                           Method entryMethod,
+                                           Method exitMethod,
+                                           Method fieldAccess,
+                                           Method fieldModify,
+                                           Method singleStep,
+                                           Method ThreadStart,
+                                           Method ThreadEnd,
+                                           Thread thr);
 }
diff --git a/test/1924-frame-pop-toggle/src/art/Trace.java b/test/1924-frame-pop-toggle/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/1924-frame-pop-toggle/src/art/Trace.java
+++ b/test/1924-frame-pop-toggle/src/art/Trace.java
@@ -53,4 +53,16 @@
   public static native void watchFieldModification(Field f);
   public static native void watchAllFieldAccesses();
   public static native void watchAllFieldModifications();
+
+  // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+  // need to add to the bottom and not modify old ones to maintain compat.
+  public static native void enableTracing2(Class<?> methodClass,
+                                           Method entryMethod,
+                                           Method exitMethod,
+                                           Method fieldAccess,
+                                           Method fieldModify,
+                                           Method singleStep,
+                                           Method ThreadStart,
+                                           Method ThreadEnd,
+                                           Thread thr);
 }
diff --git a/test/1925-self-frame-pop/src/art/Trace.java b/test/1925-self-frame-pop/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/1925-self-frame-pop/src/art/Trace.java
+++ b/test/1925-self-frame-pop/src/art/Trace.java
@@ -53,4 +53,16 @@
   public static native void watchFieldModification(Field f);
   public static native void watchAllFieldAccesses();
   public static native void watchAllFieldModifications();
+
+  // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+  // need to add to the bottom and not modify old ones to maintain compat.
+  public static native void enableTracing2(Class<?> methodClass,
+                                           Method entryMethod,
+                                           Method exitMethod,
+                                           Method fieldAccess,
+                                           Method fieldModify,
+                                           Method singleStep,
+                                           Method ThreadStart,
+                                           Method ThreadEnd,
+                                           Thread thr);
 }
diff --git a/test/1926-missed-frame-pop/src/art/Trace.java b/test/1926-missed-frame-pop/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/1926-missed-frame-pop/src/art/Trace.java
+++ b/test/1926-missed-frame-pop/src/art/Trace.java
@@ -53,4 +53,16 @@
   public static native void watchFieldModification(Field f);
   public static native void watchAllFieldAccesses();
   public static native void watchAllFieldModifications();
+
+  // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+  // need to add to the bottom and not modify old ones to maintain compat.
+  public static native void enableTracing2(Class<?> methodClass,
+                                           Method entryMethod,
+                                           Method exitMethod,
+                                           Method fieldAccess,
+                                           Method fieldModify,
+                                           Method singleStep,
+                                           Method ThreadStart,
+                                           Method ThreadEnd,
+                                           Thread thr);
 }
diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt
new file mode 100644
index 0000000..fed993c
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/expected.txt
@@ -0,0 +1,7 @@
+JNI_OnLoad called
+From GetLocalInt(), value is 42
+isInterpreted? true
+	Value is '42'
+Setting TARGET to 1337
+isInterpreted? true
+	Value is '1337'
diff --git a/test/1935-get-set-current-frame-jit/info.txt b/test/1935-get-set-current-frame-jit/info.txt
new file mode 100644
index 0000000..7342af7
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/info.txt
@@ -0,0 +1,2 @@
+Tests for jvmti get/set Local variable in the currently executing method frame.
+
diff --git a/test/1935-get-set-current-frame-jit/run b/test/1935-get-set-current-frame-jit/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java
new file mode 100644
index 0000000..eb0a637
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/Main.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import art.Locals;
+import art.StackTrace;
+import art.Suspension;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.Consumer;
+
+public class Main {
+  public static final int SET_VALUE = 1337;
+  public static final String TARGET_VAR = "TARGET";
+
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+    Locals.EnableLocalVariableAccess();
+    runGet();
+    runSet();
+  }
+
+  public static void reportValue(Object val) {
+    System.out.println("\tValue is '" + val + "'");
+  }
+
+  public static class IntRunner implements Runnable {
+    private volatile boolean continueBusyLoop;
+    private volatile boolean inBusyLoop;
+    public IntRunner() {
+      this.continueBusyLoop = true;
+      this.inBusyLoop = false;
+    }
+    public void run() {
+      int TARGET = 42;
+      // We will suspend the thread during this loop.
+      while (continueBusyLoop) {
+        inBusyLoop = true;
+      }
+      int i = 0;
+      while (Main.isInterpreted() && i < 10000) {
+        Main.ensureJitCompiled(IntRunner.class, "run");
+        i++;
+      }
+      // We shouldn't be doing OSR since we are using JVMTI and the get/set local will push us to
+      // interpreter.
+      System.out.println("isInterpreted? " + Main.isInterpreted());
+      reportValue(TARGET);
+    }
+    public void waitForBusyLoopStart() { while (!inBusyLoop) {} }
+    public void finish() {
+      continueBusyLoop = false;
+    }
+  }
+
+  public static void runGet() throws Exception {
+    Method target = IntRunner.class.getDeclaredMethod("run");
+    // Get Int
+    IntRunner int_runner = new IntRunner();
+    Thread target_get = new Thread(int_runner, "GetLocalInt - Target");
+    target_get.start();
+    int_runner.waitForBusyLoopStart();
+    try {
+      Suspension.suspend(target_get);
+    } catch (Exception e) {
+      System.out.println("FAIL: got " + e);
+      e.printStackTrace();
+      int_runner.finish();
+      target_get.join();
+      return;
+    }
+    try {
+      StackTrace.StackFrameData frame = FindStackFrame(target_get, target);
+      int depth = frame.depth;
+      if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+      int slot = FindSlot(frame);
+      int value = Locals.GetLocalVariableInt(target_get, depth, slot);
+      System.out.println("From GetLocalInt(), value is " + value);
+    } finally {
+      Suspension.resume(target_get);
+      int_runner.finish();
+      target_get.join();
+    }
+  }
+
+  public static void runSet() throws Exception {
+    Method target = IntRunner.class.getDeclaredMethod("run");
+    // Set Int
+    IntRunner int_runner = new IntRunner();
+    Thread target_set = new Thread(int_runner, "SetLocalInt - Target");
+    target_set.start();
+    int_runner.waitForBusyLoopStart();
+    try {
+      Suspension.suspend(target_set);
+    } catch (Exception e) {
+      System.out.println("FAIL: got " + e);
+      e.printStackTrace();
+      int_runner.finish();
+      target_set.join();
+      return;
+    }
+    try {
+      StackTrace.StackFrameData frame = FindStackFrame(target_set, target);
+      int depth = frame.depth;
+      if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+      int slot = FindSlot(frame);
+      System.out.println("Setting TARGET to " + SET_VALUE);
+      Locals.SetLocalVariableInt(target_set, depth, slot, SET_VALUE);
+    } finally {
+      Suspension.resume(target_set);
+      int_runner.finish();
+      target_set.join();
+    }
+  }
+
+  public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
+    long loc = frame.current_location;
+    for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
+      if (var.start_location <= loc &&
+          var.length + var.start_location > loc &&
+          var.name.equals(TARGET_VAR)) {
+        return var.slot;
+      }
+    }
+    throw new Error(
+        "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
+  }
+
+  private static StackTrace.StackFrameData FindStackFrame(Thread thr, Method target) {
+    for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+      if (frame.method.equals(target)) {
+        return frame;
+      }
+    }
+    throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
+  }
+
+  public static native void ensureJitCompiled(Class k, String f);
+  public static native boolean isInterpreted();
+}
diff --git a/test/1935-get-set-current-frame-jit/src/art/Breakpoint.java b/test/1935-get-set-current-frame-jit/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+  public static class Manager {
+    public static class BP {
+      public final Executable method;
+      public final long location;
+
+      public BP(Executable method) {
+        this(method, getStartLocation(method));
+      }
+
+      public BP(Executable method, long location) {
+        this.method = method;
+        this.location = location;
+      }
+
+      @Override
+      public boolean equals(Object other) {
+        return (other instanceof BP) &&
+            method.equals(((BP)other).method) &&
+            location == ((BP)other).location;
+      }
+
+      @Override
+      public String toString() {
+        return method.toString() + " @ " + getLine();
+      }
+
+      @Override
+      public int hashCode() {
+        return Objects.hash(method, location);
+      }
+
+      public int getLine() {
+        try {
+          LineNumber[] lines = getLineNumberTable(method);
+          int best = -1;
+          for (LineNumber l : lines) {
+            if (l.location > location) {
+              break;
+            } else {
+              best = l.line;
+            }
+          }
+          return best;
+        } catch (Exception e) {
+          return -1;
+        }
+      }
+    }
+
+    private Set<BP> breaks = new HashSet<>();
+
+    public void setBreakpoints(BP... bs) {
+      for (BP b : bs) {
+        if (breaks.add(b)) {
+          Breakpoint.setBreakpoint(b.method, b.location);
+        }
+      }
+    }
+    public void setBreakpoint(Executable method, long location) {
+      setBreakpoints(new BP(method, location));
+    }
+
+    public void clearBreakpoints(BP... bs) {
+      for (BP b : bs) {
+        if (breaks.remove(b)) {
+          Breakpoint.clearBreakpoint(b.method, b.location);
+        }
+      }
+    }
+    public void clearBreakpoint(Executable method, long location) {
+      clearBreakpoints(new BP(method, location));
+    }
+
+    public void clearAllBreakpoints() {
+      clearBreakpoints(breaks.toArray(new BP[0]));
+    }
+  }
+
+  public static void startBreakpointWatch(Class<?> methodClass,
+                                          Executable breakpointReached,
+                                          Thread thr) {
+    startBreakpointWatch(methodClass, breakpointReached, false, thr);
+  }
+
+  /**
+   * Enables the trapping of breakpoint events.
+   *
+   * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+   */
+  public static native void startBreakpointWatch(Class<?> methodClass,
+                                                 Executable breakpointReached,
+                                                 boolean allowRecursive,
+                                                 Thread thr);
+  public static native void stopBreakpointWatch(Thread thr);
+
+  public static final class LineNumber implements Comparable<LineNumber> {
+    public final long location;
+    public final int line;
+
+    private LineNumber(long loc, int line) {
+      this.location = loc;
+      this.line = line;
+    }
+
+    public boolean equals(Object other) {
+      return other instanceof LineNumber && ((LineNumber)other).line == line &&
+          ((LineNumber)other).location == location;
+    }
+
+    public int compareTo(LineNumber other) {
+      int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+      if (v != 0) {
+        return v;
+      } else {
+        return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+      }
+    }
+  }
+
+  public static native void setBreakpoint(Executable m, long loc);
+  public static void setBreakpoint(Executable m, LineNumber l) {
+    setBreakpoint(m, l.location);
+  }
+
+  public static native void clearBreakpoint(Executable m, long loc);
+  public static void clearBreakpoint(Executable m, LineNumber l) {
+    clearBreakpoint(m, l.location);
+  }
+
+  private static native Object[] getLineNumberTableNative(Executable m);
+  public static LineNumber[] getLineNumberTable(Executable m) {
+    Object[] nativeTable = getLineNumberTableNative(m);
+    long[] location = (long[])(nativeTable[0]);
+    int[] lines = (int[])(nativeTable[1]);
+    if (lines.length != location.length) {
+      throw new Error("Lines and locations have different lengths!");
+    }
+    LineNumber[] out = new LineNumber[lines.length];
+    for (int i = 0; i < lines.length; i++) {
+      out[i] = new LineNumber(location[i], lines[i]);
+    }
+    return out;
+  }
+
+  public static native long getStartLocation(Executable m);
+
+  public static int locationToLine(Executable m, long location) {
+    try {
+      Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+      int best = -1;
+      for (Breakpoint.LineNumber l : lines) {
+        if (l.location > location) {
+          break;
+        } else {
+          best = l.line;
+        }
+      }
+      return best;
+    } catch (Exception e) {
+      return -1;
+    }
+  }
+
+  public static long lineToLocation(Executable m, int line) throws Exception {
+    try {
+      Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+      for (Breakpoint.LineNumber l : lines) {
+        if (l.line == line) {
+          return l.location;
+        }
+      }
+      throw new Exception("Unable to find line " + line + " in " + m);
+    } catch (Exception e) {
+      throw new Exception("Unable to get line number info for " + m, e);
+    }
+  }
+}
+
diff --git a/test/1935-get-set-current-frame-jit/src/art/Locals.java b/test/1935-get-set-current-frame-jit/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+  public static native void EnableLocalVariableAccess();
+
+  public static class VariableDescription {
+    public final long start_location;
+    public final int length;
+    public final String name;
+    public final String signature;
+    public final String generic_signature;
+    public final int slot;
+
+    public VariableDescription(
+        long start, int length, String name, String sig, String gen_sig, int slot) {
+      this.start_location = start;
+      this.length = length;
+      this.name = name;
+      this.signature = sig;
+      this.generic_signature = gen_sig;
+      this.slot = slot;
+    }
+
+    @Override
+    public String toString() {
+      return String.format(
+          "VariableDescription { " +
+            "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+          "}",
+          this.signature,
+          this.name,
+          this.generic_signature,
+          this.slot,
+          this.start_location,
+          this.length);
+    }
+    public boolean equals(Object other) {
+      if (!(other instanceof VariableDescription)) {
+        return false;
+      } else {
+        VariableDescription v = (VariableDescription)other;
+        return Objects.equals(v.signature, signature) &&
+            Objects.equals(v.name, name) &&
+            Objects.equals(v.generic_signature, generic_signature) &&
+            v.slot == slot &&
+            v.start_location == start_location &&
+            v.length == length;
+      }
+    }
+    public int hashCode() {
+      return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+          this.start_location, this.length);
+    }
+  }
+
+  public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+  public static VariableDescription GetVariableAtLine(
+      Executable e, String name, String sig, int line) throws Exception {
+    return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+  }
+
+  public static VariableDescription GetVariableAtLocation(
+      Executable e, String name, String sig, long loc) {
+    VariableDescription[] vars = GetLocalVariableTable(e);
+    for (VariableDescription var : vars) {
+      if (var.start_location <= loc &&
+          var.length + var.start_location > loc &&
+          var.name.equals(name) &&
+          var.signature.equals(sig)) {
+        return var;
+      }
+    }
+    throw new Error(
+        "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+  }
+
+  public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+  public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+  public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+  public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+  public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+  public static native Object GetLocalInstance(Thread thr, int depth);
+
+  public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+  }
+  public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+  }
+  public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+  }
+  public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+    SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+  }
+  public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+  public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+  public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+  public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+  public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1935-get-set-current-frame-jit/src/art/StackTrace.java b/test/1935-get-set-current-frame-jit/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+  public static class StackFrameData {
+    public final Thread thr;
+    public final Executable method;
+    public final long current_location;
+    public final int depth;
+
+    public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+      this.thr = thr;
+      this.method = e;
+      this.current_location = loc;
+      this.depth = depth;
+    }
+    @Override
+    public String toString() {
+      return String.format(
+          "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+          this.thr,
+          this.method,
+          this.current_location,
+          this.depth);
+    }
+  }
+
+  public static native int GetStackDepth(Thread thr);
+
+  private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+  public static StackFrameData[] GetStackTrace(Thread thr) {
+    // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+    // suspended. The spec says that not being suspended is fine but since we want this to be
+    // consistent we will suspend for the RI.
+    boolean suspend_thread =
+        !System.getProperty("java.vm.name").equals("Dalvik") &&
+        !thr.equals(Thread.currentThread()) &&
+        !Suspension.isSuspended(thr);
+    if (suspend_thread) {
+      Suspension.suspend(thr);
+    }
+    StackFrameData[] out = nativeGetStackTrace(thr);
+    if (suspend_thread) {
+      Suspension.resume(thr);
+    }
+    return out;
+  }
+}
+
diff --git a/test/1935-get-set-current-frame-jit/src/art/Suspension.java b/test/1935-get-set-current-frame-jit/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+public class Suspension {
+  // Suspends a thread using jvmti.
+  public native static void suspend(Thread thr);
+
+  // Resumes a thread using jvmti.
+  public native static void resume(Thread thr);
+
+  public native static boolean isSuspended(Thread thr);
+
+  public native static int[] suspendList(Thread... threads);
+  public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1936-thread-end-events/check b/test/1936-thread-end-events/check
new file mode 100644
index 0000000..8a84388
--- /dev/null
+++ b/test/1936-thread-end-events/check
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The RI sends an extra event that art doesn't. Add it to the expected output.
+if [[ "$TEST_RUNTIME" == "jvm" ]]; then
+  patch -p0 expected.txt < jvm-expected.patch >/dev/null
+fi
+
+./default-check "$@"
diff --git a/test/1936-thread-end-events/expected.txt b/test/1936-thread-end-events/expected.txt
new file mode 100644
index 0000000..6b71c5e
--- /dev/null
+++ b/test/1936-thread-end-events/expected.txt
@@ -0,0 +1,42 @@
+Entered public static void art.Test1936.foo()
+Thread: test-thread
+  | alive: true
+  | interrupted: false
+  | daemon: false
+  | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered void java.lang.ThreadGroup.threadTerminated(java.lang.Thread)
+Thread: test-thread
+  | alive: true
+  | interrupted: false
+  | daemon: false
+  | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered private void java.lang.ThreadGroup.remove(java.lang.Thread)
+Thread: test-thread
+  | alive: true
+  | interrupted: false
+  | daemon: false
+  | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered public static native void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+Thread: test-thread
+  | alive: true
+  | interrupted: false
+  | daemon: false
+  | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered public static void art.Test1936.NotifyThreadEnd(java.lang.Thread)
+Thread: test-thread
+  | alive: true
+  | interrupted: false
+  | daemon: false
+  | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered public static void art.Test1936.foo()
+Thread: test-thread
+  | alive: true
+  | interrupted: false
+  | daemon: false
+  | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
diff --git a/test/1936-thread-end-events/info.txt b/test/1936-thread-end-events/info.txt
new file mode 100644
index 0000000..51986c4
--- /dev/null
+++ b/test/1936-thread-end-events/info.txt
@@ -0,0 +1,6 @@
+Tests JVMTI ThreadEnd bug
+
+We had a bug where we were still sending events after JVMTI_EVENT_THREAD_END due
+to where we sent the event. This test ensures that the placement of the
+THREAD_END event is correct.
+
diff --git a/test/1936-thread-end-events/jvm-expected.patch b/test/1936-thread-end-events/jvm-expected.patch
new file mode 100644
index 0000000..ddb30a3
--- /dev/null
+++ b/test/1936-thread-end-events/jvm-expected.patch
@@ -0,0 +1,16 @@
+7a8,14
+> Entered private void java.lang.Thread.exit()
+> Thread: test-thread
+>   | alive: true
+>   | interrupted: false
+>   | daemon: false
+>   | group: java.lang.ThreadGroup[name=main,maxpri=10]
+> 
+34c41
+<   | group: java.lang.ThreadGroup[name=main,maxpri=10]
+---
+>   | group: null
+41c48
+<   | group: java.lang.ThreadGroup[name=main,maxpri=10]
+---
+>   | group: null
diff --git a/test/1936-thread-end-events/method_trace.cc b/test/1936-thread-end-events/method_trace.cc
new file mode 100644
index 0000000..019b6a9
--- /dev/null
+++ b/test/1936-thread-end-events/method_trace.cc
@@ -0,0 +1,75 @@
+/*
+ * 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 <inttypes.h>
+
+#include <cstdio>
+#include <memory>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+#include "jni.h"
+#include "jvmti.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
+
+namespace art {
+namespace Test989StackTraceThrow {
+
+extern "C" JNIEXPORT
+jfloat JNICALL Java_art_Test989_returnFloatNative(JNIEnv* env, jclass klass) {
+  jmethodID targetMethod = env->GetStaticMethodID(klass, "doGetFloat", "()F");
+  return env->CallStaticFloatMethod(klass, targetMethod);
+}
+extern "C" JNIEXPORT
+jdouble JNICALL Java_art_Test989_returnDoubleNative(JNIEnv* env, jclass klass) {
+  jmethodID targetMethod = env->GetStaticMethodID(klass, "doGetDouble", "()D");
+  return env->CallStaticDoubleMethod(klass, targetMethod);
+}
+
+extern "C" JNIEXPORT jobject JNICALL Java_art_Test989_returnValueNative(JNIEnv* env, jclass klass) {
+  jmethodID targetMethod = env->GetStaticMethodID(klass, "mkTestObject", "()Ljava/lang/Object;");
+  return env->CallStaticObjectMethod(klass, targetMethod);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test989_doNothingNative(JNIEnv* env ATTRIBUTE_UNUSED,
+                                                                   jclass klass ATTRIBUTE_UNUSED) {
+  return;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test989_throwANative(JNIEnv* env,
+                                                                jclass klass) {
+  jmethodID targetMethod = env->GetStaticMethodID(klass, "doThrowA", "()V");
+  env->CallStaticVoidMethod(klass, targetMethod);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test989_acceptValueNative(JNIEnv* env,
+                                                                     jclass klass,
+                                                                     jobject arg) {
+  jmethodID targetMethod = env->GetStaticMethodID(klass, "printObject", "(Ljava/lang/Object;)V");
+  env->CallStaticVoidMethod(klass, targetMethod, arg);
+}
+
+}  // namespace Test989StackTraceThrow
+}  // namespace art
+
diff --git a/test/1936-thread-end-events/run b/test/1936-thread-end-events/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1936-thread-end-events/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1936-thread-end-events/src/Main.java b/test/1936-thread-end-events/src/Main.java
new file mode 100644
index 0000000..da66fc3
--- /dev/null
+++ b/test/1936-thread-end-events/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    art.Test1936.run();
+  }
+}
diff --git a/test/1936-thread-end-events/src/art/Test1936.java b/test/1936-thread-end-events/src/art/Test1936.java
new file mode 100644
index 0000000..868deca
--- /dev/null
+++ b/test/1936-thread-end-events/src/art/Test1936.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+package art;
+
+public class Test1936 {
+  public static void foo() {}
+
+  public static void NotifyThreadEnd(Thread me) {
+    // Don't actually do anything.
+    foo();
+  }
+
+  public static void NotifyMethodEntry(Object o) {
+    System.out.println("Entered " + o.toString());
+    Thread me = Thread.currentThread();
+    System.out.println(String.format(
+        "Thread: %s\n" +
+        "  | alive: %b\n" +
+        "  | interrupted: %b\n" +
+        "  | daemon: %b\n" +
+        "  | group: %s\n",
+        me.getName(), me.isAlive(), me.isInterrupted(), me.isDaemon(), me.getThreadGroup()));
+  }
+
+  public static native void waitForever();
+  private static void setupTracing(Thread target) throws Exception {
+    Trace.disableTracing(target);
+    Trace.enableTracing2(
+        Test1936.class,
+        Test1936.class.getDeclaredMethod("NotifyMethodEntry", Object.class),
+        /*exit*/null,
+        /*field_access*/null,
+        /*field_modify*/null,
+        /*single_step*/null,
+        /*thread_start*/null,
+        Test1936.class.getDeclaredMethod("NotifyThreadEnd", Thread.class),
+        target);
+  }
+
+
+  public static void run() throws Exception {
+    Thread t = new Thread(() -> {
+      try {
+        setupTracing(Thread.currentThread());
+        foo();
+      } catch (Exception e) {
+        System.out.println("Caught exception " + e + "!");
+        e.printStackTrace();
+      }
+    }, "test-thread");
+    t.start();
+    t.join();
+  }
+}
diff --git a/test/1936-thread-end-events/src/art/Trace.java b/test/1936-thread-end-events/src/art/Trace.java
new file mode 100644
index 0000000..8999bb1
--- /dev/null
+++ b/test/1936-thread-end-events/src/art/Trace.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Trace {
+  public static native void enableTracing(Class<?> methodClass,
+                                          Method entryMethod,
+                                          Method exitMethod,
+                                          Method fieldAccess,
+                                          Method fieldModify,
+                                          Method singleStep,
+                                          Thread thr);
+  public static native void disableTracing(Thread thr);
+
+  public static void enableFieldTracing(Class<?> methodClass,
+                                        Method fieldAccess,
+                                        Method fieldModify,
+                                        Thread thr) {
+    enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+  }
+
+  public static void enableMethodTracing(Class<?> methodClass,
+                                         Method entryMethod,
+                                         Method exitMethod,
+                                         Thread thr) {
+    enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+  }
+
+  public static void enableSingleStepTracing(Class<?> methodClass,
+                                             Method singleStep,
+                                             Thread thr) {
+    enableTracing(methodClass, null, null, null, null, singleStep, thr);
+  }
+
+  public static native void watchFieldAccess(Field f);
+  public static native void watchFieldModification(Field f);
+  public static native void watchAllFieldAccesses();
+  public static native void watchAllFieldModifications();
+
+  // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+  // need to add to the bottom and not modify old ones to maintain compat.
+  public static native void enableTracing2(Class<?> methodClass,
+                                           Method entryMethod,
+                                           Method exitMethod,
+                                           Method fieldAccess,
+                                           Method fieldModify,
+                                           Method singleStep,
+                                           Method ThreadStart,
+                                           Method ThreadEnd,
+                                           Thread thr);
+}
diff --git a/test/638-checker-inline-caches/profile b/test/638-checker-inline-caches/profile
index 1ca6d7b..7756a16 100644
--- a/test/638-checker-inline-caches/profile
+++ b/test/638-checker-inline-caches/profile
@@ -1,6 +1,6 @@
-LMain;->inlineMonomorphicSubA(LSuper;)I+LSubA;
-LMain;->inlinePolymophicSubASubB(LSuper;)I+LSubA;,LSubB;
-LMain;->inlinePolymophicCrossDexSubASubC(LSuper;)I+LSubA;,LSubC;
-LMain;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;
-LMain;->inlineMissingTypes(LSuper;)I+missing_types
-LMain;->noInlineCache(LSuper;)I
+HSLMain;->inlineMonomorphicSubA(LSuper;)I+LSubA;
+HSLMain;->inlinePolymophicSubASubB(LSuper;)I+LSubA;,LSubB;
+HSLMain;->inlinePolymophicCrossDexSubASubC(LSuper;)I+LSubA;,LSubC;
+HSLMain;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;
+HSLMain;->inlineMissingTypes(LSuper;)I+missing_types
+HSLMain;->noInlineCache(LSuper;)I
diff --git a/test/643-checker-bogus-ic/profile b/test/643-checker-bogus-ic/profile
index cbf7796..540a935 100644
--- a/test/643-checker-bogus-ic/profile
+++ b/test/643-checker-bogus-ic/profile
@@ -1,2 +1,2 @@
-LMain;->inlineMonomorphic(LMain;)I+LUnrelated;
-LMain;->inlinePolymorphic(LMain;)I+LUnrelated;,LMain;
+SHLMain;->inlineMonomorphic(LMain;)I+LUnrelated;
+SHLMain;->inlinePolymorphic(LMain;)I+LUnrelated;,LMain;
diff --git a/test/648-inline-caches-unresolved/profile b/test/648-inline-caches-unresolved/profile
index 92c0a41..06bc8ad 100644
--- a/test/648-inline-caches-unresolved/profile
+++ b/test/648-inline-caches-unresolved/profile
@@ -1 +1 @@
-LMain;->inlineMonomorphicUnresolvedSuper(Ljava/lang/Object;)Ljava/lang/String;+LSubclass;
+SHLMain;->inlineMonomorphicUnresolvedSuper(Ljava/lang/Object;)Ljava/lang/String;+LSubclass;
diff --git a/test/661-oat-writer-layout/expected.txt b/test/661-oat-writer-layout/expected.txt
index db28e4f..b7ad70a 100644
--- a/test/661-oat-writer-layout/expected.txt
+++ b/test/661-oat-writer-layout/expected.txt
@@ -8,15 +8,6 @@
 C::m_a$$$
 C::m_b$$$
 C::m_c$$$
-A::m_a$Hot$$
-A::m_b$Hot$$
-A::m_c$Hot$$
-B::m_a$Hot$$
-B::m_b$Hot$$
-B::m_c$Hot$$
-C::m_a$Hot$$
-C::m_b$Hot$$
-C::m_c$Hot$$
 A::m_a$$Startup$
 A::m_b$$Startup$
 A::m_c$$Startup$
diff --git a/test/661-oat-writer-layout/profile b/test/661-oat-writer-layout/profile
index 5406484..cf307c2 100644
--- a/test/661-oat-writer-layout/profile
+++ b/test/661-oat-writer-layout/profile
@@ -1,60 +1,51 @@
-HLA;->m_a$Hot$$()V
 SLA;->m_a$$Startup$()V
 HSLA;->m_a$Hot$Startup$()V
 PLA;->m_a$$$Poststartup()V
 HPLA;->m_a$Hot$$Poststartup()V
 SPLA;->m_a$$Startup$Poststartup()V
 HSPLA;->m_a$Hot$Startup$Poststartup()V
-HLA;->m_b$Hot$$()V
 SLA;->m_b$$Startup$()V
 HSLA;->m_b$Hot$Startup$()V
 PLA;->m_b$$$Poststartup()V
 HPLA;->m_b$Hot$$Poststartup()V
 SPLA;->m_b$$Startup$Poststartup()V
 HSPLA;->m_b$Hot$Startup$Poststartup()V
-HLA;->m_c$Hot$$()V
 SLA;->m_c$$Startup$()V
 HSLA;->m_c$Hot$Startup$()V
 PLA;->m_c$$$Poststartup()V
 HPLA;->m_c$Hot$$Poststartup()V
 SPLA;->m_c$$Startup$Poststartup()V
 HSPLA;->m_c$Hot$Startup$Poststartup()V
-HLB;->m_a$Hot$$()V
 SLB;->m_a$$Startup$()V
 HSLB;->m_a$Hot$Startup$()V
 PLB;->m_a$$$Poststartup()V
 HPLB;->m_a$Hot$$Poststartup()V
 SPLB;->m_a$$Startup$Poststartup()V
 HSPLB;->m_a$Hot$Startup$Poststartup()V
-HLB;->m_b$Hot$$()V
 SLB;->m_b$$Startup$()V
 HSLB;->m_b$Hot$Startup$()V
 PLB;->m_b$$$Poststartup()V
 HPLB;->m_b$Hot$$Poststartup()V
 SPLB;->m_b$$Startup$Poststartup()V
 HSPLB;->m_b$Hot$Startup$Poststartup()V
-HLB;->m_c$Hot$$()V
 SLB;->m_c$$Startup$()V
 HSLB;->m_c$Hot$Startup$()V
 PLB;->m_c$$$Poststartup()V
 HPLB;->m_c$Hot$$Poststartup()V
 SPLB;->m_c$$Startup$Poststartup()V
 HSPLB;->m_c$Hot$Startup$Poststartup()V
-HLC;->m_a$Hot$$()V
 SLC;->m_a$$Startup$()V
 HSLC;->m_a$Hot$Startup$()V
 PLC;->m_a$$$Poststartup()V
 HPLC;->m_a$Hot$$Poststartup()V
 SPLC;->m_a$$Startup$Poststartup()V
 HSPLC;->m_a$Hot$Startup$Poststartup()V
-HLC;->m_b$Hot$$()V
 SLC;->m_b$$Startup$()V
 HSLC;->m_b$Hot$Startup$()V
 PLC;->m_b$$$Poststartup()V
 HPLC;->m_b$Hot$$Poststartup()V
 SPLC;->m_b$$Startup$Poststartup()V
 HSPLC;->m_b$Hot$Startup$Poststartup()V
-HLC;->m_c$Hot$$()V
 SLC;->m_c$$Startup$()V
 HSLC;->m_c$Hot$Startup$()V
 PLC;->m_c$$$Poststartup()V
diff --git a/test/661-oat-writer-layout/src/Test.java b/test/661-oat-writer-layout/src/Test.java
index db67b48..f862e37 100644
--- a/test/661-oat-writer-layout/src/Test.java
+++ b/test/661-oat-writer-layout/src/Test.java
@@ -13,86 +13,78 @@
 // limitations under the License.
 
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 
 public class Test {
   // Returns list of all methods in Generated.java
   // This is to avoid having to introspect classes with extra code
   // (for example, we ignore <init> methods).
   public static Method[] getTestMethods() throws NoSuchMethodException, SecurityException {
-    Method[] all_methods = new Method[72];
-    all_methods[0] = A.class.getDeclaredMethod("m_a$$$");
-    all_methods[1] = A.class.getDeclaredMethod("m_a$Hot$$");
-    all_methods[2] = A.class.getDeclaredMethod("m_a$$Startup$");
-    all_methods[3] = A.class.getDeclaredMethod("m_a$Hot$Startup$");
-    all_methods[4] = A.class.getDeclaredMethod("m_a$$$Poststartup");
-    all_methods[5] = A.class.getDeclaredMethod("m_a$Hot$$Poststartup");
-    all_methods[6] = A.class.getDeclaredMethod("m_a$$Startup$Poststartup");
-    all_methods[7] = A.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup");
-    all_methods[8] = A.class.getDeclaredMethod("m_b$$$");
-    all_methods[9] = A.class.getDeclaredMethod("m_b$Hot$$");
-    all_methods[10] = A.class.getDeclaredMethod("m_b$$Startup$");
-    all_methods[11] = A.class.getDeclaredMethod("m_b$Hot$Startup$");
-    all_methods[12] = A.class.getDeclaredMethod("m_b$$$Poststartup");
-    all_methods[13] = A.class.getDeclaredMethod("m_b$Hot$$Poststartup");
-    all_methods[14] = A.class.getDeclaredMethod("m_b$$Startup$Poststartup");
-    all_methods[15] = A.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup");
-    all_methods[16] = A.class.getDeclaredMethod("m_c$$$");
-    all_methods[17] = A.class.getDeclaredMethod("m_c$Hot$$");
-    all_methods[18] = A.class.getDeclaredMethod("m_c$$Startup$");
-    all_methods[19] = A.class.getDeclaredMethod("m_c$Hot$Startup$");
-    all_methods[20] = A.class.getDeclaredMethod("m_c$$$Poststartup");
-    all_methods[21] = A.class.getDeclaredMethod("m_c$Hot$$Poststartup");
-    all_methods[22] = A.class.getDeclaredMethod("m_c$$Startup$Poststartup");
-    all_methods[23] = A.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup");
-    all_methods[24] = B.class.getDeclaredMethod("m_a$$$");
-    all_methods[25] = B.class.getDeclaredMethod("m_a$Hot$$");
-    all_methods[26] = B.class.getDeclaredMethod("m_a$$Startup$");
-    all_methods[27] = B.class.getDeclaredMethod("m_a$Hot$Startup$");
-    all_methods[28] = B.class.getDeclaredMethod("m_a$$$Poststartup");
-    all_methods[29] = B.class.getDeclaredMethod("m_a$Hot$$Poststartup");
-    all_methods[30] = B.class.getDeclaredMethod("m_a$$Startup$Poststartup");
-    all_methods[31] = B.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup");
-    all_methods[32] = B.class.getDeclaredMethod("m_b$$$");
-    all_methods[33] = B.class.getDeclaredMethod("m_b$Hot$$");
-    all_methods[34] = B.class.getDeclaredMethod("m_b$$Startup$");
-    all_methods[35] = B.class.getDeclaredMethod("m_b$Hot$Startup$");
-    all_methods[36] = B.class.getDeclaredMethod("m_b$$$Poststartup");
-    all_methods[37] = B.class.getDeclaredMethod("m_b$Hot$$Poststartup");
-    all_methods[38] = B.class.getDeclaredMethod("m_b$$Startup$Poststartup");
-    all_methods[39] = B.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup");
-    all_methods[40] = B.class.getDeclaredMethod("m_c$$$");
-    all_methods[41] = B.class.getDeclaredMethod("m_c$Hot$$");
-    all_methods[42] = B.class.getDeclaredMethod("m_c$$Startup$");
-    all_methods[43] = B.class.getDeclaredMethod("m_c$Hot$Startup$");
-    all_methods[44] = B.class.getDeclaredMethod("m_c$$$Poststartup");
-    all_methods[45] = B.class.getDeclaredMethod("m_c$Hot$$Poststartup");
-    all_methods[46] = B.class.getDeclaredMethod("m_c$$Startup$Poststartup");
-    all_methods[47] = B.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup");
-    all_methods[48] = C.class.getDeclaredMethod("m_a$$$");
-    all_methods[49] = C.class.getDeclaredMethod("m_a$Hot$$");
-    all_methods[50] = C.class.getDeclaredMethod("m_a$$Startup$");
-    all_methods[51] = C.class.getDeclaredMethod("m_a$Hot$Startup$");
-    all_methods[52] = C.class.getDeclaredMethod("m_a$$$Poststartup");
-    all_methods[53] = C.class.getDeclaredMethod("m_a$Hot$$Poststartup");
-    all_methods[54] = C.class.getDeclaredMethod("m_a$$Startup$Poststartup");
-    all_methods[55] = C.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup");
-    all_methods[56] = C.class.getDeclaredMethod("m_b$$$");
-    all_methods[57] = C.class.getDeclaredMethod("m_b$Hot$$");
-    all_methods[58] = C.class.getDeclaredMethod("m_b$$Startup$");
-    all_methods[59] = C.class.getDeclaredMethod("m_b$Hot$Startup$");
-    all_methods[60] = C.class.getDeclaredMethod("m_b$$$Poststartup");
-    all_methods[61] = C.class.getDeclaredMethod("m_b$Hot$$Poststartup");
-    all_methods[62] = C.class.getDeclaredMethod("m_b$$Startup$Poststartup");
-    all_methods[63] = C.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup");
-    all_methods[64] = C.class.getDeclaredMethod("m_c$$$");
-    all_methods[65] = C.class.getDeclaredMethod("m_c$Hot$$");
-    all_methods[66] = C.class.getDeclaredMethod("m_c$$Startup$");
-    all_methods[67] = C.class.getDeclaredMethod("m_c$Hot$Startup$");
-    all_methods[68] = C.class.getDeclaredMethod("m_c$$$Poststartup");
-    all_methods[69] = C.class.getDeclaredMethod("m_c$Hot$$Poststartup");
-    all_methods[70] = C.class.getDeclaredMethod("m_c$$Startup$Poststartup");
-    all_methods[71] = C.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup");
-    return all_methods;
+    ArrayList<Method> all_methods = new ArrayList<Method>();
+    all_methods.add(A.class.getDeclaredMethod("m_a$$$"));
+    all_methods.add(A.class.getDeclaredMethod("m_a$$Startup$"));
+    all_methods.add(A.class.getDeclaredMethod("m_a$Hot$Startup$"));
+    all_methods.add(A.class.getDeclaredMethod("m_a$$$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_a$Hot$$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_a$$Startup$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$$$"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$$Startup$"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$Hot$Startup$"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$$$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$Hot$$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$$Startup$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$$$"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$$Startup$"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$Hot$Startup$"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$$$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$Hot$$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$$Startup$Poststartup"));
+    all_methods.add(A.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$$$"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$$Startup$"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$Hot$Startup$"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$$$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$Hot$$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$$Startup$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$$$"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$$Startup$"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$Hot$Startup$"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$$$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$Hot$$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$$Startup$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$$$"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$$Startup$"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$Hot$Startup$"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$$$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$Hot$$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$$Startup$Poststartup"));
+    all_methods.add(B.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$$$"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$$Startup$"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$Hot$Startup$"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$$$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$Hot$$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$$Startup$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$$$"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$$Startup$"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$Hot$Startup$"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$$$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$Hot$$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$$Startup$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$$$"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$$Startup$"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$Hot$Startup$"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$$$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$Hot$$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$$Startup$Poststartup"));
+    all_methods.add(C.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup"));
+    return all_methods.toArray(new Method[all_methods.size()]);
   }
 }
 
diff --git a/test/707-checker-invalid-profile/profile b/test/707-checker-invalid-profile/profile
index 5979dd2..f142c40 100644
--- a/test/707-checker-invalid-profile/profile
+++ b/test/707-checker-invalid-profile/profile
@@ -1,4 +1,4 @@
-LMain;->attemptInlineMonomorphic(LMain;)I+invalid_class
-LMain;->attemptInlinePolymorphic(LMain;)I+LMain;,invalid_class
-LMain;->invalid_method
+SHLMain;->attemptInlineMonomorphic(LMain;)I+invalid_class
+SHLMain;->attemptInlinePolymorphic(LMain;)I+LMain;,invalid_class
+SHLMain;->invalid_method
 invalid_class
\ No newline at end of file
diff --git a/test/988-method-trace/src/art/Trace.java b/test/988-method-trace/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/988-method-trace/src/art/Trace.java
+++ b/test/988-method-trace/src/art/Trace.java
@@ -53,4 +53,16 @@
   public static native void watchFieldModification(Field f);
   public static native void watchAllFieldAccesses();
   public static native void watchAllFieldModifications();
+
+  // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+  // need to add to the bottom and not modify old ones to maintain compat.
+  public static native void enableTracing2(Class<?> methodClass,
+                                           Method entryMethod,
+                                           Method exitMethod,
+                                           Method fieldAccess,
+                                           Method fieldModify,
+                                           Method singleStep,
+                                           Method ThreadStart,
+                                           Method ThreadEnd,
+                                           Thread thr);
 }
diff --git a/test/989-method-trace-throw/src/art/Trace.java b/test/989-method-trace-throw/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/989-method-trace-throw/src/art/Trace.java
+++ b/test/989-method-trace-throw/src/art/Trace.java
@@ -53,4 +53,16 @@
   public static native void watchFieldModification(Field f);
   public static native void watchAllFieldAccesses();
   public static native void watchAllFieldModifications();
+
+  // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+  // need to add to the bottom and not modify old ones to maintain compat.
+  public static native void enableTracing2(Class<?> methodClass,
+                                           Method entryMethod,
+                                           Method exitMethod,
+                                           Method fieldAccess,
+                                           Method fieldModify,
+                                           Method singleStep,
+                                           Method ThreadStart,
+                                           Method ThreadEnd,
+                                           Thread thr);
 }
diff --git a/test/990-field-trace/src/art/Trace.java b/test/990-field-trace/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/990-field-trace/src/art/Trace.java
+++ b/test/990-field-trace/src/art/Trace.java
@@ -53,4 +53,16 @@
   public static native void watchFieldModification(Field f);
   public static native void watchAllFieldAccesses();
   public static native void watchAllFieldModifications();
+
+  // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+  // need to add to the bottom and not modify old ones to maintain compat.
+  public static native void enableTracing2(Class<?> methodClass,
+                                           Method entryMethod,
+                                           Method exitMethod,
+                                           Method fieldAccess,
+                                           Method fieldModify,
+                                           Method singleStep,
+                                           Method ThreadStart,
+                                           Method ThreadEnd,
+                                           Thread thr);
 }
diff --git a/test/991-field-trace-2/src/art/Trace.java b/test/991-field-trace-2/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/991-field-trace-2/src/art/Trace.java
+++ b/test/991-field-trace-2/src/art/Trace.java
@@ -53,4 +53,16 @@
   public static native void watchFieldModification(Field f);
   public static native void watchAllFieldAccesses();
   public static native void watchAllFieldModifications();
+
+  // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+  // need to add to the bottom and not modify old ones to maintain compat.
+  public static native void enableTracing2(Class<?> methodClass,
+                                           Method entryMethod,
+                                           Method exitMethod,
+                                           Method fieldAccess,
+                                           Method fieldModify,
+                                           Method singleStep,
+                                           Method ThreadStart,
+                                           Method ThreadEnd,
+                                           Thread thr);
 }
diff --git a/test/997-single-step/src/art/Trace.java b/test/997-single-step/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/997-single-step/src/art/Trace.java
+++ b/test/997-single-step/src/art/Trace.java
@@ -53,4 +53,16 @@
   public static native void watchFieldModification(Field f);
   public static native void watchAllFieldAccesses();
   public static native void watchAllFieldModifications();
+
+  // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+  // need to add to the bottom and not modify old ones to maintain compat.
+  public static native void enableTracing2(Class<?> methodClass,
+                                           Method entryMethod,
+                                           Method exitMethod,
+                                           Method fieldAccess,
+                                           Method fieldModify,
+                                           Method singleStep,
+                                           Method ThreadStart,
+                                           Method ThreadEnd,
+                                           Thread thr);
 }
diff --git a/test/Android.bp b/test/Android.bp
index d2eccb0..2af03e3 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -67,6 +67,7 @@
         "libicuuc",
         "libicui18n",
         "libnativehelper",
+        "libz",
     ],
     whole_static_libs: [
         "libsigchain",
@@ -84,7 +85,6 @@
             ],
             shared_libs: [
                 "libziparchive",
-                "libz-host",
             ],
             cflags: [
                 // gtest issue
@@ -103,7 +103,6 @@
             ],
             shared_libs: [
                 "liblog",
-                "libz",
             ],
             cflags: [
                 // gtest issue
diff --git a/test/ti-agent/trace_helper.cc b/test/ti-agent/trace_helper.cc
index 1f8ceff..8b74c7c 100644
--- a/test/ti-agent/trace_helper.cc
+++ b/test/ti-agent/trace_helper.cc
@@ -34,11 +34,36 @@
   jmethodID field_access;
   jmethodID field_modify;
   jmethodID single_step;
+  jmethodID thread_start;
+  jmethodID thread_end;
   bool in_callback;
   bool access_watch_on_load;
   bool modify_watch_on_load;
 };
 
+static void threadStartCB(jvmtiEnv* jvmti,
+                          JNIEnv* jnienv,
+                          jthread thread) {
+  TraceData* data = nullptr;
+  if (JvmtiErrorToException(jnienv, jvmti,
+                            jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+    return;
+  }
+  CHECK(data->thread_start != nullptr);
+  jnienv->CallStaticVoidMethod(data->test_klass, data->thread_start, thread);
+}
+static void threadEndCB(jvmtiEnv* jvmti,
+                          JNIEnv* jnienv,
+                          jthread thread) {
+  TraceData* data = nullptr;
+  if (JvmtiErrorToException(jnienv, jvmti,
+                            jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+    return;
+  }
+  CHECK(data->thread_end != nullptr);
+  jnienv->CallStaticVoidMethod(data->test_klass, data->thread_end, thread);
+}
+
 static void singleStepCB(jvmtiEnv* jvmti,
                          JNIEnv* jnienv,
                          jthread thread,
@@ -362,7 +387,7 @@
   env->DeleteLocalRef(klass);
 }
 
-extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing2(
     JNIEnv* env,
     jclass trace ATTRIBUTE_UNUSED,
     jclass klass,
@@ -371,6 +396,8 @@
     jobject field_access,
     jobject field_modify,
     jobject single_step,
+    jobject thread_start,
+    jobject thread_end,
     jthread thr) {
   TraceData* data = nullptr;
   if (JvmtiErrorToException(env,
@@ -386,6 +413,8 @@
   data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr;
   data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr;
   data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr;
+  data->thread_start = thread_start != nullptr ? env->FromReflectedMethod(thread_start) : nullptr;
+  data->thread_end = thread_end != nullptr ? env->FromReflectedMethod(thread_end) : nullptr;
   data->in_callback = false;
 
   TraceData* old_data = nullptr;
@@ -410,6 +439,8 @@
   cb.FieldModification = fieldModificationCB;
   cb.ClassPrepare = classPrepareCB;
   cb.SingleStep = singleStepCB;
+  cb.ThreadStart = threadStartCB;
+  cb.ThreadEnd = threadEndCB;
   if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
     return;
   }
@@ -453,6 +484,46 @@
                                                                 thr))) {
     return;
   }
+  if (thread_start != nullptr &&
+      JvmtiErrorToException(env,
+                            jvmti_env,
+                            jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+                                                                JVMTI_EVENT_THREAD_START,
+                                                                thr))) {
+    return;
+  }
+  if (thread_end != nullptr &&
+      JvmtiErrorToException(env,
+                            jvmti_env,
+                            jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+                                                                JVMTI_EVENT_THREAD_END,
+                                                                thr))) {
+    return;
+  }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
+    JNIEnv* env,
+    jclass trace,
+    jclass klass,
+    jobject enter,
+    jobject exit,
+    jobject field_access,
+    jobject field_modify,
+    jobject single_step,
+    jthread thr) {
+  Java_art_Trace_enableTracing2(env,
+                                trace,
+                                klass,
+                                enter,
+                                exit,
+                                field_access,
+                                field_modify,
+                                single_step,
+                                /* thread_start */ nullptr,
+                                /* thread_end */ nullptr,
+                                thr);
+  return;
 }
 
 extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
diff --git a/tools/ahat/src/Main.java b/tools/ahat/src/Main.java
index 623a865..31c485d 100644
--- a/tools/ahat/src/Main.java
+++ b/tools/ahat/src/Main.java
@@ -18,6 +18,7 @@
 
 import com.android.ahat.heapdump.AhatSnapshot;
 import com.android.ahat.heapdump.Diff;
+import com.android.ahat.heapdump.HprofFormatException;
 import com.android.ahat.heapdump.Parser;
 import com.android.ahat.proguard.ProguardMap;
 import com.sun.net.httpserver.HttpServer;
@@ -47,7 +48,7 @@
     out.println("");
   }
 
-  public static void main(String[] args) throws Exception {
+  public static void main(String[] args) {
     int port = 7100;
     for (String arg : args) {
       if (arg.equals("--help")) {
@@ -104,34 +105,40 @@
       return;
     }
 
-    // Launch the server before parsing the hprof file so we get
-    // BindExceptions quickly.
-    InetAddress loopback = InetAddress.getLoopbackAddress();
-    InetSocketAddress addr = new InetSocketAddress(loopback, port);
-    HttpServer server = HttpServer.create(addr, 0);
+    try {
+      // Launch the server before parsing the hprof file so we get
+      // BindExceptions quickly.
+      InetAddress loopback = InetAddress.getLoopbackAddress();
+      InetSocketAddress addr = new InetSocketAddress(loopback, port);
+      System.out.println("Preparing " + addr + " ...");
+      HttpServer server = HttpServer.create(addr, 0);
 
-    System.out.println("Processing hprof file...");
-    AhatSnapshot ahat = Parser.parseHeapDump(hprof, map);
+      System.out.println("Processing '" + hprof + "' ...");
+      AhatSnapshot ahat = Parser.parseHeapDump(hprof, map);
 
-    if (hprofbase != null) {
-      System.out.println("Processing baseline hprof file...");
-      AhatSnapshot base = Parser.parseHeapDump(hprofbase, mapbase);
+      if (hprofbase != null) {
+        System.out.println("Processing '" + hprofbase + "' ...");
+        AhatSnapshot base = Parser.parseHeapDump(hprofbase, mapbase);
 
-      System.out.println("Diffing hprof files...");
-      Diff.snapshots(ahat, base);
+        System.out.println("Diffing heap dumps ...");
+        Diff.snapshots(ahat, base);
+      }
+
+      server.createContext("/", new AhatHttpHandler(new OverviewHandler(ahat, hprof, hprofbase)));
+      server.createContext("/rooted", new AhatHttpHandler(new RootedHandler(ahat)));
+      server.createContext("/object", new AhatHttpHandler(new ObjectHandler(ahat)));
+      server.createContext("/objects", new AhatHttpHandler(new ObjectsHandler(ahat)));
+      server.createContext("/site", new AhatHttpHandler(new SiteHandler(ahat)));
+      server.createContext("/bitmap", new BitmapHandler(ahat));
+      server.createContext("/style.css", new StaticHandler("style.css", "text/css"));
+      server.setExecutor(Executors.newFixedThreadPool(1));
+      System.out.println("Server started on localhost:" + port);
+
+      server.start();
+    } catch (HprofFormatException|IOException e) {
+      System.err.println("Unable to launch ahat:");
+      e.printStackTrace();
     }
-
-    server.createContext("/", new AhatHttpHandler(new OverviewHandler(ahat, hprof, hprofbase)));
-    server.createContext("/rooted", new AhatHttpHandler(new RootedHandler(ahat)));
-    server.createContext("/object", new AhatHttpHandler(new ObjectHandler(ahat)));
-    server.createContext("/objects", new AhatHttpHandler(new ObjectsHandler(ahat)));
-    server.createContext("/site", new AhatHttpHandler(new SiteHandler(ahat)));
-    server.createContext("/bitmap", new BitmapHandler(ahat));
-    server.createContext("/style.css", new StaticHandler("style.css", "text/css"));
-    server.setExecutor(Executors.newFixedThreadPool(1));
-    System.out.println("Server started on localhost:" + port);
-
-    server.start();
   }
 }
 
diff --git a/tools/ahat/src/heapdump/HprofFormatException.java b/tools/ahat/src/heapdump/HprofFormatException.java
index 55e8958..0e128cd 100644
--- a/tools/ahat/src/heapdump/HprofFormatException.java
+++ b/tools/ahat/src/heapdump/HprofFormatException.java
@@ -20,4 +20,8 @@
   public HprofFormatException(String msg) {
     super(msg);
   }
+
+  public HprofFormatException(String msg, Exception cause) {
+    super(msg, cause);
+  }
 }
diff --git a/tools/ahat/src/heapdump/Parser.java b/tools/ahat/src/heapdump/Parser.java
index 3d5f95f..756b7d2 100644
--- a/tools/ahat/src/heapdump/Parser.java
+++ b/tools/ahat/src/heapdump/Parser.java
@@ -19,6 +19,7 @@
 import com.android.ahat.proguard.ProguardMap;
 import java.io.File;
 import java.io.IOException;
+import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.charset.StandardCharsets;
@@ -48,7 +49,11 @@
    */
   public static AhatSnapshot parseHeapDump(File hprof, ProguardMap map)
     throws IOException, HprofFormatException {
-    return parseHeapDump(new HprofBuffer(hprof), map);
+    try {
+      return parseHeapDump(new HprofBuffer(hprof), map);
+    } catch (BufferUnderflowException e) {
+      throw new HprofFormatException("Unexpected end of file", e);
+    }
   }
 
   /**
@@ -56,11 +61,15 @@
    */
   public static AhatSnapshot parseHeapDump(ByteBuffer hprof, ProguardMap map)
     throws IOException, HprofFormatException {
-    return parseHeapDump(new HprofBuffer(hprof), map);
+    try {
+      return parseHeapDump(new HprofBuffer(hprof), map);
+    } catch (BufferUnderflowException e) {
+      throw new HprofFormatException("Unexpected end of file", e);
+    }
   }
 
   private static AhatSnapshot parseHeapDump(HprofBuffer hprof, ProguardMap map)
-    throws IOException, HprofFormatException {
+    throws IOException, HprofFormatException, BufferUnderflowException {
     // Read, and mostly ignore, the hprof header info.
     {
       StringBuilder format = new StringBuilder();
@@ -409,6 +418,12 @@
                   break;
                 }
 
+                case 0x8a: { // ROOT FINALIZING (ANDROID)
+                  long objectId = hprof.getId();
+                  roots.add(new RootData(objectId, RootType.FINALIZING));
+                  break;
+                }
+
                 case 0x8b: { // ROOT DEBUGGER (ANDROID)
                   long objectId = hprof.getId();
                   roots.add(new RootData(objectId, RootType.DEBUGGER));
diff --git a/tools/ahat/src/heapdump/RootType.java b/tools/ahat/src/heapdump/RootType.java
index 7165b83..af552ea 100644
--- a/tools/ahat/src/heapdump/RootType.java
+++ b/tools/ahat/src/heapdump/RootType.java
@@ -29,7 +29,8 @@
   DEBUGGER        (1 <<  9),
   VM_INTERNAL     (1 << 10),
   UNKNOWN         (1 << 11),
-  JNI_MONITOR     (1 << 12);
+  JNI_MONITOR     (1 << 12),
+  FINALIZING      (1 << 13);
 
   public final int mask;
 
diff --git a/tools/ahat/test-dump/L.hprof b/tools/ahat/test-dump/L.hprof
index cf82557..1acdf79 100644
--- a/tools/ahat/test-dump/L.hprof
+++ b/tools/ahat/test-dump/L.hprof
Binary files differ
diff --git a/tools/ahat/test-dump/README.txt b/tools/ahat/test-dump/README.txt
index 344271c..e7ea584 100644
--- a/tools/ahat/test-dump/README.txt
+++ b/tools/ahat/test-dump/README.txt
@@ -1,5 +1,7 @@
 
 Main.java - A program used to generate a heap dump used for tests.
-L.hprof - A version of the test dump generated on Android L.
+L.hprof - A version of the test dump generated on Android L,
+          with one of the ROOT_DEBUGGER records manually changed to a
+          ROOT_FINALIZING record.
 O.hprof - A version of the test dump generated on Android O.
 RI.hprof - A version of the test dump generated on the reference implementation.