Implement JDWP InvokeMethod and breakpoints on exception throwing.

Change-Id: I1142bee843104f0850fd7270752104d5d73a44f0
diff --git a/src/check_jni.cc b/src/check_jni.cc
index 037edfc..570e937 100644
--- a/src/check_jni.cc
+++ b/src/check_jni.cc
@@ -34,7 +34,7 @@
 
 void JniAbort(const char* jni_function_name) {
   Thread* self = Thread::Current();
-  const Method* current_method = self->GetCurrentMethod();
+  Method* current_method = self->GetCurrentMethod();
 
   std::ostringstream os;
   os << "Aborting because JNI app bug detected (see above for details)";
diff --git a/src/debugger.cc b/src/debugger.cc
index b2f9629..dea4339 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -716,8 +716,7 @@
 }
 
 bool Dbg::MatchType(JDWP::RefTypeId instClassId, JDWP::RefTypeId classId) {
-  UNIMPLEMENTED(FATAL);
-  return false;
+  return gRegistry->Get<Class*>(instClassId)->InstanceOf(gRegistry->Get<Class*>(classId));
 }
 
 JDWP::FieldId ToFieldId(Field* f) {
@@ -752,6 +751,14 @@
 #endif
 }
 
+void SetLocation(JDWP::JdwpLocation& location, Method* m, uintptr_t native_pc) {
+  Class* c = m->GetDeclaringClass();
+  location.typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
+  location.classId = gRegistry->Add(c);
+  location.methodId = ToMethodId(m);
+  location.idx = m->IsNative() ? -1 : m->ToDexPC(native_pc);
+}
+
 std::string Dbg::GetMethodName(JDWP::RefTypeId refTypeId, JDWP::MethodId methodId) {
   Method* m = FromMethodId(methodId);
   return MethodHelper(m).GetName();
@@ -1119,8 +1126,7 @@
 }
 
 uint32_t Dbg::GetThreadSuspendCount(JDWP::ObjectId threadId) {
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return DecodeThread(threadId)->GetSuspendCount();
 }
 
 bool Dbg::ThreadExists(JDWP::ObjectId threadId) {
@@ -1131,8 +1137,6 @@
   return DecodeThread(threadId)->IsSuspended();
 }
 
