• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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