Merge "ART: Fix loading PIC oat files without image for oatdump."
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/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/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/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/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..2412931 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3080,6 +3080,10 @@
     UNREACHABLE();
   }
 
+  if (kUseReadBarrier) {
+    ReadBarrier::AssertToSpaceInvariant(exception.Ptr());
+  }
+
   // This is a real exception: let the instrumentation know about it.
   instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
   if (instrumentation->HasExceptionThrownListeners() &&
@@ -3121,6 +3125,18 @@
   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());
+    if (kUseReadBarrier) {
+      // Check the to-space invariant on the re-installed exception.
+      ObjPtr<mirror::Throwable> reinstalled_exception = GetException();
+      ReadBarrier::AssertToSpaceInvariant(reinstalled_exception.Ptr());
+    }
+  }
   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/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/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/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.