Add the signal catcher (for SIGQUIT and SIGUSR1).

Also fix some of the thread implementation.

Change-Id: If2d1b59a149ba1ac192ad9bc74319c8dff228549
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