/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <sys/mman.h>
#include <errno.h>
#include <cutils/ashmem.h>
#include "Dalvik.h"
#include "interp/Jit.h"
#include "CompilerInternals.h"
extern "C" void dvmCompilerTemplateStart(void);
extern "C" void dmvCompilerTemplateEnd(void);
static inline bool workQueueLength(void)
{
return gDvmJit.compilerQueueLength;
}
static CompilerWorkOrder workDequeue(void)
{
assert(gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex].kind
!= kWorkOrderInvalid);
CompilerWorkOrder work =
gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex];
gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkDequeueIndex++].kind =
kWorkOrderInvalid;
if (gDvmJit.compilerWorkDequeueIndex == COMPILER_WORK_QUEUE_SIZE) {
gDvmJit.compilerWorkDequeueIndex = 0;
}
gDvmJit.compilerQueueLength--;
if (gDvmJit.compilerQueueLength == 0) {
dvmSignalCond(&gDvmJit.compilerQueueEmpty);
}
/* Remember the high water mark of the queue length */
if (gDvmJit.compilerQueueLength > gDvmJit.compilerMaxQueued)
gDvmJit.compilerMaxQueued = gDvmJit.compilerQueueLength;
return work;
}
/*
* Enqueue a work order - retrying until successful. If attempt to enqueue
* is repeatedly unsuccessful, assume the JIT is in a bad state and force a
* code cache reset.
*/
#define ENQUEUE_MAX_RETRIES 20
void dvmCompilerForceWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info)
{
bool success;
int retries = 0;
do {
success = dvmCompilerWorkEnqueue(pc, kind, info);
if (!success) {
retries++;
if (retries > ENQUEUE_MAX_RETRIES) {
ALOGE("JIT: compiler queue wedged - forcing reset");
gDvmJit.codeCacheFull = true; // Force reset
success = true; // Because we'll drop the order now anyway
} else {
dvmLockMutex(&gDvmJit.compilerLock);
pthread_cond_wait(&gDvmJit.compilerQueueActivity,
&gDvmJit.compilerLock);
dvmUnlockMutex(&gDvmJit.compilerLock);
}
}
} while (!success);
}
/*
* Attempt to enqueue a work order, returning true if successful.
*
* NOTE: Make sure that the caller frees the info pointer if the return value
* is false.
*/
bool dvmCompilerWorkEnqueue(const u2 *pc, WorkOrderKind kind, void* info)
{
int cc;
int i;
int numWork;
bool result = true;
dvmLockMutex(&gDvmJit.compilerLock);
/*
* Return if queue or code cache is full.
*/
if (gDvmJit.compilerQueueLength == COMPILER_WORK_QUEUE_SIZE ||
gDvmJit.codeCacheFull == true) {
dvmUnlockMutex(&gDvmJit.compilerLock);
return false;
}
for (numWork = gDvmJit.compilerQueueLength,
i = gDvmJit.compilerWorkDequeueIndex;
numWork > 0;
numWork--) {
/* Already enqueued */
if (gDvmJit.compilerWorkQueue[i++].pc == pc) {
dvmUnlockMutex(&gDvmJit.compilerLock);
return true;
}
/* Wrap around */
if (i == COMPILER_WORK_QUEUE_SIZE)
i = 0;
}
CompilerWorkOrder *newOrder =
&gDvmJit.compilerWorkQueue[gDvmJit.compilerWorkEnqueueIndex];
newOrder->pc = pc;
newOrder->kind = kind;
newOrder->info = info;
newOrder->result.methodCompilationAborted = NULL;
newOrder->result.codeAddress = NULL;
newOrder->result.discardResult =
(kind == kWorkOrderTraceDebug) ? true : false;
newOrder->result.cacheVersion = gDvmJit.cacheVersion;
newOrder->result.requestingThread = dvmThreadSelf();
gDvmJit.compilerWorkEnqueueIndex++;
if (gDvmJit.compilerWorkEnqueueIndex == COMPILER_WORK_QUEUE_SIZE)
gDvmJit.compilerWorkEnqueueIndex = 0;
gDvmJit.compilerQueueLength++;
cc = pthread_cond_signal(&gDvmJit.compilerQueueActivity);
assert(cc == 0);
dvmUnlockMutex(&gDvmJit.compilerLock);
return result;
}
/* Block until the queue length is 0, or there is a pending suspend request */
void dvmCompilerDrainQueue(void)
{
Thread *self = dvmThreadSelf();
dvmLockMutex(&gDvmJit.compilerLock);
while (workQueueLength() != 0 && !gDvmJit.haltCompilerThread &&
self->suspendCount == 0) {
/*
* Use timed wait here - more than one mutator threads may be blocked
* but the compiler thread will only signal once when the queue is
* emptied. Furthermore, the compiler thread may have been shutdown
* so the blocked thread may never get the wakeup signal.
*/
dvmRelativeCondWait(&gDvmJit.compilerQueueEmpty, &gDvmJit.compilerLock, 1000, 0);
}
dvmUnlockMutex(&gDvmJit.compilerLock);
}
bool dvmCompilerSetupCodeCache(void)
{
int fd;
/* Allocate the code cache */
fd = ashmem_create_region("dalvik-jit-code-cache", gDvmJit.codeCacheSize);
if (fd < 0) {
ALOGE("Could not create %u-byte ashmem region for the JIT code cache",
gDvmJit.codeCacheSize);
return false;
}
gDvmJit.codeCache = mmap(NULL, gDvmJit.codeCacheSize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE , fd, 0);
close(fd);
if (gDvmJit.codeCache == MAP_FAILED) {
ALOGE("Failed to mmap the JIT code cache: %s", strerror(errno));
return false;
}
gDvmJit.pageSizeMask = getpagesize() - 1;
/* This can be found through "dalvik-jit-code-cache" in /proc/<pid>/maps */
// ALOGD("Code cache starts at %p", gDvmJit.codeCache);
/* Copy the template code into the beginning of the code cache */
int templateSize = (intptr_t) dmvCompilerTemplateEnd -
(intptr_t) dvmCompilerTemplateStart;
memcpy((void *) gDvmJit.codeCache,
(void *) dvmCompilerTemplateStart,
templateSize);
/*
* Work around a CPU bug by keeping the 32-bit ARM handler code in its own
* page.
*/
if (dvmCompilerInstructionSet() == DALVIK_JIT_THUMB2) {
templateSize = (templateSize + 4095) & ~4095;
}
gDvmJit.templateSize = templateSize;
gDvmJit.codeCacheByteUsed = templateSize;
/* Only flush the part in the code cache that is being used now */
dvmCompilerCacheFlush((intptr_t) gDvmJit.codeCache,
(intptr_t) gDvmJit.codeCache + templateSize, 0);
int result = mprotect(gDvmJit.codeCache, gDvmJit.codeCacheSize,
PROTECT_CODE_CACHE_ATTRS);
if (result == -1) {
ALOGE("Failed to remove the write permission for the code cache");
dvmAbort();
}
return true;
}
static void crawlDalvikStack(Thread *thread, bool print)
{
void *fp = thread->interpSave.curFrame;
StackSaveArea* saveArea = NULL;
int stackLevel = 0;
if (print) {
ALOGD("Crawling tid %d (%s / %p %s)", thread->systemTid,
dvmGetThreadStatusStr(thread->status),
thread->inJitCodeCache,
thread->inJitCodeCache ? "jit" : "interp");
}
/* Crawl the Dalvik stack frames to clear the returnAddr field */
while (fp != NULL) {
saveArea = SAVEAREA_FROM_FP(fp);
if (print) {
if (dvmIsBreakFrame((u4*)fp)) {
ALOGD(" #%d: break frame (%p)",
stackLevel, saveArea->returnAddr);
}
else {
ALOGD(" #%d: %s.%s%s (%p)",
stackLevel,
saveArea->method->clazz->descriptor,
saveArea->method->name,
dvmIsNativeMethod(saveArea->method) ?
" (native)" : "",
saveArea->returnAddr);
}
}
stackLevel++;
saveArea->returnAddr = NULL;
assert(fp != saveArea->prevFrame);
fp = saveArea->prevFrame;
}
/* Make sure the stack is fully unwound to the bottom */
assert(saveArea == NULL ||
(u1 *) (saveArea+1) == thread->interpStackStart);
}
static void resetCodeCache(void)
{
Thread* thread;
u8 startTime = dvmGetRelativeTimeUsec();
int inJit = 0;
int byteUsed = gDvmJit.codeCacheByteUsed;
/* If any thread is found stuck in the JIT state, don't reset the cache */
dvmLockThreadList(NULL);
for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
/*
* Crawl the stack to wipe out the returnAddr field so that
* 1) the soon-to-be-deleted code in the JIT cache won't be used
* 2) or the thread stuck in the JIT land will soon return
* to the interpreter land
*/
crawlDalvikStack(thread, false);
if (thread->inJitCodeCache) {
inJit++;
}
/* Cancel any ongoing trace selection */
dvmDisableSubMode(thread, kSubModeJitTraceBuild);
}
dvmUnlockThreadList();
if (inJit) {
ALOGD("JIT code cache reset delayed (%d bytes %d/%d)",
gDvmJit.codeCacheByteUsed, gDvmJit.numCodeCacheReset,
++gDvmJit.numCodeCacheResetDelayed);
return;
}
/* Lock the mutex to clean up the work queue */
dvmLockMutex(&gDvmJit.compilerLock);
/* Update the translation cache version */
gDvmJit.cacheVersion++;
/* Drain the work queue to free the work orders */
while (workQueueLength()) {
CompilerWorkOrder work = workDequeue();
free(work.info);
}
/* Reset the JitEntry table contents to the initial unpopulated state */
dvmJitResetTable();
UNPROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
/*
* Wipe out the code cache content to force immediate crashes if
* stale JIT'ed code is invoked.
*/
memset((char *) gDvmJit.codeCache + gDvmJit.templateSize,
0,
gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
dvmCompilerCacheFlush((intptr_t) gDvmJit.codeCache,
(intptr_t) gDvmJit.codeCache +
gDvmJit.codeCacheByteUsed, 0);
PROTECT_CODE_CACHE(gDvmJit.codeCache, gDvmJit.codeCacheByteUsed);
/* Reset the current mark of used bytes to the end of template code */
gDvmJit.codeCacheByteUsed = gDvmJit.templateSize;
gDvmJit.numCompilations = 0;
/* Reset the work queue */
memset(gDvmJit.compilerWorkQueue, 0,
sizeof(CompilerWorkOrder) * COMPILER_WORK_QUEUE_SIZE);
gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
gDvmJit.compilerQueueLength = 0;
/* Reset the IC patch work queue */
dvmLockMutex(&gDvmJit.compilerICPatchLock);
gDvmJit.compilerICPatchIndex = 0;
dvmUnlockMutex(&gDvmJit.compilerICPatchLock);
/*
* Reset the inflight compilation address (can only be done in safe points
* or by the compiler thread when its thread state is RUNNING).
*/
gDvmJit.inflightBaseAddr = NULL;
/* All clear now */
gDvmJit.codeCacheFull = false;
dvmUnlockMutex(&gDvmJit.compilerLock);
ALOGD("JIT code cache reset in %lld ms (%d bytes %d/%d)",
(dvmGetRelativeTimeUsec() - startTime) / 1000,
byteUsed, ++gDvmJit.numCodeCacheReset,
gDvmJit.numCodeCacheResetDelayed);
}
/*
* Perform actions that are only safe when all threads are suspended. Currently
* we do:
* 1) Check if the code cache is full. If so reset it and restart populating it
* from scratch.
* 2) Patch predicted chaining cells by consuming recorded work orders.
*/
void dvmCompilerPerformSafePointChecks(void)
{
if (gDvmJit.codeCacheFull) {
resetCodeCache();
}
dvmCompilerPatchInlineCache();
}
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(pJitProfTable);
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");
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;
}
static void *compilerThreadStart(void *arg)
{
dvmChangeStatus(NULL, THREAD_VMWAIT);
/*
* If we're not running stand-alone, wait a little before
* recieving translation requests on the assumption that process start
* up code isn't worth compiling. We'll resume when the framework
* signals us that the first screen draw has happened, or the timer
* below expires (to catch daemons).
*
* There is a theoretical race between the callback to
* VMRuntime.startJitCompiation and when the compiler thread reaches this
* point. In case the callback happens earlier, in order not to permanently
* hold the system_server (which is not using the timed wait) in
* interpreter-only mode we bypass the delay here.
*/
if (gDvmJit.runningInAndroidFramework &&
!gDvmJit.alreadyEnabledViaFramework) {
/*
* If the current VM instance is the system server (detected by having
* 0 in gDvm.systemServerPid), we will use the indefinite wait on the
* conditional variable to determine whether to start the JIT or not.
* If the system server detects that the whole system is booted in
* safe mode, the conditional variable will never be signaled and the
* system server will remain in the interpreter-only mode. All
* subsequent apps will be started with the --enable-safemode flag
* explicitly appended.
*/
if (gDvm.systemServerPid == 0) {
dvmLockMutex(&gDvmJit.compilerLock);
pthread_cond_wait(&gDvmJit.compilerQueueActivity,
&gDvmJit.compilerLock);
dvmUnlockMutex(&gDvmJit.compilerLock);
ALOGD("JIT started for system_server");
} else {
dvmLockMutex(&gDvmJit.compilerLock);
/*
* TUNING: experiment with the delay & perhaps make it
* target-specific
*/
dvmRelativeCondWait(&gDvmJit.compilerQueueActivity,
&gDvmJit.compilerLock, 3000, 0);
dvmUnlockMutex(&gDvmJit.compilerLock);
}
if (gDvmJit.haltCompilerThread) {
return NULL;
}
}
compilerThreadStartup();
dvmLockMutex(&gDvmJit.compilerLock);
/*
* Since the compiler thread will not touch any objects on the heap once
* being created, we just fake its state as VMWAIT so that it can be a
* bit late when there is suspend request pending.
*/
while (!gDvmJit.haltCompilerThread) {
if (workQueueLength() == 0) {
int cc;
cc = pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
assert(cc == 0);
pthread_cond_wait(&gDvmJit.compilerQueueActivity,
&gDvmJit.compilerLock);
continue;
} else {
do {
CompilerWorkOrder work = workDequeue();
dvmUnlockMutex(&gDvmJit.compilerLock);
#if defined(WITH_JIT_TUNING)
/*
* This is live across setjmp(). Mark it volatile to suppress
* a gcc warning. We should not need this since it is assigned
* only once but gcc is not smart enough.
*/
volatile u8 startTime = dvmGetRelativeTimeUsec();
#endif
/*
* Check whether there is a suspend request on me. This
* is necessary to allow a clean shutdown.
*
* However, in the blocking stress testing mode, let the
* compiler thread continue doing compilations to unblock
* other requesting threads. This may occasionally cause
* shutdown from proceeding cleanly in the standalone invocation
* of the vm but this should be acceptable.
*/
if (!gDvmJit.blockingMode)
dvmCheckSuspendPending(dvmThreadSelf());
/* Is JitTable filling up? */
if (gDvmJit.jitTableEntriesUsed >
(gDvmJit.jitTableSize - gDvmJit.jitTableSize/4)) {
bool resizeFail =
dvmJitResizeJitTable(gDvmJit.jitTableSize * 2);
/*
* If the jit table is full, consider it's time to reset
* the code cache too.
*/
gDvmJit.codeCacheFull |= resizeFail;
}
if (gDvmJit.haltCompilerThread) {
ALOGD("Compiler shutdown in progress - discarding request");
} else if (!gDvmJit.codeCacheFull) {
jmp_buf jmpBuf;
work.bailPtr = &jmpBuf;
bool aborted = setjmp(jmpBuf);
if (!aborted) {
bool codeCompiled = dvmCompilerDoWork(&work);
/*
* Make sure we are still operating with the
* same translation cache version. See
* Issue 4271784 for details.
*/
dvmLockMutex(&gDvmJit.compilerLock);
if ((work.result.cacheVersion ==
gDvmJit.cacheVersion) &&
codeCompiled &&
!work.result.discardResult &&
work.result.codeAddress) {
dvmJitSetCodeAddr(work.pc, work.result.codeAddress,
work.result.instructionSet,
false, /* not method entry */
work.result.profileCodeSize);
}
dvmUnlockMutex(&gDvmJit.compilerLock);
}
dvmCompilerArenaReset();
}
free(work.info);
#if defined(WITH_JIT_TUNING)
gDvmJit.jitTime += dvmGetRelativeTimeUsec() - startTime;
#endif
dvmLockMutex(&gDvmJit.compilerLock);
} while (workQueueLength() != 0);
}
}
pthread_cond_signal(&gDvmJit.compilerQueueEmpty);
dvmUnlockMutex(&gDvmJit.compilerLock);
/*
* As part of detaching the thread we need to call into Java code to update
* the ThreadGroup, and we should not be in VMWAIT state while executing
* interpreted code.
*/
dvmChangeStatus(NULL, THREAD_RUNNING);
if (gDvm.verboseShutdown)
ALOGD("Compiler thread shutting down");
return NULL;
}
bool dvmCompilerStartup(void)
{
dvmInitMutex(&gDvmJit.compilerLock);
dvmInitMutex(&gDvmJit.compilerICPatchLock);
dvmInitMutex(&gDvmJit.codeCacheProtectionLock);
dvmLockMutex(&gDvmJit.compilerLock);
pthread_cond_init(&gDvmJit.compilerQueueActivity, NULL);
pthread_cond_init(&gDvmJit.compilerQueueEmpty, NULL);
/* Reset the work queue */
gDvmJit.compilerWorkEnqueueIndex = gDvmJit.compilerWorkDequeueIndex = 0;
gDvmJit.compilerQueueLength = 0;
dvmUnlockMutex(&gDvmJit.compilerLock);
/*
* Defer rest of initialization until we're sure JIT'ng makes sense. Launch
* the compiler thread, which will do the real initialization if and
* when it is signalled to do so.
*/
return dvmCreateInternalThread(&gDvmJit.compilerHandle, "Compiler",
compilerThreadStart, NULL);
}
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.
*/
}
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();
}