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;
}
Exemple #4
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;
}