am 504af654: Merge "Rearrange the way SignalCatcher writes logs."
Merge commit '504af654f481ba978bbd2e216b3f05f9dfa55c00' into dalvik-dev
* commit '504af654f481ba978bbd2e216b3f05f9dfa55c00':
Rearrange the way SignalCatcher writes logs.
diff --git a/vm/SignalCatcher.c b/vm/SignalCatcher.c
index 7edbf38..d3b35b6 100644
--- a/vm/SignalCatcher.c
+++ b/vm/SignalCatcher.c
@@ -13,6 +13,7 @@
* 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
@@ -29,6 +30,8 @@
#include <fcntl.h>
#include <errno.h>
+#include <cutils/open_memstream.h>
+
static void* signalCatcherThreadStart(void* arg);
/*
@@ -93,87 +96,150 @@
}
/*
- * 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.
+ * Dump the stack traces for all threads to the supplied file, putting
+ * a timestamp header on it.
*/
-static void logThreadStacks(void)
+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 file */
+ /* just dump to log */
+ DebugOutputTarget target;
dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
dvmDumpAllThreadsEx(&target, true);
} else {
- FILE* fp = NULL;
- int cc, fd;
+ /* 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) {
+ dvmLockMutex(&gDvm.jniGlobalRefLock);
+ dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
+ dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+ }
+ if (false) dvmDumpTrackedAllocations(true);
+
+ dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP);
+
+ if (traceBuf != NULL) {
/*
* 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);
+ 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));
- 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));
+ } 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);
- 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);
+ free(traceBuf);
}
}
+/*
+ * Respond to a SIGUSR1 by forcing a GC. If we were built with HPROF
+ * support, generate an HPROF dump file.
+ *
+ * (The HPROF dump generation is not all that useful now that we have
+ * better ways to generate it. Consider removing this in a future release.)
+ */
+static void handleSigUsr1(void)
+{
+#if WITH_HPROF
+ LOGI("SIGUSR1 forcing GC and HPROF dump\n");
+ hprofDumpHeap(NULL, false);
+#else
+ LOGI("SIGUSR1 forcing GC (no HPROF)\n");
+ dvmCollectGarbage(false);
+#endif
+}
+
+#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
+/*
+ * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting
+ * the code cache.
+ */
+static void handleSigUsr2(void)
+{
+ static int codeCacheResetCount = 0;
+ if ((--codeCacheResetCount & 7) == 0) {
+ gDvmJit.codeCacheFull = true;
+ } else {
+ dvmCompilerDumpStats();
+ /* Stress-test unchain all */
+ dvmJitUnchainAll();
+ LOGD("Send %d more signals to rest the code cache",
+ codeCacheResetCount & 7);
+ }
+}
+#endif
/*
* Sleep in sigwait() until a signal arrives.
@@ -207,11 +273,8 @@
* 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).
+ * When running under GDB we occasionally return from sigwait()
+ * with EINTR (e.g. when other threads exit).
*/
loop:
cc = sigwait(&mask, &rcvd);
@@ -234,47 +297,21 @@
if (gDvm.haltSignalCatcher)
break;
- if (rcvd == SIGQUIT) {
- dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP);
- dvmDumpLoaderStats("sig");
-
- logThreadStacks();
-
+ switch (rcvd) {
+ case SIGQUIT:
+ handleSigQuit();
+ break;
+ case SIGUSR1:
+ handleSigUsr1();
+ break;
#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
- dvmCompilerDumpStats();
+ case SIGUSR2:
+ handleSigUsr2();
+ break;
#endif
-
- 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, false);
-#else
- LOGI("SIGUSR1 forcing GC (no HPROF)\n");
- dvmCollectGarbage(false);
-#endif
-#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
- } else if (rcvd == SIGUSR2) {
- static int codeCacheResetCount = 0;
- if ((--codeCacheResetCount & 7) == 0) {
- gDvmJit.codeCacheFull = true;
- } else {
- dvmCompilerDumpStats();
- /* Stress-test unchain all */
- dvmJitUnchainAll();
- LOGD("Send %d more signals to rest the code cache",
- codeCacheResetCount & 7);
- }
-#endif
- } else {
+ default:
LOGE("unexpected signal %d\n", rcvd);
+ break;
}
}