• 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   * 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   */
dvmSignalCatcherStartup(void)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   */
dvmSignalCatcherShutdown(void)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   */
printProcessName(const DebugOutputTarget * target)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   */
logThreadStacks(void)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   */
signalCatcherThreadStart(void * arg)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  }
277