Makes objects known to the debugger GC roots, implements the THST message, and lets DDMS request stack traces.

This fills out correct data in all columns of the "Threads" table, and
double-clicking on a thread shows that thread's stack.

Change-Id: I48f63c3612e12d35269158dc3a283f07db28c8e7
diff --git a/src/dalvik_system_VMStack.cc b/src/dalvik_system_VMStack.cc
index d796e69..5a242f9 100644
--- a/src/dalvik_system_VMStack.cc
+++ b/src/dalvik_system_VMStack.cc
@@ -25,38 +25,10 @@
 
 namespace {
 
-class StackGetter {
- public:
-  StackGetter(JNIEnv* env, Thread* thread) : env_(env), thread_(thread), trace_(NULL) {
-  }
-
-  static void Callback(void* arg) {
-    reinterpret_cast<StackGetter*>(arg)->Callback();
-  }
-
-  jobject GetTrace() {
-    return trace_;
-  }
-
- private:
-  void Callback() {
-    trace_ = thread_->CreateInternalStackTrace(env_);
-  }
-
-  JNIEnv* env_;
-  Thread* thread_;
-  jobject trace_;
-};
-
-jobject GetThreadStack(JNIEnv* env, jobject javaThread) {
+static jobject GetThreadStack(JNIEnv* env, jobject javaThread) {
+  ThreadListLock thread_list_lock;
   Thread* thread = Thread::FromManagedThread(env, javaThread);
-  if (thread == NULL) {
-    return NULL;
-  }
-  ThreadList* thread_list = Runtime::Current()->GetThreadList();
-  StackGetter stack_getter(env, thread);
-  thread_list->RunWhileSuspended(thread, StackGetter::Callback, &stack_getter);
-  return stack_getter.GetTrace();
+  return (thread != NULL) ? GetThreadStack(env, thread) : NULL;
 }
 
 jint VMStack_fillStackTraceElements(JNIEnv* env, jclass, jobject javaThread, jobjectArray javaSteArray) {
diff --git a/src/debugger.cc b/src/debugger.cc
index df32a51..2d9924d 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -50,6 +50,14 @@
     return map_.find(id) != map_.end();
   }
 
+  void VisitRoots(Heap::RootVisitor* visitor, void* arg) {
+    MutexLock mu(lock_);
+    typedef std::map<JDWP::ObjectId, Object*>::iterator It; // C++0x auto
+    for (It it = map_.begin(); it != map_.end(); ++it) {
+      visitor(it->second, arg);
+    }
+  }
+
  private:
   Mutex lock_;
   std::map<JDWP::ObjectId, Object*> map_;
@@ -285,6 +293,12 @@
   UNIMPLEMENTED(FATAL);
 }
 
+void Dbg::VisitRoots(Heap::RootVisitor* visitor, void* arg) {
+  if (gRegistry != NULL) {
+    gRegistry->VisitRoots(visitor, arg);
+  }
+}
+
 const char* Dbg::GetClassDescriptor(JDWP::RefTypeId id) {
   UNIMPLEMENTED(FATAL);
   return NULL;
@@ -781,7 +795,7 @@
   }
 }
 
-void DdmSendThreadStartCallback(Thread* t) {
+void DdmSendThreadStartCallback(Thread* t, void*) {
   DdmSendThreadNotification(t, true);
 }
 
@@ -793,7 +807,7 @@
 
   gDdmThreadNotification = enable;
   if (enable) {
-    Runtime::Current()->GetThreadList()->ForEach(DdmSendThreadStartCallback);
+    Runtime::Current()->GetThreadList()->ForEach(DdmSendThreadStartCallback, NULL);
   }
 }
 
diff --git a/src/debugger.h b/src/debugger.h
index 111282c..270b62a 100644
--- a/src/debugger.h
+++ b/src/debugger.h
@@ -116,6 +116,8 @@
   // The debugger wants the VM to exit.
   static void Exit(int status);
 
+  static void VisitRoots(Heap::RootVisitor* visitor, void* arg);
+
   /*
    * Class, Object, Array
    */
