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 35 static void* signalCatcherThreadStart(void* arg); 36 37 /* 38 * Crank up the signal catcher thread. 39 * 40 * Returns immediately. 41 */ 42 bool dvmSignalCatcherStartup() 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 */ 59 void dvmSignalCatcherShutdown() 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"); 69 } 70 71 72 /* 73 * Print the name of the current process, if we can get it. 74 */ 75 static 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 93 bail: 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 */ 102 static 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 */ 141 static void handleSigQuit() 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"); 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) dvmDumpTrackedAllocations(true); 173 174 dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP); 175 176 if (traceBuf != NULL) { 177 /* 178 * We don't know how long it will take to do the disk I/O, so put us 179 * into VMWAIT for the duration. 180 */ 181 ThreadStatus oldStatus = dvmChangeStatus(dvmThreadSelf(), THREAD_VMWAIT); 182 183 /* 184 * Open the stack trace output file, creating it if necessary. It 185 * needs to be world-writable so other processes can write to it. 186 */ 187 int fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666); 188 if (fd < 0) { 189 LOGE("Unable to open stack trace file '%s': %s", 190 gDvm.stackTraceFile, strerror(errno)); 191 } else { 192 ssize_t actual = write(fd, traceBuf, traceLen); 193 if (actual != (ssize_t) traceLen) { 194 LOGE("Failed to write stack traces to %s (%d of %zd): %s", 195 gDvm.stackTraceFile, (int) actual, traceLen, 196 strerror(errno)); 197 } else { 198 LOGI("Wrote stack traces to '%s'", gDvm.stackTraceFile); 199 } 200 close(fd); 201 } 202 203 free(traceBuf); 204 dvmChangeStatus(dvmThreadSelf(), oldStatus); 205 } 206 } 207 208 /* 209 * Respond to a SIGUSR1 by forcing a GC. 210 */ 211 static void handleSigUsr1() 212 { 213 LOGI("SIGUSR1 forcing GC (no HPROF)"); 214 dvmCollectGarbage(); 215 } 216 217 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING) 218 /* Sample callback function for dvmJitScanAllClassPointers */ 219 void printAllClass(void *ptr) 220 { 221 ClassObject **classPP = (ClassObject **) ptr; 222 LOGE("class %s", (*classPP)->descriptor); 223 224 } 225 226 /* 227 * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting 228 * the code cache. 229 */ 230 static void handleSigUsr2() 231 { 232 static int codeCacheResetCount = 0; 233 gDvmJit.receivedSIGUSR2 ^= true; 234 if ((--codeCacheResetCount & 7) == 0) { 235 /* Dump all class pointers in the traces */ 236 dvmJitScanAllClassPointers(printAllClass); 237 gDvmJit.codeCacheFull = true; 238 } else { 239 dvmCompilerDumpStats(); 240 /* Stress-test unchain all */ 241 dvmJitUnchainAll(); 242 LOGD("Send %d more signals to reset the code cache", 243 codeCacheResetCount & 7); 244 } 245 dvmCheckInterpStateConsistency(); 246 } 247 #endif 248 249 /* 250 * Sleep in sigwait() until a signal arrives. 251 */ 252 static void* signalCatcherThreadStart(void* arg) 253 { 254 Thread* self = dvmThreadSelf(); 255 sigset_t mask; 256 int cc; 257 258 UNUSED_PARAMETER(arg); 259 260 LOGV("Signal catcher thread started (threadid=%d)", self->threadId); 261 262 /* set up mask with signals we want to handle */ 263 sigemptyset(&mask); 264 sigaddset(&mask, SIGQUIT); 265 sigaddset(&mask, SIGUSR1); 266 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING) 267 sigaddset(&mask, SIGUSR2); 268 #endif 269 270 while (true) { 271 int rcvd; 272 273 dvmChangeStatus(self, THREAD_VMWAIT); 274 275 /* 276 * Signals for sigwait() must be blocked but not ignored. We 277 * block signals like SIGQUIT for all threads, so the condition 278 * is met. When the signal hits, we wake up, without any signal 279 * handlers being invoked. 280 * 281 * When running under GDB we occasionally return from sigwait() 282 * with EINTR (e.g. when other threads exit). 283 */ 284 loop: 285 cc = sigwait(&mask, &rcvd); 286 if (cc != 0) { 287 if (cc == EINTR) { 288 //LOGV("sigwait: EINTR"); 289 goto loop; 290 } 291 assert(!"bad result from sigwait"); 292 } 293 294 if (!gDvm.haltSignalCatcher) { 295 LOGI("threadid=%d: reacting to signal %d", 296 dvmThreadSelf()->threadId, rcvd); 297 } 298 299 /* set our status to RUNNING, self-suspending if GC in progress */ 300 dvmChangeStatus(self, THREAD_RUNNING); 301 302 if (gDvm.haltSignalCatcher) 303 break; 304 305 switch (rcvd) { 306 case SIGQUIT: 307 handleSigQuit(); 308 break; 309 case SIGUSR1: 310 handleSigUsr1(); 311 break; 312 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING) 313 case SIGUSR2: 314 handleSigUsr2(); 315 break; 316 #endif 317 default: 318 LOGE("unexpected signal %d", rcvd); 319 break; 320 } 321 } 322 323 return NULL; 324 } 325