| /* |
| * 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 |