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 */
dvmSignalCatcherStartup()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 */
dvmSignalCatcherShutdown()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 ALOGV("signal catcher has shut down");
69 }
70
71
72 /*
73 * Print the name of the current process, if we can get it.
74 */
printProcessName(const DebugOutputTarget * target)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 */
logThreadStacks(FILE * fp)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 dvmDumpJniStats(&target);
124 dvmDumpAllThreadsEx(&target, true);
125 fprintf(fp, "----- end %d -----\n", pid);
126 }
127
128
129 /*
130 * Respond to a SIGQUIT by dumping the thread stacks. Optionally dump
131 * a few other things while we're at it.
132 *
133 * Thread stacks can either go to the log or to a file designated for holding
134 * ANR traces. If we're writing to a file, we want to do it in one shot,
135 * so we can use a single O_APPEND write instead of contending for exclusive
136 * access with flock(). There may be an advantage in resuming the VM
137 * before doing the file write, so we don't stall the VM if disk I/O is
138 * bottlenecked.
139 *
140 * If JIT tuning is compiled in, dump compiler stats as well.
141 */
handleSigQuit()142 static void handleSigQuit()
143 {
144 char* traceBuf = NULL;
145 size_t traceLen;
146
147 dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP);
148
149 dvmDumpLoaderStats("sig");
150
151 if (gDvm.stackTraceFile == NULL) {
152 /* just dump to log */
153 DebugOutputTarget target;
154 dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
155 dvmDumpJniStats(&target);
156 dvmDumpAllThreadsEx(&target, true);
157 } else {
158 /* write to memory buffer */
159 FILE* memfp = open_memstream(&traceBuf, &traceLen);
160 if (memfp == NULL) {
161 ALOGE("Unable to create memstream for stack traces");
162 traceBuf = NULL; /* make sure it didn't touch this */
163 /* continue on */
164 } else {
165 logThreadStacks(memfp);
166 fclose(memfp);
167 }
168 }
169
170 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
171 dvmCompilerDumpStats();
172 #endif
173
174 if (false) dvmDumpTrackedAllocations(true);
175
176 dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP);
177
178 if (traceBuf != NULL) {
179 /*
180 * We don't know how long it will take to do the disk I/O, so put us
181 * into VMWAIT for the duration.
182 */
183 ThreadStatus oldStatus = dvmChangeStatus(dvmThreadSelf(), THREAD_VMWAIT);
184
185 /*
186 * Open the stack trace output file, creating it if necessary. It
187 * needs to be world-writable so other processes can write to it.
188 */
189 int fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
190 if (fd < 0) {
191 ALOGE("Unable to open stack trace file '%s': %s",
192 gDvm.stackTraceFile, strerror(errno));
193 } else {
194 ssize_t actual = TEMP_FAILURE_RETRY(write(fd, traceBuf, traceLen));
195 if (actual != (ssize_t) traceLen) {
196 ALOGE("Failed to write stack traces to %s (%d of %zd): %s",
197 gDvm.stackTraceFile, (int) actual, traceLen,
198 strerror(errno));
199 } else {
200 ALOGI("Wrote stack traces to '%s'", gDvm.stackTraceFile);
201 }
202 close(fd);
203 }
204
205 free(traceBuf);
206 dvmChangeStatus(dvmThreadSelf(), oldStatus);
207 }
208 }
209
210 /*
211 * Respond to a SIGUSR1 by forcing a GC.
212 */
handleSigUsr1()213 static void handleSigUsr1()
214 {
215 ALOGI("SIGUSR1 forcing GC (no HPROF)");
216 dvmCollectGarbage();
217 }
218
219 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
220 /* Sample callback function for dvmJitScanAllClassPointers */
printAllClass(void * ptr)221 void printAllClass(void *ptr)
222 {
223 ClassObject **classPP = (ClassObject **) ptr;
224 ALOGE("class %s", (*classPP)->descriptor);
225
226 }
227
228 /*
229 * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting
230 * the code cache.
231 */
handleSigUsr2()232 static void handleSigUsr2()
233 {
234 static int codeCacheResetCount = 0;
235 gDvmJit.receivedSIGUSR2 ^= true;
236 if ((--codeCacheResetCount & 7) == 0) {
237 /* Dump all class pointers in the traces */
238 dvmJitScanAllClassPointers(printAllClass);
239 gDvmJit.codeCacheFull = true;
240 } else {
241 dvmCompilerDumpStats();
242 /* Stress-test unchain all */
243 dvmJitUnchainAll();
244 ALOGD("Send %d more signals to reset the code cache",
245 codeCacheResetCount & 7);
246 }
247 dvmCheckInterpStateConsistency();
248 }
249 #endif
250
251 /*
252 * Sleep in sigwait() until a signal arrives.
253 */
signalCatcherThreadStart(void * arg)254 static void* signalCatcherThreadStart(void* arg)
255 {
256 Thread* self = dvmThreadSelf();
257 sigset_t mask;
258 int cc;
259
260 UNUSED_PARAMETER(arg);
261
262 ALOGV("Signal catcher thread started (threadid=%d)", self->threadId);
263
264 /* set up mask with signals we want to handle */
265 sigemptyset(&mask);
266 sigaddset(&mask, SIGQUIT);
267 sigaddset(&mask, SIGUSR1);
268 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
269 sigaddset(&mask, SIGUSR2);
270 #endif
271
272 while (true) {
273 int rcvd;
274
275 dvmChangeStatus(self, THREAD_VMWAIT);
276
277 /*
278 * Signals for sigwait() must be blocked but not ignored. We
279 * block signals like SIGQUIT for all threads, so the condition
280 * is met. When the signal hits, we wake up, without any signal
281 * handlers being invoked.
282 *
283 * When running under GDB we occasionally return from sigwait()
284 * with EINTR (e.g. when other threads exit).
285 */
286 loop:
287 cc = sigwait(&mask, &rcvd);
288 if (cc != 0) {
289 if (cc == EINTR) {
290 //ALOGV("sigwait: EINTR");
291 goto loop;
292 }
293 assert(!"bad result from sigwait");
294 }
295
296 if (!gDvm.haltSignalCatcher) {
297 ALOGI("threadid=%d: reacting to signal %d",
298 dvmThreadSelf()->threadId, rcvd);
299 }
300
301 /* set our status to RUNNING, self-suspending if GC in progress */
302 dvmChangeStatus(self, THREAD_RUNNING);
303
304 if (gDvm.haltSignalCatcher)
305 break;
306
307 switch (rcvd) {
308 case SIGQUIT:
309 handleSigQuit();
310 break;
311 case SIGUSR1:
312 handleSigUsr1();
313 break;
314 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
315 case SIGUSR2:
316 handleSigUsr2();
317 break;
318 #endif
319 default:
320 ALOGE("unexpected signal %d", rcvd);
321 break;
322 }
323 }
324
325 return NULL;
326 }
327