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(); }
/* Architecture-specific initializations and checks go here */ bool dvmCompilerArchVariantInit(void) { int i = 0; /* * Then, populate the templateEntryOffsets array with the offsets from the * the dvmCompilerTemplateStart symbol for each template. */ #define JIT_TEMPLATE(X) templateEntryOffsets[i++] = \ (intptr_t) dvmCompiler_TEMPLATE_##X - (intptr_t) dvmCompilerTemplateStart; #include "../../../template/armv6k-vfp/TemplateOpList.h" #undef JIT_TEMPLATE /* Target-specific configuration */ gDvmJit.jitTableSize = 1 << 9; // 512 gDvmJit.jitTableMask = gDvmJit.jitTableSize - 1; gDvmJit.threshold = 200; gDvmJit.codeCacheSize = 512*1024; #if defined(WITH_SELF_VERIFICATION) /* Force into blocking mode */ gDvmJit.blockingMode = true; gDvm.nativeDebuggerActive = true; #endif /* Codegen-specific assumptions */ assert(OFFSETOF_MEMBER(ClassObject, vtable) < 128 && (OFFSETOF_MEMBER(ClassObject, vtable) & 0x3) == 0); assert(OFFSETOF_MEMBER(ArrayObject, length) < 128 && (OFFSETOF_MEMBER(ArrayObject, length) & 0x3) == 0); assert(OFFSETOF_MEMBER(ArrayObject, contents) < 256); /* Up to 5 args are pushed on top of FP - sizeofStackSaveArea */ assert(sizeof(StackSaveArea) < 236); /* * EA is calculated by doing "Rn + imm5 << 2". Make sure that the last * offset from the struct is less than 128. */ if ((offsetof(Thread, jitToInterpEntries) + sizeof(struct JitToInterpEntries)) >= 128) { LOGE("Thread.jitToInterpEntries size overflow"); dvmAbort(); } /* No method JIT for Thumb backend */ gDvmJit.disableOpt |= (1 << kMethodJit); // Make sure all threads have current values dvmJitUpdateThreadStateAll(); return true; }
/** * 关闭Jit编译器 */ void dvmCompilerShutdown(void) { void *threadReturn; /* Disable new translation requests */ /* 关闭新的编译请求 */ gDvmJit.pProfTable = NULL; gDvmJit.pProfTableCopy = NULL; dvmJitUpdateThreadStateAll(); /* 更新所有线程状态 */ /* * 以下代码应该是检测在虚拟机关闭之前等待所有编译工作队列完成 * dvmCompilerDumpStats()函数应该会更新所有工作队列的当前状态 * gDvmJit.compilerQueueLength会随着这个函数进行更新,这个常数 * 即是当前工作队列的数量。 */ if (gDvm.verboseShutdown || gDvmJit.profileMode == kTraceProfilingContinuous) { dvmCompilerDumpStats(); while (gDvmJit.compilerQueueLength) sleep(5); } /* 如果编译器工作线程存在 */ if (gDvmJit.compilerHandle) { gDvmJit.haltCompilerThread = true; /* 设置关闭标志为true */ /* 发送关闭信号 */ dvmLockMutex(&gDvmJit.compilerLock); pthread_cond_signal(&gDvmJit.compilerQueueActivity); dvmUnlockMutex(&gDvmJit.compilerLock); /* 关闭compilerThreadStart线程 */ if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0) ALOGW("Compiler thread join failed"); else if (gDvm.verboseShutdown) ALOGD("Compiler thread has shut down"); } /* Break loops within the translation cache */ dvmJitUnchainAll(); /* * NOTE: our current implementatation doesn't allow for the compiler * thread to be restarted after it exits here. We aren't freeing * the JitTable or the ProfTable because threads which still may be * running or in the process of shutting down may hold references to * * them. */ }
void dvmCompilerShutdown(void) { void *threadReturn; /* Disable new translation requests */ gDvmJit.pProfTable = NULL; gDvmJit.pProfTableCopy = NULL; dvmJitUpdateThreadStateAll(); if (gDvm.verboseShutdown || gDvmJit.profileMode == kTraceProfilingContinuous) { dvmCompilerDumpStats(); while (gDvmJit.compilerQueueLength) sleep(5); } if (gDvmJit.compilerHandle) { gDvmJit.haltCompilerThread = true; dvmLockMutex(&gDvmJit.compilerLock); pthread_cond_signal(&gDvmJit.compilerQueueActivity); dvmUnlockMutex(&gDvmJit.compilerLock); if (pthread_join(gDvmJit.compilerHandle, &threadReturn) != 0) ALOGW("Compiler thread join failed"); else if (gDvm.verboseShutdown) ALOGD("Compiler thread has shut down"); } /* Break loops within the translation cache */ dvmJitUnchainAll(); /* * NOTE: our current implementatation doesn't allow for the compiler * thread to be restarted after it exits here. We aren't freeing * the JitTable or the ProfTable because threads which still may be * running or in the process of shutting down may hold references to * them. */ }
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; }
/** * @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(); }