OSDN Git Service

Merge change 24840 into eclair
[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  * 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
32 static void* signalCatcherThreadStart(void* arg);
33
34 /*
35  * Crank up the signal catcher thread.
36  *
37  * Returns immediately.
38  */
39 bool 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  */
56 void 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  */
72 static 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
90 bail:
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  */
99 static 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  */
181 static 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);
195 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
196     sigaddset(&mask, SIGUSR2);
197 #endif
198
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          */
216 loop:
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
243 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
244             dvmCompilerDumpStats();
245 #endif
246
247             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
263 #if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
264         } else if (rcvd == SIGUSR2) {
265             gDvmJit.printMe ^= true;
266             dvmCompilerDumpStats();
267             /* Stress-test unchain all */
268             dvmJitUnchainAll();
269 #endif
270         } else {
271             LOGE("unexpected signal %d\n", rcvd);
272         }
273     }
274
275     return NULL;
276 }