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 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; }
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; }
/* * Finish up the hprof dump. Returns true on success. */ bool hprofShutdown(hprof_context_t *tailCtx) { FILE *fp = NULL; /* flush output to the temp file, then prepare the output file */ hprofFlushCurrentRecord(tailCtx); LOGI("hprof: dumping heap strings to \"%s\".\n", tailCtx->fileName); if (!tailCtx->directToDdms) { fp = fopen(tailCtx->fileName, "w"); if (fp == NULL) { LOGE("can't open %s: %s\n", tailCtx->fileName, strerror(errno)); hprofFreeContext(tailCtx); return false; } } /* * Create a new context struct for the start of the file. We * heap-allocate it so we can share the "free" function. */ hprof_context_t *headCtx = malloc(sizeof(*headCtx)); if (headCtx == NULL) { LOGE("hprof: can't allocate context.\n"); if (fp != NULL) fclose(fp); hprofFreeContext(tailCtx); return NULL; } hprofContextInit(headCtx, strdup(tailCtx->fileName), fp, true, tailCtx->directToDdms); hprofDumpStrings(headCtx); hprofDumpClasses(headCtx); /* Write a dummy stack trace record so the analysis * tools don't freak out. */ hprofStartNewRecord(headCtx, HPROF_TAG_STACK_TRACE, HPROF_TIME); hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_STACK_TRACE); hprofAddU4ToRecord(&headCtx->curRec, HPROF_NULL_THREAD); hprofAddU4ToRecord(&headCtx->curRec, 0); // no frames #if WITH_HPROF_STACK hprofDumpStackFrames(headCtx); hprofDumpStacks(headCtx); #endif hprofFlushCurrentRecord(headCtx); hprofShutdown_Class(); hprofShutdown_String(); #if WITH_HPROF_STACK hprofShutdown_Stack(); hprofShutdown_StackFrame(); #endif if (tailCtx->directToDdms) { /* flush to ensure memstream pointer and size are updated */ fflush(headCtx->fp); fflush(tailCtx->fp); /* send the data off to DDMS */ struct iovec iov[2]; iov[0].iov_base = headCtx->fileDataPtr; iov[0].iov_len = headCtx->fileDataSize; iov[1].iov_base = tailCtx->fileDataPtr; iov[1].iov_len = tailCtx->fileDataSize; dvmDbgDdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2); } else { /* * Append the contents of the temp file to the output file. The temp * file was removed immediately after being opened, so it will vanish * when we close it. */ rewind(tailCtx->fp); if (!copyFileToFile(headCtx->fp, tailCtx->fp)) { LOGW("hprof: file copy failed, hprof data may be incomplete\n"); /* finish up anyway */ } } hprofFreeContext(headCtx); hprofFreeContext(tailCtx); /* throw out a log message for the benefit of "runhat" */ LOGI("hprof: heap dump completed, temp file removed\n"); return true; }