Implement debugger support for the "threads" command.

Change-Id: I266d28ee00d87fdc7ecb179ddf52634db96e573c
diff --git a/src/debugger.cc b/src/debugger.cc
index 38346e7..7918bfb 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -476,7 +476,15 @@
 }
 
 void Dbg::GetObjectType(JDWP::ObjectId objectId, uint8_t* pRefTypeTag, JDWP::RefTypeId* pRefTypeId) {
-  UNIMPLEMENTED(FATAL);
+  Object* o = gRegistry->Get<Object*>(objectId);
+  if (o->GetClass()->IsArrayClass()) {
+    *pRefTypeTag = JDWP::TT_ARRAY;
+  } else if (o->GetClass()->IsInterface()) {
+    *pRefTypeTag = JDWP::TT_INTERFACE;
+  } else {
+    *pRefTypeTag = JDWP::TT_CLASS;
+  }
+  *pRefTypeId = gRegistry->Add(o->GetClass());
 }
 
 uint8_t Dbg::GetClassObjectType(JDWP::RefTypeId refTypeId) {
@@ -688,13 +696,28 @@
 }
 
 JDWP::ObjectId Dbg::GetThreadGroup(JDWP::ObjectId threadId) {
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  Object* thread = gRegistry->Get<Object*>(threadId);
+  CHECK(thread != NULL);
+
+  Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Thread;");
+  CHECK(c != NULL);
+  Field* f = c->FindInstanceField("group", "Ljava/lang/ThreadGroup;");
+  CHECK(f != NULL);
+  Object* group = f->GetObject(thread);
+  CHECK(group != NULL);
+  return gRegistry->Add(group);
 }
 
-char* Dbg::GetThreadGroupName(JDWP::ObjectId threadGroupId) {
-  UNIMPLEMENTED(FATAL);
-  return NULL;
+std::string Dbg::GetThreadGroupName(JDWP::ObjectId threadGroupId) {
+  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("name", "Ljava/lang/String;");
+  CHECK(f != NULL);
+  String* s = reinterpret_cast<String*>(f->GetObject(thread_group));
+  return s->ToModifiedUtf8();
 }
 
 JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId threadGroupId) {
@@ -702,19 +725,50 @@
   return 0;
 }
 
+static Object* GetStaticThreadGroup(const char* field_name) {
+  Class* c = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/ThreadGroup;");
+  CHECK(c != NULL);
+  Field* f = c->FindStaticField(field_name, "Ljava/lang/ThreadGroup;");
+  CHECK(f != NULL);
+  Object* group = f->GetObject(NULL);
+  CHECK(group != NULL);
+  return group;
+}
+
 JDWP::ObjectId Dbg::GetSystemThreadGroupId() {
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return gRegistry->Add(GetStaticThreadGroup("mSystem"));
 }
 
 JDWP::ObjectId Dbg::GetMainThreadGroupId() {
-  UNIMPLEMENTED(FATAL);
-  return 0;
+  return gRegistry->Add(GetStaticThreadGroup("mMain"));
 }
 
