auto import from //depot/cupcake/@135843
diff --git a/vm/SignalCatcher.c b/vm/SignalCatcher.c
new file mode 100644
index 0000000..550f777
--- /dev/null
+++ b/vm/SignalCatcher.c
@@ -0,0 +1,263 @@
+/*
+ * 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.
+ */
+/*
+ * This is a thread that catches signals and does something useful. For
+ * example, when a SIGQUIT (Ctrl-\) arrives, suspend the VM and dump the
+ * status of all threads.
+ */
+#include "Dalvik.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static void* signalCatcherThreadStart(void* arg);
+
+/*
+ * Crank up the signal catcher thread.
+ *
+ * Returns immediately.
+ */
+bool dvmSignalCatcherStartup(void)
+{
+ gDvm.haltSignalCatcher = false;
+
+ if (!dvmCreateInternalThread(&gDvm.signalCatcherHandle,
+ "Signal Catcher", signalCatcherThreadStart, NULL))
+ return false;
+
+ return true;
+}
+
+/*
+ * Shut down the signal catcher thread if it was started.
+ *
+ * Since we know the thread is just sitting around waiting for signals
+ * to arrive, send it one.
+ */
+void dvmSignalCatcherShutdown(void)
+{
+ gDvm.haltSignalCatcher = true;
+ if (gDvm.signalCatcherHandle == 0) // not started yet
+ return;
+
+ pthread_kill(gDvm.signalCatcherHandle, SIGQUIT);
+
+ pthread_join(gDvm.signalCatcherHandle, NULL);
+ LOGV("signal catcher has shut down\n");
+}
+
+
+/*
+ * Print the name of the current process, if we can get it.
+ */
+static void printProcessName(const DebugOutputTarget* target)
+{
+ int fd = -1;
+
+ fd = open("/proc/self/cmdline", O_RDONLY, 0);
+ if (fd < 0)
+ goto bail;
+
+ char tmpBuf[256];
+ ssize_t actual;
+
+ actual = read(fd, tmpBuf, sizeof(tmpBuf)-1);
+ if (actual <= 0)
+ goto bail;
+
+ tmpBuf[actual] = '\0';
+ dvmPrintDebugMessage(target, "Cmd line: %s\n", tmpBuf);
+
+bail:
+ if (fd >= 0)
+ close(fd);
+}
+
+/*
+ * Dump the stack traces for all threads to the log or to a file. If it's
+ * to a file we have a little setup to do.
+ */
+static void logThreadStacks(void)
+{
+ DebugOutputTarget target;
+
+ if (gDvm.stackTraceFile == NULL) {
+ /* just dump to log file */
+ dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
+ dvmDumpAllThreadsEx(&target, true);
+ } else {
+ FILE* fp = NULL;
+ int cc, fd;
+
+ /*
+ * Open the stack trace output file, creating it if necessary. It
+ * needs to be world-writable so other processes can write to it.
+ */
+ fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
+ if (fd < 0) {
+ LOGE("Unable to open stack trace file '%s': %s\n",
+ gDvm.stackTraceFile, strerror(errno));
+ return;
+ }
+
+ /* gain exclusive access to the file */
+ cc = flock(fd, LOCK_EX | LOCK_UN);
+ if (cc != 0) {
+ LOGV("Sleeping on flock(%s)\n", gDvm.stackTraceFile);
+ cc = flock(fd, LOCK_EX);
+ }
+ if (cc != 0) {
+ LOGE("Unable to lock stack trace file '%s': %s\n",
+ gDvm.stackTraceFile, strerror(errno));
+ close(fd);
+ return;
+ }
+
+ fp = fdopen(fd, "a");
+ if (fp == NULL) {
+ LOGE("Unable to fdopen '%s' (%d): %s\n",
+ gDvm.stackTraceFile, fd, strerror(errno));
+ flock(fd, LOCK_UN);
+ close(fd);
+ return;
+ }
+
+ dvmCreateFileOutputTarget(&target, fp);
+
+ pid_t pid = getpid();
+ time_t now = time(NULL);
+ struct tm* ptm;
+#ifdef HAVE_LOCALTIME_R
+ struct tm tmbuf;
+ ptm = localtime_r(&now, &tmbuf);
+#else
+ ptm = localtime(&now);
+#endif
+ dvmPrintDebugMessage(&target,
+ "\n\n----- pid %d at %04d-%02d-%02d %02d:%02d:%02d -----\n",
+ pid, ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday,
+ ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
+ printProcessName(&target);
+ dvmPrintDebugMessage(&target, "\n");
+ fflush(fp); /* emit at least the header if we crash during dump */
+ dvmDumpAllThreadsEx(&target, true);
+ fprintf(fp, "----- end %d -----\n", pid);
+
+ /*
+ * Unlock and close the file, flushing pending data before we unlock
+ * it. The fclose() will close the underyling fd.
+ */
+ fflush(fp);
+ flock(fd, LOCK_UN);
+ fclose(fp);
+
+ LOGI("Wrote stack trace to '%s'\n", gDvm.stackTraceFile);
+ }
+}
+
+
+/*
+ * Sleep in sigwait() until a signal arrives.
+ */
+static void* signalCatcherThreadStart(void* arg)
+{
+ Thread* self = dvmThreadSelf();
+ sigset_t mask;
+ int cc;
+
+ UNUSED_PARAMETER(arg);
+
+ LOGV("Signal catcher thread started (threadid=%d)\n", self->threadId);
+
+ /* set up mask with signals we want to handle */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGQUIT);
+ sigaddset(&mask, SIGUSR1);
+
+ while (true) {
+ int rcvd;
+
+ dvmChangeStatus(self, THREAD_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.
+ *
+ * We want to suspend all other threads, so that it's safe to
+ * traverse their stacks.
+ *
+ * When running under GDB we occasionally return with EINTR (e.g.
+ * when other threads exit).
+ */
+loop:
+ cc = sigwait(&mask, &rcvd);
+ if (cc != 0) {
+ if (cc == EINTR) {
+ //LOGV("sigwait: EINTR\n");
+ goto loop;
+ }
+ assert(!"bad result from sigwait");
+ }
+
+ if (!gDvm.haltSignalCatcher) {
+ LOGI("threadid=%d: reacting to signal %d\n",
+ dvmThreadSelf()->threadId, rcvd);
+ }
+
+ /* set our status to RUNNING, self-suspending if GC in progress */
+ dvmChangeStatus(self, THREAD_RUNNING);
+
+ if (gDvm.haltSignalCatcher)
+ break;
+
+ if (rcvd == SIGQUIT) {
+ dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP);
+ dvmDumpLoaderStats("sig");
+
+ logThreadStacks();
+
+ if (false) {
+ dvmLockMutex(&gDvm.jniGlobalRefLock);
+ dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
+ dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+ }
+
+ //dvmDumpTrackedAllocations(true);
+ dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP);
+ } else if (rcvd == SIGUSR1) {
+#if WITH_HPROF
+ LOGI("SIGUSR1 forcing GC and HPROF dump\n");
+ hprofDumpHeap(NULL);
+#else
+ LOGI("SIGUSR1 forcing GC (no HPROF)\n");
+ dvmCollectGarbage(false);
+#endif
+ } else {
+ LOGE("unexpected signal %d\n", rcvd);
+ }
+ }
+
+ return NULL;
+}
+