blob: d3b35b691a567cd75c59bb7336dff73956152b03 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
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 */
Andy McFaddenc9dcf262010-03-10 13:42:22 -080016
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080017/*
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
Andy McFaddenc9dcf262010-03-10 13:42:22 -080033#include <cutils/open_memstream.h>
34
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080035static 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/*
Andy McFaddenc9dcf262010-03-10 13:42:22 -080099 * Dump the stack traces for all threads to the supplied file, putting
100 * a timestamp header on it.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800101 */
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800102static void logThreadStacks(FILE* fp)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800103{
104 DebugOutputTarget target;
105
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800106 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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800150 if (gDvm.stackTraceFile == NULL) {
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800151 /* just dump to log */
152 DebugOutputTarget target;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800153 dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
154 dvmDumpAllThreadsEx(&target, true);
155 } else {
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800156 /* 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 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800167
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800168#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) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800182 /*
183 * Open the stack trace output file, creating it if necessary. It
184 * needs to be world-writable so other processes can write to it.
185 */
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800186 int fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800187 if (fd < 0) {
188 LOGE("Unable to open stack trace file '%s': %s\n",
189 gDvm.stackTraceFile, strerror(errno));
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800190 } else {
191 ssize_t actual = write(fd, traceBuf, traceLen);
192 if (actual != (ssize_t) traceLen) {
193 LOGE("Failed to write stack traces to %s (%d of %zd): %s\n",
194 gDvm.stackTraceFile, (int) actual, traceLen,
195 strerror(errno));
196 } else {
197 LOGI("Wrote stack traces to '%s'\n", gDvm.stackTraceFile);
198 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800199 close(fd);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800200 }
201
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800202 free(traceBuf);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800203 }
204}
205
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800206/*
207 * Respond to a SIGUSR1 by forcing a GC. If we were built with HPROF
208 * support, generate an HPROF dump file.
209 *
210 * (The HPROF dump generation is not all that useful now that we have
211 * better ways to generate it. Consider removing this in a future release.)
212 */
213static void handleSigUsr1(void)
214{
215#if WITH_HPROF
216 LOGI("SIGUSR1 forcing GC and HPROF dump\n");
217 hprofDumpHeap(NULL, false);
218#else
219 LOGI("SIGUSR1 forcing GC (no HPROF)\n");
220 dvmCollectGarbage(false);
221#endif
222}
223
224#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
225/*
226 * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting
227 * the code cache.
228 */
229static void handleSigUsr2(void)
230{
231 static int codeCacheResetCount = 0;
232 if ((--codeCacheResetCount & 7) == 0) {
233 gDvmJit.codeCacheFull = true;
234 } else {
235 dvmCompilerDumpStats();
236 /* Stress-test unchain all */
237 dvmJitUnchainAll();
238 LOGD("Send %d more signals to rest the code cache",
239 codeCacheResetCount & 7);
240 }
241}
242#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800243
244/*
245 * Sleep in sigwait() until a signal arrives.
246 */
247static void* signalCatcherThreadStart(void* arg)
248{
249 Thread* self = dvmThreadSelf();
250 sigset_t mask;
251 int cc;
252
253 UNUSED_PARAMETER(arg);
254
255 LOGV("Signal catcher thread started (threadid=%d)\n", self->threadId);
256
257 /* set up mask with signals we want to handle */
258 sigemptyset(&mask);
259 sigaddset(&mask, SIGQUIT);
260 sigaddset(&mask, SIGUSR1);
Ben Chengba4fc8b2009-06-01 13:00:29 -0700261#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
262 sigaddset(&mask, SIGUSR2);
263#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800264
265 while (true) {
266 int rcvd;
267
268 dvmChangeStatus(self, THREAD_VMWAIT);
269
270 /*
271 * Signals for sigwait() must be blocked but not ignored. We
272 * block signals like SIGQUIT for all threads, so the condition
273 * is met. When the signal hits, we wake up, without any signal
274 * handlers being invoked.
275 *
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800276 * When running under GDB we occasionally return from sigwait()
277 * with EINTR (e.g. when other threads exit).
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800278 */
279loop:
280 cc = sigwait(&mask, &rcvd);
281 if (cc != 0) {
282 if (cc == EINTR) {
283 //LOGV("sigwait: EINTR\n");
284 goto loop;
285 }
286 assert(!"bad result from sigwait");
287 }
288
289 if (!gDvm.haltSignalCatcher) {
290 LOGI("threadid=%d: reacting to signal %d\n",
291 dvmThreadSelf()->threadId, rcvd);
292 }
293
294 /* set our status to RUNNING, self-suspending if GC in progress */
295 dvmChangeStatus(self, THREAD_RUNNING);
296
297 if (gDvm.haltSignalCatcher)
298 break;
299
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800300 switch (rcvd) {
301 case SIGQUIT:
302 handleSigQuit();
303 break;
304 case SIGUSR1:
305 handleSigUsr1();
306 break;
Ben Cheng1efc9c52009-06-08 18:25:27 -0700307#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800308 case SIGUSR2:
309 handleSigUsr2();
310 break;
Ben Cheng1efc9c52009-06-08 18:25:27 -0700311#endif
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800312 default:
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800313 LOGE("unexpected signal %d\n", rcvd);
Andy McFaddenc9dcf262010-03-10 13:42:22 -0800314 break;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800315 }
316 }
317
318 return NULL;
319}