Don't report down-calls as unhandled exceptions.

Bug: 15310540
Also, narrow scope of catch/deoptimize stack visitors that are specific to
quick exception delivery.

Change-Id: Ib13a006ce1347acb93a36b0186550d4c3ec2034b
diff --git a/runtime/Android.mk b/runtime/Android.mk
index a0648b0..17f0493 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -35,11 +35,9 @@
 	base/unix_file/random_access_file_utils.cc \
 	base/unix_file/string_file.cc \
 	check_jni.cc \
-	catch_block_stack_visitor.cc \
 	class_linker.cc \
 	common_throws.cc \
 	debugger.cc \
-	deoptimize_stack_visitor.cc \
 	dex_file.cc \
 	dex_file_verifier.cc \
 	dex_instruction.cc \
diff --git a/runtime/catch_block_stack_visitor.cc b/runtime/catch_block_stack_visitor.cc
deleted file mode 100644
index b820276..0000000
--- a/runtime/catch_block_stack_visitor.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "catch_block_stack_visitor.h"
-
-#include "dex_instruction.h"
-#include "mirror/art_method-inl.h"
-#include "quick_exception_handler.h"
-#include "handle_scope-inl.h"
-#include "verifier/method_verifier.h"
-
-namespace art {
-
-bool CatchBlockStackVisitor::VisitFrame() {
-  exception_handler_->SetHandlerFrameId(GetFrameId());
-  mirror::ArtMethod* method = GetMethod();
-  if (method == nullptr) {
-    // This is the upcall, we remember the frame and last pc so that we may long jump to them.
-    exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc());
-    exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
-    return false;  // End stack walk.
-  } else {
-    if (method->IsRuntimeMethod()) {
-      // Ignore callee save method.
-      DCHECK(method->IsCalleeSaveMethod());
-      return true;
-    } else {
-      return HandleTryItems(method);
-    }
-  }
-}
-
-bool CatchBlockStackVisitor::HandleTryItems(mirror::ArtMethod* method) {
-  uint32_t dex_pc = DexFile::kDexNoIndex;
-  if (!method->IsNative()) {
-    dex_pc = GetDexPc();
-  }
-  if (dex_pc != DexFile::kDexNoIndex) {
-    bool clear_exception = false;
-    StackHandleScope<1> hs(Thread::Current());
-    Handle<mirror::Class> to_find(hs.NewHandle((*exception_)->GetClass()));
-    uint32_t found_dex_pc = method->FindCatchBlock(to_find, dex_pc, &clear_exception);
-    exception_handler_->SetClearException(clear_exception);
-    if (found_dex_pc != DexFile::kDexNoIndex) {
-      exception_handler_->SetHandlerDexPc(found_dex_pc);
-      exception_handler_->SetHandlerQuickFramePc(method->ToNativePc(found_dex_pc));
-      exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
-      return false;  // End stack walk.
-    }
-  }
-  return true;  // Continue stack walk.
-}
-
-}  // namespace art
diff --git a/runtime/catch_block_stack_visitor.h b/runtime/catch_block_stack_visitor.h
deleted file mode 100644
index f45cf03..0000000
--- a/runtime/catch_block_stack_visitor.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_CATCH_BLOCK_STACK_VISITOR_H_
-#define ART_RUNTIME_CATCH_BLOCK_STACK_VISITOR_H_
-
-#include "mirror/object-inl.h"
-#include "stack.h"
-#include "handle_scope-inl.h"
-
-namespace art {
-
-namespace mirror {
-class Throwable;
-}  // namespace mirror
-class Context;
-class QuickExceptionHandler;
-class Thread;
-class ThrowLocation;
-
-// Finds catch handler or prepares deoptimization.
-class CatchBlockStackVisitor FINAL : public StackVisitor {
- public:
-  CatchBlockStackVisitor(Thread* self, Context* context, Handle<mirror::Throwable>* exception,
-                         QuickExceptionHandler* exception_handler)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : StackVisitor(self, context), self_(self), exception_(exception),
-        exception_handler_(exception_handler) {
-  }
-
-  bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- private:
-  bool HandleTryItems(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  Thread* const self_;
-  // The type of the exception catch block to find.
-  Handle<mirror::Throwable>* exception_;
-  QuickExceptionHandler* const exception_handler_;
-
-  DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor);
-};
-
-}  // namespace art
-#endif  // ART_RUNTIME_CATCH_BLOCK_STACK_VISITOR_H_
diff --git a/runtime/deoptimize_stack_visitor.cc b/runtime/deoptimize_stack_visitor.cc
deleted file mode 100644
index 449ccce..0000000
--- a/runtime/deoptimize_stack_visitor.cc
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "deoptimize_stack_visitor.h"
-
-#include "mirror/art_method-inl.h"
-#include "object_utils.h"
-#include "quick_exception_handler.h"
-#include "handle_scope-inl.h"
-#include "verifier/method_verifier.h"
-
-namespace art {
-
-bool DeoptimizeStackVisitor::VisitFrame() {
-  exception_handler_->SetHandlerFrameId(GetFrameId());
-  mirror::ArtMethod* method = GetMethod();
-  if (method == nullptr) {
-    // This is the upcall, we remember the frame and last pc so that we may long jump to them.
-    exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc());
-    exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
-    return false;  // End stack walk.
-  } else if (method->IsRuntimeMethod()) {
-    // Ignore callee save method.
-    DCHECK(method->IsCalleeSaveMethod());
-    return true;
-  } else {
-    return HandleDeoptimization(method);
-  }
-}
-
-bool DeoptimizeStackVisitor::HandleDeoptimization(mirror::ArtMethod* m) {
-  MethodHelper mh(m);
-  const DexFile::CodeItem* code_item = mh.GetCodeItem();
-  CHECK(code_item != nullptr);
-  uint16_t num_regs = code_item->registers_size_;
-  uint32_t dex_pc = GetDexPc();
-  const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
-  uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits();
-  ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, new_dex_pc);
-  StackHandleScope<2> hs(self_);
-  Handle<mirror::DexCache> dex_cache(hs.NewHandle(mh.GetDexCache()));
-  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(mh.GetClassLoader()));
-  verifier::MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader,
-                                    &mh.GetClassDef(), code_item, m->GetDexMethodIndex(), m,
-                                    m->GetAccessFlags(), false, true, true);
-  verifier.Verify();
-  std::vector<int32_t> kinds = verifier.DescribeVRegs(dex_pc);
-  for (uint16_t reg = 0; reg < num_regs; ++reg) {
-    VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2));
-    switch (kind) {
-      case kUndefined:
-        new_frame->SetVReg(reg, 0xEBADDE09);
-        break;
-      case kConstant:
-        new_frame->SetVReg(reg, kinds.at((reg * 2) + 1));
-        break;
-      case kReferenceVReg:
-        new_frame->SetVRegReference(reg,
-                                    reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kind)));
-        break;
-      default:
-        new_frame->SetVReg(reg, GetVReg(m, reg, kind));
-        break;
-    }
-  }
-  if (prev_shadow_frame_ != nullptr) {
-    prev_shadow_frame_->SetLink(new_frame);
-  } else {
-    self_->SetDeoptimizationShadowFrame(new_frame);
-  }
-  prev_shadow_frame_ = new_frame;
-  return true;
-}
-
-}  // namespace art
diff --git a/runtime/deoptimize_stack_visitor.h b/runtime/deoptimize_stack_visitor.h
deleted file mode 100644
index c41b803..0000000
--- a/runtime/deoptimize_stack_visitor.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_DEOPTIMIZE_STACK_VISITOR_H_
-#define ART_RUNTIME_DEOPTIMIZE_STACK_VISITOR_H_
-
-#include "base/mutex.h"
-#include "stack.h"
-#include "thread.h"
-
-namespace art {
-
-namespace mirror {
-class ArtMethod;
-}  // namespace mirror
-class QuickExceptionHandler;
-class Thread;
-
-// Prepares deoptimization.
-class DeoptimizeStackVisitor FINAL : public StackVisitor {
- public:
-  DeoptimizeStackVisitor(Thread* self, Context* context, QuickExceptionHandler* exception_handler)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
-      : StackVisitor(self, context), self_(self), exception_handler_(exception_handler),
-        prev_shadow_frame_(nullptr) {
-    CHECK(!self_->HasDeoptimizationShadowFrame());
-  }
-
-  bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- private:
-  bool HandleDeoptimization(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  Thread* const self_;
-  QuickExceptionHandler* const exception_handler_;
-  ShadowFrame* prev_shadow_frame_;
-
-  DISALLOW_COPY_AND_ASSIGN(DeoptimizeStackVisitor);
-};
-
-}  // namespace art
-#endif  // ART_RUNTIME_DEOPTIMIZE_STACK_VISITOR_H_
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 243818c..b9cec40 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -16,25 +16,105 @@
 
 #include "quick_exception_handler.h"
 
-#include "catch_block_stack_visitor.h"
-#include "deoptimize_stack_visitor.h"
+#include "dex_instruction.h"
 #include "entrypoints/entrypoint_utils.h"
-#include "mirror/art_method-inl.h"
 #include "handle_scope-inl.h"
+#include "mirror/art_method-inl.h"
+#include "verifier/method_verifier.h"
 
 namespace art {
 
+static constexpr bool kDebugExceptionDelivery = false;
+static constexpr size_t kInvalidFrameId = 0xffffffff;
+
 QuickExceptionHandler::QuickExceptionHandler(Thread* self, bool is_deoptimization)
   : self_(self), context_(self->GetLongJumpContext()), is_deoptimization_(is_deoptimization),
     method_tracing_active_(is_deoptimization ||
                            Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()),
-    handler_quick_frame_(nullptr), handler_quick_frame_pc_(0), handler_dex_pc_(0),
-    clear_exception_(false), handler_frame_id_(kInvalidFrameId) {
+    handler_quick_frame_(nullptr), handler_quick_frame_pc_(0), handler_method_(nullptr),
+    handler_dex_pc_(0), clear_exception_(false), handler_frame_id_(kInvalidFrameId) {
 }
 
+// Finds catch handler or prepares for deoptimization.
+class CatchBlockStackVisitor FINAL : public StackVisitor {
+ public:
+  CatchBlockStackVisitor(Thread* self, Context* context, Handle<mirror::Throwable>* exception,
+                         QuickExceptionHandler* exception_handler)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : StackVisitor(self, context), self_(self), exception_(exception),
+        exception_handler_(exception_handler) {
+  }
+
+  bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::ArtMethod* method = GetMethod();
+    exception_handler_->SetHandlerFrameId(GetFrameId());
+    if (method == nullptr) {
+      // This is the upcall, we remember the frame and last pc so that we may long jump to them.
+      exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc());
+      exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
+      uint32_t next_dex_pc;
+      mirror::ArtMethod* next_art_method;
+      bool has_next = GetNextMethodAndDexPc(&next_art_method, &next_dex_pc);
+      // Report the method that did the down call as the handler.
+      exception_handler_->SetHandlerDexPc(next_dex_pc);
+      exception_handler_->SetHandlerMethod(next_art_method);
+      if (!has_next) {
+        // No next method? Check exception handler is set up for the unhandled exception handler
+        // case.
+        DCHECK_EQ(0U, exception_handler_->GetHandlerDexPc());
+        DCHECK(nullptr == exception_handler_->GetHandlerMethod());
+      }
+      return false;  // End stack walk.
+    }
+    if (method->IsRuntimeMethod()) {
+      // Ignore callee save method.
+      DCHECK(method->IsCalleeSaveMethod());
+      return true;
+    }
+    return HandleTryItems(method);
+  }
+
+ private:
+  bool HandleTryItems(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    uint32_t dex_pc = DexFile::kDexNoIndex;
+    if (!method->IsNative()) {
+      dex_pc = GetDexPc();
+    }
+    if (dex_pc != DexFile::kDexNoIndex) {
+      bool clear_exception = false;
+      StackHandleScope<1> hs(Thread::Current());
+      Handle<mirror::Class> to_find(hs.NewHandle((*exception_)->GetClass()));
+      uint32_t found_dex_pc = method->FindCatchBlock(to_find, dex_pc, &clear_exception);
+      exception_handler_->SetClearException(clear_exception);
+      if (found_dex_pc != DexFile::kDexNoIndex) {
+        exception_handler_->SetHandlerMethod(method);
+        exception_handler_->SetHandlerDexPc(found_dex_pc);
+        exception_handler_->SetHandlerQuickFramePc(method->ToNativePc(found_dex_pc));
+        exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
+        return false;  // End stack walk.
+      }
+    }
+    return true;  // Continue stack walk.
+  }
+
+  Thread* const self_;
+  // The exception we're looking for the catch block of.
+  Handle<mirror::Throwable>* exception_;
+  // The quick exception handler we're visiting for.
+  QuickExceptionHandler* const exception_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor);
+};
+
 void QuickExceptionHandler::FindCatch(const ThrowLocation& throw_location,
                                       mirror::Throwable* exception) {
   DCHECK(!is_deoptimization_);
+  if (kDebugExceptionDelivery) {
+    mirror::String* msg = exception->GetDetailMessage();
+    std::string str_msg(msg != nullptr ? msg->ToModifiedUtf8() : "");
+    self_->DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception)
+                     << ": " << str_msg << "\n");
+  }
   StackHandleScope<1> hs(self_);
   Handle<mirror::Throwable> exception_ref(hs.NewHandle(exception));
 
