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 * DDM-related heap functions 18 */ 19 #include <sys/time.h> 20 #include <time.h> 21 22 #include "Dalvik.h" 23 #include "alloc/Heap.h" 24 #include "alloc/HeapInternal.h" 25 #include "alloc/DdmHeap.h" 26 #include "alloc/HeapSource.h" 27 28 #define DEFAULT_HEAP_ID 1 29 30 enum HpifWhen { 31 HPIF_WHEN_NEVER = 0, 32 HPIF_WHEN_NOW = 1, 33 HPIF_WHEN_NEXT_GC = 2, 34 HPIF_WHEN_EVERY_GC = 3 35 }; 36 37 /* 38 * Chunk HPIF (client --> server) 39 * 40 * Heap Info. General information about the heap, 41 * suitable for a summary display. 42 * 43 * [u4]: number of heaps 44 * 45 * For each heap: 46 * [u4]: heap ID 47 * [u8]: timestamp in ms since Unix epoch 48 * [u1]: capture reason (same as 'when' value from server) 49 * [u4]: max heap size in bytes (-Xmx) 50 * [u4]: current heap size in bytes 51 * [u4]: current number of bytes allocated 52 * [u4]: current number of objects allocated 53 */ 54 #define HPIF_SIZE(numHeaps) \ 55 (sizeof(u4) + (numHeaps) * (5 * sizeof(u4) + sizeof(u1) + sizeof(u8))) 56 void 57 dvmDdmSendHeapInfo(int reason, bool shouldLock) 58 { 59 struct timeval now; 60 u8 nowMs; 61 u1 *buf, *b; 62 63 buf = (u1 *)malloc(HPIF_SIZE(1)); 64 if (buf == NULL) { 65 return; 66 } 67 b = buf; 68 69 /* If there's a one-shot 'when', reset it. 70 */ 71 if (reason == gDvm.gcHeap->ddmHpifWhen) { 72 if (shouldLock && ! dvmLockHeap()) { 73 LOGW("%s(): can't lock heap to clear when\n", __func__); 74 goto skip_when; 75 } 76 if (reason == gDvm.gcHeap->ddmHpifWhen) { 77 if (gDvm.gcHeap->ddmHpifWhen == HPIF_WHEN_NEXT_GC) { 78 gDvm.gcHeap->ddmHpifWhen = HPIF_WHEN_NEVER; 79 } 80 } 81 if (shouldLock) { 82 dvmUnlockHeap(); 83 } 84 } 85 skip_when: 86 87 /* The current time, in milliseconds since 0:00 GMT, 1/1/70. 88 */ 89 if (gettimeofday(&now, NULL) < 0) { 90 nowMs = 0; 91 } else { 92 nowMs = (u8)now.tv_sec * 1000 + now.tv_usec / 1000; 93 } 94 95 /* number of heaps */ 96 set4BE(b, 1); b += 4; 97 98 /* For each heap (of which there is one) */ 99 { 100 /* heap ID */ 101 set4BE(b, DEFAULT_HEAP_ID); b += 4; 102 103 /* timestamp */ 104 set8BE(b, nowMs); b += 8; 105 106 /* 'when' value */ 107 *b++ = (u1)reason; 108 109 /* max allowed heap size in bytes */ 110 set4BE(b, gDvm.heapSizeMax); b += 4; 111 112 /* current heap size in bytes */ 113 set4BE(b, dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0)); b += 4; 114 115 /* number of bytes allocated */ 116 set4BE(b, dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0)); b += 4; 117 118 /* number of objects allocated */ 119 set4BE(b, dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, NULL, 0)); b += 4; 120 } 121 assert((intptr_t)b == (intptr_t)buf + (intptr_t)HPIF_SIZE(1)); 122 123 dvmDbgDdmSendChunk(CHUNK_TYPE("HPIF"), b - buf, buf); 124 } 125 126 bool 127 dvmDdmHandleHpifChunk(int when) 128 { 129 switch (when) { 130 case HPIF_WHEN_NOW: 131 dvmDdmSendHeapInfo(when, true); 132 break; 133 case HPIF_WHEN_NEVER: 134 case HPIF_WHEN_NEXT_GC: 135 case HPIF_WHEN_EVERY_GC: 136 if (dvmLockHeap()) { 137 gDvm.gcHeap->ddmHpifWhen = when; 138 dvmUnlockHeap(); 139 } else { 140 LOGI("%s(): can't lock heap to set when\n", __func__); 141 return false; 142 } 143 break; 144 default: 145 LOGI("%s(): bad when value 0x%08x\n", __func__, when); 146 return false; 147 } 148 149 return true; 150 } 151 152 enum HpsgSolidity { 153 SOLIDITY_FREE = 0, 154 SOLIDITY_HARD = 1, 155 SOLIDITY_SOFT = 2, 156 SOLIDITY_WEAK = 3, 157 SOLIDITY_PHANTOM = 4, 158 SOLIDITY_FINALIZABLE = 5, 159 SOLIDITY_SWEEP = 6, 160 }; 161 162 enum HpsgKind { 163 KIND_OBJECT = 0, 164 KIND_CLASS_OBJECT = 1, 165 KIND_ARRAY_1 = 2, 166 KIND_ARRAY_2 = 3, 167 KIND_ARRAY_4 = 4, 168 KIND_ARRAY_8 = 5, 169 KIND_UNKNOWN = 6, 170 KIND_NATIVE = 7, 171 }; 172 173 #define HPSG_PARTIAL (1<<7) 174 #define HPSG_STATE(solidity, kind) \ 175 ((u1)((((kind) & 0x7) << 3) | ((solidity) & 0x7))) 176 177 typedef struct HeapChunkContext { 178 u1 *buf; 179 u1 *p; 180 u1 *pieceLenField; 181 size_t bufLen; 182 size_t totalAllocationUnits; 183 int type; 184 bool merge; 185 bool needHeader; 186 } HeapChunkContext; 187 188 #define ALLOCATION_UNIT_SIZE 8 189 190 static void 191 flush_hpsg_chunk(HeapChunkContext *ctx) 192 { 193 /* Patch the "length of piece" field. 194 */ 195 assert(ctx->buf <= ctx->pieceLenField && 196 ctx->pieceLenField <= ctx->p); 197 set4BE(ctx->pieceLenField, ctx->totalAllocationUnits); 198 199 /* Send the chunk. 200 */ 201 dvmDbgDdmSendChunk(ctx->type, ctx->p - ctx->buf, ctx->buf); 202 203 /* Reset the context. 204 */ 205 ctx->p = ctx->buf; 206 ctx->totalAllocationUnits = 0; 207 ctx->needHeader = true; 208 ctx->pieceLenField = NULL; 209 } 210 211 static void 212 heap_chunk_callback(const void *chunkptr, size_t chunklen, 213 const void *userptr, size_t userlen, void *arg) 214 { 215 HeapChunkContext *ctx = (HeapChunkContext *)arg; 216 u1 state; 217 218 UNUSED_PARAMETER(userlen); 219 220 assert((chunklen & (ALLOCATION_UNIT_SIZE-1)) == 0); 221 222 /* Make sure there's enough room left in the buffer. 223 * We need to use two bytes for every fractional 256 224 * allocation units used by the chunk. 225 */ 226 { 227 size_t needed = (((chunklen/ALLOCATION_UNIT_SIZE + 255) / 256) * 2); 228 size_t bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf); 229 if (bytesLeft < needed) { 230 flush_hpsg_chunk(ctx); 231 } 232 233 bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf); 234 if (bytesLeft < needed) { 235 LOGW("chunk is too big to transmit (chunklen=%zd, %zd bytes)\n", 236 chunklen, needed); 237 return; 238 } 239 } 240 241 //TODO: notice when there's a gap and start a new heap, or at least a new range. 242 if (ctx->needHeader) { 243 /* 244 * Start a new HPSx chunk. 245 */ 246 247 /* [u4]: heap ID */ 248 set4BE(ctx->p, DEFAULT_HEAP_ID); ctx->p += 4; 249 250 /* [u1]: size of allocation unit, in bytes */ 251 *ctx->p++ = 8; 252 253 /* [u4]: virtual address of segment start */ 254 set4BE(ctx->p, (uintptr_t)chunkptr); ctx->p += 4; 255 256 /* [u4]: offset of this piece (relative to the virtual address) */ 257 set4BE(ctx->p, 0); ctx->p += 4; 258 259 /* [u4]: length of piece, in allocation units 260 * We won't know this until we're done, so save the offset 261 * and stuff in a dummy value. 262 */ 263 ctx->pieceLenField = ctx->p; 264 set4BE(ctx->p, 0x55555555); ctx->p += 4; 265 266 ctx->needHeader = false; 267 } 268 269 /* Determine the type of this chunk. 270 */ 271 if (userptr == NULL) { 272 /* It's a free chunk. 273 */ 274 state = HPSG_STATE(SOLIDITY_FREE, 0); 275 } else { 276 const Object *obj = userptr; 277 /* If we're looking at the native heap, we'll just return 278 * (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks 279 */ 280 bool native = ctx->type == CHUNK_TYPE("NHSG"); 281 282 /* It's an allocated chunk. Figure out what it is. 283 */ 284 //TODO: if ctx.merge, see if this chunk is different from the last chunk. 285 // If it's the same, we should combine them. 286 if (!native && dvmIsValidObject(obj)) { 287 ClassObject *clazz = obj->clazz; 288 if (clazz == NULL) { 289 /* The object was probably just created 290 * but hasn't been initialized yet. 291 */ 292 state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT); 293 } else if (clazz == gDvm.classJavaLangClass) { 294 state = HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT); 295 } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) { 296 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) { 297 state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4); 298 } else { 299 switch (clazz->elementClass->primitiveType) { 300 case PRIM_BOOLEAN: 301 case PRIM_BYTE: 302 state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1); 303 break; 304 case PRIM_CHAR: 305 case PRIM_SHORT: 306 state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2); 307 break; 308 case PRIM_INT: 309 case PRIM_FLOAT: 310 state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4); 311 break; 312 case PRIM_DOUBLE: 313 case PRIM_LONG: 314 state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8); 315 break; 316 default: 317 assert(!"Unknown GC heap object type"); 318 state = HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN); 319 break; 320 } 321 } 322 } else { 323 state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT); 324 } 325 } else { 326 obj = NULL; // it's not actually an object 327 state = HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE); 328 } 329 } 330 331 /* Write out the chunk description. 332 */ 333 chunklen /= ALLOCATION_UNIT_SIZE; // convert to allocation units 334 ctx->totalAllocationUnits += chunklen; 335 while (chunklen > 256) { 336 *ctx->p++ = state | HPSG_PARTIAL; 337 *ctx->p++ = 255; // length - 1 338 chunklen -= 256; 339 } 340 *ctx->p++ = state; 341 *ctx->p++ = chunklen - 1; 342 } 343 344 enum HpsgWhen { 345 HPSG_WHEN_NEVER = 0, 346 HPSG_WHEN_EVERY_GC = 1, 347 }; 348 enum HpsgWhat { 349 HPSG_WHAT_MERGED_OBJECTS = 0, 350 HPSG_WHAT_DISTINCT_OBJECTS = 1, 351 }; 352 353 /* 354 * Maximum chunk size. Obtain this from the formula: 355 * 356 * (((maximum_heap_size / ALLOCATION_UNIT_SIZE) + 255) / 256) * 2 357 */ 358 #define HPSx_CHUNK_SIZE (16384 - 16) 359 360 void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*),void*); 361 362 static void 363 walkHeap(bool merge, bool native) 364 { 365 HeapChunkContext ctx; 366 367 memset(&ctx, 0, sizeof(ctx)); 368 ctx.bufLen = HPSx_CHUNK_SIZE; 369 ctx.buf = (u1 *)malloc(ctx.bufLen); 370 if (ctx.buf == NULL) { 371 return; 372 } 373 374 ctx.merge = merge; 375 if (native) { 376 ctx.type = CHUNK_TYPE("NHSG"); 377 } else { 378 if (ctx.merge) { 379 ctx.type = CHUNK_TYPE("HPSG"); 380 } else { 381 ctx.type = CHUNK_TYPE("HPSO"); 382 } 383 } 384 385 ctx.p = ctx.buf; 386 ctx.needHeader = true; 387 if (native) { 388 dlmalloc_walk_heap(heap_chunk_callback, (void *)&ctx); 389 } else { 390 dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx); 391 } 392 if (ctx.p > ctx.buf) { 393 flush_hpsg_chunk(&ctx); 394 } 395 396 free(ctx.buf); 397 } 398 399 void 400 dvmDdmSendHeapSegments(bool shouldLock, bool native) 401 { 402 u1 heapId[sizeof(u4)]; 403 GcHeap *gcHeap = gDvm.gcHeap; 404 int when, what; 405 bool merge; 406 407 /* Don't even grab the lock if there's nothing to do when we're called. 408 */ 409 if (!native) { 410 when = gcHeap->ddmHpsgWhen; 411 what = gcHeap->ddmHpsgWhat; 412 if (when == HPSG_WHEN_NEVER) { 413 return; 414 } 415 } else { 416 when = gcHeap->ddmNhsgWhen; 417 what = gcHeap->ddmNhsgWhat; 418 if (when == HPSG_WHEN_NEVER) { 419 return; 420 } 421 } 422 if (shouldLock && !dvmLockHeap()) { 423 LOGW("Can't lock heap for DDM HPSx dump\n"); 424 return; 425 } 426 427 /* Figure out what kind of chunks we'll be sending. 428 */ 429 if (what == HPSG_WHAT_MERGED_OBJECTS) { 430 merge = true; 431 } else if (what == HPSG_WHAT_DISTINCT_OBJECTS) { 432 merge = false; 433 } else { 434 assert(!"bad HPSG.what value"); 435 return; 436 } 437 438 /* First, send a heap start chunk. 439 */ 440 set4BE(heapId, DEFAULT_HEAP_ID); 441 dvmDbgDdmSendChunk(native ? CHUNK_TYPE("NHST") : CHUNK_TYPE("HPST"), 442 sizeof(u4), heapId); 443 444 /* Send a series of heap segment chunks. 445 */ 446 walkHeap(merge, native); 447 448 /* Finally, send a heap end chunk. 449 */ 450 dvmDbgDdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), 451 sizeof(u4), heapId); 452 453 if (shouldLock) { 454 dvmUnlockHeap(); 455 } 456 } 457 458 bool 459 dvmDdmHandleHpsgNhsgChunk(int when, int what, bool native) 460 { 461 LOGI("dvmDdmHandleHpsgChunk(when %d, what %d, heap %d)\n", when, what, 462 native); 463 switch (when) { 464 case HPSG_WHEN_NEVER: 465 case HPSG_WHEN_EVERY_GC: 466 break; 467 default: 468 LOGI("%s(): bad when value 0x%08x\n", __func__, when); 469 return false; 470 } 471 472 switch (what) { 473 case HPSG_WHAT_MERGED_OBJECTS: 474 case HPSG_WHAT_DISTINCT_OBJECTS: 475 break; 476 default: 477 LOGI("%s(): bad what value 0x%08x\n", __func__, what); 478 return false; 479 } 480 481 if (dvmLockHeap()) { 482 if (!native) { 483 gDvm.gcHeap->ddmHpsgWhen = when; 484 gDvm.gcHeap->ddmHpsgWhat = what; 485 } else { 486 gDvm.gcHeap->ddmNhsgWhen = when; 487 gDvm.gcHeap->ddmNhsgWhat = what; 488 } 489 //TODO: if what says we should dump immediately, signal (or do) it from here 490 dvmUnlockHeap(); 491 } else { 492 LOGI("%s(): can't lock heap to set when/what\n", __func__); 493 return false; 494 } 495 496 return true; 497 } 498