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 * This is a thread that catches signals and does something useful. For 18 * example, when a SIGQUIT (Ctrl-\) arrives, suspend the VM and dump the 19 * status of all threads. 20 */ 21 #include "Dalvik.h" 22 23 #include <stdlib.h> 24 #include <unistd.h> 25 #include <signal.h> 26 #include <pthread.h> 27 #include <sys/file.h> 28 #include <sys/time.h> 29 #include <fcntl.h> 30 #include <errno.h> 31 32 static void* signalCatcherThreadStart(void* arg); 33 34 /* 35 * Crank up the signal catcher thread. 36 * 37 * Returns immediately. 38 */ dvmSignalCatcherStartup(void)39 bool dvmSignalCatcherStartup(void) 40 { 41 gDvm.haltSignalCatcher = false; 42 43 if (!dvmCreateInternalThread(&gDvm.signalCatcherHandle, 44 "Signal Catcher", signalCatcherThreadStart, NULL)) 45 return false; 46 47 return true; 48 } 49 50 /* 51 * Shut down the signal catcher thread if it was started. 52 * 53 * Since we know the thread is just sitting around waiting for signals 54 * to arrive, send it one. 55 */ dvmSignalCatcherShutdown(void)56 void dvmSignalCatcherShutdown(void) 57 { 58 gDvm.haltSignalCatcher = true; 59 if (gDvm.signalCatcherHandle == 0) // not started yet 60 return; 61 62 pthread_kill(gDvm.signalCatcherHandle, SIGQUIT); 63 64 pthread_join(gDvm.signalCatcherHandle, NULL); 65 LOGV("signal catcher has shut down\n"); 66 } 67 68 69 /* 70 * Print the name of the current process, if we can get it. 71 */ printProcessName(const DebugOutputTarget * target)72 static void printProcessName(const DebugOutputTarget* target) 73 { 74 int fd = -1; 75 76 fd = open("/proc/self/cmdline", O_RDONLY, 0); 77 if (fd < 0) 78 goto bail; 79 80 char tmpBuf[256]; 81 ssize_t actual; 82 83 actual = read(fd, tmpBuf, sizeof(tmpBuf)-1); 84 if (actual <= 0) 85 goto bail; 86 87 tmpBuf[actual] = '\0'; 88 dvmPrintDebugMessage(target, "Cmd line: %s\n", tmpBuf); 89 90 bail: 91 if (fd >= 0) 92 close(fd); 93 } 94 95 /* 96 * Dump the stack traces for all threads to the log or to a file. If it's 97 * to a file we have a little setup to do. 98 */ logThreadStacks(void)99 static void logThreadStacks(void) 100 { 101 DebugOutputTarget target; 102 103 if (gDvm.stackTraceFile == NULL) { 104 /* just dump to log file */ 105 dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG); 106 dvmDumpAllThreadsEx(&target, true); 107 } else { 108 FILE* fp = NULL; 109 int cc, fd; 110 111 /* 112 * Open the stack trace output file, creating it if necessary. It 113 * needs to be world-writable so other processes can write to it. 114 */ 115 fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666); 116 if (fd < 0) { 117 LOGE("Unable to open stack trace file '%s': %s\n", 118 gDvm.stackTraceFile, strerror(errno)); 119 return; 120 } 121 122 /* gain exclusive access to the file */ 123 cc = flock(fd, LOCK_EX | LOCK_UN); 124 if (cc != 0) { 125 LOGV("Sleeping on flock(%s)\n", gDvm.stackTraceFile); 126 cc = flock(fd, LOCK_EX); 127 } 128 if (cc != 0) { 129 LOGE("Unable to lock stack trace file '%s': %s\n", 130 gDvm.stackTraceFile, strerror(errno)); 131 close(fd); 132 return; 133 } 134 135 fp = fdopen(fd, "a"); 136 if (fp == NULL) { 137 LOGE("Unable to fdopen '%s' (%d): %s\n", 138 gDvm.stackTraceFile, fd, strerror(errno)); 139 flock(fd, LOCK_UN); 140 close(fd); 141 return; 142 } 143 144 dvmCreateFileOutputTarget(&target, fp); 145 146 pid_t pid = getpid(); 147 time_t now = time(NULL); 148 struct tm* ptm; 149 #ifdef HAVE_LOCALTIME_R 150 struct tm tmbuf; 151 ptm = localtime_r(&now, &tmbuf); 152 #else 153 ptm = localtime(&now); 154 #endif 155 dvmPrintDebugMessage(&target, 156 "\n\n----- pid %d at %04d-%02d-%02d %02d:%02d:%02d -----\n", 157 pid, ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday, 158 ptm->tm_hour, ptm->tm_min, ptm->tm_sec); 159 printProcessName(&target); 160 dvmPrintDebugMessage(&target, "\n"); 161 fflush(fp); /* emit at least the header if we crash during dump */ 162 dvmDumpAllThreadsEx(&target, true); 163 fprintf(fp, "----- end %d -----\n", pid); 164 165 /* 166 * Unlock and close the file, flushing pending data before we unlock 167 * it. The fclose() will close the underyling fd. 168 */ 169 fflush(fp); 170 flock(fd, LOCK_UN); 171 fclose(fp); 172 173 LOGI("Wrote stack trace to '%s'\n", gDvm.stackTraceFile); 174 } 175 } 176 177 178 /* 179 * Sleep in sigwait() until a signal arrives. 180 */ signalCatcherThreadStart(void * arg)181 static void* signalCatcherThreadStart(void* arg) 182 { 183 Thread* self = dvmThreadSelf(); 184 sigset_t mask; 185 int cc; 186 187 UNUSED_PARAMETER(arg); 188 189 LOGV("Signal catcher thread started (threadid=%d)\n", self->threadId); 190 191 /* set up mask with signals we want to handle */ 192 sigemptyset(&mask); 193 sigaddset(&mask, SIGQUIT); 194 sigaddset(&mask, SIGUSR1); 195 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING) 196 sigaddset(&mask, SIGUSR2); 197 #endif 198 199 while (true) { 200 int rcvd; 201 202 dvmChangeStatus(self, THREAD_VMWAIT); 203 204 /* 205 * Signals for sigwait() must be blocked but not ignored. We 206 * block signals like SIGQUIT for all threads, so the condition 207 * is met. When the signal hits, we wake up, without any signal 208 * handlers being invoked. 209 * 210 * We want to suspend all other threads, so that it's safe to 211 * traverse their stacks. 212 * 213 * When running under GDB we occasionally return with EINTR (e.g. 214 * when other threads exit). 215 */ 216 loop: 217 cc = sigwait(&mask, &rcvd); 218 if (cc != 0) { 219 if (cc == EINTR) { 220 //LOGV("sigwait: EINTR\n"); 221 goto loop; 222 } 223 assert(!"bad result from sigwait"); 224 } 225 226 if (!gDvm.haltSignalCatcher) { 227 LOGI("threadid=%d: reacting to signal %d\n", 228 dvmThreadSelf()->threadId, rcvd); 229 } 230 231 /* set our status to RUNNING, self-suspending if GC in progress */ 232 dvmChangeStatus(self, THREAD_RUNNING); 233 234 if (gDvm.haltSignalCatcher) 235 break; 236 237 if (rcvd == SIGQUIT) { 238 dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP); 239 dvmDumpLoaderStats("sig"); 240 241 logThreadStacks(); 242 243 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING) 244 dvmCompilerDumpStats(); 245 #endif 246 247 if (false) { 248 dvmLockMutex(&gDvm.jniGlobalRefLock); 249 //dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global"); 250 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 251 } 252 253 //dvmDumpTrackedAllocations(true); 254 dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP); 255 } else if (rcvd == SIGUSR1) { 256 #if WITH_HPROF 257 LOGI("SIGUSR1 forcing GC and HPROF dump\n"); 258 hprofDumpHeap(NULL); 259 #else 260 LOGI("SIGUSR1 forcing GC (no HPROF)\n"); 261 dvmCollectGarbage(false); 262 #endif 263 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING) 264 } else if (rcvd == SIGUSR2) { 265 gDvmJit.printMe ^= true; 266 dvmCompilerDumpStats(); 267 /* Stress-test unchain all */ 268 dvmJitUnchainAll(); 269 #endif 270 } else { 271 LOGE("unexpected signal %d\n", rcvd); 272 } 273 } 274 275 return NULL; 276 } 277