/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <fcntl.h> #include <stdlib.h> #include "Dalvik.h" #include "HeapInternal.h" #include "HeapSource.h" #include "Float12.h" int dvmGetHeapDebugInfo(HeapDebugInfoType info) { switch (info) { case kVirtualHeapSize: return (int)dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0); case kVirtualHeapAllocated: return (int)dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0); default: return -1; } } /* Looks up the cmdline for the process and tries to find * the most descriptive five characters, then inserts the * short name into the provided event value. */ #define PROC_NAME_LEN 5 static void insertProcessName(long long *ep) { static bool foundRealName = false; static char name[PROC_NAME_LEN] = { 'X', 'X', 'X', 'X', 'X' }; long long event = *ep; if (!foundRealName) { int fd = open("/proc/self/cmdline", O_RDONLY); if (fd > 0) { char buf[128]; ssize_t n = read(fd, buf, sizeof(buf) - 1); close(fd); if (n > 0) { memset(name, 0, sizeof(name)); if (n <= PROC_NAME_LEN) { // The whole name fits. memcpy(name, buf, n); } else { /* We need to truncate. The name will look something * like "com.android.home". Favor the characters * immediately following the last dot. */ buf[n] = '\0'; char *dot = strrchr(buf, '.'); if (dot == NULL) { /* Or, look for a slash, in case it's something like * "/system/bin/runtime". */ dot = strrchr(buf, '/'); } if (dot != NULL) { dot++; // Skip the dot size_t dotlen = strlen(dot); if (dotlen < PROC_NAME_LEN) { /* Use all available characters. We know that * n > PROC_NAME_LEN from the check above. */ dot -= PROC_NAME_LEN - dotlen; } strncpy(name, dot, PROC_NAME_LEN); } else { // No dot; just use the leading characters. memcpy(name, buf, PROC_NAME_LEN); } } if (strcmp(buf, "zygote") != 0) { /* If the process is no longer called "zygote", * cache this name. */ foundRealName = true; } } } } event &= ~(0xffffffffffLL << 24); event |= (long long)name[0] << 56; event |= (long long)name[1] << 48; event |= (long long)name[2] << 40; event |= (long long)name[3] << 32; event |= (long long)name[4] << 24; *ep = event; } // See device/data/etc/event-log-tags #define EVENT_LOG_TAG_dvm_gc_info 20001 #define EVENT_LOG_TAG_dvm_gc_madvise_info 20002 void dvmLogGcStats(size_t numFreed, size_t sizeFreed, size_t gcTimeMs) { size_t perHeapActualSize[HEAP_SOURCE_MAX_HEAP_COUNT], perHeapAllowedSize[HEAP_SOURCE_MAX_HEAP_COUNT], perHeapNumAllocated[HEAP_SOURCE_MAX_HEAP_COUNT], perHeapSizeAllocated[HEAP_SOURCE_MAX_HEAP_COUNT]; unsigned char eventBuf[1 + (1 + sizeof(long long)) * 4]; size_t actualSize, allowedSize, numAllocated, sizeAllocated; size_t softLimit = dvmHeapSourceGetIdealFootprint(); size_t nHeaps = dvmHeapSourceGetNumHeaps(); /* Enough to quiet down gcc for unitialized variable check */ perHeapActualSize[0] = perHeapAllowedSize[0] = perHeapNumAllocated[0] = perHeapSizeAllocated[0] = 0; actualSize = dvmHeapSourceGetValue(HS_FOOTPRINT, perHeapActualSize, HEAP_SOURCE_MAX_HEAP_COUNT); allowedSize = dvmHeapSourceGetValue(HS_ALLOWED_FOOTPRINT, perHeapAllowedSize, HEAP_SOURCE_MAX_HEAP_COUNT); numAllocated = dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, perHeapNumAllocated, HEAP_SOURCE_MAX_HEAP_COUNT); sizeAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, perHeapSizeAllocated, HEAP_SOURCE_MAX_HEAP_COUNT); /* * Construct the the first 64-bit value to write to the log. * Global information: * * [63 ] Must be zero * [62-24] ASCII process identifier * [23-12] GC time in ms * [11- 0] Bytes freed * */ long long event0; event0 = 0LL << 63 | (long long)intToFloat12(gcTimeMs) << 12 | (long long)intToFloat12(sizeFreed); insertProcessName(&event0); /* * Aggregated heap stats: * * [63-62] 10 * [61-60] Reserved; must be zero * [59-48] Objects freed * [47-36] Actual size (current footprint) * [35-24] Allowed size (current hard max) * [23-12] Objects allocated * [11- 0] Bytes allocated */ long long event1; event1 = 2LL << 62 | (long long)intToFloat12(numFreed) << 48 | (long long)intToFloat12(actualSize) << 36 | (long long)intToFloat12(allowedSize) << 24 | (long long)intToFloat12(numAllocated) << 12 | (long long)intToFloat12(sizeAllocated); /* * Report the current state of the zygote heap(s). * * The active heap is always heap[0]. We can be in one of three states * at present: * * (1) Still in the zygote. Zygote using heap[0]. * (2) In the zygote, when the first child is started. We created a * new heap just before the first fork() call, so the original * "zygote heap" is now heap[1], and we have a small heap[0] for * anything we do from here on. * (3) In an app process. The app gets a new heap[0], and can also * see the two zygote heaps [1] and [2] (probably unwise to * assume any specific ordering). * * So if nHeaps == 1, we want the stats from heap[0]; else we want * the sum of the values from heap[1] to heap[nHeaps-1]. * * * Zygote heap stats (except for the soft limit, which belongs to the * active heap): * * [63-62] 11 * [61-60] Reserved; must be zero * [59-48] Soft Limit (for the active heap) * [47-36] Actual size (current footprint) * [35-24] Allowed size (current hard max) * [23-12] Objects allocated * [11- 0] Bytes allocated */ long long event2; size_t zActualSize, zAllowedSize, zNumAllocated, zSizeAllocated; int firstHeap = (nHeaps == 1) ? 0 : 1; size_t hh; zActualSize = zAllowedSize = zNumAllocated = zSizeAllocated = 0; for (hh = firstHeap; hh < nHeaps; hh++) { zActualSize += perHeapActualSize[hh]; zAllowedSize += perHeapAllowedSize[hh]; zNumAllocated += perHeapNumAllocated[hh]; zSizeAllocated += perHeapSizeAllocated[hh]; } event2 = 3LL << 62 | (long long)intToFloat12(softLimit) << 48 | (long long)intToFloat12(zActualSize) << 36 | (long long)intToFloat12(zAllowedSize) << 24 | (long long)intToFloat12(zNumAllocated) << 12 | (long long)intToFloat12(zSizeAllocated); /* * Report the current external allocation stats and the native heap * summary. * * [63-48] Reserved; must be zero (TODO: put new data in these slots) * [47-36] dlmalloc_footprint * [35-24] mallinfo: total allocated space * [23-12] External byte limit * [11- 0] External bytes allocated */ long long event3; size_t externalLimit, externalBytesAllocated; size_t uordblks, footprint; #if 0 /* * This adds 2-5msec to the GC cost on a DVT, or about 2-3% of the cost * of a GC, so it's not horribly expensive but it's not free either. */ extern size_t dlmalloc_footprint(void); struct mallinfo mi; //u8 start, end; //start = dvmGetRelativeTimeNsec(); mi = mallinfo(); uordblks = mi.uordblks; footprint = dlmalloc_footprint(); //end = dvmGetRelativeTimeNsec(); //LOGD("mallinfo+footprint took %dusec; used=%zd footprint=%zd\n", // (int)((end - start) / 1000), mi.uordblks, footprint); #else uordblks = footprint = 0; #endif externalLimit = dvmHeapSourceGetValue(HS_EXTERNAL_LIMIT, NULL, 0); externalBytesAllocated = dvmHeapSourceGetValue(HS_EXTERNAL_BYTES_ALLOCATED, NULL, 0); event3 = (long long)intToFloat12(footprint) << 36 | (long long)intToFloat12(uordblks) << 24 | (long long)intToFloat12(externalLimit) << 12 | (long long)intToFloat12(externalBytesAllocated); /* Build the event data. * [ 0: 0] item count (4) * [ 1: 1] EVENT_TYPE_LONG * [ 2: 9] event0 * [10:10] EVENT_TYPE_LONG * [11:18] event1 * [19:19] EVENT_TYPE_LONG * [20:27] event2 * [28:28] EVENT_TYPE_LONG * [29:36] event2 */ unsigned char *c = eventBuf; *c++ = 4; *c++ = EVENT_TYPE_LONG; memcpy(c, &event0, sizeof(event0)); c += sizeof(event0); *c++ = EVENT_TYPE_LONG; memcpy(c, &event1, sizeof(event1)); c += sizeof(event1); *c++ = EVENT_TYPE_LONG; memcpy(c, &event2, sizeof(event2)); c += sizeof(event2); *c++ = EVENT_TYPE_LONG; memcpy(c, &event3, sizeof(event3)); (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_info, EVENT_TYPE_LIST, eventBuf, sizeof(eventBuf)); } void dvmLogMadviseStats(size_t madvisedSizes[], size_t arrayLen) { unsigned char eventBuf[1 + (1 + sizeof(int)) * 2]; size_t total, zyg; size_t firstHeap, i; size_t nHeaps = dvmHeapSourceGetNumHeaps(); assert(arrayLen >= nHeaps); firstHeap = nHeaps > 1 ? 1 : 0; total = 0; zyg = 0; for (i = 0; i < nHeaps; i++) { total += madvisedSizes[i]; if (i >= firstHeap) { zyg += madvisedSizes[i]; } } /* Build the event data. * [ 0: 0] item count (2) * [ 1: 1] EVENT_TYPE_INT * [ 2: 5] total madvise byte count * [ 6: 6] EVENT_TYPE_INT * [ 7:10] zygote heap madvise byte count */ unsigned char *c = eventBuf; *c++ = 2; *c++ = EVENT_TYPE_INT; memcpy(c, &total, sizeof(total)); c += sizeof(total); *c++ = EVENT_TYPE_INT; memcpy(c, &zyg, sizeof(zyg)); c += sizeof(zyg); (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_madvise_info, EVENT_TYPE_LIST, eventBuf, sizeof(eventBuf)); } #if 0 #include <errno.h> #include <stdio.h> typedef struct HeapDumpContext { FILE *fp; void *chunkStart; size_t chunkLen; bool chunkFree; } HeapDumpContext; static void dump_context(const HeapDumpContext *ctx) { fprintf(ctx->fp, "0x%08x %12.12zd %s\n", (uintptr_t)ctx->chunkStart, ctx->chunkLen, ctx->chunkFree ? "FREE" : "USED"); } static void heap_chunk_callback(const void *chunkptr, size_t chunklen, const void *userptr, size_t userlen, void *arg) { HeapDumpContext *ctx = (HeapDumpContext *)arg; bool chunkFree = (userptr == NULL); if (chunkFree != ctx->chunkFree || ((char *)ctx->chunkStart + ctx->chunkLen) != chunkptr) { /* The new chunk is of a different type or isn't * contiguous with the current chunk. Dump the * old one and start a new one. */ if (ctx->chunkStart != NULL) { /* It's not the first chunk. */ dump_context(ctx); } ctx->chunkStart = (void *)chunkptr; ctx->chunkLen = chunklen; ctx->chunkFree = chunkFree; } else { /* Extend the current chunk. */ ctx->chunkLen += chunklen; } } /* Dumps free and used ranges, as text, to the named file. */ void dvmDumpHeapToFile(const char *fileName) { HeapDumpContext ctx; FILE *fp; fp = fopen(fileName, "w+"); if (fp == NULL) { LOGE("Can't open %s for writing: %s\n", fileName, strerror(errno)); return; } LOGW("Dumping heap to %s...\n", fileName); fprintf(fp, "==== Dalvik heap dump ====\n"); memset(&ctx, 0, sizeof(ctx)); ctx.fp = fp; dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx); dump_context(&ctx); fprintf(fp, "==== end heap dump ====\n"); LOGW("Dumped heap to %s.\n", fileName); fclose(fp); } #endif