예제 #1
0
void dvmCompilerUpdateGlobalState()
{
    bool jitActive;
    bool jitActivate;
    bool needUnchain = false;

    /*
     * The tableLock might not be initialized yet by the compiler thread if
     * debugger is attached from the very beginning of the VM launch. If
     * pProfTableCopy is NULL, the lock is not initialized yet and we don't
     * need to refresh anything either.
     */
    if (gDvmJit.pProfTableCopy == NULL) {
        return;
    }

    /*
     * On the first enabling of method tracing, switch the compiler
     * into a mode that includes trace support for invokes and returns.
     * If there are any existing translations, flush them.  NOTE:  we
     * can't blindly flush the translation cache because this code
     * may be executed before the compiler thread has finished
     * initialization.
     */
    if ((gDvm.activeProfilers != 0) &&
        !gDvmJit.methodTraceSupport) {
        bool resetRequired;
        /*
         * compilerLock will prevent new compilations from being
         * installed while we are working.
         */
        dvmLockMutex(&gDvmJit.compilerLock);
        gDvmJit.cacheVersion++; // invalidate compilations in flight
        gDvmJit.methodTraceSupport = true;
        resetRequired = (gDvmJit.numCompilations != 0);
        dvmUnlockMutex(&gDvmJit.compilerLock);
        if (resetRequired) {
            dvmSuspendAllThreads(SUSPEND_FOR_CC_RESET);
            resetCodeCache();
            dvmResumeAllThreads(SUSPEND_FOR_CC_RESET);
        }
    }

    dvmLockMutex(&gDvmJit.tableLock);
    jitActive = gDvmJit.pProfTable != NULL;
    jitActivate = !dvmDebuggerOrProfilerActive();

    if (jitActivate && !jitActive) {
        gDvmJit.pProfTable = gDvmJit.pProfTableCopy;
    } else if (!jitActivate && jitActive) {
        gDvmJit.pProfTable = NULL;
        needUnchain = true;
    }
    dvmUnlockMutex(&gDvmJit.tableLock);
    if (needUnchain)
        dvmJitUnchainAll();
    // Make sure all threads have current values
    dvmJitUpdateThreadStateAll();
}
예제 #2
0
/*
 * Initiate garbage collection.
 *
 * NOTES:
 * - If we don't hold gDvm.threadListLock, it's possible for a thread to
 *   be added to the thread list while we work.  The thread should NOT
 *   start executing, so this is only interesting when we start chasing
 *   thread stacks.  (Before we do so, grab the lock.)
 *
 * We are not allowed to GC when the debugger has suspended the VM, which
 * is awkward because debugger requests can cause allocations.  The easiest
 * way to enforce this is to refuse to GC on an allocation made by the
 * JDWP thread -- we have to expand the heap or fail.
 */
