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