@@ -42,14 +122,14 @@
   CatchBlockStackVisitor visitor(self_, context_, &exception_ref, this);
   visitor.WalkStack(true);
 
-  mirror::ArtMethod* catch_method = handler_quick_frame_->AsMirrorPtr();
   if (kDebugExceptionDelivery) {
-    if (catch_method == nullptr) {
+    if (handler_quick_frame_->AsMirrorPtr() == nullptr) {
       LOG(INFO) << "Handler is upcall";
-    } else {
-      const DexFile& dex_file = *catch_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
-      int line_number = dex_file.GetLineNumFromPC(catch_method, handler_dex_pc_);
-      LOG(INFO) << "Handler: " << PrettyMethod(catch_method) << " (line: " << line_number << ")";
+    }
+    if (handler_method_ != nullptr) {
+      const DexFile& dex_file = *handler_method_->GetDeclaringClass()->GetDexCache()->GetDexFile();
+      int line_number = dex_file.GetLineNumFromPC(handler_method_, handler_dex_pc_);
+      LOG(INFO) << "Handler: " << PrettyMethod(handler_method_) << " (line: " << line_number << ")";
     }
   }
   if (clear_exception_) {
@@ -62,12 +142,94 @@
   // The debugger may suspend this thread and walk its stack. Let's do this before popping
   // instrumentation frames.
   instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
-  instrumentation->ExceptionCaughtEvent(self_, throw_location, catch_method, handler_dex_pc_,
+  instrumentation->ExceptionCaughtEvent(self_, throw_location, handler_method_, handler_dex_pc_,
                                         exception_ref.Get());
 }
 
+// Prepares deoptimization.
+class DeoptimizeStackVisitor FINAL : public StackVisitor {
+ public:
+  DeoptimizeStackVisitor(Thread* self, Context* context, QuickExceptionHandler* exception_handler)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : StackVisitor(self, context), self_(self), exception_handler_(exception_handler),
+        prev_shadow_frame_(nullptr) {
+    CHECK(!self_->HasDeoptimizationShadowFrame());
+  }
+
+  bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    exception_handler_->SetHandlerFrameId(GetFrameId());
+    mirror::ArtMethod* method = GetMethod();
+    if (method == nullptr) {
+      // This is the upcall, we remember the frame and last pc so that we may long jump to them.
+      exception_handler_->SetHandlerQuickFramePc(GetCurrentQuickFramePc());
+      exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
+      return false;  // End stack walk.
+    } else if (method->IsRuntimeMethod()) {
+      // Ignore callee save method.
+      DCHECK(method->IsCalleeSaveMethod());
+      return true;
+    } else {
+      return HandleDeoptimization(method);
+    }
+  }
+
+ private:
+  bool HandleDeoptimization(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    MethodHelper mh(m);
+    const DexFile::CodeItem* code_item = mh.GetCodeItem();
+    CHECK(code_item != nullptr);
+    uint16_t num_regs = code_item->registers_size_;
+    uint32_t dex_pc = GetDexPc();
+    const Instruction* inst = Instruction::At(code_item->insns_ + dex_pc);
+    uint32_t new_dex_pc = dex_pc + inst->SizeInCodeUnits();
+    ShadowFrame* new_frame = ShadowFrame::Create(num_regs, nullptr, m, new_dex_pc);
+    StackHandleScope<2> hs(self_);
+    Handle<mirror::DexCache> dex_cache(hs.NewHandle(mh.GetDexCache()));
+    Handle<mirror::ClassLoader> class_loader(hs.NewHandle(mh.GetClassLoader()));
+    verifier::MethodVerifier verifier(&mh.GetDexFile(), &dex_cache, &class_loader,
+                                      &mh.GetClassDef(), code_item, m->GetDexMethodIndex(), m,
+                                      m->GetAccessFlags(), false, true, true);
+    verifier.Verify();
+    std::vector<int32_t> kinds = verifier.DescribeVRegs(dex_pc);
+    for (uint16_t reg = 0; reg < num_regs; ++reg) {
+      VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2));
+      switch (kind) {
+        case kUndefined:
+          new_frame->SetVReg(reg, 0xEBADDE09);
+          break;
+        case kConstant:
+          new_frame->SetVReg(reg, kinds.at((reg * 2) + 1));
+          break;
+        case kReferenceVReg:
+          new_frame->SetVRegReference(reg,
+                                      reinterpret_cast<mirror::Object*>(GetVReg(m, reg, kind)));
+          break;
+        default:
+          new_frame->SetVReg(reg, GetVReg(m, reg, kind));
+          break;
+      }
+    }
+    if (prev_shadow_frame_ != nullptr) {
+      prev_shadow_frame_->SetLink(new_frame);
+    } else {
+      self_->SetDeoptimizationShadowFrame(new_frame);
+    }
+    prev_shadow_frame_ = new_frame;
+    return true;
+  }
+
+  Thread* const self_;
+  QuickExceptionHandler* const exception_handler_;
+  ShadowFrame* prev_shadow_frame_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeoptimizeStackVisitor);
+};
+
 void QuickExceptionHandler::DeoptimizeStack() {
   DCHECK(is_deoptimization_);
+  if (kDebugExceptionDelivery) {
+    self_->DumpStack(LOG(INFO) << "Deoptimizing: ");
+  }
 
   DeoptimizeStackVisitor visitor(self_, context_, this);
   visitor.WalkStack(true);
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index 2597ebd..a4229b3 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -32,9 +32,6 @@
 class ThrowLocation;
 class ShadowFrame;
 
-static constexpr bool kDebugExceptionDelivery = false;
-static constexpr size_t kInvalidFrameId = 0xffffffff;
-
 // Manages exception delivery for Quick backend. Not used by Portable backend.
 class QuickExceptionHandler {
  public:
@@ -59,6 +56,18 @@
     handler_quick_frame_pc_ = handler_quick_frame_pc;
   }
 
+  mirror::ArtMethod* GetHandlerMethod() const {
+    return handler_method_;
+  }
+
+  void SetHandlerMethod(mirror::ArtMethod* handler_quick_method) {
+    handler_method_ = handler_quick_method;
+  }
+
+  uint32_t GetHandlerDexPc() const {
+    return handler_dex_pc_;
+  }
+
   void SetHandlerDexPc(uint32_t dex_pc) {
     handler_dex_pc_ = dex_pc;
   }
@@ -81,7 +90,9 @@
   StackReference<mirror::ArtMethod>* handler_quick_frame_;
   // PC to branch to for the handler.
   uintptr_t handler_quick_frame_pc_;
-  // Associated dex PC.
+  // The handler method to report to the debugger.
+  mirror::ArtMethod* handler_method_;
+  // The handler's dex PC, zero implies an uncaught exception.
   uint32_t handler_dex_pc_;
   // Should the exception be cleared as the catch block has no move-exception?
   bool clear_exception_;
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 6633159..ef09816 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -95,6 +95,13 @@
   DCHECK(thread == Thread::Current() || thread->IsSuspended()) << *thread;
 }
 