diff --git a/src/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/src/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index fab4968..4730127 100644
--- a/src/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/src/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -16,6 +16,8 @@
 
 #include "debugger.h"
 #include "logging.h"
+#include "stack.h"
+#include "thread_list.h"
 
 #include "JniConstants.h"  // Last to avoid problems with LOG redefinition.
 #include "ScopedPrimitiveArray.h"  // Last to avoid problems with LOG redefinition.
@@ -47,23 +49,108 @@
   //return (gDvm.allocRecords != NULL);
 }
 
+static Thread* FindThreadByThinLockId(uint32_t thin_lock_id) {
+  struct ThreadFinder {
+    ThreadFinder(uint32_t thin_lock_id) : thin_lock_id(thin_lock_id), thread(NULL) {
+    }
+
+    static void Callback(Thread* t, void* context) {
+      ThreadFinder* finder = reinterpret_cast<ThreadFinder*>(context);
+      if (t->GetThinLockId() == finder->thin_lock_id) {
+        finder->thread = t;
+      }
+    }
+
+    uint32_t thin_lock_id;
+    Thread* thread;
+  };
+  ThreadFinder finder(thin_lock_id);
+  Runtime::Current()->GetThreadList()->ForEach(ThreadFinder::Callback, &finder);
+  return finder.thread;
+}
+
 /*
  * Get a stack trace as an array of StackTraceElement objects.  Returns
  * NULL on failure, e.g. if the threadId couldn't be found.
  */
-static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint threadId) {
-  UNIMPLEMENTED(WARNING);
-  return NULL;
-  //ArrayObject* trace = dvmDdmGetStackTraceById(threadId);
-  //return reinterpret_cast<jobjectArray>(addLocalReference(env, trace));
+static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) {
+  ThreadListLock thread_list_lock;
+  Thread* thread = FindThreadByThinLockId(static_cast<uint32_t>(thin_lock_id));
+  if (thread == NULL) {
+    return NULL;
+  }
+  jobject stack = GetThreadStack(env, thread);
+  return (stack != NULL) ? Thread::InternalStackTraceToStackTraceElementArray(env, stack) : NULL;
+}
+
+static void ThreadCountCallback(Thread*, void* context) {
+  uint16_t& count = *reinterpret_cast<uint16_t*>(context);
+  ++count;
+}
+
+static const int kThstBytesPerEntry = 18;
+static const int kThstHeaderLen = 4;
+
+static void ThreadStatsGetterCallback(Thread* t, void* context) {
+  uint8_t** ptr = reinterpret_cast<uint8_t**>(context);
+  uint8_t* buf = *ptr;
+
+  /*
+   * Generate the contents of a THST chunk.  The data encompasses all known
+   * threads.
+   *
+   * Response has:
+   *  (1b) header len
+   *  (1b) bytes per entry
+   *  (2b) thread count
+   * Then, for each thread:
+   *  (4b) threadId
+   *  (1b) thread status
+   *  (4b) tid
+   *  (4b) utime
+   *  (4b) stime
+   *  (1b) is daemon?
+   *
+   * The length fields exist in anticipation of adding additional fields
+   * without wanting to break ddms or bump the full protocol version.  I don't
+   * think it warrants full versioning.  They might be extraneous and could
+   * be removed from a future version.
+   */
+  int utime, stime, task_cpu;
+  GetTaskStats(t->GetTid(), utime, stime, task_cpu);
+
+  JDWP::set4BE(buf+0, t->GetThinLockId());
+  JDWP::set1(buf+4, t->GetState());
+  JDWP::set4BE(buf+5, t->GetTid());
+  JDWP::set4BE(buf+9, utime);
+  JDWP::set4BE(buf+13, stime);
+  JDWP::set1(buf+17, t->IsDaemon());
+
+  *ptr += kThstBytesPerEntry;
 }
 
 static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) {
-  UNIMPLEMENTED(WARNING);
-  return NULL;
-  //ArrayObject* result = dvmDdmGenerateThreadStats();
-  //dvmReleaseTrackedAlloc(result, NULL);
-  //return reinterpret_cast<jbyteArray>(addLocalReference(env, result));
+  std::vector<uint8_t> bytes;
+  {
+    ThreadListLock thread_list_lock;
+    ThreadList* thread_list = Runtime::Current()->GetThreadList();
+
+    uint16_t thread_count;
+    thread_list->ForEach(ThreadCountCallback, &thread_count);
+
+    bytes.resize(kThstHeaderLen + thread_count * kThstBytesPerEntry);
+
+    JDWP::set1(&bytes[0], kThstHeaderLen);
+    JDWP::set1(&bytes[1], kThstBytesPerEntry);
+    JDWP::set2BE(&bytes[2], thread_count);
+
+    uint8_t* ptr = &bytes[kThstHeaderLen];
+    thread_list->ForEach(ThreadStatsGetterCallback, &ptr);
+  }
+
+  jbyteArray result = env->NewByteArray(bytes.size());
+  env->SetByteArrayRegion(result, 0, bytes.size(), reinterpret_cast<const jbyte*>(&bytes[0]));
+  return result;
 }
 
 static jint DdmVmInternal_heapInfoNotify(JNIEnv* env, jclass, jint when) {
diff --git a/src/runtime.cc b/src/runtime.cc
index 8672d29..9e8b28b 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -680,6 +680,7 @@
 }
 
 void Runtime::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
