Add the signal catcher (for SIGQUIT and SIGUSR1).

Also fix some of the thread implementation.

Change-Id: If2d1b59a149ba1ac192ad9bc74319c8dff228549
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 04a17cf..3907d07 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1920,4 +1920,9 @@
   return resolved;
 }
 
+size_t ClassLinker::NumLoadedClasses() const {
+  MutexLock mu(classes_lock_);
+  return classes_.size();
+}
+
 }  // namespace art
diff --git a/src/class_linker.h b/src/class_linker.h
index cb21cb1..c848825 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -38,6 +38,8 @@
     return FindClass(descriptor, NULL);
   }
 
+  size_t NumLoadedClasses() const;
+
   // Resolve a String with the given ID from the DexFile, storing the
   // result in the DexCache.
   String* ResolveString(const DexFile& dex_file,
diff --git a/src/runtime.cc b/src/runtime.cc
index 80914db..2443cc6 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -12,6 +12,7 @@
 #include "heap.h"
 #include "jni_internal.h"
 #include "scoped_ptr.h"
+#include "signal_catcher.h"
 #include "thread.h"
 
 namespace art {
@@ -22,6 +23,7 @@
   // TODO: use smart pointers instead. (we'll need the pimpl idiom.)
   delete class_linker_;
   Heap::Destroy();
+  delete signal_catcher_;
   delete thread_list_;
   delete java_vm_;
   Thread::Shutdown();
@@ -321,6 +323,8 @@
   LoadJniLibrary(instance_->GetJavaVM(), "javacore");
   self->SetState(old_state);
 
+  instance_->signal_catcher_ = new SignalCatcher;
+
   return instance_;
 }
 
@@ -360,6 +364,18 @@
   return true;
 }
 