+StackVisitor::StackVisitor(Thread* thread, Context* context, size_t num_frames)
+    : thread_(thread), cur_shadow_frame_(NULL),
+      cur_quick_frame_(NULL), cur_quick_frame_pc_(0), num_frames_(num_frames), cur_depth_(0),
+      context_(context) {
+  DCHECK(thread == Thread::Current() || thread->IsSuspended()) << *thread;
+}
+
 uint32_t StackVisitor::GetDexPc(bool abort_on_failure) const {
   if (cur_shadow_frame_ != NULL) {
     return cur_shadow_frame_->GetDexPC();
@@ -223,7 +230,7 @@
     explicit NumFramesVisitor(Thread* thread)
         : StackVisitor(thread, NULL), frames(0) {}
 
-    virtual bool VisitFrame() {
+    bool VisitFrame() OVERRIDE {
       frames++;
       return true;
     }
@@ -235,12 +242,47 @@
   return visitor.frames;
 }
 
+bool StackVisitor::GetNextMethodAndDexPc(mirror::ArtMethod** next_method, uint32_t* next_dex_pc) {
+  struct HasMoreFramesVisitor : public StackVisitor {
+    explicit HasMoreFramesVisitor(Thread* thread, size_t num_frames, size_t frame_height)
+        : StackVisitor(thread, nullptr, num_frames), frame_height_(frame_height),
+          found_frame_(false), has_more_frames_(false), next_method_(nullptr), next_dex_pc_(0) {
+    }
+
+    bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+      if (found_frame_) {
+        mirror::ArtMethod* method = GetMethod();
+        if (method != nullptr && !method->IsRuntimeMethod()) {
+          has_more_frames_ = true;
+          next_method_ = method;
+          next_dex_pc_ = GetDexPc();
+          return false;  // End stack walk once next method is found.
+        }
+      } else if (GetFrameHeight() == frame_height_) {
+        found_frame_ = true;
+      }
+      return true;
+    }
+
+    size_t frame_height_;
+    bool found_frame_;
+    bool has_more_frames_;
+    mirror::ArtMethod* next_method_;
+    uint32_t next_dex_pc_;
+  };
+  HasMoreFramesVisitor visitor(thread_, GetNumFrames(), GetFrameHeight());
+  visitor.WalkStack(true);
+  *next_method = visitor.next_method_;
+  *next_dex_pc = visitor.next_dex_pc_;
+  return visitor.has_more_frames_;
+}
+
 void StackVisitor::DescribeStack(Thread* thread) {
   struct DescribeStackVisitor : public StackVisitor {
     explicit DescribeStackVisitor(Thread* thread)
         : StackVisitor(thread, NULL) {}
 
-    virtual bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    bool VisitFrame() OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
       LOG(INFO) << "Frame Id=" << GetFrameId() << " " << DescribeLocation();
       return true;
     }
diff --git a/runtime/stack.h b/runtime/stack.h
index e93fcbc..fabdd4f 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -557,6 +557,10 @@
     return num_frames_;
   }
 
+  // Get the method and dex pc immediately after the one that's currently being visited.
+  bool GetNextMethodAndDexPc(mirror::ArtMethod** next_method, uint32_t* next_dex_pc)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   uint32_t GetVReg(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -691,6 +695,10 @@
   static void DescribeStack(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
  private:
+  // Private constructor known in the case that num_frames_ has already been computed.
+  StackVisitor(Thread* thread, Context* context, size_t num_frames)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   instrumentation::InstrumentationStackFrame& GetInstrumentationStackFrame(uint32_t depth) const;
 
   void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index a8135e0..758944c 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1867,16 +1867,6 @@
   // resolution.
   ClearException();
   bool is_deoptimization = (exception == GetDeoptimizationException());
-  if (kDebugExceptionDelivery) {
-    if (!is_deoptimization) {
-      mirror::String* msg = exception->GetDetailMessage();
-      std::string str_msg(msg != nullptr ? msg->ToModifiedUtf8() : "");
-      DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception)
-                << ": " << str_msg << "\n");
-    } else {
-      DumpStack(LOG(INFO) << "Deoptimizing: ");
-    }
-  }
   QuickExceptionHandler exception_handler(this, is_deoptimization);
   if (is_deoptimization) {
     exception_handler.DeoptimizeStack();
diff --git a/runtime/thread.h b/runtime/thread.h
index 4601248..88b4b0d 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -1090,6 +1090,7 @@
   friend class Dbg;  // For SetStateUnsafe.
   friend class gc::collector::SemiSpace;  // For getting stack traces.
   friend class Runtime;  // For CreatePeer.
+  friend class QuickExceptionHandler;  // For dumping the stack.
   friend class ScopedThreadStateChange;
   friend class SignalCatcher;  // For SetStateUnsafe.
   friend class StubTest;  // For accessing entrypoints.