Implement "GetThreadGroupParent", "Suspend", and "Resume".

This is enough to get the GUI jswat working for poking around
threads, stacks, and locals.

Change-Id: Ib02d9666cee8d39c09e4a09cf3961cebff1768ac
diff --git a/src/debugger.cc b/src/debugger.cc
index f4969dc..f46c402 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -993,8 +993,15 @@
 }
 
 JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId threadGroupId) {
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  Object* thread_group = gRegistry->Get<Object*>(threadGroupId);
+  CHECK(thread_group != NULL);
+
+  Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
+  CHECK(c != NULL);
+  Field* f = c->FindInstanceField("parent", "Ljava/lang/ThreadGroup;");
+  CHECK(f != NULL);
+  Object* parent = f->GetObject(thread_group);
+  return gRegistry->Add(parent);
 }
 
 static Object* GetStaticThreadGroup(const char* field_name) {
@@ -1176,11 +1183,25 @@
 }
 
 void Dbg::SuspendThread(JDWP::ObjectId threadId) {
-  UNIMPLEMENTED(FATAL);
+  Object* peer = gRegistry->Get<Object*>(threadId);
+  ScopedThreadListLock thread_list_lock;
+  Thread* thread = Thread::FromManagedThread(peer);
+  if (thread == NULL) {
+    LOG(WARNING) << "No such thread for suspend: " << peer;
+    return;
+  }
+  Runtime::Current()->GetThreadList()->Suspend(thread, true);
 }
 
 void Dbg::ResumeThread(JDWP::ObjectId threadId) {
-  UNIMPLEMENTED(FATAL);
+  Object* peer = gRegistry->Get<Object*>(threadId);
+  ScopedThreadListLock thread_list_lock;
+  Thread* thread = Thread::FromManagedThread(peer);
+  if (thread == NULL) {
+    LOG(WARNING) << "No such thread for resume: " << peer;
+    return;
+  }
+  Runtime::Current()->GetThreadList()->Resume(thread, true);
 }
 
 void Dbg::SuspendSelf() {
diff --git a/src/thread_list.cc b/src/thread_list.cc
index 9a762ac..2179697 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -201,14 +201,14 @@
   }
 }
 
-void ThreadList::Suspend(Thread* thread) {
+void ThreadList::Suspend(Thread* thread, bool for_debugger) {
   DCHECK(thread != Thread::Current());
   thread_list_lock_.AssertHeld();
 
   // TODO: add another thread_suspend_lock_ to avoid GC/debugger races.
 
   if (verbose_) {
-    LOG(INFO) << "Suspend(" << *thread << ") starting...";
+    LOG(INFO) << "Suspend(" << *thread << ") starting..." << (for_debugger ? " (debugger)" : "");
   }
 
   if (!Contains(thread)) {
@@ -217,7 +217,7 @@
 
   {
     MutexLock mu(thread_suspend_count_lock_);
-    ModifySuspendCount(thread, +1, false);
+    ModifySuspendCount(thread, +1, for_debugger);
   }
 
   thread->WaitUntilSuspended();
@@ -309,12 +309,12 @@
   }
 }
 
-void ThreadList::Resume(Thread* thread) {
+void ThreadList::Resume(Thread* thread, bool for_debugger) {
   DCHECK(thread != Thread::Current());
   thread_list_lock_.AssertHeld();
 
   if (verbose_) {
-    LOG(INFO) << "Resume(" << *thread << ") starting...";
+    LOG(INFO) << "Resume(" << *thread << ") starting..." << (for_debugger ? " (debugger)" : "");
   }
 
   {
@@ -322,7 +322,7 @@
     if (!Contains(thread)) {
       return;
     }
-    ModifySuspendCount(thread, -1, false);
+    ModifySuspendCount(thread, -1, for_debugger);
   }
 
   {
diff --git a/src/thread_list.h b/src/thread_list.h
index 57f9cf4..d93ec3b 100644
--- a/src/thread_list.h
+++ b/src/thread_list.h
@@ -37,9 +37,11 @@
   // Thread suspension support.
   void FullSuspendCheck(Thread* thread);
   void ResumeAll(bool for_debugger = false);
+  void Resume(Thread* thread, bool for_debugger = false);
+  void RunWhileSuspended(Thread* thread, void (*callback)(void*), void* arg);
   void SuspendAll(bool for_debugger = false);
   void SuspendSelfForDebugger();
-  void RunWhileSuspended(Thread* thread, void (*callback)(void*), void* arg);
+  void Suspend(Thread* thread, bool for_debugger = false);
   void UndoDebuggerSuspensions();
 
   // Iterates over all the threads. The caller must hold the thread list lock.
@@ -61,8 +63,6 @@
   uint32_t AllocThreadId();
   bool Contains(Thread* thread);
   void ReleaseThreadId(uint32_t id);
-  void Resume(Thread* thread);
-  void Suspend(Thread* thread);
   void SuspendAllDaemonThreads();
   void WaitForNonDaemonThreadsToExit();