-bool Dbg::GetThreadStatus(JDWP::ObjectId threadId, uint32_t* threadStatus, uint32_t* suspendStatus) {
-  UNIMPLEMENTED(FATAL);
-  return false;
+bool Dbg::GetThreadStatus(JDWP::ObjectId threadId, uint32_t* pThreadStatus, uint32_t* pSuspendStatus) {
+  ScopedThreadListLock thread_list_lock;
+
+  Thread* thread = DecodeThread(threadId);
+  if (thread == NULL) {
+    return false;
+  }
+
+  switch (thread->GetState()) {
+  case Thread::kTerminated:   *pThreadStatus = JDWP::TS_ZOMBIE;   break;
+  case Thread::kRunnable:     *pThreadStatus = JDWP::TS_RUNNING;  break;
+  case Thread::kTimedWaiting: *pThreadStatus = JDWP::TS_SLEEPING; break;
+  case Thread::kBlocked:      *pThreadStatus = JDWP::TS_MONITOR;  break;
+  case Thread::kWaiting:      *pThreadStatus = JDWP::TS_WAIT;     break;
+  case Thread::kInitializing: *pThreadStatus = JDWP::TS_ZOMBIE;   break;
+  case Thread::kStarting:     *pThreadStatus = JDWP::TS_ZOMBIE;   break;
+  case Thread::kNative:       *pThreadStatus = JDWP::TS_RUNNING;  break;
+  case Thread::kVmWait:       *pThreadStatus = JDWP::TS_WAIT;     break;
+  case Thread::kSuspended:    *pThreadStatus = JDWP::TS_RUNNING;  break;
+  default:
+    LOG(FATAL) << "unknown thread state " << thread->GetState();
+  }
+
+  *pSuspendStatus = (thread->IsSuspended() ? JDWP::SUSPEND_STATUS_SUSPENDED : 0);
+
+  return true;
 }
 
 uint32_t Dbg::GetThreadSuspendCount(JDWP::ObjectId threadId) {
diff --git a/src/debugger.h b/src/debugger.h
index ca1d2a0..ca39316 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -178,7 +178,7 @@
    */
   static bool GetThreadName(JDWP::ObjectId threadId, std::string& name);
   static JDWP::ObjectId GetThreadGroup(JDWP::ObjectId threadId);
-  static char* GetThreadGroupName(JDWP::ObjectId threadGroupId);
+  static std::string GetThreadGroupName(JDWP::ObjectId threadGroupId);
   static JDWP::ObjectId GetThreadGroupParent(JDWP::ObjectId threadGroupId);
   static JDWP::ObjectId GetSystemThreadGroupId();
   static JDWP::ObjectId GetMainThreadGroupId();
diff --git a/src/jdwp/jdwp_handler.cc b/src/jdwp/jdwp_handler.cc
index 9b18c14..84e1afc 100644
--- a/src/jdwp/jdwp_handler.cc
+++ b/src/jdwp/jdwp_handler.cc
@@ -1082,15 +1082,7 @@
   ObjectId threadGroupId = ReadObjectId(&buf);
   LOG(VERBOSE) << StringPrintf("  Req for name of threadGroupId=0x%llx", threadGroupId);
 
-  char* name = Dbg::GetThreadGroupName(threadGroupId);
-  if (name != NULL) {
-    expandBufAddUtf8String(pReply, name);
-  } else {
-    expandBufAddUtf8String(pReply, "BAD-GROUP-ID");
-    LOG(VERBOSE) << StringPrintf("bad thread group ID");
-  }
-
-  free(name);
+  expandBufAddUtf8String(pReply, Dbg::GetThreadGroupName(threadGroupId).c_str());
 
   return ERR_NONE;
 }
diff --git a/src/thread.h b/src/thread.h
index dca983f..823b72c 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -68,12 +68,12 @@
     kMaxPriority = 10,
   };
   enum State {
-    // These match up with JDWP values.
-    kTerminated   = 0,        // TERMINATED
-    kRunnable     = 1,        // RUNNABLE or running now
-    kTimedWaiting = 2,        // TIMED_WAITING in Object.wait()
-    kBlocked      = 3,        // BLOCKED on a monitor
-    kWaiting      = 4,        // WAITING in Object.wait()
+    // These correspond to JDWP states (but needn't share the same values).
+    kTerminated   = 0,        // TS_ZOMBIE
+    kRunnable     = 1,        // TS_RUNNING
+    kTimedWaiting = 2,        // TS_SLEEPING in Object.wait()
+    kBlocked      = 3,        // TS_MONITOR on a monitor
+    kWaiting      = 4,        // TS_WAIT in Object.wait()
     // Non-JDWP states.
     kInitializing = 5,        // allocated, not yet running --- TODO: unnecessary?
     kStarting     = 6,        // native thread started, not yet ready to run managed code