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 const GcHeap *gcHeap = gDvm.gcHeap; 114 size_t perHeapActualSize[HEAP_SOURCE_MAX_HEAP_COUNT], 115 perHeapAllowedSize[HEAP_SOURCE_MAX_HEAP_COUNT], 116 perHeapNumAllocated[HEAP_SOURCE_MAX_HEAP_COUNT], 117 perHeapSizeAllocated[HEAP_SOURCE_MAX_HEAP_COUNT]; 118 unsigned char eventBuf[1 + (1 + sizeof(long long)) * 4]; 119 size_t actualSize, allowedSize, numAllocated, sizeAllocated; 120 size_t i; 121 size_t softLimit = dvmHeapSourceGetIdealFootprint(); 122 size_t nHeaps = dvmHeapSourceGetNumHeaps(); 123 124 /* Enough to quiet down gcc for unitialized variable check */ 125 perHeapActualSize[0] = perHeapAllowedSize[0] = perHeapNumAllocated[0] = 126 perHeapSizeAllocated[0] = 0; 127 actualSize = dvmHeapSourceGetValue(HS_FOOTPRINT, perHeapActualSize, 128 HEAP_SOURCE_MAX_HEAP_COUNT); 129 allowedSize = dvmHeapSourceGetValue(HS_ALLOWED_FOOTPRINT, 130 perHeapAllowedSize, HEAP_SOURCE_MAX_HEAP_COUNT); 131 numAllocated = dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, 132 perHeapNumAllocated, HEAP_SOURCE_MAX_HEAP_COUNT); 133 sizeAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, 134 perHeapSizeAllocated, HEAP_SOURCE_MAX_HEAP_COUNT); 135 136 /* 137 * Construct the the first 64-bit value to write to the log. 138 * Global information: 139 * 140 * [63 ] Must be zero 141 * [62-24] ASCII process identifier 142 * [23-12] GC time in ms 143 * [11- 0] Bytes freed 144 * 145 */ 146 long long event0; 147 event0 = 0LL << 63 | 148 (long long)intToFloat12(gcTimeMs) << 12 | 149 (long long)intToFloat12(sizeFreed); 150 insertProcessName(&event0); 151 152 /* 153 * Aggregated heap stats: 154 * 155 * [63-62] 10 156 * [61-60] Reserved; must be zero 157 * [59-48] Objects freed 158 * [47-36] Actual size (current footprint) 159 * [35-24] Allowed size (current hard max) 160 * [23-12] Objects allocated 161 * [11- 0] Bytes allocated 162 */ 163 long long event1; 164 event1 = 2LL << 62 | 165 (long long)intToFloat12(numFreed) << 48 | 166 (long long)intToFloat12(actualSize) << 36 | 167 (long long)intToFloat12(allowedSize) << 24 | 168 (long long)intToFloat12(numAllocated) << 12 | 169 (long long)intToFloat12(sizeAllocated); 170 171 /* 172 * Report the current state of the zygote heap(s). 173 * 174 * The active heap is always heap[0]. We can be in one of three states 175 * at present: 176 * 177 * (1) Still in the zygote. Zygote using heap[0]. 178 * (2) In the zygote, when the first child is started. We created a 179 * new heap just before the first fork() call, so the original 180 * "zygote heap" is now heap[1], and we have a small heap[0] for 181 * anything we do from here on. 182 * (3) In an app process. The app gets a new heap[0], and can also 183 * see the two zygote heaps [1] and [2] (probably unwise to 184 * assume any specific ordering). 185 * 186 * So if nHeaps == 1, we want the stats from heap[0]; else we want 187 * the sum of the values from heap[1] to heap[nHeaps-1]. 188 * 189 * 190 * Zygote heap stats (except for the soft limit, which belongs to the 191 * active heap): 192 * 193 * [63-62] 11 194 * [61-60] Reserved; must be zero 195 * [59-48] Soft Limit (for the active heap) 196 * [47-36] Actual size (current footprint) 197 * [35-24] Allowed size (current hard max) 198 * [23-12] Objects allocated 199 * [11- 0] Bytes allocated 200 */ 201 long long event2; 202 size_t zActualSize, zAllowedSize, zNumAllocated, zSizeAllocated; 203 int firstHeap = (nHeaps == 1) ? 0 : 1; 204 size_t hh; 205 206 zActualSize = zAllowedSize = zNumAllocated = zSizeAllocated = 0; 207 for (hh = firstHeap; hh < nHeaps; hh++) { 208 zActualSize += perHeapActualSize[hh]; 209 zAllowedSize += perHeapAllowedSize[hh]; 210 zNumAllocated += perHeapNumAllocated[hh]; 211 zSizeAllocated += perHeapSizeAllocated[hh]; 212 } 213 event2 = 3LL << 62 | 214 (long long)intToFloat12(softLimit) << 48 | 215 (long long)intToFloat12(zActualSize) << 36 | 216 (long long)intToFloat12(zAllowedSize) << 24 | 217 (long long)intToFloat12(zNumAllocated) << 12 | 218 (long long)intToFloat12(zSizeAllocated); 219 220 /* 221 * Report the current external allocation stats and the native heap 222 * summary. 223 * 224 * [63-48] Reserved; must be zero (TODO: put new data in these slots) 225 * [47-36] dlmalloc_footprint 226 * [35-24] mallinfo: total allocated space 227 * [23-12] External byte limit 228 * [11- 0] External bytes allocated 229 */ 230 long long event3; 231 size_t externalLimit, externalBytesAllocated; 232 size_t uordblks, footprint; 233 234 #if 0 235 /* 236 * This adds 2-5msec to the GC cost on a DVT, or about 2-3% of the cost 237 * of a GC, so it's not horribly expensive but it's not free either. 238 */ 239 extern size_t dlmalloc_footprint(void); 240 struct mallinfo mi; 241 //u8 start, end; 242 243 //start = dvmGetRelativeTimeNsec(); 244 mi = mallinfo(); 245 uordblks = mi.uordblks; 246 footprint = dlmalloc_footprint(); 247 //end = dvmGetRelativeTimeNsec(); 248 //LOGD("mallinfo+footprint took %dusec; used=%zd footprint=%zd\n", 249 // (int)((end - start) / 1000), mi.uordblks, footprint); 250 #else 251 uordblks = footprint = 0; 252 #endif 253 254 externalLimit = 255 dvmHeapSourceGetValue(HS_EXTERNAL_LIMIT, NULL, 0); 256 externalBytesAllocated = 257 dvmHeapSourceGetValue(HS_EXTERNAL_BYTES_ALLOCATED, NULL, 0); 258 event3 = 259 (long long)intToFloat12(footprint) << 36 | 260 (long long)intToFloat12(uordblks) << 24 | 261 (long long)intToFloat12(externalLimit) << 12 | 262 (long long)intToFloat12(externalBytesAllocated); 263 264 /* Build the event data. 265 * [ 0: 0] item count (4) 266 * [ 1: 1] EVENT_TYPE_LONG 267 * [ 2: 9] event0 268 * [10:10] EVENT_TYPE_LONG 269 * [11:18] event1 270 * [19:19] EVENT_TYPE_LONG 271 * [20:27] event2 272 * [28:28] EVENT_TYPE_LONG 273 * [29:36] event2 274 */ 275 unsigned char *c = eventBuf; 276 *c++ = 4; 277 *c++ = EVENT_TYPE_LONG; 278 memcpy(c, &event0, sizeof(event0)); 279 c += sizeof(event0); 280 *c++ = EVENT_TYPE_LONG; 281 memcpy(c, &event1, sizeof(event1)); 282 c += sizeof(event1); 283 *c++ = EVENT_TYPE_LONG; 284 memcpy(c, &event2, sizeof(event2)); 285 c += sizeof(event2); 286 *c++ = EVENT_TYPE_LONG; 287 memcpy(c, &event3, sizeof(event3)); 288 289 (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_info, EVENT_TYPE_LIST, 290 eventBuf, sizeof(eventBuf)); 291 } 292 293 void dvmLogMadviseStats(size_t madvisedSizes[], size_t arrayLen) 294 { 295 unsigned char eventBuf[1 + (1 + sizeof(int)) * 2]; 296 size_t total, zyg; 297 size_t firstHeap, i; 298 size_t nHeaps = dvmHeapSourceGetNumHeaps(); 299 300 assert(arrayLen >= nHeaps); 301 302 firstHeap = nHeaps > 1 ? 1 : 0; 303 total = 0; 304 zyg = 0; 305 for (i = 0; i < nHeaps; i++) { 306 total += madvisedSizes[i]; 307 if (i >= firstHeap) { 308 zyg += madvisedSizes[i]; 309 } 310 } 311 312 /* Build the event data. 313 * [ 0: 0] item count (2) 314 * [ 1: 1] EVENT_TYPE_INT 315 * [ 2: 5] total madvise byte count 316 * [ 6: 6] EVENT_TYPE_INT 317 * [ 7:10] zygote heap madvise byte count 318 */ 319 unsigned char *c = eventBuf; 320 *c++ = 2; 321 *c++ = EVENT_TYPE_INT; 322 memcpy(c, &total, sizeof(total)); 323 c += sizeof(total); 324 *c++ = EVENT_TYPE_INT; 325 memcpy(c, &zyg, sizeof(zyg)); 326 c += sizeof(zyg); 327 328 (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_madvise_info, 329 EVENT_TYPE_LIST, eventBuf, sizeof(eventBuf)); 330 } 331 332 #if 0 333 #include <errno.h> 334 #include <stdio.h> 335 336 typedef struct HeapDumpContext { 337 FILE *fp; 338 void *chunkStart; 339 size_t chunkLen; 340 bool chunkFree; 341 } HeapDumpContext; 342 343 static void 344 dump_context(const HeapDumpContext *ctx) 345 { 346 fprintf(ctx->fp, "0x%08x %12.12zd %s\n", (uintptr_t)ctx->chunkStart, 347 ctx->chunkLen, ctx->chunkFree ? "FREE" : "USED"); 348 } 349 350 static void 351 heap_chunk_callback(const void *chunkptr, size_t chunklen, 352 const void *userptr, size_t userlen, void *arg) 353 { 354 HeapDumpContext *ctx = (HeapDumpContext *)arg; 355 bool chunkFree = (userptr == NULL); 356 357 if (chunkFree != ctx->chunkFree || 358 ((char *)ctx->chunkStart + ctx->chunkLen) != chunkptr) 359 { 360 /* The new chunk is of a different type or isn't 361 * contiguous with the current chunk. Dump the 362 * old one and start a new one. 363 */ 364 if (ctx->chunkStart != NULL) { 365 /* It's not the first chunk. */ 366 dump_context(ctx); 367 } 368 ctx->chunkStart = (void *)chunkptr; 369 ctx->chunkLen = chunklen; 370 ctx->chunkFree = chunkFree; 371 } else { 372 /* Extend the current chunk. 373 */ 374 ctx->chunkLen += chunklen; 375 } 376 } 377 378 /* Dumps free and used ranges, as text, to the named file. 379 */ 380 void dvmDumpHeapToFile(const char *fileName) 381 { 382 HeapDumpContext ctx; 383 FILE *fp; 384 385 fp = fopen(fileName, "w+"); 386 if (fp == NULL) { 387 LOGE("Can't open %s for writing: %s\n", fileName, strerror(errno)); 388 return; 389 } 390 LOGW("Dumping heap to %s...\n", fileName); 391 392 fprintf(fp, "==== Dalvik heap dump ====\n"); 393 memset(&ctx, 0, sizeof(ctx)); 394 ctx.fp = fp; 395 dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx); 396 dump_context(&ctx); 397 fprintf(fp, "==== end heap dump ====\n"); 398 399 LOGW("Dumped heap to %s.\n", fileName); 400 401 fclose(fp); 402 } 403 #endif 404