static void append_chunk(HeapChunkContext *ctx, u1 state, void* ptr, size_t length) { /* Make sure there's enough room left in the buffer. * We need to use two bytes for every fractional 256 * allocation units used by the chunk and 17 bytes for * any header. */ { size_t needed = (((length/ALLOCATION_UNIT_SIZE + 255) / 256) * 2) + 17; size_t bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf); if (bytesLeft < needed) { flush_hpsg_chunk(ctx); } bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf); if (bytesLeft < needed) { ALOGW("chunk is too big to transmit (length=%zd, %zd bytes)", length, needed); return; } } if (ctx->needHeader) { /* * Start a new HPSx chunk. */ /* [u4]: heap ID */ set4BE(ctx->p, DEFAULT_HEAP_ID); ctx->p += 4; /* [u1]: size of allocation unit, in bytes */ *ctx->p++ = 8; /* [u4]: virtual address of segment start */ set4BE(ctx->p, (uintptr_t)ptr); ctx->p += 4; /* [u4]: offset of this piece (relative to the virtual address) */ set4BE(ctx->p, 0); ctx->p += 4; /* [u4]: length of piece, in allocation units * We won't know this until we're done, so save the offset * and stuff in a dummy value. */ ctx->pieceLenField = ctx->p; set4BE(ctx->p, 0x55555555); ctx->p += 4; ctx->needHeader = false; } /* Write out the chunk description. */ length /= ALLOCATION_UNIT_SIZE; // convert to allocation units ctx->totalAllocationUnits += length; while (length > 256) { *ctx->p++ = state | HPSG_PARTIAL; *ctx->p++ = 255; // length - 1 length -= 256; } *ctx->p++ = state; *ctx->p++ = length - 1; }
static void walkHeap(bool merge, bool native) { HeapChunkContext ctx; memset(&ctx, 0, sizeof(ctx)); ctx.bufLen = HPSx_CHUNK_SIZE; ctx.buf = (u1 *)malloc(ctx.bufLen); if (ctx.buf == NULL) { return; } ctx.merge = merge; if (native) { ctx.type = CHUNK_TYPE("NHSG"); } else { if (ctx.merge) { ctx.type = CHUNK_TYPE("HPSG"); } else { ctx.type = CHUNK_TYPE("HPSO"); } } ctx.p = ctx.buf; ctx.needHeader = true; if (native) { #ifdef USE_DLMALLOC dlmalloc_inspect_all(heap_chunk_callback, (void*)&ctx); #endif } else { dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx); } if (ctx.p > ctx.buf) { flush_hpsg_chunk(&ctx); } free(ctx.buf); }
/* * Called by dlmalloc_inspect_all. If used_bytes != 0 then start is * the start of a malloc-ed piece of memory of size used_bytes. If * start is 0 then start is the beginning of any free space not * including dlmalloc's book keeping and end the start of the next * dlmalloc chunk. Regions purely containing book keeping don't * callback. */ static void heap_chunk_callback(void* start, void* end, size_t used_bytes, void* arg) { u1 state; HeapChunkContext *ctx = (HeapChunkContext *)arg; UNUSED_PARAMETER(end); if (used_bytes == 0) { if (start == NULL) { // Reset for start of new heap. ctx->startOfNextMemoryChunk = NULL; flush_hpsg_chunk(ctx); } // Only process in use memory so that free region information // also includes dlmalloc book keeping. return; } /* If we're looking at the native heap, we'll just return * (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks */ bool native = ctx->type == CHUNK_TYPE("NHSG"); if (ctx->startOfNextMemoryChunk != NULL) { // Transmit any pending free memory. Native free memory of // over kMaxFreeLen could be because of the use of mmaps, so // don't report. If not free memory then start a new segment. bool flush = true; if (start > ctx->startOfNextMemoryChunk) { const size_t kMaxFreeLen = 2 * SYSTEM_PAGE_SIZE; void* freeStart = ctx->startOfNextMemoryChunk; void* freeEnd = start; size_t freeLen = (char*)freeEnd - (char*)freeStart; if (!native || freeLen < kMaxFreeLen) { append_chunk(ctx, HPSG_STATE(SOLIDITY_FREE, 0), freeStart, freeLen); flush = false; } } if (flush) { ctx->startOfNextMemoryChunk = NULL; flush_hpsg_chunk(ctx); } } const Object *obj = (const Object *)start; /* It's an allocated chunk. Figure out what it is. */ //TODO: if ctx.merge, see if this chunk is different from the last chunk. // If it's the same, we should combine them. if (!native && dvmIsValidObject(obj)) { ClassObject *clazz = obj->clazz; if (clazz == NULL) { /* The object was probably just created * but hasn't been initialized yet. */ state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT); } else if (dvmIsTheClassClass(clazz)) { state = HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT); } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) { if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) { state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4); } else { switch (clazz->elementClass->primitiveType) { case PRIM_BOOLEAN: case PRIM_BYTE: state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1); break; case PRIM_CHAR: case PRIM_SHORT: state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2); break; case PRIM_INT: case PRIM_FLOAT: state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4); break; case PRIM_DOUBLE: case PRIM_LONG: state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8); break; default: assert(!"Unknown GC heap object type"); state = HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN); break; } } } else { state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT); } } else { obj = NULL; // it's not actually an object state = HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE); } append_chunk(ctx, state, start, used_bytes + HEAP_SOURCE_CHUNK_OVERHEAD); ctx->startOfNextMemoryChunk = (char*)start + used_bytes + HEAP_SOURCE_CHUNK_OVERHEAD; }
static void heap_chunk_callback(const void *chunkptr, size_t chunklen, const void *userptr, size_t userlen, void *arg) { HeapChunkContext *ctx = (HeapChunkContext *)arg; u1 state; UNUSED_PARAMETER(userlen); assert((chunklen & (ALLOCATION_UNIT_SIZE-1)) == 0); /* Make sure there's enough room left in the buffer. * We need to use two bytes for every fractional 256 * allocation units used by the chunk. */ { size_t needed = (((chunklen/ALLOCATION_UNIT_SIZE + 255) / 256) * 2); size_t bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf); if (bytesLeft < needed) { flush_hpsg_chunk(ctx); } bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf); if (bytesLeft < needed) { LOGW("chunk is too big to transmit (chunklen=%zd, %zd bytes)\n", chunklen, needed); return; } } //TODO: notice when there's a gap and start a new heap, or at least a new range. if (ctx->needHeader) { /* * Start a new HPSx chunk. */ /* [u4]: heap ID */ set4BE(ctx->p, DEFAULT_HEAP_ID); ctx->p += 4; /* [u1]: size of allocation unit, in bytes */ *ctx->p++ = 8; /* [u4]: virtual address of segment start */ set4BE(ctx->p, (uintptr_t)chunkptr); ctx->p += 4; /* [u4]: offset of this piece (relative to the virtual address) */ set4BE(ctx->p, 0); ctx->p += 4; /* [u4]: length of piece, in allocation units * We won't know this until we're done, so save the offset * and stuff in a dummy value. */ ctx->pieceLenField = ctx->p; set4BE(ctx->p, 0x55555555); ctx->p += 4; ctx->needHeader = false; } /* Determine the type of this chunk. */ if (userptr == NULL) { /* It's a free chunk. */ state = HPSG_STATE(SOLIDITY_FREE, 0); } else { const DvmHeapChunk *hc = (const DvmHeapChunk *)userptr; const Object *obj = chunk2ptr(hc); /* If we're looking at the native heap, we'll just return * (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks */ bool native = ctx->type == CHUNK_TYPE("NHSG"); /* It's an allocated chunk. Figure out what it is. */ //TODO: if ctx.merge, see if this chunk is different from the last chunk. // If it's the same, we should combine them. if (!native && dvmIsValidObject(obj)) { ClassObject *clazz = obj->clazz; if (clazz == NULL) { /* The object was probably just created * but hasn't been initialized yet. */ state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT); } else if (clazz == gDvm.unlinkedJavaLangClass || clazz == gDvm.classJavaLangClass) { state = HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT); } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) { if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) { state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4); } else { switch (clazz->elementClass->primitiveType) { case PRIM_BOOLEAN: case PRIM_BYTE: state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1); break; case PRIM_CHAR: case PRIM_SHORT: state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2); break; case PRIM_INT: case PRIM_FLOAT: state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4); break; case PRIM_DOUBLE: case PRIM_LONG: state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8); break; default: assert(!"Unknown GC heap object type"); state = HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN); break; } } } else { state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT); } } else { obj = NULL; // it's not actually an object state = HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE); } } /* Write out the chunk description. */ chunklen /= ALLOCATION_UNIT_SIZE; // convert to allocation units ctx->totalAllocationUnits += chunklen; while (chunklen > 256) { *ctx->p++ = state | HPSG_PARTIAL; *ctx->p++ = 255; // length - 1 chunklen -= 256; } *ctx->p++ = state; *ctx->p++ = chunklen - 1; }