More SIGQUIT handling, various Thread fixes.

Change-Id: I3233c300d1c838c2eee0ba9be6018b7fbd907386
diff --git a/src/check_jni.cc b/src/check_jni.cc
index 8a30279..dc58565 100644
--- a/src/check_jni.cc
+++ b/src/check_jni.cc
@@ -917,7 +917,7 @@
    * to "running" mode before doing the checks.
    */
   void checkInstance(InstanceKind kind, jobject java_object) {
-    const char* what;
+    const char* what = NULL;
     switch (kind) {
     case kClass:
       what = "jclass";
diff --git a/src/dex_file.cc b/src/dex_file.cc
index c8e7c70..ad0b3b9 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -215,7 +215,7 @@
     // Note that current_thread can be NULL if we're parsing the bootclasspath
     // during JNI_CreateJavaVM.
     Thread* current_thread = Thread::Current();
-    Thread::State old;
+    Thread::State old(Thread::kUnknown);
     if (current_thread != NULL) {
         old = current_thread->SetState(Thread::kNative);
     }
diff --git a/src/image_test.cc b/src/image_test.cc
index fd88b20..b205fe2 100644
--- a/src/image_test.cc
+++ b/src/image_test.cc
@@ -74,7 +74,8 @@
   // enable to display maps to debug boot_base and boot_limit checking problems below
   if (false) {
     const char* maps_file = "/proc/self/maps";
-    std::string contents = ReadFileToString(maps_file);
+    std::string contents;
+    CHECK(ReadFileToString(maps_file, &contents));
     LG << maps_file << ":\n" << contents;
   }
 
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 4cd809e..a7c7dc7 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -490,7 +490,8 @@
   CHECK_GE(args.version, JNI_VERSION_1_2);
 
   Runtime* runtime = reinterpret_cast<JavaVMExt*>(vm)->runtime;
-  return runtime->AttachCurrentThread(args.name, p_env, as_daemon) ? JNI_OK : JNI_ERR;
+  runtime->AttachCurrentThread(args.name, p_env, as_daemon);
+  return JNI_OK;
 }
 
 class SharedLibrary {
diff --git a/src/runtime.cc b/src/runtime.cc
index 3934571..bb3ead7 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -335,8 +335,8 @@
   if (!Thread::Startup()) {
     return false;
   }
-  Thread* current_thread = Thread::Attach(this);
-  thread_list_->Register(current_thread);
+
+  thread_list_->Register(Thread::Attach(this));
 
   class_linker_ = ClassLinker::Create(options->boot_class_path_, intern_table_, Heap::GetBootSpace());
 
@@ -420,18 +420,18 @@
   CHECK_EQ(sigprocmask(SIG_BLOCK, &sigset, NULL), 0);
 }
 
-bool Runtime::AttachCurrentThread(const char* name, JNIEnv** penv, bool as_daemon) {
+void Runtime::AttachCurrentThread(const char* name, JNIEnv** penv, bool as_daemon) {
   if (as_daemon) {
     UNIMPLEMENTED(WARNING) << "TODO: do something different for daemon threads";
   }
-  return Thread::Attach(instance_) != NULL;
+  Thread* t = Thread::Attach(instance_);
+  thread_list_->Register(t);
 }
 
-bool Runtime::DetachCurrentThread() {
+void Runtime::DetachCurrentThread() {
   Thread* self = Thread::Current();
   thread_list_->Unregister(self);
   delete self;
-  return true;
 }
 
 void Runtime::VisitRoots(Heap::RootVisitor* visitor, void* arg) const {
diff --git a/src/runtime.h b/src/runtime.h
index 4ae15bf..4e00ec1 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -79,12 +79,12 @@
   static void Abort(const char* file, int line);
 
   // Attaches the current native thread to the runtime.
-  bool AttachCurrentThread(const char* name, JNIEnv** jni_env, bool as_daemon);
+  void AttachCurrentThread(const char* name, JNIEnv** jni_env, bool as_daemon);
 
   void CallExitHook(jint status);
 
   // Detaches the current native thread from the runtime.
-  bool DetachCurrentThread();
+  void DetachCurrentThread();
 
   void DumpStatistics(std::ostream& os);
 
@@ -106,6 +106,10 @@
     return java_vm_;
   }
 
+  ThreadList* GetThreadList() const {
+    return thread_list_;
+  }
+
   void VisitRoots(Heap::RootVisitor* visitor, void* arg) const;
 
  private:
diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc
index 5979a0f..e2e2360 100644
--- a/src/signal_catcher.cc
+++ b/src/signal_catcher.cc
@@ -50,23 +50,31 @@
 void SignalCatcher::HandleSigQuit() {
   // TODO: suspend all threads
 
-  std::stringstream buffer;
-  buffer << "\n"
-         << "\n"
-         << "----- pid " << getpid() << " at " << GetIsoDate() << " -----\n"
-         << "Cmd line: " << ReadFileToString("/proc/self/cmdline") << "\n";
+  std::stringstream os;
+  os << "\n"
+     << "\n"
+     << "----- pid " << getpid() << " at " << GetIsoDate() << " -----\n";
 
-  Runtime::Current()->DumpStatistics(buffer);
+  std::string cmdline;
+  if (ReadFileToString("/proc/self/cmdline", &cmdline)) {
+    std::replace(cmdline.begin(), cmdline.end(), '\0', ' ');
+    os << "Cmd line: " << cmdline << "\n";
+  }
 
-  // TODO: dump all threads.
-  // dvmDumpAllThreadsEx(&target, true);
+  Runtime* runtime = Runtime::Current();
+  runtime->DumpStatistics(os);
+  runtime->GetThreadList()->Dump(os);
 
-  buffer << "/proc/self/maps:\n" << ReadFileToString("/proc/self/maps");
-  buffer << "----- end " << getpid() << " -----";
+  std::string maps;
+  if (ReadFileToString("/proc/self/maps", &maps)) {
+    os << "/proc/self/maps:\n" << maps;
+  }
+
+  os << "----- end " << getpid() << " -----";
 
   // TODO: resume all threads
 
-  LOG(INFO) << buffer.str();
+  LOG(INFO) << os.str();
 }
 
 void SignalCatcher::HandleSigUsr1() {
@@ -93,12 +101,10 @@
 }
 
 void* SignalCatcher::Run(void*) {
-  CHECK(Runtime::Current()->AttachCurrentThread("Signal Catcher", NULL, true));
+  Runtime::Current()->AttachCurrentThread("Signal Catcher", NULL, true);
   Thread* self = Thread::Current();
   CHECK(self != NULL);
 
-  LOG(INFO) << "Signal catcher thread started " << *self;
-
   // Set up mask with signals we want to handle.
   sigset_t mask;
   sigemptyset(&mask);
@@ -108,6 +114,7 @@
   while (true) {
     int signal_number = WaitForSignal(self, mask);
     if (halt_) {
+      Runtime::Current()->DetachCurrentThread();
       return NULL;
     }
 
diff --git a/src/thread.cc b/src/thread.cc
index 7f48fa1..c0e986c 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -206,6 +206,7 @@
   thread->InitCpu();
 
   thread->handle_ = pthread_self();
+  thread->tid_ = gettid();
 
   thread->state_ = kRunnable;
 
@@ -220,11 +221,151 @@
 }
 
 void Thread::Dump(std::ostream& os) const {
-  os << "UNIMPLEMENTED: Thread::Dump\n";
+  /*
+   * Get the java.lang.Thread object.  This function gets called from
+   * some weird debug contexts, so it's possible that there's a GC in
+   * progress on some other thread.  To decrease the chances of the
+   * thread object being moved out from under us, we add the reference
+   * to the tracked allocation list, which pins it in place.
+   *
+   * If threadObj is NULL, the thread is still in the process of being
+   * attached to the VM, and there's really nothing interesting to
+   * say about it yet.
+   */
+  os << "TODO: pin Thread before dumping\n";
+#if 0
+  if (java_thread_ == NULL) {
+    LOGI("Can't dump thread %d: threadObj not set", threadId);
+    return;
+  }
+  dvmAddTrackedAlloc(java_thread_, NULL);
+#endif
+
+  DumpState(os);
+  DumpStack(os);
+
+#if 0
+  dvmReleaseTrackedAlloc(java_thread_, NULL);
+#endif
 }
 
-pid_t Thread::GetTid() const {
-  return gettid();
+std::string GetSchedulerGroup(pid_t tid) {
+  // /proc/<pid>/group looks like this:
+  // 2:devices:/
+  // 1:cpuacct,cpu:/
+  // We want the third field from the line whose second field contains the "cpu" token.
+  std::string cgroup_file;
+  if (!ReadFileToString("/proc/self/cgroup", &cgroup_file)) {
+    return "";
+  }
+  std::vector<std::string> cgroup_lines;
+  Split(cgroup_file, '\n', cgroup_lines);
+  for (size_t i = 0; i < cgroup_lines.size(); ++i) {
+    std::vector<std::string> cgroup_fields;
+    Split(cgroup_lines[i], ':', cgroup_fields);
+    std::vector<std::string> cgroups;
+    Split(cgroup_fields[1], ',', cgroups);
+    for (size_t i = 0; i < cgroups.size(); ++i) {
+      if (cgroups[i] == "cpu") {
+        return cgroup_fields[2].substr(1); // Skip the leading slash.
+      }
+    }
+  }
+  return "";
+}
+
+void Thread::DumpState(std::ostream& os) const {
+  std::string thread_name("unknown");
+  int priority = -1;
+  bool is_daemon = false;
+#if 0 // TODO
+  nameStr = (StringObject*) dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_name);
+  threadName = dvmCreateCstrFromString(nameStr);
+  priority = dvmGetFieldInt(threadObj, gDvm.offJavaLangThread_priority);
+  is_daemon = dvmGetFieldBoolean(threadObj, gDvm.offJavaLangThread_daemon);
+#else
+  thread_name = "TODO";
+  priority = -1;
+  is_daemon = false;
+#endif
+
+  int policy;
+  sched_param sp;
+  errno = pthread_getschedparam(handle_, &policy, &sp);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_getschedparam failed";
+  }
+
+  std::string scheduler_group(GetSchedulerGroup(GetTid()));
+  if (scheduler_group.empty()) {
+    scheduler_group = "default";
+  }
+
+  std::string group_name("(null; initializing?)");
+#if 0
+  groupObj = (Object*) dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
+  if (groupObj != NULL) {
+    nameStr = (StringObject*) dvmGetFieldObject(groupObj, gDvm.offJavaLangThreadGroup_name);
+    groupName = dvmCreateCstrFromString(nameStr);
+  }
+#else
+  group_name = "TODO";
+#endif
+
+  os << '"' << thread_name << '"';
+  if (is_daemon) {
+    os << " daemon";
+  }
+  os << " prio=" << priority
+     << " tid=" << GetId()
+     << " " << state_ << "\n";
+
+  int suspend_count = 0; // TODO
+  int debug_suspend_count = 0; // TODO
+  void* java_thread_ = NULL; // TODO
+  os << "  | group=\"" << group_name << "\""
+     << " sCount=" << suspend_count
+     << " dsCount=" << debug_suspend_count
+     << " obj=" << reinterpret_cast<void*>(java_thread_)
+     << " self=" << reinterpret_cast<const void*>(this) << "\n";
+  os << "  | sysTid=" << GetTid()
+     << " nice=" << getpriority(PRIO_PROCESS, GetTid())
+     << " sched=" << policy << "/" << sp.sched_priority
+     << " cgrp=" << scheduler_group
+     << " handle=" << GetImpl() << "\n";
+
+  // Grab the scheduler stats for this thread.
+  std::string scheduler_stats;
+  if (ReadFileToString(StringPrintf("/proc/self/task/%d/schedstat", GetTid()).c_str(), &scheduler_stats)) {
+    scheduler_stats.resize(scheduler_stats.size() - 1); // Lose the trailing '\n'.
+  } else {
+    scheduler_stats = "0 0 0";
+  }
+
+  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);
+  }
+
+  os << "  | schedstat=( " << scheduler_stats << " )"
+     << " utm=" << utime
+     << " stm=" << stime
+     << " core=" << task_cpu
+     << " HZ=" << sysconf(_SC_CLK_TCK) << "\n";
+}
+
+void Thread::DumpStack(std::ostream& os) const {
+  os << "UNIMPLEMENTED: Thread::DumpStack\n";
 }
 
 static void ThreadExitCheck(void* arg) {
@@ -589,13 +730,24 @@
   return find(list_.begin(), list_.end(), thread) != list_.end();
 }
 
+void ThreadList::Dump(std::ostream& os) {
+  MutexLock mu(lock_);
+  os << "DALVIK THREADS (" << list_.size() << "):\n";
+  typedef std::list<Thread*>::const_iterator It; // TODO: C++0x auto
+  for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
+    (*it)->Dump(os);
+  }
+}
+
 void ThreadList::Register(Thread* thread) {
+  //LOG(INFO) << "ThreadList::Register() " << *thread;
   MutexLock mu(lock_);
   CHECK(!Contains(thread));
   list_.push_front(thread);
 }
 
 void ThreadList::Unregister(Thread* thread) {
+  //LOG(INFO) << "ThreadList::Unregister() " << *thread;
   MutexLock mu(lock_);
   CHECK(Contains(thread));
   list_.remove(thread);
diff --git a/src/thread.h b/src/thread.h
index 87cfb6c..2e2444f 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -256,7 +256,9 @@
     return id_;
   }
 