void dvmCollectGarbageInternal(const GcSpec* spec)
{
    GcHeap *gcHeap = gDvm.gcHeap;
    u4 gcEnd = 0;
    u4 rootStart = 0 , rootEnd = 0;
    u4 dirtyStart = 0, dirtyEnd = 0;
    size_t numObjectsFreed, numBytesFreed;
    size_t currAllocated, currFootprint;
    size_t percentFree;
    int oldThreadPriority = INT_MAX;

    /* The heap lock must be held.
     */

    if (gcHeap->gcRunning) {
        LOGW_HEAP("Attempted recursive GC");
        return;
    }

    gcHeap->gcRunning = true;

    rootStart = dvmGetRelativeTimeMsec();
    dvmSuspendAllThreads(SUSPEND_FOR_GC);

    /*
     * If we are not marking concurrently raise the priority of the
     * thread performing the garbage collection.
     */
    if (!spec->isConcurrent) {
        oldThreadPriority = os_raiseThreadPriority();
    }
    if (gDvm.preVerify) {
        LOGV_HEAP("Verifying roots and heap before GC");
        verifyRootsAndHeap();
    }

    dvmMethodTraceGCBegin();

    /* Set up the marking context.
     */
    if (!dvmHeapBeginMarkStep(spec->isPartial)) {
        LOGE_HEAP("dvmHeapBeginMarkStep failed; aborting");
        dvmAbort();
    }

    /* Mark the set of objects that are strongly reachable from the roots.
     */
    LOGD_HEAP("Marking...");
    dvmHeapMarkRootSet();

    /* dvmHeapScanMarkedObjects() will build the lists of known
     * instances of the Reference classes.
     */
    assert(gcHeap->softReferences == NULL);
    assert(gcHeap->weakReferences == NULL);
    assert(gcHeap->finalizerReferences == NULL);
    assert(gcHeap->phantomReferences == NULL);
    assert(gcHeap->clearedReferences == NULL);

    if (spec->isConcurrent) {
        /*
         * Resume threads while tracing from the roots.  We unlock the
         * heap to allow mutator threads to allocate from free space.
         */
        dvmClearCardTable();
        dvmUnlockHeap();
        dvmResumeAllThreads(SUSPEND_FOR_GC);
        rootEnd = dvmGetRelativeTimeMsec();
    }

    /* Recursively mark any objects that marked objects point to strongly.
     * If we're not collecting soft references, soft-reachable
     * objects will also be marked.
     */
    LOGD_HEAP("Recursing...");
    dvmHeapScanMarkedObjects();

    if (spec->isConcurrent) {
        /*
         * Re-acquire the heap lock and perform the final thread
         * suspension.
         */
        dirtyStart = dvmGetRelativeTimeMsec();
        dvmLockHeap();
        dvmSuspendAllThreads(SUSPEND_FOR_GC);
        /*
         * As no barrier intercepts root updates, we conservatively
         * assume all roots may be gray and re-mark them.
         */
        dvmHeapReMarkRootSet();
        /*
         * With the exception of reference objects and weak interned
         * strings, all gray objects should now be on dirty cards.
         */
        if (gDvm.verifyCardTable) {
            dvmVerifyCardTable();
        }
        /*
         * Recursively mark gray objects pointed to by the roots or by
         * heap objects dirtied during the concurrent mark.
         */
        dvmHeapReScanMarkedObjects();
    }

    /*
     * All strongly-reachable objects have now been marked.  Process
     * weakly-reachable objects discovered while tracing.
     */
    dvmHeapProcessReferences(&gcHeap->softReferences,
                             spec->doPreserve == false,
                             &gcHeap->weakReferences,
                             &gcHeap->finalizerReferences,
                             &gcHeap->phantomReferences);

#if defined(WITH_JIT)
    /*
     * Patching a chaining cell is very cheap as it only updates 4 words. It's
     * the overhead of stopping all threads and synchronizing the I/D cache
     * that makes it expensive.
     *
     * Therefore we batch those work orders in a queue and go through them
     * when threads are suspended for GC.
     */
    dvmCompilerPerformSafePointChecks();
#endif

    LOGD_HEAP("Sweeping...");

    dvmHeapSweepSystemWeaks();

    /*
     * Live objects have a bit set in the mark bitmap, swap the mark
     * and live bitmaps.  The sweep can proceed concurrently viewing
     * the new live bitmap as the old mark bitmap, and vice versa.
     */
    dvmHeapSourceSwapBitmaps();

    if (gDvm.postVerify) {
        LOGV_HEAP("Verifying roots and heap after GC");
        verifyRootsAndHeap();
    }

    if (spec->isConcurrent) {
        dvmUnlockHeap();
        dvmResumeAllThreads(SUSPEND_FOR_GC);
        dirtyEnd = dvmGetRelativeTimeMsec();
    }
    dvmHeapSweepUnmarkedObjects(spec->isPartial, spec->isConcurrent,
                                &numObjectsFreed, &numBytesFreed);
    LOGD_HEAP("Cleaning up...");
    dvmHeapFinishMarkStep();
    if (spec->isConcurrent) {
        dvmLockHeap();
    }

    LOGD_HEAP("Done.");

    /* Now's a good time to adjust the heap size, since
     * we know what our utilization is.
     *
     * This doesn't actually resize any memory;
     * it just lets the heap grow more when necessary.
     */
    dvmHeapSourceGrowForUtilization();

    currAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
    currFootprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);

    dvmMethodTraceGCEnd();
    LOGV_HEAP("GC finished");

    gcHeap->gcRunning = false;

    LOGV_HEAP("Resuming threads");

    if (spec->isConcurrent) {
        /*
         * Wake-up any threads that blocked after a failed allocation
         * request.
         */
        dvmBroadcastCond(&gDvm.gcHeapCond);
    }

    if (!spec->isConcurrent) {
        dvmResumeAllThreads(SUSPEND_FOR_GC);
        dirtyEnd = dvmGetRelativeTimeMsec();
        /*
         * Restore the original thread scheduling priority if it was
         * changed at the start of the current garbage collection.
         */
        if (oldThreadPriority != INT_MAX) {
            os_lowerThreadPriority(oldThreadPriority);
        }
    }

    /*
     * Move queue of pending references back into Java.
     */
    dvmEnqueueClearedReferences(&gDvm.gcHeap->clearedReferences);

    gcEnd = dvmGetRelativeTimeMsec();
    percentFree = 100 - (size_t)(100.0f * (float)currAllocated / currFootprint);
    if (!spec->isConcurrent) {
        u4 markSweepTime = dirtyEnd - rootStart;
        u4 gcTime = gcEnd - rootStart;
        bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
        ALOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, paused %ums, total %ums",
             spec->reason,
             isSmall ? "<" : "",
             numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
             percentFree,
             currAllocated / 1024, currFootprint / 1024,
             markSweepTime, gcTime);
    } else {
        u4 rootTime = rootEnd - rootStart;
        u4 dirtyTime = dirtyEnd - dirtyStart;
        u4 gcTime = gcEnd - rootStart;
        bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
        ALOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, paused %ums+%ums, total %ums",
             spec->reason,
             isSmall ? "<" : "",
             numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
             percentFree,
             currAllocated / 1024, currFootprint / 1024,
             rootTime, dirtyTime, gcTime);
    }
    if (gcHeap->ddmHpifWhen != 0) {
        LOGD_HEAP("Sending VM heap info to DDM");
        dvmDdmSendHeapInfo(gcHeap->ddmHpifWhen, false);
    }
    if (gcHeap->ddmHpsgWhen != 0) {
        LOGD_HEAP("Dumping VM heap to DDM");
        dvmDdmSendHeapSegments(false, false);
    }
    if (gcHeap->ddmNhsgWhen != 0) {
        LOGD_HEAP("Dumping native heap to DDM");
        dvmDdmSendHeapSegments(false, true);
    }
}
예제 #3
0
static bool compilerThreadStartup(void)
{
    JitEntry *pJitTable = NULL;
    unsigned char *pJitProfTable = NULL;
    JitTraceProfCounters *pJitTraceProfCounters = NULL;
    unsigned int i;

    if (!dvmCompilerArchInit())
        goto fail;

    /*
     * Setup the code cache if we have not inherited a valid code cache
     * from the zygote.
     */
    if (gDvmJit.codeCache == NULL) {
        if (!dvmCompilerSetupCodeCache())
            goto fail;
    }

    /* Allocate the initial arena block */
    if (dvmCompilerHeapInit() == false) {
        goto fail;
    }

    /* Cache the thread pointer */
    gDvmJit.compilerThread = dvmThreadSelf();

    dvmLockMutex(&gDvmJit.compilerLock);

    /* Track method-level compilation statistics */
    gDvmJit.methodStatsTable =  dvmHashTableCreate(32, NULL);

#if defined(WITH_JIT_TUNING)
    gDvm.verboseShutdown = true;
#endif

    dvmUnlockMutex(&gDvmJit.compilerLock);

    /* Set up the JitTable */

    /* Power of 2? */
    assert(gDvmJit.jitTableSize &&
           !(gDvmJit.jitTableSize & (gDvmJit.jitTableSize - 1)));

    dvmInitMutex(&gDvmJit.tableLock);
    dvmLockMutex(&gDvmJit.tableLock);
    pJitTable = (JitEntry*)
                calloc(gDvmJit.jitTableSize, sizeof(*pJitTable));
    if (!pJitTable) {
        ALOGE("jit table allocation failed");
        dvmUnlockMutex(&gDvmJit.tableLock);
        goto fail;
    }
    /*
     * NOTE: the profile table must only be allocated once, globally.
     * Profiling is turned on and off by nulling out gDvm.pJitProfTable
     * and then restoring its original value.  However, this action
     * is not synchronized for speed so threads may continue to hold
     * and update the profile table after profiling has been turned
     * off by null'ng the global pointer.  Be aware.
     */
    pJitProfTable = (unsigned char *)malloc(JIT_PROF_SIZE);
    if (!pJitProfTable) {
        ALOGE("jit prof table allocation failed");
        free(pJitTable);
        dvmUnlockMutex(&gDvmJit.tableLock);
        goto fail;
    }
    memset(pJitProfTable, gDvmJit.threshold, JIT_PROF_SIZE);
    for (i=0; i < gDvmJit.jitTableSize; i++) {
       pJitTable[i].u.info.chain = gDvmJit.jitTableSize;
    }
    /* Is chain field wide enough for termination pattern? */
    assert(pJitTable[0].u.info.chain == gDvmJit.jitTableSize);

    /* Allocate the trace profiling structure */
    pJitTraceProfCounters = (JitTraceProfCounters*)
                             calloc(1, sizeof(*pJitTraceProfCounters));
    if (!pJitTraceProfCounters) {
        ALOGE("jit trace prof counters allocation failed");
        free(pJitTable);
        free(pJitProfTable);
        dvmUnlockMutex(&gDvmJit.tableLock);
        goto fail;
    }

    gDvmJit.pJitEntryTable = pJitTable;
    gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1;
    gDvmJit.jitTableEntriesUsed = 0;
    gDvmJit.compilerHighWater =
        COMPILER_WORK_QUEUE_SIZE - (COMPILER_WORK_QUEUE_SIZE/4);
    /*
     * If the VM is launched with wait-on-the-debugger, we will need to hide
     * the profile table here
     */
    gDvmJit.pProfTable = dvmDebuggerOrProfilerActive() ? NULL : pJitProfTable;
    gDvmJit.pProfTableCopy = pJitProfTable;
    gDvmJit.pJitTraceProfCounters = pJitTraceProfCounters;
    dvmJitUpdateThreadStateAll();
    dvmUnlockMutex(&gDvmJit.tableLock);

    /* Signal running threads to refresh their cached pJitTable pointers */
    dvmSuspendAllThreads(SUSPEND_FOR_REFRESH);
    dvmResumeAllThreads(SUSPEND_FOR_REFRESH);

    /* Enable signature breakpoints by customizing the following code */
#if defined(SIGNATURE_BREAKPOINT)
    /*
     * Suppose one sees the following native crash in the bugreport:
     * I/DEBUG   ( 1638): Build fingerprint: 'unknown'
     * I/DEBUG   ( 1638): pid: 2468, tid: 2507  >>> com.google.android.gallery3d
     * I/DEBUG   ( 1638): signal 11 (SIGSEGV), fault addr 00001400
     * I/DEBUG   ( 1638):  r0 44ea7190  r1 44e4f7b8  r2 44ebc710  r3 00000000
     * I/DEBUG   ( 1638):  r4 00000a00  r5 41862dec  r6 4710dc10  r7 00000280
     * I/DEBUG   ( 1638):  r8 ad010f40  r9 46a37a12  10 001116b0  fp 42a78208
     * I/DEBUG   ( 1638):  ip 00000090  sp 4710dbc8  lr ad060e67  pc 46b90682
     * cpsr 00000030
     * I/DEBUG   ( 1638):  #00  pc 46b90682 /dev/ashmem/dalvik-jit-code-cache
     * I/DEBUG   ( 1638):  #01  pc 00060e62  /system/lib/libdvm.so
     *
     * I/DEBUG   ( 1638): code around pc:
     * I/DEBUG   ( 1638): 46b90660 6888d01c 34091dcc d2174287 4a186b68
     * I/DEBUG   ( 1638): 46b90670 d0052800 68006809 28004790 6b68d00e
     * I/DEBUG   ( 1638): 46b90680 512000bc 37016eaf 6ea866af 6f696028
     * I/DEBUG   ( 1638): 46b90690 682a6069 429a686b e003da08 6df1480b
     * I/DEBUG   ( 1638): 46b906a0 1c2d4788 47806d70 46a378fa 47806d70
     *
     * Clearly it is a JIT bug. To find out which translation contains the
     * offending code, the content of the memory dump around the faulting PC
     * can be pasted into the gDvmJit.signatureBreakpoint[] array and next time
     * when a similar compilation is being created, the JIT compiler replay the
     * trace in the verbose mode and one can investigate the instruction
     * sequence in details.
     *
     * The length of the signature may need additional experiments to determine.
     * The rule of thumb is don't include PC-relative instructions in the
     * signature since it may be affected by the alignment of the compiled code.
     * However, a signature that's too short might increase the chance of false
     * positive matches. Using gdbjithelper to disassembly the memory content
     * first might be a good companion approach.
     *
     * For example, if the next 4 words starting from 46b90680 is pasted into
     * the data structure:
     */

    gDvmJit.signatureBreakpointSize = 4;
    gDvmJit.signatureBreakpoint =
        malloc(sizeof(u4) * gDvmJit.signatureBreakpointSize);
    gDvmJit.signatureBreakpoint[0] = 0x512000bc;
    gDvmJit.signatureBreakpoint[1] = 0x37016eaf;
    gDvmJit.signatureBreakpoint[2] = 0x6ea866af;
    gDvmJit.signatureBreakpoint[3] = 0x6f696028;

    /*
     * The following log will be printed when a match is found in subsequent
     * testings:
     *
     * D/dalvikvm( 2468): Signature match starting from offset 0x34 (4 words)
     * D/dalvikvm( 2468): --------
     * D/dalvikvm( 2468): Compiler: Building trace for computeVisibleItems,
     * offset 0x1f7
     * D/dalvikvm( 2468): 0x46a37a12: 0x0090 add-int v42, v5, v26
     * D/dalvikvm( 2468): 0x46a37a16: 0x004d aput-object v13, v14, v42
     * D/dalvikvm( 2468): 0x46a37a1a: 0x0028 goto, (#0), (#0)
     * D/dalvikvm( 2468): 0x46a3794e: 0x00d8 add-int/lit8 v26, v26, (#1)
     * D/dalvikvm( 2468): 0x46a37952: 0x0028 goto, (#0), (#0)
     * D/dalvikvm( 2468): 0x46a378ee: 0x0002 move/from16 v0, v26, (#0)
     * D/dalvikvm( 2468): 0x46a378f2: 0x0002 move/from16 v1, v29, (#0)
     * D/dalvikvm( 2468): 0x46a378f6: 0x0035 if-ge v0, v1, (#10)
     * D/dalvikvm( 2468): TRACEINFO (554): 0x46a37624
     * Lcom/cooliris/media/GridLayer;computeVisibleItems 0x1f7 14 of 934, 8
     * blocks
     *     :
     *     :
     * D/dalvikvm( 2468): 0x20 (0020): ldr     r0, [r5, #52]
     * D/dalvikvm( 2468): 0x22 (0022): ldr     r2, [pc, #96]
     * D/dalvikvm( 2468): 0x24 (0024): cmp     r0, #0
     * D/dalvikvm( 2468): 0x26 (0026): beq     0x00000034
     * D/dalvikvm( 2468): 0x28 (0028): ldr     r1, [r1, #0]
     * D/dalvikvm( 2468): 0x2a (002a): ldr     r0, [r0, #0]
     * D/dalvikvm( 2468): 0x2c (002c): blx     r2
     * D/dalvikvm( 2468): 0x2e (002e): cmp     r0, #0
     * D/dalvikvm( 2468): 0x30 (0030): beq     0x00000050
     * D/dalvikvm( 2468): 0x32 (0032): ldr     r0, [r5, #52]
     * D/dalvikvm( 2468): 0x34 (0034): lsls    r4, r7, #2
     * D/dalvikvm( 2468): 0x36 (0036): str     r0, [r4, r4]
     * D/dalvikvm( 2468): -------- dalvik offset: 0x01fb @ goto, (#0), (#0)
     * D/dalvikvm( 2468): L0x0195:
     * D/dalvikvm( 2468): -------- dalvik offset: 0x0195 @ add-int/lit8 v26,
     * v26, (#1)
     * D/dalvikvm( 2468): 0x38 (0038): ldr     r7, [r5, #104]
     * D/dalvikvm( 2468): 0x3a (003a): adds    r7, r7, #1
     * D/dalvikvm( 2468): 0x3c (003c): str     r7, [r5, #104]
     * D/dalvikvm( 2468): -------- dalvik offset: 0x0197 @ goto, (#0), (#0)
     * D/dalvikvm( 2468): L0x0165:
     * D/dalvikvm( 2468): -------- dalvik offset: 0x0165 @ move/from16 v0, v26,
     * (#0)
     * D/dalvikvm( 2468): 0x3e (003e): ldr     r0, [r5, #104]
     * D/dalvikvm( 2468): 0x40 (0040): str     r0, [r5, #0]
     *
     * The "str r0, [r4, r4]" is indeed the culprit of the native crash.
     */
#endif

    return true;

fail:
    return false;

}
/*
 * Sleep in sigwait() until a signal arrives.
 */
