1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <fcntl.h> 18 #include <stdlib.h> 19 20 #include "Dalvik.h" 21 #include "HeapInternal.h" 22 #include "HeapSource.h" 23 #include "Float12.h" 24 25 int dvmGetHeapDebugInfo(HeapDebugInfoType info) 26 { 27 switch (info) { 28 case kVirtualHeapSize: 29 return (int)dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0); 30 case kVirtualHeapAllocated: 31 return (int)dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0); 32 default: 33 return -1; 34 } 35 } 36 37 /* Looks up the cmdline for the process and tries to find 38 * the most descriptive five characters, then inserts the 39 * short name into the provided event value. 40 */ 41 #define PROC_NAME_LEN 5 42 static void insertProcessName(long long *ep) 43 { 44 static bool foundRealName = false; 45 static char name[PROC_NAME_LEN] = { 'X', 'X', 'X', 'X', 'X' }; 46 long long event = *ep; 47 48 if (!foundRealName) { 49 int fd = open("/proc/self/cmdline", O_RDONLY); 50 if (fd > 0) { 51 char buf[128]; 52 ssize_t n = read(fd, buf, sizeof(buf) - 1); 53 close(fd); 54 if (n > 0) { 55 memset(name, 0, sizeof(name)); 56 if (n <= PROC_NAME_LEN) { 57 // The whole name fits. 58 memcpy(name, buf, n); 59 } else { 60 /* We need to truncate. The name will look something 61 * like "com.android.home". Favor the characters 62 * immediately following the last dot. 63 */ 64 buf[n] = '\0'; 65 char *dot = strrchr(buf, '.'); 66 if (dot == NULL) { 67 /* Or, look for a slash, in case it's something like 68 * "/system/bin/runtime". 69 */ 70 dot = strrchr(buf, '/'); 71 } 72 if (dot != NULL) { 73 dot++; // Skip the dot 74 size_t dotlen = strlen(dot); 75 if (dotlen < PROC_NAME_LEN) { 76 /* Use all available characters. We know that 77 * n > PROC_NAME_LEN from the check above. 78 */ 79 dot -= PROC_NAME_LEN - dotlen; 80 } 81 strncpy(name, dot, PROC_NAME_LEN); 82 } else { 83 // No dot; just use the leading characters. 84 memcpy(name, buf, PROC_NAME_LEN); 85 } 86 } 87 if (strcmp(buf, "zygote") != 0) { 88 /* If the process is no longer called "zygote", 89 * cache this name. 90 */ 91 foundRealName = true; 92 } 93 } 94 } 95 } 96 97 event &= ~(0xffffffffffLL << 24); 98 event |= (long long)name[0] << 56; 99 event |= (long long)name[1] << 48; 100 event |= (long long)name[2] << 40; 101 event |= (long long)name[3] << 32; 102 event |= (long long)name[4] << 24; 103 104 *ep = event; 105 } 106 107 // See device/data/etc/event-log-tags 108 #define EVENT_LOG_TAG_dvm_gc_info 20001 109 #define EVENT_LOG_TAG_dvm_gc_madvise_info 20002 110 111 void dvmLogGcStats(size_t numFreed, size_t sizeFreed, size_t gcTimeMs) 112 { 113 size_t perHeapActualSize[HEAP_SOURCE_MAX_HEAP_COUNT], 114 perHeapAllowedSize[HEAP_SOURCE_MAX_HEAP_COUNT], 115 perHeapNumAllocated[HEAP_SOURCE_MAX_HEAP_COUNT], 116 perHeapSizeAllocated[HEAP_SOURCE_MAX_HEAP_COUNT]; 117 unsigned char eventBuf[1 + (1 + sizeof(long long)) * 4]; 118 size_t actualSize, allowedSize, numAllocated, sizeAllocated; 119 size_t softLimit = dvmHeapSourceGetIdealFootprint(); 120 size_t nHeaps = dvmHeapSourceGetNumHeaps(); 121 122 /* Enough to quiet down gcc for unitialized variable check */ 123 perHeapActualSize[0] = perHeapAllowedSize[0] = perHeapNumAllocated[0] = 124 perHeapSizeAllocated[0] = 0; 125 actualSize = dvmHeapSourceGetValue(HS_FOOTPRINT, perHeapActualSize, 126 HEAP_SOURCE_MAX_HEAP_COUNT); 127 allowedSize = dvmHeapSourceGetValue(HS_ALLOWED_FOOTPRINT, 128 perHeapAllowedSize, HEAP_SOURCE_MAX_HEAP_COUNT); 129 numAllocated = dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, 130 perHeapNumAllocated, HEAP_SOURCE_MAX_HEAP_COUNT); 131 sizeAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, 132 perHeapSizeAllocated, HEAP_SOURCE_MAX_HEAP_COUNT); 133 134 /* 135 * Construct the the first 64-bit value to write to the log. 136 * Global information: 137 * 138 * [63 ] Must be zero 139 * [62-24] ASCII process identifier 140 * [23-12] GC time in ms 141 * [11- 0] Bytes freed 142 * 143 */ 144 long long event0; 145 event0 = 0LL << 63 | 146 (long long)intToFloat12(gcTimeMs) << 12 | 147 (long long)intToFloat12(sizeFreed); 148 insertProcessName(&event0); 149 150 /* 151 * Aggregated heap stats: 152 * 153 * [63-62] 10 154 * [61-60] Reserved; must be zero 155 * [59-48] Objects freed 156 * [47-36] Actual size (current footprint) 157 * [35-24] Allowed size (current hard max) 158 * [23-12] Objects allocated 159 * [11- 0] Bytes allocated 160 */ 161 long long event1; 162 event1 = 2LL << 62 | 163 (long long)intToFloat12(numFreed) << 48 | 164 (long long)intToFloat12(actualSize) << 36 | 165 (long long)intToFloat12(allowedSize) << 24 | 166 (long long)intToFloat12(numAllocated) << 12 | 167 (long long)intToFloat12(sizeAllocated); 168 169 /* 170 * Report the current state of the zygote heap(s). 171 * 172 * The active heap is always heap[0]. We can be in one of three states 173 * at present: 174 * 175 * (1) Still in the zygote. Zygote using heap[0]. 176 * (2) In the zygote, when the first child is started. We created a 177 * new heap just before the first fork() call, so the original 178 * "zygote heap" is now heap[1], and we have a small heap[0] for 179 * anything we do from here on. 180 * (3) In an app process. The app gets a new heap[0], and can also 181 * see the two zygote heaps [1] and [2] (probably unwise to 182 * assume any specific ordering). 183 * 184 * So if nHeaps == 1, we want the stats from heap[0]; else we want 185 * the sum of the values from heap[1] to heap[nHeaps-1]. 186 * 187 * 188 * Zygote heap stats (except for the soft limit, which belongs to the 189 * active heap): 190 * 191 * [63-62] 11 192 * [61-60] Reserved; must be zero 193 * [59-48] Soft Limit (for the active heap) 194 * [47-36] Actual size (current footprint) 195 * [35-24] Allowed size (current hard max) 196 * [23-12] Objects allocated 197 * [11- 0] Bytes allocated 198 */ 199 long long event2; 200 size_t zActualSize, zAllowedSize, zNumAllocated, zSizeAllocated; 201 int firstHeap = (nHeaps == 1) ? 0 : 1; 202 size_t hh; 203 204 zActualSize = zAllowedSize = zNumAllocated = zSizeAllocated = 0; 205 for (hh = firstHeap; hh < nHeaps; hh++) { 206 zActualSize += perHeapActualSize[hh]; 207 zAllowedSize += perHeapAllowedSize[hh]; 208 zNumAllocated += perHeapNumAllocated[hh]; 209 zSizeAllocated += perHeapSizeAllocated[hh]; 210 } 211 event2 = 3LL << 62 | 212 (long long)intToFloat12(softLimit) << 48 | 213 (long long)intToFloat12(zActualSize) << 36 | 214 (long long)intToFloat12(zAllowedSize) << 24 | 215 (long long)intToFloat12(zNumAllocated) << 12 | 216 (long long)intToFloat12(zSizeAllocated); 217 218 /* 219 * Report the current external allocation stats and the native heap 220 * summary. 221 * 222 * [63-48] Reserved; must be zero (TODO: put new data in these slots) 223 * [47-36] dlmalloc_footprint 224 * [35-24] mallinfo: total allocated space 225 * [23-12] External byte limit 226 * [11- 0] External bytes allocated 227 */ 228 long long event3; 229 size_t externalLimit, externalBytesAllocated; 230 size_t uordblks, footprint; 231 232 #if 0 233 /* 234 * This adds 2-5msec to the GC cost on a DVT, or about 2-3% of the cost 235 * of a GC, so it's not horribly expensive but it's not free either. 236 */ 237 extern size_t dlmalloc_footprint(void); 238 struct mallinfo mi; 239 //u8 start, end; 240 241 //start = dvmGetRelativeTimeNsec(); 242 mi = mallinfo(); 243 uordblks = mi.uordblks; 244 footprint = dlmalloc_footprint(); 245 //end = dvmGetRelativeTimeNsec(); 246 //LOGD("mallinfo+footprint took %dusec; used=%zd footprint=%zd\n", 247 // (int)((end - start) / 1000), mi.uordblks, footprint); 248 #else 249 uordblks = footprint = 0; 250 #endif 251 252 externalLimit = 253 dvmHeapSourceGetValue(HS_EXTERNAL_LIMIT, NULL, 0); 254 externalBytesAllocated = 255 dvmHeapSourceGetValue(HS_EXTERNAL_BYTES_ALLOCATED, NULL, 0); 256 event3 = 257 (long long)intToFloat12(footprint) << 36 | 258 (long long)intToFloat12(uordblks) << 24 | 259 (long long)intToFloat12(externalLimit) << 12 | 260 (long long)intToFloat12(externalBytesAllocated); 261 262 /* Build the event data. 263 * [ 0: 0] item count (4) 264 * [ 1: 1] EVENT_TYPE_LONG 265 * [ 2: 9] event0 266 * [10:10] EVENT_TYPE_LONG 267 * [11:18] event1 268 * [19:19] EVENT_TYPE_LONG 269 * [20:27] event2 270 * [28:28] EVENT_TYPE_LONG 271 * [29:36] event2 272 */ 273 unsigned char *c = eventBuf; 274 *c++ = 4; 275 *c++ = EVENT_TYPE_LONG; 276 memcpy(c, &event0, sizeof(event0)); 277 c += sizeof(event0); 278 *c++ = EVENT_TYPE_LONG; 279 memcpy(c, &event1, sizeof(event1)); 280 c += sizeof(event1); 281 *c++ = EVENT_TYPE_LONG; 282 memcpy(c, &event2, sizeof(event2)); 283 c += sizeof(event2); 284 *c++ = EVENT_TYPE_LONG; 285 memcpy(c, &event3, sizeof(event3)); 286 287 (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_info, EVENT_TYPE_LIST, 288 eventBuf, sizeof(eventBuf)); 289 } 290 291 void dvmLogMadviseStats(size_t madvisedSizes[], size_t arrayLen) 292 { 293 unsigned char eventBuf[1 + (1 + sizeof(int)) * 2]; 294 size_t total, zyg; 295 size_t firstHeap, i; 296 size_t nHeaps = dvmHeapSourceGetNumHeaps(); 297 298 assert(arrayLen >= nHeaps); 299 300 firstHeap = nHeaps > 1 ? 1 : 0; 301 total = 0; 302 zyg = 0; 303 for (i = 0; i < nHeaps; i++) { 304 total += madvisedSizes[i]; 305 if (i >= firstHeap) { 306 zyg += madvisedSizes[i]; 307 } 308 } 309 310 /* Build the event data. 311 * [ 0: 0] item count (2) 312 * [ 1: 1] EVENT_TYPE_INT 313 * [ 2: 5] total madvise byte count 314 * [ 6: 6] EVENT_TYPE_INT 315 * [ 7:10] zygote heap madvise byte count 316 */ 317 unsigned char *c = eventBuf; 318 *c++ = 2; 319 *c++ = EVENT_TYPE_INT; 320 memcpy(c, &total, sizeof(total)); 321 c += sizeof(total); 322 *c++ = EVENT_TYPE_INT; 323 memcpy(c, &zyg, sizeof(zyg)); 324 c += sizeof(zyg); 325 326 (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_madvise_info, 327 EVENT_TYPE_LIST, eventBuf, sizeof(eventBuf)); 328 } 329 330 #if 0 331 #include <errno.h> 332 #include <stdio.h> 333 334 typedef struct HeapDumpContext { 335 FILE *fp; 336 void *chunkStart; 337 size_t chunkLen; 338 bool chunkFree; 339 } HeapDumpContext; 340 341 static void 342 dump_context(const HeapDumpContext *ctx) 343 { 344 fprintf(ctx->fp, "0x%08x %12.12zd %s\n", (uintptr_t)ctx->chunkStart, 345 ctx->chunkLen, ctx->chunkFree ? "FREE" : "USED"); 346 } 347 348 static void 349 heap_chunk_callback(const void *chunkptr, size_t chunklen, 350 const void *userptr, size_t userlen, void *arg) 351 { 352 HeapDumpContext *ctx = (HeapDumpContext *)arg; 353 bool chunkFree = (userptr == NULL); 354 355 if (chunkFree != ctx->chunkFree || 356 ((char *)ctx->chunkStart + ctx->chunkLen) != chunkptr) 357 { 358 /* The new chunk is of a different type or isn't 359 * contiguous with the current chunk. Dump the 360 * old one and start a new one. 361 */ 362 if (ctx->chunkStart != NULL) { 363 /* It's not the first chunk. */ 364 dump_context(ctx); 365 } 366 ctx->chunkStart = (void *)chunkptr; 367 ctx->chunkLen = chunklen; 368 ctx->chunkFree = chunkFree; 369 } else { 370 /* Extend the current chunk. 371 */ 372 ctx->chunkLen += chunklen; 373 } 374 } 375 376 /* Dumps free and used ranges, as text, to the named file. 377 */ 378 void dvmDumpHeapToFile(const char *fileName) 379 { 380 HeapDumpContext ctx; 381 FILE *fp; 382 383 fp = fopen(fileName, "w+"); 384 if (fp == NULL) { 385 LOGE("Can't open %s for writing: %s\n", fileName, strerror(errno)); 386 return; 387 } 388 LOGW("Dumping heap to %s...\n", fileName); 389 390 fprintf(fp, "==== Dalvik heap dump ====\n"); 391 memset(&ctx, 0, sizeof(ctx)); 392 ctx.fp = fp; 393 dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx); 394 dump_context(&ctx); 395 fprintf(fp, "==== end heap dump ====\n"); 396 397 LOGW("Dumped heap to %s.\n", fileName); 398 399 fclose(fp); 400 } 401 #endif 402