+  Dbg::VisitRoots(visitor, arg);
   class_linker_->VisitRoots(visitor, arg);
   intern_table_->VisitRoots(visitor, arg);
   java_vm_->VisitRoots(visitor, arg);
diff --git a/src/stack.cc b/src/stack.cc
index 985fd38..1e1f932 100644
--- a/src/stack.cc
+++ b/src/stack.cc
@@ -18,6 +18,7 @@
 
 #include "compiler.h"
 #include "object.h"
+#include "thread_list.h"
 
 int oatVRegOffsetFromMethod(art::Method* method, int reg);
 
@@ -68,4 +69,34 @@
   return *reinterpret_cast<Method**>(next_sp);
 }
 
+class StackGetter {
+ public:
+  StackGetter(JNIEnv* env, Thread* thread) : env_(env), thread_(thread), trace_(NULL) {
+  }
+
+  static void Callback(void* arg) {
+    reinterpret_cast<StackGetter*>(arg)->Callback();
+  }
+
+  jobject GetTrace() {
+    return trace_;
+  }
+
+ private:
+  void Callback() {
+    trace_ = thread_->CreateInternalStackTrace(env_);
+  }
+
+  JNIEnv* env_;
+  Thread* thread_;
+  jobject trace_;
+};
+
+jobject GetThreadStack(JNIEnv* env, Thread* thread) {
+  ThreadList* thread_list = Runtime::Current()->GetThreadList();
+  StackGetter stack_getter(env, thread);
+  thread_list->RunWhileSuspended(thread, StackGetter::Callback, &stack_getter);
+  return stack_getter.GetTrace();
+}
+
 }  // namespace art
diff --git a/src/stack.h b/src/stack.h
index ae413d2..f1fb08f 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -17,6 +17,7 @@
 #ifndef ART_SRC_STACK_H_
 #define ART_SRC_STACK_H_
 
+#include "jni.h"
 #include "macros.h"
 
 #include <stdint.h>
@@ -26,6 +27,8 @@
 class Method;
 class Thread;
 