-//void Dbg::WaitForSuspend(JDWP::ObjectId threadId);
-
 void Dbg::GetThreadGroupThreadsImpl(Object* thread_group, JDWP::ObjectId** ppThreadIds, uint32_t* pThreadCount) {
   struct ThreadListVisitor {
     static void Visit(Thread* t, void* arg) {
@@ -1212,15 +1216,7 @@
 
       if (depth == desired_frame_number) {
         *pFrameId = reinterpret_cast<JDWP::FrameId>(f.GetSP());
-
-        Method* m = f.GetMethod();
-        Class* c = m->GetDeclaringClass();
-
-        pLoc->typeTag = c->IsInterface() ? JDWP::TT_INTERFACE : JDWP::TT_CLASS;
-        pLoc->classId = gRegistry->Add(c);
-        pLoc->methodId = ToMethodId(m);
-        pLoc->idx = m->IsNative() ? -1 : m->ToDexPC(pc);
-
+        SetLocation(*pLoc, f.GetMethod(), pc);
         found = true;
       }
       ++depth;
@@ -1276,15 +1272,15 @@
   Runtime::Current()->GetThreadList()->SuspendSelfForDebugger();
 }
 
-bool Dbg::GetThisObject(JDWP::ObjectId threadId, JDWP::FrameId frameId, JDWP::ObjectId* pThisId) {
+bool Dbg::GetThisObject(JDWP::FrameId frameId, JDWP::ObjectId* pThisId) {
   Method** sp = reinterpret_cast<Method**>(frameId);
   Frame f;
   f.SetSP(sp);
-  uint16_t reg = DemangleSlot(0, f);
   Method* m = f.GetMethod();
 
   Object* o = NULL;
   if (!m->IsNative() && !m->IsStatic()) {
+    uint16_t reg = DemangleSlot(0, f);
     o = reinterpret_cast<Object*>(f.GetVReg(m, reg));
   }
   *pThisId = gRegistry->Add(o);
@@ -1436,8 +1432,29 @@
   UNIMPLEMENTED(FATAL);
 }
 
-void Dbg::PostException(void* throwFp, int throwRelPc, void* catchFp, int catchRelPc, Object* exception) {
-  UNIMPLEMENTED(FATAL);
+void Dbg::PostException(Method** sp, Method* throwMethod, uintptr_t throwNativePc, Method* catchMethod, uintptr_t catchNativePc, Object* exception) {
+  JDWP::JdwpLocation throw_location;
+  SetLocation(throw_location, throwMethod, throwNativePc);
+  JDWP::JdwpLocation catch_location;
+  SetLocation(catch_location, catchMethod, catchNativePc);
+
+  // We need 'this' for InstanceOnly filters.
+  JDWP::ObjectId this_id;
+  GetThisObject(reinterpret_cast<JDWP::FrameId>(sp), &this_id);
+
+  /*
+   * Hand the event to the JDWP exception handler.  Note we're using the
+   * "NoReg" objectID on the exception, which is not strictly correct --
+   * the exception object WILL be passed up to the debugger if the
+   * debugger is interested in the event.  We do this because the current
+   * implementation of the debugger object registry never throws anything
+   * away, and some people were experiencing a fatal build up of exception
+   * objects when dealing with certain libraries.
+   */
+  JDWP::ObjectId exception_id = static_cast<JDWP::ObjectId>(reinterpret_cast<uintptr_t>(exception));
+  JDWP::RefTypeId exception_class_id = gRegistry->Add(exception->GetClass());
+
+  gJdwpState->PostException(&throw_location, exception_id, exception_class_id, &catch_location, this_id);
 }
 
 void Dbg::PostClassPrepare(Class* c) {
@@ -1462,17 +1479,182 @@
   UNIMPLEMENTED(FATAL);
 }
 
-JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId threadId, JDWP::ObjectId objectId, JDWP::RefTypeId classId, JDWP::MethodId methodId, uint32_t numArgs, uint64_t* argArray, uint32_t options, JDWP::JdwpTag* pResultTag, uint64_t* pResultValue, JDWP::ObjectId* pExceptObj) {
-  UNIMPLEMENTED(FATAL);
-  return JDWP::ERR_NONE;
+JDWP::JdwpError Dbg::InvokeMethod(JDWP::ObjectId threadId, JDWP::ObjectId objectId, JDWP::RefTypeId classId, JDWP::MethodId methodId, uint32_t numArgs, uint64_t* argArray, uint32_t options, JDWP::JdwpTag* pResultTag, uint64_t* pResultValue, JDWP::ObjectId* pExceptionId) {
+  ThreadList* thread_list = Runtime::Current()->GetThreadList();
+
+  Thread* targetThread = NULL;
+  DebugInvokeReq* req = NULL;
+  {
+    ScopedThreadListLock thread_list_lock;
+    targetThread = DecodeThread(threadId);
+    if (targetThread == NULL) {
+      LOG(ERROR) << "InvokeMethod request for non-existent thread " << threadId;
+      return JDWP::ERR_INVALID_THREAD;
+    }
+    req = targetThread->GetInvokeReq();
+    if (!req->ready) {
+      LOG(ERROR) << "InvokeMethod request for thread not stopped by event: " << *targetThread;
+      return JDWP::ERR_INVALID_THREAD;
+    }
+
+    /*
+     * We currently have a bug where we don't successfully resume the
+     * target thread if the suspend count is too deep.  We're expected to
+     * require one "resume" for each "suspend", but when asked to execute
+     * a method we have to resume fully and then re-suspend it back to the
+     * same level.  (The easiest way to cause this is to type "suspend"
+     * multiple times in jdb.)
+     *
+     * It's unclear what this means when the event specifies "resume all"
+     * and some threads are suspended more deeply than others.  This is
+     * a rare problem, so for now we just prevent it from hanging forever
+     * by rejecting the method invocation request.  Without this, we will
+     * be stuck waiting on a suspended thread.
+     */
+    int suspend_count = targetThread->GetSuspendCount();
+    if (suspend_count > 1) {
+      LOG(ERROR) << *targetThread << " suspend count too deep for method invocation: " << suspend_count;
+      return JDWP::ERR_THREAD_SUSPENDED; // Probably not expected here.
+    }
+
+    /*
+     * TODO: ought to screen the various IDs, and verify that the argument
+     * list is valid.
+     */
+    req->receiver_ = gRegistry->Get<Object*>(objectId);
+    req->thread_ = gRegistry->Get<Object*>(threadId);
+    req->class_ = gRegistry->Get<Class*>(classId);
+    req->method_ = FromMethodId(methodId);
+    req->num_args_ = numArgs;
+    req->arg_array_ = argArray;
+    req->options_ = options;
+    req->invoke_needed_ = true;
+  }
+
+  // The fact that we've released the thread list lock is a bit risky --- if the thread goes
+  // away we're sitting high and dry -- but we must release this before the ResumeAllThreads
+  // call, and it's unwise to hold it during WaitForSuspend.
+
+  {
+    /*
+     * We change our (JDWP thread) status, which should be THREAD_RUNNING,
+     * so the VM can suspend for a GC if the invoke request causes us to
+     * run out of memory.  It's also a good idea to change it before locking
+     * the invokeReq mutex, although that should never be held for long.
+     */
+    ScopedThreadStateChange tsc(Thread::Current(), Thread::kVmWait);
+
+    LOG(VERBOSE) << "    Transferring control to event thread";
+    {
+      MutexLock mu(req->lock_);
+
+      if ((options & JDWP::INVOKE_SINGLE_THREADED) == 0) {
+        LOG(VERBOSE) << "      Resuming all threads";
+        thread_list->ResumeAll(true);
+      } else {
+        LOG(VERBOSE) << "      Resuming event thread only";
+        thread_list->Resume(targetThread, true);
+      }
+
+      // Wait for the request to finish executing.
+      while (req->invoke_needed_) {
+        req->cond_.Wait(req->lock_);
+      }
+    }
+    LOG(VERBOSE) << "    Control has returned from event thread";
+
+    /* wait for thread to re-suspend itself */
+    targetThread->WaitUntilSuspended();
+    //dvmWaitForSuspend(targetThread);
+  }
+
+  /*
+   * Suspend the threads.  We waited for the target thread to suspend
+   * itself, so all we need to do is suspend the others.
+   *
+   * The suspendAllThreads() call will double-suspend the event thread,
+   * so we want to resume the target thread once to keep the books straight.
+   */
+  if ((options & JDWP::INVOKE_SINGLE_THREADED) == 0) {
+    LOG(VERBOSE) << "      Suspending all threads";
+    thread_list->SuspendAll(true);
+    LOG(VERBOSE) << "      Resuming event thread to balance the count";
+    thread_list->Resume(targetThread, true);
+  }
+
+  // Copy the result.
+  *pResultTag = req->result_tag;
+  if (IsPrimitiveTag(req->result_tag)) {
+    *pResultValue = req->result_value.j;
+  } else {
+    *pResultValue = gRegistry->Add(req->result_value.l);
+  }
+  *pExceptionId = req->exception;
+  return req->error;
 }
 
 void Dbg::ExecuteMethod(DebugInvokeReq* pReq) {
-  UNIMPLEMENTED(FATAL);
+  Thread* self = Thread::Current();
+
+  // We can be called while an exception is pending in the VM.  We need
+  // to preserve that across the method invocation.
+  SirtRef<Throwable> old_exception(self->GetException());
+  self->ClearException();
+
+  ScopedThreadStateChange tsc(self, Thread::kRunnable);
+
+  // Translate the method through the vtable, unless the debugger wants to suppress it.
+  Method* m = pReq->method_;
+  if ((pReq->options_ & JDWP::INVOKE_NONVIRTUAL) == 0 && pReq->receiver_ != NULL) {
+    m = pReq->class_->FindVirtualMethodForVirtualOrInterface(pReq->method_);
+  }
+  CHECK(m != NULL);
+
+  CHECK_EQ(sizeof(jvalue), sizeof(uint64_t));
+
+  pReq->result_value = InvokeWithJValues(self, pReq->receiver_, m, reinterpret_cast<JValue*>(pReq->arg_array_));
+
+  pReq->exception = gRegistry->Add(self->GetException());
+  pReq->result_tag = BasicTagFromDescriptor(MethodHelper(m).GetShorty());
+  if (pReq->exception != 0) {
+    Object* exc = self->GetException();
+    LOG(VERBOSE) << "  JDWP invocation returning with exception=" << exc << " " << PrettyTypeOf(exc);
+    self->ClearException();
+    pReq->result_value.j = 0;
+  } else if (pReq->result_tag == JDWP::JT_OBJECT) {
+    /* if no exception thrown, examine object result more closely */
+    JDWP::JdwpTag new_tag = TagFromObject(pReq->result_value.l);
+    if (new_tag != pReq->result_tag) {
+      LOG(VERBOSE) << "  JDWP promoted result from " << pReq->result_tag << " to " << new_tag;
+      pReq->result_tag = new_tag;
+    }
+
+    /*
+     * Register the object.  We don't actually need an ObjectId yet,
+     * but we do need to be sure that the GC won't move or discard the
+     * object when we switch out of RUNNING.  The ObjectId conversion
+     * will add the object to the "do not touch" list.
+     *
+     * We can't use the "tracked allocation" mechanism here because
+     * the object is going to be handed off to a different thread.
+     */
+    gRegistry->Add(pReq->result_value.l);
+  }
+
+  if (old_exception.get() != NULL) {
+    self->SetException(old_exception.get());
+  }
 }
 
+/*
+ * Register an object ID that might not have been registered previously.
+ *
+ * Normally this wouldn't happen -- the conversion to an ObjectId would
+ * have added the object to the registry -- but in some cases (e.g.
+ * throwing exceptions) we really want to do the registration late.
+ */
 void Dbg::RegisterObjectId(JDWP::ObjectId id) {
-  UNIMPLEMENTED(FATAL);
+  gRegistry->Add(reinterpret_cast<Object*>(id));
 }
 
 /*
diff --git a/src/debugger.h b/src/debugger.h
index 2faca36..381f1db 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -44,20 +44,20 @@
   bool ready;
 
   /* boolean; set if the JDWP thread wants this thread to do work */
-  bool invoke_needed;
+  bool invoke_needed_;
 
   /* request */
-  Object* obj;        /* not used for ClassType.InvokeMethod */
-  Object* thread;
+  Object* receiver_;      /* not used for ClassType.InvokeMethod */
+  Object* thread_;
   Class* class_;
-  Method* method;
-  uint32_t num_args;
-  uint64_t* arg_array;   /* will be NULL if numArgs==0 */
-  uint32_t options;
+  Method* method_;
+  uint32_t num_args_;
+  uint64_t* arg_array_;   /* will be NULL if numArgs==0 */
+  uint32_t options_;
 
   /* result */
   JDWP::JdwpError error;
-  uint8_t result_tag;
+  JDWP::JdwpTag result_tag;
   JValue result_value;
   JDWP::ObjectId exception;
 
@@ -199,7 +199,7 @@
   static void ResumeThread(JDWP::ObjectId threadId);
   static void SuspendSelf();
 
-  static bool GetThisObject(JDWP::ObjectId threadId, JDWP::FrameId frameId, JDWP::ObjectId* pThisId);
+  static bool GetThisObject(JDWP::FrameId frameId, JDWP::ObjectId* pThisId);
   static void GetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint8_t* buf, size_t expectedLen);
   static void SetLocalValue(JDWP::ObjectId threadId, JDWP::FrameId frameId, int slot, JDWP::JdwpTag tag, uint64_t value, size_t width);
 
@@ -213,7 +213,7 @@
     kMethodExit     = 0x08,
   };
   static void PostLocationEvent(const Method* method, int pcOffset, Object* thisPtr, int eventFlags);
-  static void PostException(void* throwFp, int throwRelPc, void* catchFp, int catchRelPc, Object* exception);
+  static void PostException(Method** sp, Method* throwMethod, uintptr_t throwNativePc, Method* catchMethod, uintptr_t catchNativePc, Object* exception);
   static void PostThreadStart(Thread* t);
   static void PostThreadDeath(Thread* t);
   static void PostClassPrepare(Class* c);
diff --git a/src/indirect_reference_table.cc b/src/indirect_reference_table.cc
index 5f5541e..7bb4dc2 100644
--- a/src/indirect_reference_table.cc
+++ b/src/indirect_reference_table.cc
@@ -86,7 +86,7 @@
 
   DCHECK(obj != NULL);
   // TODO: stronger sanity check on the object (such as in heap)
-  DCHECK_ALIGNED(reinterpret_cast<intptr_t>(obj), 8);
+  DCHECK_ALIGNED(reinterpret_cast<uintptr_t>(obj), 8);
   DCHECK(table_ != NULL);
   DCHECK_LE(alloc_entries_, max_entries_);
   DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
diff --git a/src/jdwp/jdwp.h b/src/jdwp/jdwp.h
index b628e6c..36e2657 100644
--- a/src/jdwp/jdwp.h
+++ b/src/jdwp/jdwp.h
@@ -31,6 +31,7 @@
 
 namespace art {
 
+struct Method;
 struct Thread;
 
 namespace JDWP {
@@ -56,6 +57,7 @@
 static inline RefTypeId ReadRefTypeId(const uint8_t** pBuf) { return Read8BE(pBuf); }
 static inline FrameId ReadFrameId(const uint8_t** pBuf) { return Read8BE(pBuf); }
 static inline JdwpTag ReadTag(const uint8_t** pBuf) { return static_cast<JdwpTag>(Read1(pBuf)); }
+static inline JdwpTypeTag ReadTypeTag(const uint8_t** pBuf) { return static_cast<JdwpTypeTag>(Read1(pBuf)); }
 static inline void SetFieldId(uint8_t* buf, FieldId val) { return Set4BE(buf, val); }
 static inline void SetMethodId(uint8_t* buf, MethodId val) { return Set4BE(buf, val); }
 static inline void SetObjectId(uint8_t* buf, ObjectId val) { return Set8BE(buf, val); }
@@ -71,10 +73,10 @@
  * Holds a JDWP "location".
  */
 struct JdwpLocation {
-  uint8_t typeTag;        /* class or interface? */
-  RefTypeId classId;        /* method->clazz */
-  MethodId methodId;       /* method in which "idx" resides */
-  uint64_t idx;            /* relative index into code block */
+  JdwpTypeTag typeTag;
+  RefTypeId classId;
+  MethodId methodId;
+  uint64_t idx; // A Dex PC.
 };
 std::ostream& operator<<(std::ostream& os, const JdwpLocation& rhs);
 
diff --git a/src/jdwp/jdwp_event.cc b/src/jdwp/jdwp_event.cc
index 15e1151..424d87f 100644
--- a/src/jdwp/jdwp_event.cc
+++ b/src/jdwp/jdwp_event.cc
@@ -535,7 +535,7 @@
      * The JDWP thread has told us (and possibly all other threads) to
      * resume.  See if it has left anything in our DebugInvokeReq mailbox.
      */
-    if (!pReq->invoke_needed) {
+    if (!pReq->invoke_needed_) {
       /*LOGD("SuspendByPolicy: no invoke needed");*/
       break;
     }
@@ -543,14 +543,14 @@
     /* grab this before posting/suspending again */
     SetWaitForEventThread(Dbg::GetThreadSelfId());
 
-    /* leave pReq->invoke_needed raised so we can check reentrancy */
+    /* leave pReq->invoke_needed_ raised so we can check reentrancy */
     LOG(VERBOSE) << "invoking method...";
     Dbg::ExecuteMethod(pReq);
 
     pReq->error = ERR_NONE;
 
     /* clear this before signaling */
-    pReq->invoke_needed = false;
+    pReq->invoke_needed_ = false;
 
     LOG(VERBOSE) << "invoke complete, signaling and self-suspending";
     MutexLock mu(pReq->lock_);
@@ -567,7 +567,7 @@
  */
 bool JdwpState::InvokeInProgress() {
   DebugInvokeReq* pReq = Dbg::GetInvokeReq();
-  return pReq->invoke_needed;
+  return pReq->invoke_needed_;
 }
 
 /*
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index 9b5c080..238775e 100644
--- a/src/jdwp/jdwp_handler.cc
+++ b/src/jdwp/jdwp_handler.cc
@@ -49,7 +49,7 @@
  */
 static void jdwpReadLocation(const uint8_t** pBuf, JdwpLocation* pLoc) {
   memset(pLoc, 0, sizeof(*pLoc));     /* allows memcmp() later */
-  pLoc->typeTag = Read1(pBuf);
+  pLoc->typeTag = ReadTypeTag(pBuf);
   pLoc->classId = ReadObjectId(pBuf);
   pLoc->methodId = ReadMethodId(pBuf);
   pLoc->idx = Read8BE(pBuf);
@@ -1467,16 +1467,16 @@
  * Returns the value of "this" for the specified frame.
  */
 static JdwpError handleSF_ThisObject(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
-  ObjectId threadId = ReadObjectId(&buf);
+  ReadObjectId(&buf); // Skip thread id.
   FrameId frameId = ReadFrameId(&buf);
 
   ObjectId objectId;
-  if (!Dbg::GetThisObject(threadId, frameId, &objectId)) {
+  if (!Dbg::GetThisObject(frameId, &objectId)) {
     return ERR_INVALID_FRAMEID;
   }
 
   uint8_t objectTag = Dbg::GetObjectTag(objectId);
-  LOG(VERBOSE) << StringPrintf("  Req for 'this' in thread=%llx frame=%llx --> %llx '%c'", threadId, frameId, objectId, (char)objectTag);
+  LOG(VERBOSE) << StringPrintf("  Req for 'this' in frame=%llx --> %llx '%c'", frameId, objectId, (char)objectTag);
 
   expandBufAdd1(pReply, objectTag);
   expandBufAddObjectId(pReply, objectId);
diff --git a/src/jdwp/jdwp_main.cc b/src/jdwp/jdwp_main.cc
index 07ffc52..98490e2 100644
--- a/src/jdwp/jdwp_main.cc
+++ b/src/jdwp/jdwp_main.cc
@@ -451,9 +451,9 @@
 }
 
 std::ostream& operator<<(std::ostream& os, const JdwpLocation& rhs) {
-  // TODO: do we really want the Class* and Method* as pointers?
-  os << rhs.typeTag << " " << (void*) rhs.classId << " " << (void*) rhs.methodId << " " << rhs.idx
-     << " (" << Dbg::GetClassDescriptor(rhs.classId) << "." << Dbg::GetMethodName(rhs.classId, rhs.methodId) << ")";
+  os << "JdwpLocation["
+     << Dbg::GetClassDescriptor(rhs.classId) << "." << Dbg::GetMethodName(rhs.classId, rhs.methodId)
+     << "@" << rhs.idx << " " << rhs.typeTag << "]";
   return os;
 }
 
@@ -481,6 +481,17 @@
   return os;
 }
 
+std::ostream& operator<<(std::ostream& os, const JdwpTypeTag& value) {
+  switch (value) {
+  case TT_CLASS: os << "TT_CLASS"; break;
+  case TT_INTERFACE: os << "TT_INTERFACE"; break;
+  case TT_ARRAY: os << "TT_ARRAY"; break;
+  default:
+    os << "JdwpTypeTag[" << static_cast<int32_t>(value) << "]";
+  }
+  return os;
+}
+
 }  // namespace JDWP
 
 }  // namespace art
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 718659f..7e145a8 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -236,6 +236,42 @@
   return arg_array.release();
 }
 
+static byte* CreateArgArray(Method* method, JValue* args) {
+  const char* shorty = MethodHelper(method).GetShorty();
+  size_t shorty_len = strlen(shorty);
+  size_t num_bytes = NumArgArrayBytes(shorty);
+  UniquePtr<byte[]> arg_array(new byte[num_bytes]);
+  for (size_t i = 1, offset = 0; i < shorty_len; ++i) {
+    switch (shorty[i]) {
+    case 'Z':
+    case 'B':
+    case 'C':
+    case 'S':
+    case 'I':
+      *reinterpret_cast<uint32_t*>(&arg_array[offset]) = args[i - 1].i;
+      offset += 4;
+      break;
+    case 'F':
+      *reinterpret_cast<float*>(&arg_array[offset]) = args[i - 1].f;
+      offset += 4;
+      break;
+    case 'L':
+      *reinterpret_cast<Object**>(&arg_array[offset]) = args[i - 1].l;
+      offset += sizeof(Object*);
+      break;
+    case 'D':
+      *reinterpret_cast<double*>(&arg_array[offset]) = args[i - 1].d;
+      offset += 8;
+      break;
+    case 'J':
+      *reinterpret_cast<uint64_t*>(&arg_array[offset]) = args[i - 1].j;
+      offset += 8;
+      break;
+    }
+  }
+  return arg_array.release();
+}
+
 static JValue InvokeWithArgArray(JNIEnv* public_env, Object* receiver, Method* method, byte* args) {
   JNIEnvExt* env = reinterpret_cast<JNIEnvExt*>(public_env);
   JValue result;
@@ -657,6 +693,11 @@
   return InvokeWithArgArray(env, receiver, method, arg_array.get());
 }
 
+JValue InvokeWithJValues(Thread* self, Object* receiver, Method* m, JValue* args) {
+  UniquePtr<byte[]> arg_array(CreateArgArray(m, args));
+  return InvokeWithArgArray(self->GetJniEnv(), receiver, m, arg_array.get());
+}
+
 class JNI {
  public:
 
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 6950dae..f39740b 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -62,6 +62,7 @@
 
 size_t NumArgArrayBytes(const char* shorty);
 JValue InvokeWithJValues(JNIEnv* env, jobject obj, jmethodID mid, jvalue* args);
+JValue InvokeWithJValues(Thread* self, Object* receiver, Method* m, JValue* args);
 
 struct JavaVMExt : public JavaVM {
   JavaVMExt(Runtime* runtime, Runtime::ParsedOptions* options);
diff --git a/src/monitor.cc b/src/monitor.cc
index 69eb9ba..8d04e2d 100644
--- a/src/monitor.cc
+++ b/src/monitor.cc
@@ -238,8 +238,7 @@
   // When debugging, save the current monitor holder for future
   // acquisition failures to use in sampled logging.
   if (lock_profiling_threshold_ != 0) {
-    locking_method_ = self->GetCurrentMethod();
-    locking_pc_ = self->GetCurrentReturnPc();
+    locking_method_ = self->GetCurrentMethod(&locking_pc_);
   }
 }
 
diff --git a/src/monitor_android.cc b/src/monitor_android.cc
index 39dc524..f80d248 100644
--- a/src/monitor_android.cc
+++ b/src/monitor_android.cc
@@ -76,9 +76,11 @@
   cp = EventLogWriteInt(cp, wait_ms);
 
   // Emit the source code file name, <= 37 bytes.
+  uintptr_t pc;
+  Method* m = self->GetCurrentMethod(&pc);
   const char* filename;
   uint32_t line_number;
-  TranslateLocation(self->GetCurrentMethod(), self->GetCurrentReturnPc(), filename, line_number);
+  TranslateLocation(m, pc, filename, line_number);
   cp = EventLogWriteString(cp, filename, strlen(filename));
 
   // Emit the source code line number, 5 bytes.
diff --git a/src/thread.cc b/src/thread.cc
index 53155b6..26eaf68 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1347,10 +1347,10 @@
 
 void Thread::DeliverException() {
   const bool kDebugExceptionDelivery = false;
-  Throwable *exception = GetException();  // Get exception from thread
+  Throwable* exception = GetException();  // Get exception from thread
   CHECK(exception != NULL);
   // Don't leave exception visible while we try to find the handler, which may cause class
-  // resolution
+  // resolution.
   ClearException();
   if (kDebugExceptionDelivery) {
     DumpStack(LOG(INFO) << "Delivering exception: " << PrettyTypeOf(exception) << std::endl);
@@ -1360,22 +1360,27 @@
   CatchBlockStackVisitor catch_finder(exception->GetClass(), long_jump_context);
   WalkStackUntilUpCall(&catch_finder, true);
 
+  Method** sp;
+  uintptr_t throw_native_pc;
+  Method* throw_method = GetCurrentMethod(&throw_native_pc, &sp);
+  uintptr_t catch_native_pc = catch_finder.handler_pc_;
+  Method* catch_method = catch_finder.handler_frame_.GetMethod();
+  Dbg::PostException(sp, throw_method, throw_native_pc, catch_method, catch_native_pc, exception);
+
   if (kDebugExceptionDelivery) {
-    Method* handler_method = catch_finder.handler_frame_.GetMethod();
-    if (handler_method == NULL) {
+    if (catch_method == NULL) {
       LOG(INFO) << "Handler is upcall";
     } else {
       ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
       const DexFile& dex_file =
-          class_linker->FindDexFile(handler_method->GetDeclaringClass()->GetDexCache());
-      int line_number = dex_file.GetLineNumFromPC(handler_method,
-                                                handler_method->ToDexPC(catch_finder.handler_pc_));
-      LOG(INFO) << "Handler: " << PrettyMethod(handler_method)
-          << " (line: " << line_number << ")";
+          class_linker->FindDexFile(catch_method->GetDeclaringClass()->GetDexCache());
+      int line_number = dex_file.GetLineNumFromPC(catch_method,
+          catch_method->ToDexPC(catch_finder.handler_pc_));
+      LOG(INFO) << "Handler: " << PrettyMethod(catch_method) << " (line: " << line_number << ")";
     }
   }
   SetException(exception);
-  long_jump_context->SetSP(reinterpret_cast<intptr_t>(catch_finder.handler_frame_.GetSP()));
+  long_jump_context->SetSP(reinterpret_cast<uintptr_t>(catch_finder.handler_frame_.GetSP()));
   long_jump_context->SetPC(catch_finder.handler_pc_);
   long_jump_context->DoLongJump();
 }
@@ -1389,25 +1394,24 @@
   return result;
 }
 
-const Method* Thread::GetCurrentMethod() const {
-  Method* m = top_of_managed_stack_.GetMethod();
+Method* Thread::GetCurrentMethod(uintptr_t* pc, Method*** sp) const {
+  Frame f = top_of_managed_stack_;
+  Method* m = f.GetMethod();
   // We use JNI internally for exception throwing, so it's possible to arrive
   // here via a "FromCode" function, in which case there's a synthetic
   // callee-save method at the top of the stack. These shouldn't be user-visible,
   // so if we find one, skip it and return the compiled method underneath.
   if (m != NULL && m->IsCalleeSaveMethod()) {
-    Frame f = top_of_managed_stack_;
     f.Next();
     m = f.GetMethod();
   }
-  return m;
-}
-
-uint32_t Thread::GetCurrentReturnPc() const {
-  if (top_of_managed_stack_.GetMethod() == NULL) {
-    return 0;
+  if (pc != NULL) {
+    *pc = ManglePc(f.GetReturnPC());
   }
-  return ManglePc(top_of_managed_stack_.GetReturnPC());
+  if (sp != NULL) {
+    *sp = f.GetSP();
+  }
+  return m;
 }
 
 bool Thread::HoldsLock(Object* object) {
diff --git a/src/thread.h b/src/thread.h
index 823b72c..c4c619a 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -241,11 +241,12 @@
     return &stats_;
   }
 
-  // Returns the Method* for the current method.
-  // This is used by the JNI implementation for logging and diagnostic purposes.
-  const Method* GetCurrentMethod() const;
+  int GetSuspendCount() const {
+    return suspend_count_;
+  }
 
-  uint32_t GetCurrentReturnPc() const;
+  // Returns the current Method* and native PC (not dex PC) for this thread.
+  Method* GetCurrentMethod(uintptr_t* pc = NULL, Method*** sp = NULL) const;
 
   bool IsExceptionPending() const {
     return exception_ != NULL;
diff --git a/src/thread_list.cc b/src/thread_list.cc
index 2179697..e646231 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -311,7 +311,10 @@
 
 void ThreadList::Resume(Thread* thread, bool for_debugger) {
   DCHECK(thread != Thread::Current());
-  thread_list_lock_.AssertHeld();
+
+  if (!for_debugger) { // The debugger is very naughty. See Dbg::InvokeMethod.
+    thread_list_lock_.AssertHeld();
+  }
 
   if (verbose_) {
     LOG(INFO) << "Resume(" << *thread << ") starting..." << (for_debugger ? " (debugger)" : "");