1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * This is a thread that catches signals and does something useful. For 19 * example, when a SIGQUIT (Ctrl-\) arrives, suspend the VM and dump the 20 * status of all threads. 21 */ 22#include "Dalvik.h" 23 24#include <stdlib.h> 25#include <unistd.h> 26#include <signal.h> 27#include <pthread.h> 28#include <sys/file.h> 29#include <sys/time.h> 30#include <fcntl.h> 31#include <errno.h> 32 33#include <cutils/open_memstream.h> 34 35static void* signalCatcherThreadStart(void* arg); 36 37/* 38 * Crank up the signal catcher thread. 39 * 40 * Returns immediately. 41 */ 42bool dvmSignalCatcherStartup(void) 43{ 44 gDvm.haltSignalCatcher = false; 45 46 if (!dvmCreateInternalThread(&gDvm.signalCatcherHandle, 47 "Signal Catcher", signalCatcherThreadStart, NULL)) 48 return false; 49 50 return true; 51} 52 53/* 54 * Shut down the signal catcher thread if it was started. 55 * 56 * Since we know the thread is just sitting around waiting for signals 57 * to arrive, send it one. 58 */ 59void dvmSignalCatcherShutdown(void) 60{ 61 gDvm.haltSignalCatcher = true; 62 if (gDvm.signalCatcherHandle == 0) // not started yet 63 return; 64 65 pthread_kill(gDvm.signalCatcherHandle, SIGQUIT); 66 67 pthread_join(gDvm.signalCatcherHandle, NULL); 68 LOGV("signal catcher has shut down\n"); 69} 70 71 72/* 73 * Print the name of the current process, if we can get it. 74 */ 75static void printProcessName(const DebugOutputTarget* target) 76{ 77 int fd = -1; 78 79 fd = open("/proc/self/cmdline", O_RDONLY, 0); 80 if (fd < 0) 81 goto bail; 82 83 char tmpBuf[256]; 84 ssize_t actual; 85 86 actual = read(fd, tmpBuf, sizeof(tmpBuf)-1); 87 if (actual <= 0) 88 goto bail; 89 90 tmpBuf[actual] = '\0'; 91 dvmPrintDebugMessage(target, "Cmd line: %s\n", tmpBuf); 92 93bail: 94 if (fd >= 0) 95 close(fd); 96} 97 98/* 99 * Dump the stack traces for all threads to the supplied file, putting 100 * a timestamp header on it. 101 */ 102static void logThreadStacks(FILE* fp) 103{ 104 DebugOutputTarget target; 105 106 dvmCreateFileOutputTarget(&target, fp); 107 108 pid_t pid = getpid(); 109 time_t now = time(NULL); 110 struct tm* ptm; 111#ifdef HAVE_LOCALTIME_R 112 struct tm tmbuf; 113 ptm = localtime_r(&now, &tmbuf); 114#else 115 ptm = localtime(&now); 116#endif 117 dvmPrintDebugMessage(&target, 118 "\n\n----- pid %d at %04d-%02d-%02d %02d:%02d:%02d -----\n", 119 pid, ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday, 120 ptm->tm_hour, ptm->tm_min, ptm->tm_sec); 121 printProcessName(&target); 122 dvmPrintDebugMessage(&target, "\n"); 123 dvmDumpAllThreadsEx(&target, true); 124 fprintf(fp, "----- end %d -----\n", pid); 125} 126 127 128/* 129 * Respond to a SIGQUIT by dumping the thread stacks. Optionally dump 130 * a few other things while we're at it. 131 * 132 * Thread stacks can either go to the log or to a file designated for holding 133 * ANR traces. If we're writing to a file, we want to do it in one shot, 134 * so we can use a single O_APPEND write instead of contending for exclusive 135 * access with flock(). There may be an advantage in resuming the VM 136 * before doing the file write, so we don't stall the VM if disk I/O is 137 * bottlenecked. 138 * 139 * If JIT tuning is compiled in, dump compiler stats as well. 140 */ 141static void handleSigQuit(void) 142{ 143 char* traceBuf = NULL; 144 size_t traceLen; 145 146 dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP); 147 148 dvmDumpLoaderStats("sig"); 149 150 if (gDvm.stackTraceFile == NULL) { 151 /* just dump to log */ 152 DebugOutputTarget target; 153 dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG); 154 dvmDumpAllThreadsEx(&target, true); 155 } else { 156 /* write to memory buffer */ 157 FILE* memfp = open_memstream(&traceBuf, &traceLen); 158 if (memfp == NULL) { 159 LOGE("Unable to create memstream for stack traces\n"); 160 traceBuf = NULL; /* make sure it didn't touch this */ 161 /* continue on */ 162 } else { 163 logThreadStacks(memfp); 164 fclose(memfp); 165 } 166 } 167 168#if defined(WITH_JIT) && defined(WITH_JIT_TUNING) 169 dvmCompilerDumpStats(); 170#endif 171 172 if (false) { 173 dvmLockMutex(&gDvm.jniGlobalRefLock); 174 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global"); 175 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 176 } 177 if (false) dvmDumpTrackedAllocations(true); 178 179 dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP); 180 181 if (traceBuf != NULL) { 182 /* 183 * We don't know how long it will take to do the disk I/O, so put us 184 * into VMWAIT for the duration. 185 */ 186 int oldStatus = dvmChangeStatus(dvmThreadSelf(), THREAD_VMWAIT); 187 188 /* 189 * Open the stack trace output file, creating it if necessary. It 190 * needs to be world-writable so other processes can write to it. 191 */ 192 int fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666); 193 if (fd < 0) { 194 LOGE("Unable to open stack trace file '%s': %s\n", 195 gDvm.stackTraceFile, strerror(errno)); 196 } else { 197 ssize_t actual = write(fd, traceBuf, traceLen); 198 if (actual != (ssize_t) traceLen) { 199 LOGE("Failed to write stack traces to %s (%d of %zd): %s\n", 200 gDvm.stackTraceFile, (int) actual, traceLen, 201 strerror(errno)); 202 } else { 203 LOGI("Wrote stack traces to '%s'\n", gDvm.stackTraceFile); 204 } 205 close(fd); 206 } 207 208 free(traceBuf); 209 dvmChangeStatus(dvmThreadSelf(), oldStatus); 210 } 211} 212 213/* 214 * Respond to a SIGUSR1 by forcing a GC. 215 */ 216static void handleSigUsr1(void) 217{ 218 LOGI("SIGUSR1 forcing GC (no HPROF)\n"); 219 dvmCollectGarbage(false); 220} 221 222#if defined(WITH_JIT) && defined(WITH_JIT_TUNING) 223/* 224 * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting 225 * the code cache. 226 */ 227static void handleSigUsr2(void) 228{ 229 static int codeCacheResetCount = 0; 230 if ((--codeCacheResetCount & 7) == 0) { 231 gDvmJit.codeCacheFull = true; 232 } else { 233 dvmCompilerDumpStats(); 234 /* Stress-test unchain all */ 235 dvmJitUnchainAll(); 236 LOGD("Send %d more signals to rest the code cache", 237 codeCacheResetCount & 7); 238 } 239} 240#endif 241 242/* 243 * Sleep in sigwait() until a signal arrives. 244 */ 245static void* signalCatcherThreadStart(void* arg) 246{ 247 Thread* self = dvmThreadSelf(); 248 sigset_t mask; 249 int cc; 250 251 UNUSED_PARAMETER(arg); 252 253 LOGV("Signal catcher thread started (threadid=%d)\n", self->threadId); 254 255 /* set up mask with signals we want to handle */ 256 sigemptyset(&mask); 257 sigaddset(&mask, SIGQUIT); 258 sigaddset(&mask, SIGUSR1); 259#if defined(WITH_JIT) && defined(WITH_JIT_TUNING) 260 sigaddset(&mask, SIGUSR2); 261#endif 262 263 while (true) { 264 int rcvd; 265 266 dvmChangeStatus(self, THREAD_VMWAIT); 267 268 /* 269 * Signals for sigwait() must be blocked but not ignored. We 270 * block signals like SIGQUIT for all threads, so the condition 271 * is met. When the signal hits, we wake up, without any signal 272 * handlers being invoked. 273 * 274 * When running under GDB we occasionally return from sigwait() 275 * with EINTR (e.g. when other threads exit). 276 */ 277loop: 278 cc = sigwait(&mask, &rcvd); 279 if (cc != 0) { 280 if (cc == EINTR) { 281 //LOGV("sigwait: EINTR\n"); 282 goto loop; 283 } 284 assert(!"bad result from sigwait"); 285 } 286 287 if (!gDvm.haltSignalCatcher) { 288 LOGI("threadid=%d: reacting to signal %d\n", 289 dvmThreadSelf()->threadId, rcvd); 290 } 291 292 /* set our status to RUNNING, self-suspending if GC in progress */ 293 dvmChangeStatus(self, THREAD_RUNNING); 294 295 if (gDvm.haltSignalCatcher) 296 break; 297 298 switch (rcvd) { 299 case SIGQUIT: 300 handleSigQuit(); 301 break; 302 case SIGUSR1: 303 handleSigUsr1(); 304 break; 305#if defined(WITH_JIT) && defined(WITH_JIT_TUNING) 306 case SIGUSR2: 307 handleSigUsr2(); 308 break; 309#endif 310 default: 311 LOGE("unexpected signal %d\n", rcvd); 312 break; 313 } 314 } 315 316 return NULL; 317} 318