diff options
author | Carl Shapiro <cshapiro@google.com> | 2011-04-15 18:38:06 -0700 |
---|---|---|
committer | Carl Shapiro <cshapiro@google.com> | 2011-04-15 21:18:10 -0700 |
commit | d5c36b9040bd26a81219a7f399513526f9b46324 (patch) | |
tree | 921c49ef9ced8819389ef699ae61296741db71a5 /vm/SignalCatcher.cpp | |
parent | c469fa622ebadfa3defc73a064e2e724f0ab7c75 (diff) | |
download | android_dalvik-d5c36b9040bd26a81219a7f399513526f9b46324.tar.gz android_dalvik-d5c36b9040bd26a81219a7f399513526f9b46324.tar.bz2 android_dalvik-d5c36b9040bd26a81219a7f399513526f9b46324.zip |
Move the remaining non-compiler VM code into C++.
Change-Id: Id8693208d2741c55a7b0474d1264f2112019d11f
Diffstat (limited to 'vm/SignalCatcher.cpp')
-rw-r--r-- | vm/SignalCatcher.cpp | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/vm/SignalCatcher.cpp b/vm/SignalCatcher.cpp new file mode 100644 index 000000000..a8fc59dde --- /dev/null +++ b/vm/SignalCatcher.cpp @@ -0,0 +1,324 @@ +/* + * 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> + +#include <cutils/open_memstream.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 supplied file, putting + * a timestamp header on it. + */ +static void logThreadStacks(FILE* fp) +{ + DebugOutputTarget target; + + 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"); + dvmDumpAllThreadsEx(&target, true); + fprintf(fp, "----- end %d -----\n", pid); +} + + +/* + * Respond to a SIGQUIT by dumping the thread stacks. Optionally dump + * a few other things while we're at it. + * + * Thread stacks can either go to the log or to a file designated for holding + * ANR traces. If we're writing to a file, we want to do it in one shot, + * so we can use a single O_APPEND write instead of contending for exclusive + * access with flock(). There may be an advantage in resuming the VM + * before doing the file write, so we don't stall the VM if disk I/O is + * bottlenecked. + * + * If JIT tuning is compiled in, dump compiler stats as well. + */ +static void handleSigQuit(void) +{ + char* traceBuf = NULL; + size_t traceLen; + + dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP); + + dvmDumpLoaderStats("sig"); + + if (gDvm.stackTraceFile == NULL) { + /* just dump to log */ + DebugOutputTarget target; + dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG); + dvmDumpAllThreadsEx(&target, true); + } else { + /* write to memory buffer */ + FILE* memfp = open_memstream(&traceBuf, &traceLen); + if (memfp == NULL) { + LOGE("Unable to create memstream for stack traces\n"); + traceBuf = NULL; /* make sure it didn't touch this */ + /* continue on */ + } else { + logThreadStacks(memfp); + fclose(memfp); + } + } + +#if defined(WITH_JIT) && defined(WITH_JIT_TUNING) + dvmCompilerDumpStats(); +#endif + + if (false) dvmDumpTrackedAllocations(true); + + dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP); + + if (traceBuf != NULL) { + /* + * We don't know how long it will take to do the disk I/O, so put us + * into VMWAIT for the duration. + */ + ThreadStatus oldStatus = dvmChangeStatus(dvmThreadSelf(), THREAD_VMWAIT); + + /* + * Open the stack trace output file, creating it if necessary. It + * needs to be world-writable so other processes can write to it. + */ + int 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)); + } else { + ssize_t actual = write(fd, traceBuf, traceLen); + if (actual != (ssize_t) traceLen) { + LOGE("Failed to write stack traces to %s (%d of %zd): %s\n", + gDvm.stackTraceFile, (int) actual, traceLen, + strerror(errno)); + } else { + LOGI("Wrote stack traces to '%s'\n", gDvm.stackTraceFile); + } + close(fd); + } + + free(traceBuf); + dvmChangeStatus(dvmThreadSelf(), oldStatus); + } +} + +/* + * Respond to a SIGUSR1 by forcing a GC. + */ +static void handleSigUsr1(void) +{ + LOGI("SIGUSR1 forcing GC (no HPROF)\n"); + dvmCollectGarbage(); +} + +#if defined(WITH_JIT) && defined(WITH_JIT_TUNING) +/* Sample callback function for dvmJitScanAllClassPointers */ +void printAllClass(void *ptr) +{ + ClassObject **classPP = (ClassObject **) ptr; + LOGE("class %s", (*classPP)->descriptor); + +} + +/* + * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting + * the code cache. + */ +static void handleSigUsr2(void) +{ + static int codeCacheResetCount = 0; + gDvmJit.receivedSIGUSR2 ^= true; + if ((--codeCacheResetCount & 7) == 0) { + /* Dump all class pointers in the traces */ + dvmJitScanAllClassPointers(printAllClass); + gDvmJit.codeCacheFull = true; + } else { + dvmCompilerDumpStats(); + /* Stress-test unchain all */ + dvmJitUnchainAll(); + LOGD("Send %d more signals to reset the code cache", + codeCacheResetCount & 7); + } + dvmCheckInterpStateConsistency(); +} +#endif + +/* + * 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); +#if defined(WITH_JIT) && defined(WITH_JIT_TUNING) + sigaddset(&mask, SIGUSR2); +#endif + + 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. + * + * When running under GDB we occasionally return from sigwait() + * 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; + + switch (rcvd) { + case SIGQUIT: + handleSigQuit(); + break; + case SIGUSR1: + handleSigUsr1(); + break; +#if defined(WITH_JIT) && defined(WITH_JIT_TUNING) + case SIGUSR2: + handleSigUsr2(); + break; +#endif + default: + LOGE("unexpected signal %d\n", rcvd); + break; + } + } + + return NULL; +} |