Merge "More debugger support." into dalvik-dev
diff --git a/src/debugger.cc b/src/debugger.cc
index 7c26bd0..7d6d344 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -18,8 +18,35 @@
 
 #include <sys/uio.h>
 
+#include "thread_list.h"
+
 namespace art {
 
+class ObjectRegistry {
+ public:
+  ObjectRegistry() : lock_("ObjectRegistry lock") {
+  }
+
+  JDWP::ObjectId Add(Object* o) {
+    if (o == NULL) {
+      return 0;
+    }
+    JDWP::ObjectId id = static_cast<JDWP::ObjectId>(reinterpret_cast<uintptr_t>(o));
+    MutexLock mu(lock_);
+    map_[id] = o;
+    return id;
+  }
+
+  bool Contains(JDWP::ObjectId id) {
+    MutexLock mu(lock_);
+    return map_.find(id) != map_.end();
+  }
+
+ private:
+  Mutex lock_;
+  std::map<JDWP::ObjectId, Object*> map_;
+};
+
 // JDWP is allowed unless the Zygote forbids it.
 static bool gJdwpAllowed = true;
 
@@ -34,6 +61,8 @@
 static bool gDebuggerConnected;  // debugger or DDMS is connected.
 static bool gDebuggerActive;     // debugger is making requests.
 
+static ObjectRegistry* gRegistry = NULL;
+
 /*
  * Handle one of the JDWP name/value pairs.
  *
@@ -146,12 +175,16 @@
     return;
   }
 
+  CHECK(gRegistry == NULL);
+  gRegistry = new ObjectRegistry;
+
   // Init JDWP if the debugger is enabled. This may connect out to a
   // debugger, passively listen for a debugger, or block waiting for a
   // debugger.
   gJdwpState = JDWP::JdwpState::Create(&gJdwpOptions);
   if (gJdwpState == NULL) {
     LOG(WARNING) << "debugger thread failed to initialize";
+    return;
   }
 
   // If a debugger has already attached, send the "welcome" message.
@@ -166,6 +199,8 @@
 
 void Dbg::StopJdwp() {
   delete gJdwpState;
+  delete gRegistry;
+  gRegistry = NULL;
 }
 
 void Dbg::SetJdwpAllowed(bool allowed) {
@@ -173,8 +208,15 @@
 }
 
 DebugInvokeReq* Dbg::GetInvokeReq() {
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+  return Thread::Current()->GetInvokeReq();
+}
+
+Thread* Dbg::GetDebugThread() {
+  return (gJdwpState != NULL) ? gJdwpState->GetDebugThread() : NULL;
+}
+
+void Dbg::ClearWaitForEventThread() {
+  gJdwpState->ClearWaitForEventThread();
 }
 
 void Dbg::Connected() {
@@ -472,16 +514,15 @@
 }
 
 JDWP::ObjectId Dbg::GetThreadSelfId() {
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return gRegistry->Add(Thread::Current()->GetPeer());
 }
 
-void Dbg::SuspendVM(bool isEvent) {
-  UNIMPLEMENTED(FATAL);
+void Dbg::SuspendVM() {
+  Runtime::Current()->GetThreadList()->SuspendAll(true);
 }
 
 void Dbg::ResumeVM() {
-  UNIMPLEMENTED(FATAL);
+  Runtime::Current()->GetThreadList()->ResumeAll(true);
 }
 
 void Dbg::SuspendThread(JDWP::ObjectId threadId) {
@@ -493,7 +534,7 @@
 }
 
 void Dbg::SuspendSelf() {
-  UNIMPLEMENTED(FATAL);
+  Runtime::Current()->GetThreadList()->SuspendSelfForDebugger();
 }
 
 bool Dbg::GetThisObject(JDWP::ObjectId threadId, JDWP::FrameId frameId, JDWP::ObjectId* pThisId) {
diff --git a/src/debugger.h b/src/debugger.h
index fa870b1..6eabf33 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -36,26 +36,29 @@
  * Invoke-during-breakpoint support.
  */
 struct DebugInvokeReq {
+  DebugInvokeReq() : lock_("a DebugInvokeReq lock"), cond_("a DebugInvokeReq condition variable") {
+  }
+
   /* boolean; only set when we're in the tail end of an event handler */
   bool ready;
 
   /* boolean; set if the JDWP thread wants this thread to do work */
-  bool invokeNeeded;
+  bool invoke_needed;
 
   /* request */
   Object* obj;        /* not used for ClassType.InvokeMethod */
   Object* thread;
   Class* class_;
   Method* method;
-  uint32_t numArgs;
-  uint64_t* argArray;   /* will be NULL if numArgs==0 */
+  uint32_t num_args;
+  uint64_t* arg_array;   /* will be NULL if numArgs==0 */
   uint32_t options;
 
   /* result */
-  JDWP::JdwpError err;
-  uint8_t resultTag;
-  JValue resultValue;
-  JDWP::ObjectId exceptObj;
+  JDWP::JdwpError error;
+  uint8_t result_tag;
+  JValue result_value;
+  JDWP::ObjectId exception;
 
   /* condition variable to wait on while the method executes */
   Mutex lock_;
@@ -73,6 +76,9 @@
   // Return the DebugInvokeReq for the current thread.
   static DebugInvokeReq* GetInvokeReq();
 
+  static Thread* GetDebugThread();
+  static void ClearWaitForEventThread();
+
   /*
    * Enable/disable breakpoints and step modes.  Used to provide a heads-up
    * when the debugger attaches.
@@ -182,7 +188,7 @@
   static bool GetThreadFrame(JDWP::ObjectId threadId, int num, JDWP::FrameId* pFrameId, JDWP::JdwpLocation* pLoc);
 
   static JDWP::ObjectId GetThreadSelfId();
-  static void SuspendVM(bool isEvent);
+  static void SuspendVM();
   static void ResumeVM();
   static void SuspendThread(JDWP::ObjectId threadId);
   static void ResumeThread(JDWP::ObjectId threadId);
diff --git a/src/jdwp/jdwp.h b/src/jdwp/jdwp.h
index cd2bfa7..a590c27 100644
--- a/src/jdwp/jdwp.h
+++ b/src/jdwp/jdwp.h
@@ -31,6 +31,8 @@
 
 namespace art {
 
+struct Thread;
+
 namespace JDWP {
 
 /*
@@ -118,11 +120,10 @@
    */
   bool IsActive();
 
-  /*
-   * Return the debugger thread's handle, or 0 if the debugger thread isn't
-   * running.
+  /**
+   * Returns the Thread* for the JDWP daemon thread.
    */
-  pthread_t GetDebugThread();
+  Thread* GetDebugThread();
 
   /*
    * Get time, in milliseconds, since the last debugger activity.
@@ -247,7 +248,8 @@
   ConditionVariable thread_start_cond_;
 
   volatile int32_t debug_thread_started_;
-  pthread_t debugThreadHandle;
+  pthread_t pthread_;
+  Thread* thread_;
 public: // TODO: fix privacy
   ObjectId debugThreadId;
 private:
diff --git a/src/jdwp/jdwp_event.cc b/src/jdwp/jdwp_event.cc
index 835b182..c6bdddb 100644
--- a/src/jdwp/jdwp_event.cc
+++ b/src/jdwp/jdwp_event.cc
@@ -539,7 +539,7 @@
   }
 
   if (suspendPolicy == SP_ALL) {
-    Dbg::SuspendVM(true);
+    Dbg::SuspendVM();
   } else {
     CHECK_EQ(suspendPolicy, SP_EVENT_THREAD);
   }
@@ -560,7 +560,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->invokeNeeded) {
+    if (!pReq->invoke_needed) {
       /*LOGD("suspendByPolicy: no invoke needed");*/
       break;
     }
@@ -568,14 +568,14 @@
     /* grab this before posting/suspending again */
     state->SetWaitForEventThread(Dbg::GetThreadSelfId());
 
-    /* leave pReq->invokeNeeded raised so we can check reentrancy */
+    /* leave pReq->invoke_needed raised so we can check reentrancy */
     LOG(VERBOSE) << "invoking method...";
     Dbg::ExecuteMethod(pReq);
 
-    pReq->err = ERR_NONE;
+    pReq->error = ERR_NONE;
 
     /* clear this before signaling */
-    pReq->invokeNeeded = false;
+    pReq->invoke_needed = false;
 
     LOG(VERBOSE) << "invoke complete, signaling and self-suspending";
     MutexLock mu(pReq->lock_);
@@ -587,12 +587,12 @@
  * Determine if there is a method invocation in progress in the current
  * thread.
  *
- * We look at the "invokeNeeded" flag in the per-thread DebugInvokeReq
+ * We look at the "invoke_needed" flag in the per-thread DebugInvokeReq
  * state.  If set, we're in the process of invoking a method.
  */
 static bool invokeInProgress(JdwpState* state) {
   DebugInvokeReq* pReq = Dbg::GetInvokeReq();
-  return pReq->invokeNeeded;
+  return pReq->invoke_needed;
 }
 
 /*
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index 5e566ae..7003151 100644
--- a/src/jdwp/jdwp_handler.cc
+++ b/src/jdwp/jdwp_handler.cc
@@ -310,7 +310,7 @@
  * This needs to increment the "suspend count" on all threads.
  */
 static JdwpError handleVM_Suspend(JdwpState* state, const uint8_t* buf, int dataLen, ExpandBuf* pReply) {
-  Dbg::SuspendVM(false);
+  Dbg::SuspendVM();
   return ERR_NONE;
 }
 
diff --git a/src/jdwp/jdwp_main.cc b/src/jdwp/jdwp_main.cc
index a7857a1..1df0d66 100644
--- a/src/jdwp/jdwp_main.cc
+++ b/src/jdwp/jdwp_main.cc
@@ -151,7 +151,7 @@
    * We have bound to a port, or are trying to connect outbound to a
    * debugger.  Create the JDWP thread and let it continue the mission.
    */
-  CHECK_PTHREAD_CALL(pthread_create, (&state->debugThreadHandle, NULL, StartJdwpThread, state), "JDWP thread");
+  CHECK_PTHREAD_CALL(pthread_create, (&state->pthread_, NULL, StartJdwpThread, state), "JDWP thread");
 
   /*
    * Wait until the thread finishes basic initialization.
@@ -238,7 +238,7 @@
     if (debug_thread_started_) {
       run = false;
       void* threadReturn;
-      if (pthread_join(debugThreadHandle, &threadReturn) != 0) {
+      if (pthread_join(pthread_, &threadReturn) != 0) {
         LOG(WARNING) << "JDWP thread join failed";
       }
     }
@@ -281,7 +281,7 @@
    * Finish initializing, then notify the creating thread that
    * we're running.
    */
-  debugThreadHandle = pthread_self();
+  thread_ = Thread::Current();
   run = true;
   android_atomic_release_store(true, &debug_thread_started_);
 
@@ -387,8 +387,8 @@
   runtime->DetachCurrentThread();
 }
 