static void* signalCatcherThreadStart(void* arg)
{
    Thread* self = dvmThreadSelf();
    sigset_t mask;
    int cc;

    UNUSED_PARAMETER(arg);

    LOGV("Signal catcher thread started (threadid=%d)\n", self->threadId);

    /* set up mask with signals we want to handle */
    sigemptyset(&mask);
    sigaddset(&mask, SIGQUIT);
    sigaddset(&mask, SIGUSR1);

    while (true) {
        int rcvd;

        dvmChangeStatus(self, THREAD_VMWAIT);

        /*
         * Signals for sigwait() must be blocked but not ignored.  We
         * block signals like SIGQUIT for all threads, so the condition
         * is met.  When the signal hits, we wake up, without any signal
         * handlers being invoked.
         *
         * We want to suspend all other threads, so that it's safe to
         * traverse their stacks.
         *
         * When running under GDB we occasionally return with EINTR (e.g.
         * when other threads exit).
         */
loop:
        cc = sigwait(&mask, &rcvd);
        if (cc != 0) {
            if (cc == EINTR) {
                //LOGV("sigwait: EINTR\n");
                goto loop;
            }
            assert(!"bad result from sigwait");
        }

        if (!gDvm.haltSignalCatcher) {
            LOGI("threadid=%d: reacting to signal %d\n",
                dvmThreadSelf()->threadId, rcvd);
        }

        /* set our status to RUNNING, self-suspending if GC in progress */
        dvmChangeStatus(self, THREAD_RUNNING);

        if (gDvm.haltSignalCatcher)
            break;

        if (rcvd == SIGQUIT) {
            dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP);
            dvmDumpLoaderStats("sig");

            logThreadStacks();

            if (false) {
                dvmLockMutex(&gDvm.jniGlobalRefLock);
                dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
                dvmUnlockMutex(&gDvm.jniGlobalRefLock);
            }

            //dvmDumpTrackedAllocations(true);
            dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP);
        } else if (rcvd == SIGUSR1) {
#if WITH_HPROF
            LOGI("SIGUSR1 forcing GC and HPROF dump\n");
            hprofDumpHeap(NULL);
#else
            LOGI("SIGUSR1 forcing GC (no HPROF)\n");
            dvmCollectGarbage(false);
#endif
        } else {
            LOGE("unexpected signal %d\n", rcvd);
        }
    }

    return NULL;
}
예제 #5
0
/*
 * Respond to a SIGQUIT by dumping the thread stacks.  Optionally dump
 * a few other things while we're at it.
 *
 * Thread stacks can either go to the log or to a file designated for holding
 * ANR traces.  If we're writing to a file, we want to do it in one shot,
 * so we can use a single O_APPEND write instead of contending for exclusive
 * access with flock().  There may be an advantage in resuming the VM
 * before doing the file write, so we don't stall the VM if disk I/O is
 * bottlenecked.
 *
 * If JIT tuning is compiled in, dump compiler stats as well.
 */