+jobject GetThreadStack(JNIEnv*, Thread*);
+
 struct NativeToManagedRecord {
   NativeToManagedRecord* link_;
   void* last_top_of_managed_stack_;
diff --git a/src/thread.cc b/src/thread.cc
index d2c43d9..68acb9b 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -433,17 +433,7 @@
   int utime = 0;
   int stime = 0;
   int task_cpu = 0;
-  std::string stats;
-  if (ReadFileToString(StringPrintf("/proc/self/task/%d/stat", GetTid()).c_str(), &stats)) {
-    // Skip the command, which may contain spaces.
-    stats = stats.substr(stats.find(')') + 2);
-    // Extract the three fields we care about.
-    std::vector<std::string> fields;
-    Split(stats, ' ', fields);
-    utime = strtoull(fields[11].c_str(), NULL, 10);
-    stime = strtoull(fields[12].c_str(), NULL, 10);
-    task_cpu = strtoull(fields[36].c_str(), NULL, 10);
-  }
+  GetTaskStats(GetTid(), utime, stime, task_cpu);
 
   os << "  | schedstat=( " << scheduler_stats << " )"
      << " utm=" << utime
diff --git a/src/thread_list.cc b/src/thread_list.cc
index 875b14e..91fc0f2 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -194,6 +194,7 @@
 
 void ThreadList::Suspend(Thread* thread) {
   DCHECK(thread != Thread::Current());
+  thread_list_lock_.AssertHeld();
 
   // TODO: add another thread_suspend_lock_ to avoid GC/debugger races.
 
@@ -201,7 +202,6 @@
     LOG(INFO) << "Suspend(" << *thread << ") starting...";
   }
 
-  ThreadListLocker locker(this);
   if (!Contains(thread)) {
     return;
   }
@@ -303,13 +303,13 @@
 
 void ThreadList::Resume(Thread* thread) {
   DCHECK(thread != Thread::Current());
+  thread_list_lock_.AssertHeld();
 
   if (verbose_) {
     LOG(INFO) << "Resume(" << *thread << ") starting...";
   }
 
   {
-    ThreadListLocker locker(this);
     MutexLock mu(thread_suspend_count_lock_);
     if (!Contains(thread)) {
       return;
@@ -423,10 +423,10 @@
   thread_exit_cond_.Signal();
 }
 
-void ThreadList::ForEach(void (*callback)(Thread*)) {
+void ThreadList::ForEach(void (*callback)(Thread*, void*), void* context) {
   thread_list_lock_.AssertHeld();
   for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
-    callback(*it);
+    callback(*it, context);
   }
 }
 
diff --git a/src/thread_list.h b/src/thread_list.h
index d19622e..7bbd866 100644
--- a/src/thread_list.h
+++ b/src/thread_list.h
@@ -43,7 +43,7 @@
   void UndoDebuggerSuspensions();
 
   // Iterates over all the threads. The caller must hold the thread list lock.
-  void ForEach(void (*callback)(Thread*));
+  void ForEach(void (*callback)(Thread*, void*), void* context);
 
   void Register();
   void Unregister();
diff --git a/src/utils.cc b/src/utils.cc
index a74e231..639339a 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -504,6 +504,22 @@
 #endif
 }
 
+void GetTaskStats(pid_t tid, int& utime, int& stime, int& task_cpu) {
+  utime = stime = task_cpu = 0;
+  std::string stats;
+  if (!ReadFileToString(StringPrintf("/proc/self/task/%d/stat", GetTid()).c_str(), &stats)) {
+    return;
+  }
+  // Skip the command, which may contain spaces.
+  stats = stats.substr(stats.find(')') + 2);
+  // Extract the three fields we care about.
+  std::vector<std::string> fields;
+  Split(stats, ' ', fields);
+  utime = strtoull(fields[11].c_str(), NULL, 10);
+  stime = strtoull(fields[12].c_str(), NULL, 10);
+  task_cpu = strtoull(fields[36].c_str(), NULL, 10);
+}
+
 std::string GetArtCacheOrDie() {
   const char* data_root = getenv("ANDROID_DATA");
   if (data_root == NULL) {
diff --git a/src/utils.h b/src/utils.h
index 66cda54..bf8191e 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -219,6 +219,9 @@
 // Returns the calling thread's tid. (The C libraries don't expose this.)
 pid_t GetTid();
 
+// Reads data from "/proc/self/task/${tid}/stat".
+void GetTaskStats(pid_t tid, int& utime, int& stime, int& task_cpu);
+
 // Sets the name of the current thread. The name may be truncated to an
 // implementation-defined limit.
 void SetThreadName(const char* name);