/*
 * Retrieve the native heap information and the info from /proc/self/maps,
 * copy them into a byte[] with a "struct Header" that holds data offsets,
 * and return the array.
 */
static jbyteArray DdmHandleNativeHeap_getLeakInfo(JNIEnv* env, jobject) {
    Header header;
    memset(&header, 0, sizeof(header));

    String8 maps;
    ReadFile("/proc/self/maps", maps);
    header.mapSize = maps.size();

    uint8_t* allocBytes;
    get_malloc_leak_info(&allocBytes, &header.allocSize, &header.allocInfoSize,
                         &header.totalMemory, &header.backtraceSize);

    ALOGD("*** mapSize: %zu allocSize: %zu allocInfoSize: %zu totalMemory: %zu",
          header.mapSize, header.allocSize, header.allocInfoSize, header.totalMemory);

#if defined(__LP64__)
    header.signature = DDMS_HEADER_SIGNATURE;
    header.version = DDMS_VERSION;
    header.pointerSize = sizeof(void*);
#endif

    jbyteArray array = env->NewByteArray(sizeof(Header) + header.mapSize + header.allocSize);
    if (array != NULL) {
        env->SetByteArrayRegion(array, 0,
                                sizeof(header), reinterpret_cast<jbyte*>(&header));
        env->SetByteArrayRegion(array, sizeof(header),
                                maps.size(), reinterpret_cast<const jbyte*>(maps.string()));
        env->SetByteArrayRegion(array, sizeof(header) + maps.size(),
                                header.allocSize, reinterpret_cast<jbyte*>(allocBytes));
    }

    free_malloc_leak_info(allocBytes);
    return array;
}
/*
 * Retrieve the native heap information and the info from /proc/<self>/maps,
 * copy them into a byte[] with a "struct Header" that holds data offsets,
 * and return the array.
 */
static jbyteArray getLeakInfo(JNIEnv *env, jobject clazz)
{
#if defined(__arm__)
    // get the info in /proc/[pid]/map
    Header header;
    memset(&header, 0, sizeof(header));

    pid_t pid = getpid();

    char path[FILENAME_MAX];
    sprintf(path, "/proc/%d/maps", pid);

    struct stat sb;
    int ret = stat(path, &sb);

    uint8_t* mapsFile = NULL;
    if (ret == 0) {
        mapsFile = (uint8_t*)malloc(MAPS_FILE_SIZE);
        int fd = open(path, O_RDONLY);
    
        if (mapsFile != NULL && fd != -1) {
            int amount = 0;
            do {
                uint8_t* ptr = mapsFile + header.mapSize;
                amount = read(fd, ptr, MAPS_FILE_SIZE);
                if (amount <= 0) {
                    if (errno != EINTR)
                        break; 
                    else
                        continue;
                }
                header.mapSize += amount;
            } while (header.mapSize < MAPS_FILE_SIZE);
            
            LOGD("**** read %d bytes from '%s'", (int) header.mapSize, path);
        }
    }

    uint8_t* allocBytes;
    get_malloc_leak_info(&allocBytes, &header.allocSize, &header.allocInfoSize, 
            &header.totalMemory, &header.backtraceSize);

    jbyte* bytes = NULL;
    jbyte* ptr = NULL;
    jbyteArray array = env->NewByteArray(sizeof(Header) + header.mapSize + header.allocSize);
    if (array == NULL) {
        goto done;
    }

    bytes = env->GetByteArrayElements(array, NULL);
    ptr = bytes;

//    LOGD("*** mapSize: %d allocSize: %d allocInfoSize: %d totalMemory: %d", 
//            header.mapSize, header.allocSize, header.allocInfoSize, header.totalMemory);

    memcpy(ptr, &header, sizeof(header));
    ptr += sizeof(header);
    
    if (header.mapSize > 0 && mapsFile != NULL) {
        memcpy(ptr, mapsFile, header.mapSize);
        ptr += header.mapSize;
    }
    
    memcpy(ptr, allocBytes, header.allocSize);
    env->ReleaseByteArrayElements(array, bytes, 0);

done:
    if (mapsFile != NULL) {
        free(mapsFile);
    }
    // free the info up!
    free_malloc_leak_info(allocBytes);

    return array;
#else
    return NULL;
#endif
}
Exemplo n.º 3
0
void dumpMemoryAddresses(int fd)
{
    const size_t SIZE = 256;
    char buffer[SIZE];
    MyString8 result;

    typedef struct {
        size_t size;
        size_t dups;
        intptr_t * backtrace;
    } AllocEntry;

    uint8_t *info = NULL;
    size_t overallSize = 0;
    size_t infoSize = 0;
    size_t totalMemory = 0;
    size_t backtraceSize = 0;

    get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory, &backtraceSize);
    if (info) {
        uint8_t *ptr = info;
        size_t count = overallSize / infoSize;

        snprintf(buffer, SIZE, " Allocation count %i\n", count);
        result.append(buffer);
        snprintf(buffer, SIZE, " Total memory %i\n", totalMemory);
        result.append(buffer);

        AllocEntry * entries = new AllocEntry[count];

        for (size_t i = 0; i < count; i++) {
            // Each entry should be size_t, size_t, intptr_t[backtraceSize]
            AllocEntry *e = &entries[i];

            e->size = *reinterpret_cast<size_t *>(ptr);
            ptr += sizeof(size_t);

            e->dups = *reinterpret_cast<size_t *>(ptr);
            ptr += sizeof(size_t);

            e->backtrace = reinterpret_cast<intptr_t *>(ptr);
            ptr += sizeof(intptr_t) * backtraceSize;
        }

        // Now we need to sort the entries.  They come sorted by size but
        // not by stack trace which causes problems using diff.
        bool moved;
        do {
            moved = false;
            for (size_t i = 0; i < (count - 1); i++) {
                AllocEntry *e1 = &entries[i];
                AllocEntry *e2 = &entries[i+1];

                bool swap = e1->size < e2->size;
                if (e1->size == e2->size) {
                    for(size_t j = 0; j < backtraceSize; j++) {
                        if (e1->backtrace[j] == e2->backtrace[j]) {
                            continue;
                        }
                        swap = e1->backtrace[j] < e2->backtrace[j];
                        break;
                    }
                }
                if (swap) {
                    AllocEntry t = entries[i];
                    entries[i] = entries[i+1];
                    entries[i+1] = t;
                    moved = true;
                }
            }
        } while (moved);

        write(fd, result.string(), result.size());
        result.clear();

        for (size_t i = 0; i < count; i++) {
            AllocEntry *e = &entries[i];

            snprintf(buffer, SIZE, "size %8i, dup %4i, ", e->size, e->dups);
            result.append(buffer);
            for (size_t ct = 0; (ct < backtraceSize) && e->backtrace[ct]; ct++) {
                if (ct) {
                    result.append(", ");
                }
                snprintf(buffer, SIZE, "0x%08x", e->backtrace[ct]);
                result.append(buffer);
            }
            result.append("\n");

            write(fd, result.string(), result.size());
            result.clear();
        }

        delete[] entries;
        free_malloc_leak_info(info);
    }
}
/*
 * The get_malloc_leak_info() call returns an array of structs that
 * look like this:
 *
 *   size_t size
 *   size_t allocations
 *   intptr_t backtrace[32]
 *
 * "size" is the size of the allocation, "backtrace" is a fixed-size
 * array of function pointers, and "allocations" is the number of
 * allocations with the exact same size and backtrace.
 *
 * The entries are sorted by descending total size (i.e. size*allocations)
 * then allocation count.  For best results with "diff" we'd like to sort
 * primarily by individual size then stack trace.  Since the entries are
 * fixed-size, and we're allowed (by the current implementation) to mangle
 * them, we can do this in place.
 */