static void handleSigQuit()
{
    char* traceBuf = NULL;
    size_t traceLen;

    dvmSuspendAllThreads(SUSPEND_FOR_STACK_DUMP);

    dvmDumpLoaderStats("sig");

    if (gDvm.stackTraceFile == NULL) {
        /* just dump to log */
        DebugOutputTarget target;
        dvmCreateLogOutputTarget(&target, ANDROID_LOG_INFO, LOG_TAG);
        dvmDumpJniStats(&target);
        dvmDumpAllThreadsEx(&target, true);
    } else {
        /* write to memory buffer */
        FILE* memfp = open_memstream(&traceBuf, &traceLen);
        if (memfp == NULL) {
            ALOGE("Unable to create memstream for stack traces");
            traceBuf = NULL;        /* make sure it didn't touch this */
            /* continue on */
        } else {
            logThreadStacks(memfp);
            fclose(memfp);
        }
    }

#if defined(WITH_JIT) && defined(WITH_JIT_TUNING)
    dvmCompilerDumpStats();
#endif

    if (false) dvmDumpTrackedAllocations(true);

    dvmResumeAllThreads(SUSPEND_FOR_STACK_DUMP);

    if (traceBuf != NULL) {
        /*
         * We don't know how long it will take to do the disk I/O, so put us
         * into VMWAIT for the duration.
         */
        ThreadStatus oldStatus = dvmChangeStatus(dvmThreadSelf(), THREAD_VMWAIT);

        /*
         * Open the stack trace output file, creating it if necessary.  It
         * needs to be world-writable so other processes can write to it.
         */
        int fd = open(gDvm.stackTraceFile, O_WRONLY | O_APPEND | O_CREAT, 0666);
        if (fd < 0) {
            ALOGE("Unable to open stack trace file '%s': %s",
                gDvm.stackTraceFile, strerror(errno));
        } else {
            ssize_t actual = write(fd, traceBuf, traceLen);
            if (actual != (ssize_t) traceLen) {
                ALOGE("Failed to write stack traces to %s (%d of %zd): %s",
                    gDvm.stackTraceFile, (int) actual, traceLen,
                    strerror(errno));
            } else {
                ALOGI("Wrote stack traces to '%s'", gDvm.stackTraceFile);
            }
            close(fd);
        }

        free(traceBuf);
        dvmChangeStatus(dvmThreadSelf(), oldStatus);
    }
}
예제 #6
0
/**
 * @brief 更新编译器全局状态
 * @note 在"vm/Profile.cpp"中的updateActiveProfilers中被调用
 */