+void Runtime::DumpStatistics(std::ostream& os) {
+  // TODO: dump other runtime statistics?
+  os << "Loaded classes: " << class_linker_->NumLoadedClasses() << "\n";
+  os << "Intern table size: " << class_linker_->GetInternTable().Size() << "\n";
+  // LOGV("VM stats: meth=%d ifld=%d sfld=%d linear=%d",
+  //    gDvm.numDeclaredMethods,
+  //    gDvm.numDeclaredInstFields,
+  //    gDvm.numDeclaredStaticFields,
+  //    gDvm.pBootLoaderAlloc->curOffset);
+  // LOGI("GC precise methods: %d", dvmPointerSetGetCount(gDvm.preciseMethods));
+}
+
 void Runtime::BlockSignals() {
   sigset_t sigset;
   if (sigemptyset(&sigset) == -1) {
diff --git a/src/runtime.h b/src/runtime.h
index b6873e3..eaf8ace 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -5,6 +5,7 @@
 
 #include <stdio.h>
 
+#include <iosfwd>
 #include <string>
 #include <utility>
 #include <vector>
@@ -23,6 +24,7 @@
 class DexFile;
 class Heap;
 class JavaVMExt;
+class SignalCatcher;
 class String;
 class ThreadList;
 
@@ -76,6 +78,8 @@
   // Detaches the current native thread from the runtime.
   bool DetachCurrentThread();
 
+  void DumpStatistics(std::ostream& os);
+
   ~Runtime();
 
   size_t GetStackSize() const {
@@ -106,6 +110,8 @@
 
   ClassLinker* class_linker_;
 
+  SignalCatcher* signal_catcher_;
+
   JavaVMExt* java_vm_;
 
   // Hooks supported by JNI_CreateJavaVM
diff --git a/src/signal_catcher.cc b/src/signal_catcher.cc
new file mode 100644
index 0000000..e6e423d
--- /dev/null
+++ b/src/signal_catcher.cc
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "signal_catcher.h"
+
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "heap.h"
+#include "thread.h"
+#include "utils.h"
+
+namespace art {
+
+bool SignalCatcher::halt_ = false;
+
+SignalCatcher::SignalCatcher() {
+  // Create a raw pthread; its start routine will attach to the runtime.
+  errno = pthread_create(&thread_, NULL, &Run, NULL);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_create failed for signal catcher thread";
+  }
+}
+
+SignalCatcher::~SignalCatcher() {
+  // Since we know the thread is just sitting around waiting for signals
+  // to arrive, send it one.
+  halt_ = true;
+  pthread_kill(thread_, SIGQUIT);
+  pthread_join(thread_, NULL);
+}
+
+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";
+
+  Runtime::Current()->DumpStatistics(buffer);
+
+  // TODO: dump all threads.
+  // dvmDumpAllThreadsEx(&target, true);
+
+  buffer << "/proc/self/maps:\n" << ReadFileToString("/proc/self/maps");
+  buffer << "----- end " << getpid() << " -----";
+
+  // TODO: resume all threads
+
+  LOG(INFO) << buffer.str();
+}
+
+void SignalCatcher::HandleSigUsr1() {
+  LOG(INFO) << "SIGUSR1 forcing GC (no HPROF)";
+  Heap::CollectGarbage();
+}
+
+void* SignalCatcher::Run(void*) {
+  CHECK(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);
+  sigaddset(&mask, SIGQUIT);
+  sigaddset(&mask, SIGUSR1);
+
+  while (true) {
+    self->SetState(Thread::kWaiting); // TODO: VMWAIT
+
+    // Signals for sigwait() must be blocked but not ignored.  We
+    // block signals like SIGQUIT for all threads, so the condition
+    // is met.  When the signal hits, we wake up, without any signal
+    // handlers being invoked.
+
+    // Sleep in sigwait() until a signal arrives. gdb causes EINTR failures.
+    int signal_number;
+    int rc = TEMP_FAILURE_RETRY(sigwait(&mask, &signal_number));
+    if (rc != 0) {
+      PLOG(FATAL) << "sigwait failed";
+    }
+
+    if (!halt_) {
+      LOG(INFO) << *self << ": reacting to signal " << signal_number;
+    }
+
+    // Set our status to runnable, self-suspending if GC in progress.
+    self->SetState(Thread::kRunnable);
+
+    if (halt_) {
+      return NULL;
+    }
+
+    switch (signal_number) {
+    case SIGQUIT:
+      HandleSigQuit();
+      break;
+    case SIGUSR1:
+      HandleSigUsr1();
+      break;
+    default:
+      LOG(ERROR) << "Unexpected signal %d" << signal_number;
+      break;
+    }
+  }
+}
+
+}  // namespace art
diff --git a/src/signal_catcher.h b/src/signal_catcher.h
new file mode 100644
index 0000000..1c08542
--- /dev/null
+++ b/src/signal_catcher.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_SIGNAL_CATCHER_H_
+#define ART_SRC_SIGNAL_CATCHER_H_
+
+#include <pthread.h>
+
+namespace art {
+
+class Runtime;
+class Thread;
+
+/*
+ * A thread that catches signals and does something useful. For
+ * example, when a SIGQUIT (Ctrl-\) arrives, we suspend and dump the
+ * status of all threads.
+ */
+class SignalCatcher {
+ public:
+  SignalCatcher();
+  ~SignalCatcher();
+
+ private:
+  static void* Run(void* arg);
+  static void HandleSigQuit();
+  static void HandleSigUsr1();
+
+  static bool halt_;
+  pthread_t thread_;
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_SIGNAL_CATCHER_H_
diff --git a/src/thread.cc b/src/thread.cc
index 40aaa7d..2f43e23 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -18,6 +18,18 @@
 
 namespace art {
 
+/* desktop Linux needs a little help with gettid() */
+#if !defined(HAVE_ANDROID_OS)
+#define __KERNEL__
+# include <linux/unistd.h>
+#ifdef _syscall0
+_syscall0(pid_t, gettid)
+#else
+pid_t gettid() { return syscall(__NR_gettid);}
+#endif
+#undef __KERNEL__
+#endif
+
 pthread_key_t Thread::pthread_key_self_;
 
 void Thread::InitFunctionPointers() {
@@ -126,33 +138,35 @@
 
 Thread* Thread::Create(const Runtime* runtime) {
   size_t stack_size = runtime->GetStackSize();
-  scoped_ptr<MemMap> stack(MemMap::Map(stack_size, PROT_READ | PROT_WRITE));
-  if (stack == NULL) {
-    LOG(FATAL) << "failed to allocate thread stack";
-    // notreached
-    return NULL;
-  }
 
   Thread* new_thread = new Thread;
   new_thread->InitCpu();
-  new_thread->stack_.reset(stack.release());
-  // Since stacks are assumed to grown downward the base is the limit and the limit is the base.
-  new_thread->stack_limit_ = stack->GetAddress();
-  new_thread->stack_base_ = stack->GetLimit();
 
   pthread_attr_t attr;
-  int result = pthread_attr_init(&attr);
-  CHECK_EQ(result, 0);
+  errno = pthread_attr_init(&attr);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_attr_init failed";
+  }
 
-  result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-  CHECK_EQ(result, 0);
+  errno = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_attr_setdetachstate(PTHREAD_CREATE_DETACHED) failed";
+  }
 
-  pthread_t handle;
-  result = pthread_create(&handle, &attr, ThreadStart, new_thread);
-  CHECK_EQ(result, 0);
+  errno = pthread_attr_setstacksize(&attr, stack_size);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_attr_setstacksize(" << stack_size << ") failed";
+  }
 
-  result = pthread_attr_destroy(&attr);
-  CHECK_EQ(result, 0);
+  errno = pthread_create(&new_thread->handle_, &attr, ThreadStart, new_thread);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_create failed";
+  }
+
+  errno = pthread_attr_destroy(&attr);
+  if (errno != 0) {
+    PLOG(FATAL) << "pthread_attr_destroy failed";
+  }
 
   return new_thread;
 }
@@ -160,11 +174,6 @@
 Thread* Thread::Attach(const Runtime* runtime) {
   Thread* thread = new Thread;
   thread->InitCpu();
-  thread->stack_limit_ = reinterpret_cast<byte*>(-1);  // TODO: getrlimit
-  uintptr_t addr = reinterpret_cast<uintptr_t>(&thread);  // TODO: ask pthreads
-  uintptr_t stack_base = RoundUp(addr, kPageSize);
-  thread->stack_base_ = reinterpret_cast<byte*>(stack_base);
-  // TODO: set the stack size
 
   thread->handle_ = pthread_self();
 
@@ -180,6 +189,10 @@
   return thread;
 }
 
+pid_t Thread::GetTid() const {
+  return gettid();
+}
+
 static void ThreadExitCheck(void* arg) {
   LG << "Thread exit check";
 }
@@ -401,9 +414,10 @@
 
 std::ostream& operator<<(std::ostream& os, const Thread& thread) {
   os << "Thread[" << &thread
-      << ",id=" << thread.GetId()
-      << ",tid=" << thread.GetNativeId()
-      << ",state=" << thread.GetState() << "]";
+     << ",pthread_t=" << thread.GetImpl()
+     << ",tid=" << thread.GetTid()
+     << ",id=" << thread.GetId()
+     << ",state=" << thread.GetState() << "]";
   return os;
 }
 
diff --git a/src/thread.h b/src/thread.h
index ac1d7e3..e8ca156 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -225,8 +225,10 @@
     return id_;
   }
 
