Home | History | Annotate | Download | only in vm
      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()
     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()
     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  */
     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     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  */
    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  */
    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 */
    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  */
    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  */
    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