void dvmCompilerUpdateGlobalState()
{
    bool jitActive;
    bool jitActivate;
    bool needUnchain = false;

    /*
     * The tableLock might not be initialized yet by the compiler thread if
     * debugger is attached from the very beginning of the VM launch. If
     * pProfTableCopy is NULL, the lock is not initialized yet and we don't
     * need to refresh anything either.
     */
	/*
	 * 如果在调试器附加到虚拟机启动器的非常早之前,编译器线程对tableLock
	 * 并没有初始化完成。这是我们不能更新任何状态。
	 */
    if (gDvmJit.pProfTableCopy == NULL) {
        return;
    }

    /*
     * On the first enabling of method tracing, switch the compiler
     * into a mode that includes trace support for invokes and returns.
     * If there are any existing translations, flush them.  NOTE:  we
     * can't blindly flush the translation cache because this code
     * may be executed before the compiler thread has finished
     * initialization.
     */

	/*
	 * 第一次启用函数tracing,转化编译器到保护支持invokes与returns
	 * 指令的trace格式。如果已经存在了一些编译代码则直接刷入他们到缓存中。
	 * NOTE:我们不能在编译器线程未初始化完成之前刷入代码
	 */
	/* activeProfilers 表明 开启profiler */
    if ((gDvm.activeProfilers != 0) &&
        !gDvmJit.methodTraceSupport) {	/* 这里表明第一次启动tracing */
        bool resetRequired;
        /*
         * compilerLock will prevent new compilations from being
         * installed while we are working.
         */
        dvmLockMutex(&gDvmJit.compilerLock);
		/* 增加缓冲版本,无效编译 */
        gDvmJit.cacheVersion++; // invalidate compilations in flight
        gDvmJit.methodTraceSupport = true;	/* 开启 */
        resetRequired = (gDvmJit.numCompilations != 0);	/* 检测是否重设代码缓冲区*/
        dvmUnlockMutex(&gDvmJit.compilerLock);
        if (resetRequired) {
            dvmSuspendAllThreads(SUSPEND_FOR_CC_RESET);
            resetCodeCache();		/* 重新更新代码缓冲区 */
            dvmResumeAllThreads(SUSPEND_FOR_CC_RESET);
        }
    }

    dvmLockMutex(&gDvmJit.tableLock);
	/* 通过判断pProfTable表判断JIT是否被激活 */
    jitActive = gDvmJit.pProfTable != NULL;
    jitActivate = !dvmDebuggerOrProfilerActive();	/* 处于调试阶段或者profile开启 */

	/* jitActivate为TRUE表明处于调试阶段 */
    if (jitActivate && !jitActive) {
        gDvmJit.pProfTable = gDvmJit.pProfTableCopy;	/* 处于调试获取副本 */
    } else if (!jitActivate && jitActive) {				/* 处于正常运行 */
        gDvmJit.pProfTable = NULL;
        needUnchain = true;/* 需要解链操作 */
    }
    dvmUnlockMutex(&gDvmJit.tableLock);
    if (needUnchain)				/* 不再调试模式下为TRUE */
        dvmJitUnchainAll();			
    // Make sure all threads have current values
	/* 对所有线程设置JitTable表 */
    dvmJitUpdateThreadStateAll();
}