-  pid_t GetNativeId() const {
-    return native_id_;
+  pid_t GetTid() const;
+
+  pthread_t GetImpl() const {
+    return handle_;
   }
 
   bool IsExceptionPending() const {
@@ -428,9 +430,6 @@
 
   State state_;
 
-  // Native (kernel) thread id.
-  pid_t native_id_;
-
   // Native thread handle.
   pthread_t handle_;
 
@@ -452,15 +451,6 @@
   // useful for testing.
   const ClassLoader* class_loader_override_;
 
-  // The memory mapping of the stack for non-attached threads.
-  scoped_ptr<MemMap> stack_;
-
-  // The inclusive base of the control stack.
-  byte* stack_base_;
-
-  // The exclusive limit of the control stack.
-  byte* stack_limit_;
-
   // TLS key used to retrieve the VM thread object.
   static pthread_key_t pthread_key_self_;
 
diff --git a/src/utils.cc b/src/utils.cc
index fd30a36..84b1556 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -25,6 +25,15 @@
   return contents;
 }
 
+std::string GetIsoDate() {
+  time_t now = time(NULL);
+  struct tm tmbuf;
+  struct tm* ptm = localtime_r(&now, &tmbuf);
+  return StringPrintf("%04d-%02d-%02d %02d:%02d:%02d",
+      ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday,
+      ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+}
+
 std::string PrettyDescriptor(const String* java_descriptor) {
   std::string descriptor(java_descriptor->ToModifiedUtf8());
 
diff --git a/src/utils.h b/src/utils.h
index e52f476..b5210ec 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -163,6 +163,9 @@
 
 std::string ReadFileToString(const char* file_name);
 
+// Returns the current date in ISO yyyy-mm-dd hh:mm:ss format.
+std::string GetIsoDate();
+
 }  // namespace art
 
 #endif  // ART_SRC_UTILS_H_