OSDN Git Service

Merge "Extend a trace with a backward branch into a loop." into dalvik-dev
[android-x86/dalvik.git] / vm / SignalCatcher.c
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  */
42 bool 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  */
59 void 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  */
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  */
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     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  */
141 static void handleSigQuit(void)
142 {
143     char* traceBuf = NULL;
144     size_t traceLen;
145
146     dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP);
147
148     dvmDumpLoaderStats("sig");
149
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);
155     } else {
156         /* 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     }
167
168 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
169     dvmCompilerDumpStats();
170 #endif
171
172     if (false) dvmDumpTrackedAllocations(true);
173
174     dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP);
175
176     if (traceBuf != NULL) {
177         /*
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.
180          */
181         int oldStatus = dvmChangeStatus(dvmThreadSelf(), THREAD_VMWAIT);
182
183         /*
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.
186          */
187         int fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
188         if (fd < 0) {
189             LOGE("Unable to open stack trace file '%s': %s\n",
190                 gDvm.stackTraceFile, strerror(errno));
191         } else {
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\n",
195                     gDvm.stackTraceFile, (int) actual, traceLen,
196                     strerror(errno));
197             } else {
198                 LOGI("Wrote stack traces to '%s'\n", gDvm.stackTraceFile);
199             }
200             close(fd);
201         }
202
203         free(traceBuf);
204         dvmChangeStatus(dvmThreadSelf(), oldStatus);
205     }
206 }
207
208 /*
209  * Respond to a SIGUSR1 by forcing a GC.
210  */
211 static void handleSigUsr1(void)
212 {
213     LOGI("SIGUSR1 forcing GC (no HPROF)\n");
214     dvmCollectGarbage();
215 }
216
217 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
218 /* Sample callback function for dvmJitScanAllClassPointers */
219 void printAllClass(void *ptr)
220 {
221     ClassObject **classPP = (ClassObject **) ptr;
222     LOGE("class %s", (*classPP)->descriptor);
223
224 }
225
226 /*
227  * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting
228  * the code cache.
229  */
230 static void handleSigUsr2(void)
231 {
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;
238     } else {
239         dvmCompilerDumpStats();
240         /* Stress-test unchain all */
241         dvmJitUnchainAll();
242         LOGD("Send %d more signals to rest the code cache",
243              codeCacheResetCount & 7);
244     }
245 }
246 #endif
247
248 /*
249  * Sleep in sigwait() until a signal arrives.
250  */
251 static void* signalCatcherThreadStart(void* arg)
252 {
253     Thread* self = dvmThreadSelf();
254     sigset_t mask;
255     int cc;
256
257     UNUSED_PARAMETER(arg);
258
259     LOGV("Signal catcher thread started (threadid=%d)\n", self->threadId);
260
261     /* set up mask with signals we want to handle */
262     sigemptyset(&mask);
263     sigaddset(&mask, SIGQUIT);
264     sigaddset(&mask, SIGUSR1);
265 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
266     sigaddset(&mask, SIGUSR2);
267 #endif
268
269     while (true) {
270         int rcvd;
271
272         dvmChangeStatus(self, THREAD_VMWAIT);
273
274         /*
275          * Signals for sigwait() must be blocked but not ignored.  We
276          * block signals like SIGQUIT for all threads, so the condition
277          * is met.  When the signal hits, we wake up, without any signal
278          * handlers being invoked.
279          *
280          * When running under GDB we occasionally return from sigwait()
281          * with EINTR (e.g. when other threads exit).
282          */
283 loop:
284         cc = sigwait(&mask, &rcvd);
285         if (cc != 0) {
286             if (cc == EINTR) {
287                 //LOGV("sigwait: EINTR\n");
288                 goto loop;
289             }
290             assert(!"bad result from sigwait");
291         }
292
293         if (!gDvm.haltSignalCatcher) {
294             LOGI("threadid=%d: reacting to signal %d\n",
295                 dvmThreadSelf()->threadId, rcvd);
296         }
297
298         /* set our status to RUNNING, self-suspending if GC in progress */
299         dvmChangeStatus(self, THREAD_RUNNING);
300
301         if (gDvm.haltSignalCatcher)
302             break;
303
304         switch (rcvd) {
305         case SIGQUIT:
306             handleSigQuit();
307             break;
308         case SIGUSR1:
309             handleSigUsr1();
310             break;
311 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
312         case SIGUSR2:
313             handleSigUsr2();
314             break;
315 #endif
316         default:
317             LOGE("unexpected signal %d\n", rcvd);
318             break;
319         }
320     }
321
322     return NULL;
323 }