Collect per-method code size statistics to show compiled vs overall Dalvik portion and total native code size.
diff --git a/vm/Globals.h b/vm/Globals.h
index 1d3460e..d43a77e 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -690,6 +690,9 @@
/* Compiled code cache */
void* codeCache;
+ /* Bytes used by the code templates */
+ unsigned int templateSize;
+
/* Bytes already used in the code cache */
unsigned int codeCacheByteUsed;
@@ -716,6 +719,9 @@
/* Flag to count trace execution */
bool profile;
+
+ /* Table to track the overall and trace statistics of hot methods */
+ HashTable* methodStatsTable;
};
extern struct DvmJitGlobals gDvmJit;
diff --git a/vm/compiler/Compiler.c b/vm/compiler/Compiler.c
index 9473834..f70b978 100644
--- a/vm/compiler/Compiler.c
+++ b/vm/compiler/Compiler.c
@@ -169,6 +169,8 @@
memcpy((void *) gDvmJit.codeCache,
(void *) dvmCompilerTemplateStart,
templateSize);
+
+ gDvmJit.templateSize = templateSize;
gDvmJit.codeCacheByteUsed = templateSize;
/* Flush dcache and invalidate the icache to maintain coherence */
@@ -224,6 +226,9 @@
goto fail;
}
+ /* Track method-level compilation statistics */
+ gDvmJit.methodStatsTable = dvmHashTableCreate(32, NULL);
+
dvmUnlockMutex(&gDvmJit.compilerLock);
return true;
diff --git a/vm/compiler/Compiler.h b/vm/compiler/Compiler.h
index 1ab6687..093d48a 100644
--- a/vm/compiler/Compiler.h
+++ b/vm/compiler/Compiler.h
@@ -84,6 +84,13 @@
JitTraceRun trace[];
} JitTraceDescription;
+typedef struct CompilerMethodStats {
+ const Method *method; // Used as hash entry signature
+ int dalvikSize; // # of bytes for dalvik bytecodes
+ int compiledDalvikSize; // # of compiled dalvik bytecodes
+ int nativeSize; // # of bytes for produced native code
+} CompilerMethodStats;
+
bool dvmCompilerSetupCodeCache(void);
bool dvmCompilerArchInit(void);
void dvmCompilerArchDump(void);
@@ -91,7 +98,7 @@
void dvmCompilerShutdown(void);
bool dvmCompilerWorkEnqueue(const u2* pc, WorkOrderKind kind, void* info);
void *dvmCheckCodeCache(void *method);
-void *dvmCompileMethod(Method *method);
+void *dvmCompileMethod(const Method *method);
void *dvmCompileTrace(JitTraceDescription *trace, int numMaxInsts);
void dvmCompilerDumpStats(void);
void dvmCompilerDrainQueue(void);
diff --git a/vm/compiler/Frontend.c b/vm/compiler/Frontend.c
index c8f3abc..76d9312 100644
--- a/vm/compiler/Frontend.c
+++ b/vm/compiler/Frontend.c
@@ -30,19 +30,9 @@
OpCode opcode = instr & 0xff;
int insnWidth;
- // Need to check if this is a real NOP or a pseudo opcode
+ // Don't parse instruction data
if (opcode == OP_NOP && instr != 0) {
- if (instr == kPackedSwitchSignature) {
- insnWidth = 4 + codePtr[1] * 2;
- } else if (instr == kSparseSwitchSignature) {
- insnWidth = 2 + codePtr[1] * 4;
- } else if (instr == kArrayDataSignature) {
- int width = codePtr[1];
- int size = codePtr[2] | (codePtr[3] << 16);
- // The plus 1 is to round up for odd size and width
- insnWidth = 4 + ((size * width) + 1) / 2;
- }
- insnWidth = 0;
+ return 0;
} else {
insnWidth = gDvm.instrWidth[opcode];
if (insnWidth < 0) {
@@ -88,7 +78,7 @@
const Method *calleeMethod =
caller->clazz->super->vtable[mIndex];
- if (!dvmIsNativeMethod(calleeMethod)) {
+ if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
*target = (unsigned int) calleeMethod->insns;
}
*isInvoke = true;
@@ -100,7 +90,7 @@
const Method *calleeMethod =
caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB];
- if (!dvmIsNativeMethod(calleeMethod)) {
+ if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
*target = (unsigned int) calleeMethod->insns;
}
*isInvoke = true;
@@ -112,7 +102,7 @@
const Method *calleeMethod =
caller->clazz->super->vtable[insn->dalvikInsn.vB];
- if (!dvmIsNativeMethod(calleeMethod)) {
+ if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
*target = (unsigned int) calleeMethod->insns;
}
*isInvoke = true;
@@ -123,7 +113,7 @@
case OP_INVOKE_DIRECT_RANGE: {
const Method *calleeMethod =
caller->clazz->pDvmDex->pResMethods[insn->dalvikInsn.vB];
- if (!dvmIsNativeMethod(calleeMethod)) {
+ if (calleeMethod && !dvmIsNativeMethod(calleeMethod)) {
*target = (unsigned int) calleeMethod->insns;
}
*isInvoke = true;
@@ -179,6 +169,72 @@
}
/*
+ * dvmHashTableLookup() callback
+ */
+static int compareMethod(const CompilerMethodStats *m1,
+ const CompilerMethodStats *m2)
+{
+ return (int) m1->method - (int) m2->method;
+}
+
+/*
+ * Analyze each method whose traces are ever compiled. Collect a variety of
+ * statistics like the ratio of exercised vs overall code and code bloat
+ * ratios.
+ */
+static CompilerMethodStats *analyzeMethodBody(const Method *method)
+{
+ const DexCode *dexCode = dvmGetMethodCode(method);
+ const u2 *codePtr = dexCode->insns;
+ const u2 *codeEnd = dexCode->insns + dexCode->insnsSize;
+ int insnSize = 0;
+ int hashValue = dvmComputeUtf8Hash(method->name);
+
+ CompilerMethodStats dummyMethodEntry; // For hash table lookup
+ CompilerMethodStats *realMethodEntry; // For hash table storage
+
+ /* For lookup only */
+ dummyMethodEntry.method = method;
+ realMethodEntry = dvmHashTableLookup(gDvmJit.methodStatsTable, hashValue,
+ &dummyMethodEntry,
+ (HashCompareFunc) compareMethod,
+ false);
+
+ /* Part of this method has been compiled before - just return the entry */
+ if (realMethodEntry != NULL) {
+ return realMethodEntry;
+ }
+
+ /*
+ * First time to compile this method - set up a new entry in the hash table
+ */
+ realMethodEntry =
+ (CompilerMethodStats *) calloc(1, sizeof(CompilerMethodStats));
+ realMethodEntry->method = method;
+
+ dvmHashTableLookup(gDvmJit.methodStatsTable, hashValue,
+ realMethodEntry,
+ (HashCompareFunc) compareMethod,
+ true);
+
+ /* Count the number of instructions */
+ while (codePtr < codeEnd) {
+ DecodedInstruction dalvikInsn;
+ int width = parseInsn(codePtr, &dalvikInsn, false);
+
+ /* Terminate when the data section is seen */
+ if (width == 0)
+ break;
+
+ insnSize += width;
+ codePtr += width;
+ }
+
+ realMethodEntry->dalvikSize = insnSize * 2;
+ return realMethodEntry;
+}
+
+/*
* Main entry point to start trace compilation. Basic blocks are constructed
* first and they will be passed to the codegen routines to convert Dalvik
* bytecode into machine code.
@@ -190,15 +246,19 @@
unsigned int curOffset = currRun->frag.startOffset;
unsigned int numInsts = currRun->frag.numInsts;
const u2 *codePtr = dexCode->insns + curOffset;
- int traceSize = 0;
+ int traceSize = 0; // # of half-words
const u2 *startCodePtr = codePtr;
BasicBlock *startBB, *curBB, *lastBB;
int numBlocks = 0;
static int compilationId;
CompilationUnit cUnit;
- memset(&cUnit, 0, sizeof(CompilationUnit));
+ CompilerMethodStats *methodStats;
compilationId++;
+ memset(&cUnit, 0, sizeof(CompilationUnit));
+
+ /* Locate the entry to store compilation statistics for this method */
+ methodStats = analyzeMethodBody(desc->method);
cUnit.registerScoreboard.nullCheckedRegs =
dvmAllocBitVector(desc->method->registersSize, false);
@@ -292,6 +352,9 @@
insn = dvmCompilerNew(sizeof(MIR),false);
insn->offset = curOffset;
width = parseInsn(codePtr, &insn->dalvikInsn, cUnit.printMe);
+
+ /* The trace should never incude instruction data */
+ assert(width);
insn->width = width;
traceSize += width;
dvmCompilerAppendMIR(curBB, insn);
@@ -320,6 +383,9 @@
}
}
+ /* Convert # of half-word to bytes */
+ methodStats->compiledDalvikSize += traceSize * 2;
+
/*
* Now scan basic blocks containing real code to connect the
* taken/fallthrough links. Also create chaining cells for code not included
@@ -478,6 +544,7 @@
* translation's entry point.
*/
if (!cUnit.halveInstCount) {
+ methodStats->nativeSize += cUnit.totalSize;
return cUnit.baseAddr + cUnit.headerSize;
/* Halve the instruction count and retry again */
@@ -493,7 +560,7 @@
* TODO: implementation will be revisited when the trace builder can provide
* whole-method traces.
*/
-void *dvmCompileMethod(Method *method)
+void *dvmCompileMethod(const Method *method)
{
const DexCode *dexCode = dvmGetMethodCode(method);
const u2 *codePtr = dexCode->insns;
@@ -520,6 +587,9 @@
const Method *callee;
insn->width = width;
+ /* Terminate when the data section is seen */
+ if (width == 0)
+ break;
dvmCompilerAppendMIR(firstBlock, insn);
/*
* Check whether this is a block ending instruction and whether it
@@ -590,6 +660,7 @@
BasicBlock *newBB = dvmCompilerNewBB(DALVIK_BYTECODE);
newBB->id = blockID++;
newBB->firstMIRInsn = insn;
+ newBB->startOffset = insn->offset;
newBB->lastMIRInsn = curBB->lastMIRInsn;
curBB->lastMIRInsn = insn->prev;
insn->prev->next = NULL;
@@ -645,7 +716,8 @@
}
}
- if (j == numBlocks) {
+ /* Don't create dummy block for the callee yet */
+ if (j == numBlocks && !isInvoke) {
LOGE("Target not found for insn %x: expect target %x\n",
curBB->lastMIRInsn->offset, target);
dvmAbort();
diff --git a/vm/compiler/Utility.c b/vm/compiler/Utility.c
index 54f6971..b0c40e6 100644
--- a/vm/compiler/Utility.c
+++ b/vm/compiler/Utility.c
@@ -154,17 +154,56 @@
}
/*
+ * dvmHashForeach callback.
+ */
+static int dumpMethodStats(void *compilerMethodStats, void *totalMethodStats)
+{
+ CompilerMethodStats *methodStats =
+ (CompilerMethodStats *) compilerMethodStats;
+ CompilerMethodStats *totalStats =
+ (CompilerMethodStats *) totalMethodStats;
+ const Method *method = methodStats->method;
+
+ totalStats->dalvikSize += methodStats->dalvikSize;
+ totalStats->compiledDalvikSize += methodStats->compiledDalvikSize;
+ totalStats->nativeSize += methodStats->nativeSize;
+
+ int limit = (methodStats->dalvikSize >> 2) * 3;
+
+ /* If over 3/4 of the Dalvik code is compiled, print something */
+ if (methodStats->compiledDalvikSize >= limit) {
+ LOGD("Method stats: %s%s, %d/%d (compiled/total Dalvik), %d (native)",
+ method->clazz->descriptor, method->name,
+ methodStats->compiledDalvikSize,
+ methodStats->dalvikSize,
+ methodStats->nativeSize);
+ }
+ return 0;
+}
+
+/*
* Dump the current stats of the compiler, including number of bytes used in
* the code cache, arena size, and work queue length, and various JIT stats.
*/
void dvmCompilerDumpStats(void)
{
- LOGD("%d compilations using %d bytes",
- gDvmJit.numCompilations, gDvmJit.codeCacheByteUsed);
+ CompilerMethodStats totalMethodStats;
+
+ memset(&totalMethodStats, 0, sizeof(CompilerMethodStats));
+ LOGD("%d compilations using %d + %d bytes",
+ gDvmJit.numCompilations,
+ gDvmJit.templateSize,
+ gDvmJit.codeCacheByteUsed - gDvmJit.templateSize);
LOGD("Compiler arena uses %d blocks (%d bytes each)",
numArenaBlocks, ARENA_DEFAULT_SIZE);
LOGD("Compiler work queue length is %d/%d", gDvmJit.compilerQueueLength,
gDvmJit.compilerMaxQueued);
dvmJitStats();
dvmCompilerArchDump();
+ dvmHashForeach(gDvmJit.methodStatsTable, dumpMethodStats,
+ &totalMethodStats);
+ LOGD("Code size stats: %d/%d (compiled/total Dalvik), %d (native)",
+ totalMethodStats.compiledDalvikSize,
+ totalMethodStats.dalvikSize,
+ totalMethodStats.nativeSize);
}
diff --git a/vm/compiler/codegen/armv5te/Codegen.c b/vm/compiler/codegen/armv5te/Codegen.c
index b272e48..10589e1 100644
--- a/vm/compiler/codegen/armv5te/Codegen.c
+++ b/vm/compiler/codegen/armv5te/Codegen.c
@@ -3228,7 +3228,7 @@
}
}
if (strlen(buf)) {
- LOGD("dalvik.vm.jitop = %s", buf);
+ LOGD("dalvik.vm.jit.op = %s", buf);
}
}