/* * 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"); dvmDumpJniStats(&target); dvmDumpAllThreadsEx(&target, true); fprintf(fp, "----- end %d -----\n", pid); }
/* * 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); } }
/* * 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() { 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); dvmDumpJniStats(&target); dvmDumpAllThreadsEx(&target, true); } else { /* write to memory buffer */ FILE* memfp = open_memstream(&traceBuf, &traceLen); if (memfp == NULL) { ALOGE("Unable to create memstream for stack traces"); 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) { ALOGE("Unable to open stack trace file '%s': %s", gDvm.stackTraceFile, strerror(errno)); } else { ssize_t actual = write(fd, traceBuf, traceLen); if (actual != (ssize_t) traceLen) { ALOGE("Failed to write stack traces to %s (%d of %zd): %s", gDvm.stackTraceFile, (int) actual, traceLen, strerror(errno)); } else { ALOGI("Wrote stack traces to '%s'", gDvm.stackTraceFile); } close(fd); } free(traceBuf); dvmChangeStatus(dvmThreadSelf(), oldStatus); } }