OSDN Git Service

Merge remote-tracking branch 'korg/froyo' into froyo-x86
[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) {
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) {
182         /*
183          * We don't know how long it will take to do the disk I/O, so put us
184          * into VMWAIT for the duration.
185          */
186         int oldStatus = dvmChangeStatus(dvmThreadSelf(), THREAD_VMWAIT);
187
188         /*
189          * Open the stack trace output file, creating it if necessary.  It
190          * needs to be world-writable so other processes can write to it.
191          */
192         int fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
193         if (fd < 0) {
194             LOGE("Unable to open stack trace file '%s': %s\n",
195                 gDvm.stackTraceFile, strerror(errno));
196         } else {
197             ssize_t actual = write(fd, traceBuf, traceLen);
198             if (actual != (ssize_t) traceLen) {
199                 LOGE("Failed to write stack traces to %s (%d of %zd): %s\n",
200                     gDvm.stackTraceFile, (int) actual, traceLen,
201                     strerror(errno));
202             } else {
203                 LOGI("Wrote stack traces to '%s'\n", gDvm.stackTraceFile);
204             }
205             close(fd);
206         }
207
208         free(traceBuf);
209         dvmChangeStatus(dvmThreadSelf(), oldStatus);
210     }
211 }
212
213 /*
214  * Respond to a SIGUSR1 by forcing a GC.  If we were built with HPROF
215  * support, generate an HPROF dump file.
216  *
217  * (The HPROF dump generation is not all that useful now that we have
218  * better ways to generate it.  Consider removing this in a future release.)
219  */
220 static void handleSigUsr1(void)
221 {
222 #if WITH_HPROF
223     LOGI("SIGUSR1 forcing GC and HPROF dump\n");
224     hprofDumpHeap(NULL, false);
225 #else
226     LOGI("SIGUSR1 forcing GC (no HPROF)\n");
227     dvmCollectGarbage(false);
228 #endif
229 }
230
231 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
232 /*
233  * Respond to a SIGUSR2 by dumping some JIT stats and possibly resetting
234  * the code cache.
235  */
236 static void handleSigUsr2(void)
237 {
238     static int codeCacheResetCount = 0;
239     if ((--codeCacheResetCount & 7) == 0) {
240         gDvmJit.codeCacheFull = true;
241     } else {
242         dvmCompilerDumpStats();
243         /* Stress-test unchain all */
244         dvmJitUnchainAll();
245         LOGD("Send %d more signals to rest the code cache",
246              codeCacheResetCount & 7);
247     }
248 }
249 #endif
250
251 /*
252  * Sleep in sigwait() until a signal arrives.
253  */
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     LOGV("Signal catcher thread started (threadid=%d)\n", 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                 //LOGV("sigwait: EINTR\n");
291                 goto loop;
292             }
293             assert(!"bad result from sigwait");
294         }
295
296         if (!gDvm.haltSignalCatcher) {
297             LOGI("threadid=%d: reacting to signal %d\n",
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             LOGE("unexpected signal %d\n", rcvd);
321             break;
322         }
323     }
324
325     return NULL;
326 }