static void dumpNativeHeap(FILE* fp)
{
    uint8_t* info = NULL;
    size_t overallSize, infoSize, totalMemory, backtraceSize;

    get_malloc_leak_info(&info, &overallSize, &infoSize, &totalMemory,
        &backtraceSize);
    if (info == NULL) {
        fprintf(fp, "Native heap dump not available. To enable, run these"
                    " commands (requires root):\n");
        fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n");
        fprintf(fp, "$ adb shell stop\n");
        fprintf(fp, "$ adb shell start\n");
        return;
    }
    assert(infoSize != 0);
    assert(overallSize % infoSize == 0);

    fprintf(fp, "Android Native Heap Dump v1.0\n\n");

    size_t recordCount = overallSize / infoSize;
    fprintf(fp, "Total memory: %zu\n", totalMemory);
    fprintf(fp, "Allocation records: %zd\n", recordCount);
    if (backtraceSize != BACKTRACE_SIZE) {
        fprintf(fp, "WARNING: mismatched backtrace sizes (%d vs. %d)\n",
            backtraceSize, BACKTRACE_SIZE);
    }
    fprintf(fp, "\n");

    /* re-sort the entries */
    qsort(info, recordCount, infoSize, compareHeapRecords);

    /* dump the entries to the file */
    const uint8_t* ptr = info;
    for (size_t idx = 0; idx < recordCount; idx++) {
        size_t size = *(size_t*) ptr;
        size_t allocations = *(size_t*) (ptr + sizeof(size_t));
        intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2);

        fprintf(fp, "z %d  sz %8zu  num %4zu  bt",
                (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
                size & ~SIZE_FLAG_ZYGOTE_CHILD,
                allocations);
        for (size_t bt = 0; bt < backtraceSize; bt++) {
            if (backtrace[bt] == 0) {
                break;
            } else {
                fprintf(fp, " %08x", backtrace[bt]);
            }
        }
        fprintf(fp, "\n");

        ptr += infoSize;
    }

    free_malloc_leak_info(info);

    fprintf(fp, "MAPS\n");
    const char* maps = "/proc/self/maps";
    FILE* in = fopen(maps, "r");
    if (in == NULL) {
        fprintf(fp, "Could not open %s\n", maps);
        return;
    }
    char buf[BUFSIZ];
    while (size_t n = fread(buf, sizeof(char), BUFSIZ, in)) {
        fwrite(buf, sizeof(char), n, fp);
    }
    fclose(in);

    fprintf(fp, "END\n");
}