-  pid_t GetTid() const;
+  pid_t GetTid() const {
+    return tid_;
+  }
 
   pthread_t GetImpl() const {
     return handle_;
@@ -442,6 +444,9 @@
   ~Thread();
   friend class Runtime;  // For ~Thread.
 
+  void DumpState(std::ostream& os) const;
+  void DumpStack(std::ostream& os) const;
+
   void InitCpu();
   void InitFunctionPointers();
 
@@ -450,6 +455,12 @@
   // Managed thread id.
   uint32_t id_;
 
+  // System thread id.
+  pid_t tid_;
+
+  // Native thread handle.
+  pthread_t handle_;
+
   // FIXME: placeholder for the gc cardTable
   uint32_t card_table_;
 
@@ -470,9 +481,6 @@
 
   State state_;
 
-  // Native thread handle.
-  pthread_t handle_;
-
   // Initialized to "this". On certain architectures (such as x86) reading
   // off of Thread::Current is easy but getting the address of Thread::Current
   // is hard. This field can be read off of Thread::Current to give the address.
@@ -515,6 +523,8 @@
 
   ~ThreadList();
 
+  void Dump(std::ostream& os);
+
   void Register(Thread* thread);
 
   void Unregister(Thread* thread);
diff --git a/src/utils.cc b/src/utils.cc
index f957580..3be3fa6 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -9,21 +9,23 @@
 
 namespace art {
 
-std::string ReadFileToString(const char* file_name) {
-  UniquePtr<File> file(OS::OpenFile(file_name, false));
-  CHECK(file.get() != NULL);
+bool ReadFileToString(const std::string& file_name, std::string* result) {
+  UniquePtr<File> file(OS::OpenFile(file_name.c_str(), false));
+  if (file.get() == NULL) {
+    return false;
+  }
 
-  std::string contents;
   char buf[8 * KB];
   while (true) {
     int64_t n = file->Read(buf, sizeof(buf));
-    CHECK_NE(-1, n);
-    if (n == 0) {
-        break;
+    if (n == -1) {
+      return false;
     }
-    contents.append(buf, n);
+    if (n == 0) {
+      return true;
+    }
+    result->append(buf, n);
   }
-  return contents;
 }
 
 std::string GetIsoDate() {
diff --git a/src/utils.h b/src/utils.h
index 3a6f404..4c2a7f9 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -168,7 +168,7 @@
 // Returns the JNI native function name for the overloaded method 'm'.
 std::string JniLongName(const Method* m);
 
-std::string ReadFileToString(const char* file_name);
+bool ReadFileToString(const std::string& file_name, std::string* result);
 
 // Returns the current date in ISO yyyy-mm-dd hh:mm:ss format.
 std::string GetIsoDate();