blob: 495fbf263306738763d475b03b47782598b593df [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 */
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
32static void* signalCatcherThreadStart(void* arg);
33
34/*
35 * Crank up the signal catcher thread.
36 *
37 * Returns immediately.
38 */
39bool 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 */
56void 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 */
72static 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
90bail:
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 */
99static 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 */
181static 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);
Ben Chengba4fc8b2009-06-01 13:00:29 -0700195#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
196 sigaddset(&mask, SIGUSR2);
197#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800198
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 */
216loop:
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
Ben Cheng1efc9c52009-06-08 18:25:27 -0700243#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
244 dvmCompilerDumpStats();
245#endif
246
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800247 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
Ben Chengba4fc8b2009-06-01 13:00:29 -0700263#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
264 } else if (rcvd == SIGUSR2) {
265 gDvmJit.printMe ^= true;
266 dvmCompilerDumpStats();
267#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800268 } else {
269 LOGE("unexpected signal %d\n", rcvd);
270 }
271 }
272
273 return NULL;
274}