-pthread_t JdwpState::GetDebugThread() {
-  return debugThreadHandle;
+Thread* JdwpState::GetDebugThread() {
+  return thread_;
 }
 
 /*
diff --git a/src/thread.cc b/src/thread.cc
index f4ca2ab..b8dbd6b 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -731,7 +731,8 @@
       class_loader_override_(NULL),
       long_jump_context_(NULL),
       throwing_OutOfMemoryError_(false),
-      pre_allocated_OutOfMemoryError_(NULL) {
+      pre_allocated_OutOfMemoryError_(NULL),
+      debug_invoke_req_(new DebugInvokeReq) {
   CHECK_EQ((sizeof(Thread) % 4), 0U) << sizeof(Thread);
 }
 
@@ -774,6 +775,8 @@
   delete wait_mutex_;
 
   delete long_jump_context_;
+
+  delete debug_invoke_req_;
 }
 
 void Thread::HandleUncaughtExceptions() {
diff --git a/src/thread.h b/src/thread.h
index 8edeaf2..d81fd9a 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -43,6 +43,7 @@
 class ClassLinker;
 class ClassLoader;
 class Context;
+class DebugInvokeReq;
 class Method;
 class Monitor;
 class Object;
@@ -455,6 +456,10 @@
 
   void WalkStack(StackVisitor* visitor) const;
 
+  DebugInvokeReq* GetInvokeReq() {
+    return debug_invoke_req_;
+  }
+
  private:
   Thread();
   ~Thread();
@@ -583,6 +588,9 @@
 
   Throwable* pre_allocated_OutOfMemoryError_;
 
+  // JDWP invoke-during-breakpoint support.
+  DebugInvokeReq* debug_invoke_req_;
+
   // TLS key used to retrieve the VM thread object.
   static pthread_key_t pthread_key_self_;
 
diff --git a/src/thread_list.cc b/src/thread_list.cc
index dfce2a2..f386547 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -18,6 +18,8 @@
 
 #include <unistd.h>
 
+#include "debugger.h"
+
 namespace art {
 
 // TODO: merge with ThreadListLock?
@@ -113,23 +115,24 @@
   }
 }
 
-void ThreadList::SuspendAll() {
+void ThreadList::SuspendAll(bool for_debugger) {
   Thread* self = Thread::Current();
 
   // TODO: add another thread_suspend_lock_ to avoid GC/debugger races.
 
   if (verbose_) {
-    LOG(INFO) << *self << " SuspendAll starting...";
+    LOG(INFO) << *self << " SuspendAll starting..." << (for_debugger ? " (debugger)" : "");
   }
 
   ThreadListLocker locker(this);
+  Thread* debug_thread = Dbg::GetDebugThread();
 
   {
     // Increment everybody's suspend count (except our own).
     MutexLock mu(thread_suspend_count_lock_);
     for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
       Thread* thread = *it;
-      if (thread != self) {
+      if (thread != self && thread != debug_thread) {
         if (verbose_) {
           LOG(INFO) << "requesting thread suspend: " << *thread;
         }
@@ -154,7 +157,7 @@
    */
   for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
     Thread* thread = *it;
-    if (thread != self) {
+    if (thread != self && thread != debug_thread) {
       thread->WaitUntilSuspended();
       if (verbose_) {
         LOG(INFO) << "thread suspended: " << *thread;
@@ -193,12 +196,56 @@
   }
 }
 
+void ThreadList::SuspendSelfForDebugger() {
+  Thread* self = Thread::Current();
 
-void ThreadList::ResumeAll() {
+  // The debugger thread must not suspend itself due to debugger activity!
+  Thread* debug_thread = Dbg::GetDebugThread();
+  CHECK(debug_thread != NULL);
+  CHECK(self != debug_thread);
+
+  // Collisions with other suspends aren't really interesting. We want
+  // to ensure that we're the only one fiddling with the suspend count
+  // though.
+  ThreadListLocker locker(this);
+  MutexLock mu(thread_suspend_count_lock_);
+  ++self->suspend_count_;
+
+  // Suspend ourselves.
+  CHECK_GT(self->suspend_count_, 0);
+  self->SetState(Thread::kSuspended);
+  if (verbose_) {
+    LOG(INFO) << *self << " self-suspending (dbg)";
+  }
+
+  // Tell JDWP that we've completed suspension. The JDWP thread can't
+  // tell us to resume before we're fully asleep because we hold the
+  // suspend count lock.
+  Dbg::ClearWaitForEventThread();
+
+  while (self->suspend_count_ != 0) {
+    thread_suspend_count_cond_.Wait(thread_suspend_count_lock_);
+    if (self->suspend_count_ != 0) {
+      // The condition was signaled but we're still suspended. This
+      // can happen if the debugger lets go while a SIGQUIT thread
+      // dump event is pending (assuming SignalCatcher was resumed for
+      // just long enough to try to grab the thread-suspend lock).
+      LOG(DEBUG) << *self << " still suspended after undo "
+                 << "(suspend count=" << self->suspend_count_ << ")";
+    }
+  }
+  CHECK_EQ(self->suspend_count_, 0);
+  self->SetState(Thread::kRunnable);
+  if (verbose_) {
+    LOG(INFO) << *self << " self-reviving (dbg)";
+  }
+}
+
+void ThreadList::ResumeAll(bool for_debugger) {
   Thread* self = Thread::Current();
 
   if (verbose_) {
-    LOG(INFO) << *self << " ResumeAll starting";
+    LOG(INFO) << *self << " ResumeAll starting" << (for_debugger ? " (debugger)" : "");
   }
 
   // Decrement the suspend counts for all threads.  No need for atomic
@@ -206,10 +253,11 @@
   // We do need to hold the thread list because of JNI attaches.
   {
     ThreadListLocker locker(this);
+    Thread* debug_thread = Dbg::GetDebugThread();
     MutexLock mu(thread_suspend_count_lock_);
     for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
       Thread* thread = *it;
-      if (thread != self) {
+      if (thread != self && thread != debug_thread) {
         if (thread->suspend_count_ > 0) {
           --thread->suspend_count_;
         } else {
diff --git a/src/thread_list.h b/src/thread_list.h
index 3a20405..baa7dad 100644
--- a/src/thread_list.h
+++ b/src/thread_list.h
@@ -36,8 +36,9 @@
 
   // Thread suspension support.
   void FullSuspendCheck(Thread* thread);
-  void ResumeAll();
-  void SuspendAll();
+  void ResumeAll(bool for_debugger = false);
+  void SuspendAll(bool for_debugger = false);
+  void SuspendSelfForDebugger();
   void RunWhileSuspended(Thread* thread, void (*callback)(void*), void* arg);
 
   void Register();