2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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.
33 #include <cutils/open_memstream.h>
35 static void* signalCatcherThreadStart(void* arg);
38 * Crank up the signal catcher thread.
40 * Returns immediately.
42 bool dvmSignalCatcherStartup()
44 gDvm.haltSignalCatcher = false;
46 if (!dvmCreateInternalThread(&gDvm.signalCatcherHandle,
47 "Signal Catcher", signalCatcherThreadStart, NULL))
54 * Shut down the signal catcher thread if it was started.
56 * Since we know the thread is just sitting around waiting for signals
57 * to arrive, send it one.
59 void dvmSignalCatcherShutdown()
61 gDvm.haltSignalCatcher = true;
62 if (gDvm.signalCatcherHandle == 0) // not started yet
65 pthread_kill(gDvm.signalCatcherHandle, SIGQUIT);
67 pthread_join(gDvm.signalCatcherHandle, NULL);
68 LOGV("signal catcher has shut down");
73 * Print the name of the current process, if we can get it.
75 static void printProcessName(const DebugOutputTarget* target)
79 fd = open("/proc/self/cmdline", O_RDONLY, 0);
86 actual = read(fd, tmpBuf, sizeof(tmpBuf)-1);
90 tmpBuf[actual] = '\0';
91 dvmPrintDebugMessage(target, "Cmd line: %s\n", tmpBuf);
99 * Dump the stack traces for all threads to the supplied file, putting
100 * a timestamp header on it.
102 static void logThreadStacks(FILE* fp)
104 DebugOutputTarget target;
106 dvmCreateFileOutputTarget(&target, fp);
108 pid_t pid = getpid();
109 time_t now = time(NULL);
111 #ifdef HAVE_LOCALTIME_R
113 ptm = localtime_r(&now, &tmbuf);
115 ptm = localtime(&now);
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);
129 * Respond to a SIGQUIT by dumping the thread stacks. Optionally dump
130 * a few other things while we're at it.
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
139 * If JIT tuning is compiled in, dump compiler stats as well.
141 static void handleSigQuit()
143 char* traceBuf = NULL;
146 dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP);
148 dvmDumpLoaderStats("sig");
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);
156 /* write to memory buffer */
157 FILE* memfp = open_memstream(&traceBuf, &traceLen);
159 LOGE("Unable to create memstream for stack traces");
160 traceBuf = NULL; /* make sure it didn't touch this */
163 logThreadStacks(memfp);
168 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
169 dvmCompilerDumpStats();
172 if (false) dvmDumpTrackedAllocations(true);
174 dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP);
176 if (traceBuf != NULL) {
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.
181 ThreadStatus oldStatus = dvmChangeStatus(dvmThreadSelf(), THREAD_VMWAIT);
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.
187 int fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
189 LOGE("Unable to open stack trace file '%s': %s",
190 gDvm.stackTraceFile, strerror(errno));
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,
198 LOGI("Wrote stack traces to '%s'", gDvm.stackTraceFile);
204 dvmChangeStatus(dvmThreadSelf(), oldStatus);
209 * Respond to a SIGUSR1 by forcing a GC.
211 static void handleSigUsr1()
213 LOGI("SIGUSR1 forcing GC (no HPROF)");
217 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
218 /* Sample callback function for dvmJitScanAllClassPointers */
219 void printAllClass(void *ptr)
221 ClassObject **classPP = (ClassObject **) ptr;
222 LOGE("class %s", (*classPP)->descriptor);
227 * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting
230 static void handleSigUsr2()
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;
239 dvmCompilerDumpStats();
240 /* Stress-test unchain all */
242 LOGD("Send %d more signals to reset the code cache",
243 codeCacheResetCount & 7);
245 dvmCheckInterpStateConsistency();
250 * Sleep in sigwait() until a signal arrives.
252 static void* signalCatcherThreadStart(void* arg)
254 Thread* self = dvmThreadSelf();
258 UNUSED_PARAMETER(arg);
260 LOGV("Signal catcher thread started (threadid=%d)", self->threadId);
262 /* set up mask with signals we want to handle */
264 sigaddset(&mask, SIGQUIT);
265 sigaddset(&mask, SIGUSR1);
266 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
267 sigaddset(&mask, SIGUSR2);
273 dvmChangeStatus(self, THREAD_VMWAIT);
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.
281 * When running under GDB we occasionally return from sigwait()
282 * with EINTR (e.g. when other threads exit).
285 cc = sigwait(&mask, &rcvd);
288 //LOGV("sigwait: EINTR");
291 assert(!"bad result from sigwait");
294 if (!gDvm.haltSignalCatcher) {
295 LOGI("threadid=%d: reacting to signal %d",
296 dvmThreadSelf()->threadId, rcvd);
299 /* set our status to RUNNING, self-suspending if GC in progress */
300 dvmChangeStatus(self, THREAD_RUNNING);
302 if (gDvm.haltSignalCatcher)
312 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
318 LOGE("unexpected signal %d", rcvd);