int hprofShutdown_Stack() { HashIter iter; /* This will be called when a GC has completed. */ for (dvmHashIterBegin(gStackTraceHashTable, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { StackTraceEntry *stackTraceEntry; /* * If the 'live' bit is 0, the trace is not in use by any current * heap object and may be destroyed. */ stackTraceEntry = (StackTraceEntry *) dvmHashIterData(&iter); if (!stackTraceEntry->live) { dvmHashTableRemove(gStackTraceHashTable, computeStackTraceHash(stackTraceEntry), stackTraceEntry); free(stackTraceEntry); } } return 0; }
/* * Evaluate the amount of probing required for the specified hash table. * * We do this by running through all entries in the hash table, computing * the hash value and then doing a lookup. * * The caller should lock the table before calling here. */ void dvmHashTableProbeCount(HashTable* pHashTable, HashCalcFunc calcFunc, HashCompareFunc cmpFunc) { int numEntries, minProbe, maxProbe, totalProbe; HashIter iter; numEntries = maxProbe = totalProbe = 0; minProbe = 65536*32767; for (dvmHashIterBegin(pHashTable, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { const void* data = (const void*)dvmHashIterData(&iter); int count; count = countProbes(pHashTable, (*calcFunc)(data), data, cmpFunc); numEntries++; if (count < minProbe) minProbe = count; if (count > maxProbe) maxProbe = count; totalProbe += count; } LOGI("Probe: min=%d max=%d, total=%d in %d (%d), avg=%.3f\n", minProbe, maxProbe, totalProbe, numEntries, pHashTable->tableSize, (float) totalProbe / (float) numEntries); }
int hprofStartup_StackFrame() { HashIter iter; /* Cache the string "<unknown>" for use when the source file is * unknown. */ hprofLookupStringId("<unknown>"); /* This will be called when a GC begins. */ for (dvmHashIterBegin(gStackFrameHashTable, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { StackFrameEntry *stackFrameEntry; const Method *method; /* Clear the 'live' bit at the start of the GC pass. */ stackFrameEntry = (StackFrameEntry *) dvmHashIterData(&iter); stackFrameEntry->live = 0; method = stackFrameEntry->frame.method; if (method == NULL) { continue; } /* Make sure the method name, descriptor, and source file are in * the string table, and that the method class is in the class * table. This is needed because strings and classes will be dumped * before stack frames. */ if (method->name) { hprofLookupStringId(method->name); } DexStringCache cache; const char* descriptor; dexStringCacheInit(&cache); descriptor = dexProtoGetMethodDescriptor(&method->prototype, &cache); hprofLookupStringId(descriptor); dexStringCacheRelease(&cache); const char* sourceFile = dvmGetMethodSourceFile(method); if (sourceFile) { hprofLookupStringId(sourceFile); } if (method->clazz) { hprofLookupClassId(method->clazz); } } return 0; }
int hprofDumpStacks(hprof_context_t *ctx) { HashIter iter; hprof_record_t *rec = &ctx->curRec; dvmHashTableLock(gStackTraceHashTable); for (dvmHashIterBegin(gStackTraceHashTable, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { const StackTraceEntry *stackTraceEntry; int count; int i; hprofStartNewRecord(ctx, HPROF_TAG_STACK_TRACE, HPROF_TIME); stackTraceEntry = (const StackTraceEntry *) dvmHashIterData(&iter); assert(stackTraceEntry != NULL); /* STACK TRACE format: * * u4: serial number for this stack * u4: serial number for the running thread * u4: number of frames * [ID]*: ID for the stack frame */ hprofAddU4ToRecord(rec, stackTraceEntry->trace.serialNumber); hprofAddU4ToRecord(rec, stackTraceEntry->trace.threadSerialNumber); count = 0; while ((count < STACK_DEPTH) && (stackTraceEntry->trace.frameIds[count] != 0)) { count++; } hprofAddU4ToRecord(rec, count); for (i = 0; i < count; i++) { hprofAddU4ToRecord(rec, stackTraceEntry->trace.frameIds[i]); } } dvmHashTableUnlock(gStackTraceHashTable); return 0; }
int hprofStartup_Stack() { HashIter iter; /* This will be called when a GC begins. */ for (dvmHashIterBegin(gStackTraceHashTable, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { StackTraceEntry *stackTraceEntry; /* Clear the 'live' bit at the start of the GC pass. */ stackTraceEntry = (StackTraceEntry *) dvmHashIterData(&iter); stackTraceEntry->live = 0; } return 0; }
/* * Test iterator. */ static void dumpIterator(HashTable* pTab) { int count = 0; //printf("Print from iterator:\n"); HashIter iter; for (dvmHashIterBegin(pTab, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { //const char* str = (const char*) dvmHashIterData(&iter); //printf(" '%s'\n", str); // (should verify strings) count++; } if (count != kNumTestEntries) { ALOGE("TestHash iterator test failed"); assert(false); } }
int hprofDumpClasses(hprof_context_t *ctx) { HashIter iter; hprof_record_t *rec = &ctx->curRec; int err; dvmHashTableLock(gClassHashTable); for (err = 0, dvmHashIterBegin(gClassHashTable, &iter); err == 0 && !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { err = hprofStartNewRecord(ctx, HPROF_TAG_LOAD_CLASS, HPROF_TIME); if (err == 0) { const ClassObject *clazz; clazz = (const ClassObject *)dvmHashIterData(&iter); assert(clazz != NULL); /* LOAD CLASS format: * * u4: class serial number (always > 0) * ID: class object ID * u4: stack trace serial number * ID: class name string ID * * We use the address of the class object structure as its ID. */ hprofAddU4ToRecord(rec, clazz->serialNumber); hprofAddIdToRecord(rec, (hprof_class_object_id)clazz); hprofAddU4ToRecord(rec, HPROF_NULL_STACK_TRACE); hprofAddIdToRecord(rec, getPrettyClassNameId(clazz->descriptor)); } } dvmHashTableUnlock(gClassHashTable); return err; }
/* * Some quick hash table tests. */ bool dvmTestHash() { HashTable* pTab; char tmpStr[64]; const char* str; u4 hash; int i; ALOGV("TestHash BEGIN"); pTab = dvmHashTableCreate(dvmHashSize(12), free); if (pTab == NULL) return false; dvmHashTableLock(pTab); /* add some entries */ for (i = 0; i < kNumTestEntries; i++) { sprintf(tmpStr, "entry %d", i); hash = dvmComputeUtf8Hash(tmpStr); dvmHashTableLookup(pTab, hash, strdup(tmpStr), (HashCompareFunc) strcmp, true); } dvmHashTableUnlock(pTab); /* make sure we can find all entries */ for (i = 0; i < kNumTestEntries; i++) { sprintf(tmpStr, "entry %d", i); hash = dvmComputeUtf8Hash(tmpStr); str = (const char*) dvmHashTableLookup(pTab, hash, tmpStr, (HashCompareFunc) strcmp, false); if (str == NULL) { ALOGE("TestHash: failure: could not find '%s'", tmpStr); /* return false */ } } /* make sure it behaves correctly when entry not found and !doAdd */ sprintf(tmpStr, "entry %d", 17); hash = dvmComputeUtf8Hash(tmpStr); str = (const char*) dvmHashTableLookup(pTab, hash, tmpStr, (HashCompareFunc) strcmp, false); if (str == NULL) { /* good */ } else { ALOGE("TestHash found nonexistent string (improper add?)"); } dumpForeach(pTab); dumpIterator(pTab); /* make sure they all get freed */ dvmHashTableFree(pTab); /* * Round 2: verify probing & tombstones. */ pTab = dvmHashTableCreate(dvmHashSize(2), free); if (pTab == NULL) return false; hash = 0; /* two entries, same hash, different values */ const char* str1; str1 = (char*) dvmHashTableLookup(pTab, hash, strdup("one"), (HashCompareFunc) strcmp, true); assert(str1 != NULL); str = (const char*) dvmHashTableLookup(pTab, hash, strdup("two"), (HashCompareFunc) strcmp, true); /* remove the first one */ if (!dvmHashTableRemove(pTab, hash, (void*)str1)) ALOGE("TestHash failed to delete item"); else free((void*)str1); // "Remove" doesn't call the free func /* make sure iterator doesn't included deleted entries */ int count = 0; HashIter iter; for (dvmHashIterBegin(pTab, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { count++; } if (count != 1) { ALOGE("TestHash wrong number of entries (%d)", count); } /* see if we can find them */ str = (const char*) dvmHashTableLookup(pTab, hash, (void*)"one", (HashCompareFunc) strcmp,false); if (str != NULL) ALOGE("TestHash deleted entry has returned!"); str = (const char*) dvmHashTableLookup(pTab, hash, (void*)"two", (HashCompareFunc) strcmp,false); if (str == NULL) ALOGE("TestHash entry vanished"); /* force a table realloc to exercise tombstone removal */ for (i = 0; i < 20; i++) { sprintf(tmpStr, "entry %d", i); str = (const char*) dvmHashTableLookup(pTab, hash, strdup(tmpStr), (HashCompareFunc) strcmp, true); assert(str != NULL); } dvmHashTableFree(pTab); ALOGV("TestHash END"); return true; }
int hprofDumpStackFrames(hprof_context_t *ctx) { HashIter iter; hprof_record_t *rec = &ctx->curRec; dvmHashTableLock(gStackFrameHashTable); for (dvmHashIterBegin(gStackFrameHashTable, &iter); !dvmHashIterDone(&iter); dvmHashIterNext(&iter)) { const StackFrameEntry *stackFrameEntry; const Method *method; int pc; const char *sourceFile; ClassObject *clazz; int lineNum; hprofStartNewRecord(ctx, HPROF_TAG_STACK_FRAME, HPROF_TIME); stackFrameEntry = (const StackFrameEntry *) dvmHashIterData(&iter); assert(stackFrameEntry != NULL); method = stackFrameEntry->frame.method; pc = stackFrameEntry->frame.pc; sourceFile = dvmGetMethodSourceFile(method); if (sourceFile == NULL) { sourceFile = "<unknown>"; lineNum = 0; } else { lineNum = dvmLineNumFromPC(method, pc); } clazz = (ClassObject *) hprofLookupClassId(method->clazz); /* STACK FRAME format: * * ID: ID for this stack frame * ID: ID for the method name * ID: ID for the method descriptor * ID: ID for the source file name * u4: class serial number * u4: line number, 0 = no line information * * We use the address of the stack frame as its ID. */ DexStringCache cache; const char* descriptor; dexStringCacheInit(&cache); descriptor = dexProtoGetMethodDescriptor(&method->prototype, &cache); hprofAddIdToRecord(rec, (u4) stackFrameEntry); hprofAddIdToRecord(rec, hprofLookupStringId(method->name)); hprofAddIdToRecord(rec, hprofLookupStringId(descriptor)); hprofAddIdToRecord(rec, hprofLookupStringId(sourceFile)); hprofAddU4ToRecord(rec, (u4) clazz->serialNumber); hprofAddU4ToRecord(rec, (u4) lineNum); dexStringCacheRelease(&cache); } dvmHashTableUnlock(gStackFrameHashTable); return 0; }