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 * Heap object dump 18 */ 19#include "Hprof.h" 20 21#include "alloc/HeapInternal.h" 22#include "alloc/HeapSource.h" 23 24/* Set DUMP_PRIM_DATA to 1 if you want to include the contents 25 * of primitive arrays (byte arrays, character arrays, etc.) 26 * in heap dumps. This can be a large amount of data. 27 */ 28#define DUMP_PRIM_DATA 1 29 30#define OBJECTS_PER_SEGMENT ((size_t)128) 31#define BYTES_PER_SEGMENT ((size_t)4096) 32 33/* The static field-name for the synthetic object generated to account 34 * for class Static overhead. 35 */ 36#define STATIC_OVERHEAD_NAME "$staticOverhead" 37/* The ID for the synthetic object generated to account for class 38 * Static overhead. 39 */ 40#define CLASS_STATICS_ID(clazz) ((hprof_object_id)(((u4)(clazz)) | 1)) 41 42int 43hprofStartHeapDump(hprof_context_t *ctx) 44{ 45 UNUSED_PARAMETER(ctx); 46 47 ctx->objectsInSegment = OBJECTS_PER_SEGMENT; 48 ctx->currentHeap = HPROF_HEAP_DEFAULT; 49 return 0; 50} 51 52int 53hprofFinishHeapDump(hprof_context_t *ctx) 54{ 55 return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME); 56} 57 58int 59hprofSetGcScanState(hprof_context_t *ctx, 60 hprof_heap_tag_t state, u4 threadSerialNumber) 61{ 62 /* Used by hprofMarkRootObject() 63 */ 64 ctx->gcScanState = state; 65 ctx->gcThreadSerialNumber = threadSerialNumber; 66 return 0; 67} 68 69static hprof_basic_type 70signatureToBasicTypeAndSize(const char *sig, size_t *sizeOut) 71{ 72 char c = sig[0]; 73 hprof_basic_type ret; 74 size_t size; 75 76 switch (c) { 77 case '[': 78 case 'L': ret = hprof_basic_object; size = 4; break; 79 case 'Z': ret = hprof_basic_boolean; size = 1; break; 80 case 'C': ret = hprof_basic_char; size = 2; break; 81 case 'F': ret = hprof_basic_float; size = 4; break; 82 case 'D': ret = hprof_basic_double; size = 8; break; 83 case 'B': ret = hprof_basic_byte; size = 1; break; 84 case 'S': ret = hprof_basic_short; size = 2; break; 85 default: assert(false); 86 case 'I': ret = hprof_basic_int; size = 4; break; 87 case 'J': ret = hprof_basic_long; size = 8; break; 88 } 89 90 if (sizeOut != NULL) { 91 *sizeOut = size; 92 } 93 94 return ret; 95} 96 97static hprof_basic_type 98primitiveToBasicTypeAndSize(PrimitiveType prim, size_t *sizeOut) 99{ 100 hprof_basic_type ret; 101 size_t size; 102 103 switch (prim) { 104 case PRIM_BOOLEAN: ret = hprof_basic_boolean; size = 1; break; 105 case PRIM_CHAR: ret = hprof_basic_char; size = 2; break; 106 case PRIM_FLOAT: ret = hprof_basic_float; size = 4; break; 107 case PRIM_DOUBLE: ret = hprof_basic_double; size = 8; break; 108 case PRIM_BYTE: ret = hprof_basic_byte; size = 1; break; 109 case PRIM_SHORT: ret = hprof_basic_short; size = 2; break; 110 default: assert(false); 111 case PRIM_INT: ret = hprof_basic_int; size = 4; break; 112 case PRIM_LONG: ret = hprof_basic_long; size = 8; break; 113 } 114 115 if (sizeOut != NULL) { 116 *sizeOut = size; 117 } 118 119 return ret; 120} 121 122/* Always called when marking objects, but only does 123 * something when ctx->gcScanState is non-zero, which is usually 124 * only true when marking the root set or unreachable 125 * objects. Used to add rootset references to obj. 126 */ 127int 128hprofMarkRootObject(hprof_context_t *ctx, const Object *obj, jobject jniObj) 129{ 130 hprof_record_t *rec = &ctx->curRec; 131 int err; 132 hprof_heap_tag_t heapTag = ctx->gcScanState; 133 134 if (heapTag == 0) { 135 return 0; 136 } 137 138 if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT || 139 rec->length >= BYTES_PER_SEGMENT) 140 { 141 /* This flushes the old segment and starts a new one. 142 */ 143 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME); 144 ctx->objectsInSegment = 0; 145 } 146 147 switch (heapTag) { 148 /* ID: object ID 149 */ 150 case HPROF_ROOT_UNKNOWN: 151 case HPROF_ROOT_STICKY_CLASS: 152 case HPROF_ROOT_MONITOR_USED: 153 case HPROF_ROOT_INTERNED_STRING: 154 case HPROF_ROOT_FINALIZING: 155 case HPROF_ROOT_DEBUGGER: 156 case HPROF_ROOT_REFERENCE_CLEANUP: 157 case HPROF_ROOT_VM_INTERNAL: 158 hprofAddU1ToRecord(rec, heapTag); 159 hprofAddIdToRecord(rec, (hprof_object_id)obj); 160 break; 161 162 /* ID: object ID 163 * ID: JNI global ref ID 164 */ 165 case HPROF_ROOT_JNI_GLOBAL: 166 hprofAddU1ToRecord(rec, heapTag); 167 hprofAddIdToRecord(rec, (hprof_object_id)obj); 168 hprofAddIdToRecord(rec, (hprof_id)jniObj); 169 break; 170 171 /* ID: object ID 172 * u4: thread serial number 173 * u4: frame number in stack trace (-1 for empty) 174 */ 175 case HPROF_ROOT_JNI_LOCAL: 176 case HPROF_ROOT_JNI_MONITOR: 177 case HPROF_ROOT_JAVA_FRAME: 178 hprofAddU1ToRecord(rec, heapTag); 179 hprofAddIdToRecord(rec, (hprof_object_id)obj); 180 hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber); 181 hprofAddU4ToRecord(rec, (u4)-1); 182 break; 183 184 /* ID: object ID 185 * u4: thread serial number 186 */ 187 case HPROF_ROOT_NATIVE_STACK: 188 case HPROF_ROOT_THREAD_BLOCK: 189 hprofAddU1ToRecord(rec, heapTag); 190 hprofAddIdToRecord(rec, (hprof_object_id)obj); 191 hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber); 192 break; 193 194 /* ID: thread object ID 195 * u4: thread serial number 196 * u4: stack trace serial number 197 */ 198 case HPROF_ROOT_THREAD_OBJECT: 199 hprofAddU1ToRecord(rec, heapTag); 200 hprofAddIdToRecord(rec, (hprof_object_id)obj); 201 hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber); 202 hprofAddU4ToRecord(rec, (u4)-1); //xxx 203 break; 204 205 default: 206 err = 0; 207 break; 208 } 209 210 ctx->objectsInSegment++; 211 212 return err; 213} 214 215static int 216stackTraceSerialNumber(const void *obj) 217 218{ 219#if WITH_HPROF_STACK 220 DvmHeapChunk *chunk = ptr2chunk(obj); 221 return chunk->stackTraceSerialNumber; 222#else 223 return HPROF_NULL_STACK_TRACE; 224#endif 225} 226 227int 228hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj) 229{ 230 const ClassObject *clazz; 231 hprof_record_t *rec = &ctx->curRec; 232 HprofHeapId desiredHeap; 233 234 desiredHeap = 235 dvmHeapSourceGetPtrFlag(obj, HS_ALLOCATED_IN_ZYGOTE) ? 236 HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP; 237 238 if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT || 239 rec->length >= BYTES_PER_SEGMENT) 240 { 241 /* This flushes the old segment and starts a new one. 242 */ 243 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME); 244 ctx->objectsInSegment = 0; 245 246 /* Starting a new HEAP_DUMP resets the heap to default. 247 */ 248 ctx->currentHeap = HPROF_HEAP_DEFAULT; 249 } 250 251 if (desiredHeap != ctx->currentHeap) { 252 hprof_string_id nameId; 253 254 /* This object is in a different heap than the current one. 255 * Emit a HEAP_DUMP_INFO tag to change heaps. 256 */ 257 hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO); 258 hprofAddU4ToRecord(rec, (u4)desiredHeap); // u4: heap id 259 switch (desiredHeap) { 260 case HPROF_HEAP_APP: 261 nameId = hprofLookupStringId("app"); 262 break; 263 case HPROF_HEAP_ZYGOTE: 264 nameId = hprofLookupStringId("zygote"); 265 break; 266 default: 267 /* Internal error. */ 268 assert(!"Unexpected desiredHeap"); 269 nameId = hprofLookupStringId("<ILLEGAL>"); 270 break; 271 } 272 hprofAddIdToRecord(rec, nameId); 273 ctx->currentHeap = desiredHeap; 274 } 275 276 clazz = obj->clazz; 277 278 if (clazz == NULL) { 279 /* This object will bother HprofReader, because it has a NULL 280 * class, so just don't dump it. It could be 281 * gDvm.unlinkedJavaLangClass or it could be an object just 282 * allocated which hasn't been initialized yet. 283 */ 284 } else { 285 hprof_class_object_id clazzId; 286 287 clazzId = hprofLookupClassId(clazz); 288 289 if (clazz == gDvm.classJavaLangClass) { 290 const ClassObject *thisClass = (const ClassObject *)obj; 291 int i, sFieldCount, iFieldCount; 292 /* obj is a ClassObject. 293 */ 294 sFieldCount = thisClass->sfieldCount; 295 if (sFieldCount != 0) { 296 int byteLength = sFieldCount*sizeof(StaticField); 297 /* Create a byte array to reflect the allocation of the 298 * StaticField array at the end of this class. 299 */ 300 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP); 301 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj)); 302 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj)); 303 hprofAddU4ToRecord(rec, byteLength); 304 hprofAddU1ToRecord(rec, hprof_basic_byte); 305 for (i = 0; i < byteLength; i++) { 306 hprofAddU1ToRecord(rec, 0); 307 } 308 } 309 310 hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP); 311 hprofAddIdToRecord(rec, hprofLookupClassId(thisClass)); 312 hprofAddU4ToRecord(rec, stackTraceSerialNumber(thisClass)); 313 hprofAddIdToRecord(rec, hprofLookupClassId(thisClass->super)); 314 hprofAddIdToRecord(rec, (hprof_object_id)thisClass->classLoader); 315 hprofAddIdToRecord(rec, (hprof_object_id)0); // no signer 316 hprofAddIdToRecord(rec, (hprof_object_id)0); // no prot domain 317 hprofAddIdToRecord(rec, (hprof_id)0); // reserved 318 hprofAddIdToRecord(rec, (hprof_id)0); // reserved 319 if (obj == (Object *)gDvm.classJavaLangClass) { 320 // ClassObjects have their static fields appended, so 321 // aren't all the same size. But they're at least this 322 // size. 323 hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size 324 } else { 325 hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size 326 } 327 328 hprofAddU2ToRecord(rec, 0); // empty const pool 329 330 /* Static fields 331 */ 332 if (sFieldCount == 0) { 333 hprofAddU2ToRecord(rec, (u2)0); 334 } else { 335 hprofAddU2ToRecord(rec, (u2)(sFieldCount+1)); 336 hprofAddIdToRecord(rec, 337 hprofLookupStringId(STATIC_OVERHEAD_NAME)); 338 hprofAddU1ToRecord(rec, hprof_basic_object); 339 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj)); 340 for (i = 0; i < sFieldCount; i++) { 341 hprof_basic_type t; 342 size_t size; 343 const StaticField *f = &thisClass->sfields[i]; 344 345 t = signatureToBasicTypeAndSize(f->field.signature, &size); 346 hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name)); 347 hprofAddU1ToRecord(rec, t); 348 if (size == 1) { 349 hprofAddU1ToRecord(rec, (u1)f->value.b); 350 } else if (size == 2) { 351 hprofAddU2ToRecord(rec, (u2)f->value.c); 352 } else if (size == 4) { 353 hprofAddU4ToRecord(rec, (u4)f->value.i); 354 } else if (size == 8) { 355 hprofAddU8ToRecord(rec, (u8)f->value.j); 356 } else { 357 assert(false); 358 } 359 } 360 } 361 362 /* Instance fields for this class (no superclass fields) 363 */ 364 iFieldCount = thisClass->ifieldCount; 365 hprofAddU2ToRecord(rec, (u2)iFieldCount); 366 for (i = 0; i < iFieldCount; i++) { 367 const InstField *f = &thisClass->ifields[i]; 368 hprof_basic_type t; 369 370 t = signatureToBasicTypeAndSize(f->field.signature, NULL); 371 hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name)); 372 hprofAddU1ToRecord(rec, t); 373 } 374 } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) { 375 const ArrayObject *aobj = (const ArrayObject *)obj; 376 u4 length = aobj->length; 377 378 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) { 379 /* obj is an object array. 380 */ 381 hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP); 382 383 hprofAddIdToRecord(rec, (hprof_object_id)obj); 384 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj)); 385 hprofAddU4ToRecord(rec, length); 386 hprofAddIdToRecord(rec, hprofLookupClassId(clazz)); 387 388 /* Dump the elements, which are always objects or NULL. 389 */ 390 hprofAddIdListToRecord(rec, 391 (const hprof_object_id *)aobj->contents, length); 392 } else { 393 hprof_basic_type t; 394 size_t size; 395 396 t = primitiveToBasicTypeAndSize(clazz->elementClass-> 397 primitiveType, &size); 398 399 /* obj is a primitive array. 400 */ 401#if DUMP_PRIM_DATA 402 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP); 403#else 404 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP); 405#endif 406 407 hprofAddIdToRecord(rec, (hprof_object_id)obj); 408 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj)); 409 hprofAddU4ToRecord(rec, length); 410 hprofAddU1ToRecord(rec, t); 411 412#if DUMP_PRIM_DATA 413 /* Dump the raw, packed element values. 414 */ 415 if (size == 1) { 416 hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents, 417 length); 418 } else if (size == 2) { 419 hprofAddU2ListToRecord(rec, (const u2 *)aobj->contents, 420 length); 421 } else if (size == 4) { 422 hprofAddU4ListToRecord(rec, (const u4 *)aobj->contents, 423 length); 424 } else if (size == 8) { 425 hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents, 426 length); 427 } 428#endif 429 } 430 } else { 431 const ClassObject *sclass; 432 size_t sizePatchOffset, savedLen; 433 434 /* obj is an instance object. 435 */ 436 hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP); 437 hprofAddIdToRecord(rec, (hprof_object_id)obj); 438 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj)); 439 hprofAddIdToRecord(rec, hprofLookupClassId(clazz)); 440 441 /* Reserve some space for the length of the instance 442 * data, which we won't know until we're done writing 443 * it. 444 */ 445 sizePatchOffset = rec->length; 446 hprofAddU4ToRecord(rec, 0x77777777); 447 448 /* Write the instance data; fields for this 449 * class, followed by super class fields, and so on. 450 */ 451 sclass = clazz; 452 while (sclass != NULL) { 453 int i, ifieldCount; 454 455 ifieldCount = sclass->ifieldCount; 456 for (i = 0; i < ifieldCount; i++) { 457 const InstField *f = &sclass->ifields[i]; 458 hprof_basic_type t; 459 size_t size; 460 461 t = signatureToBasicTypeAndSize(f->field.signature, &size); 462 if (size == 1) { 463 hprofAddU1ToRecord(rec, 464 (u1)dvmGetFieldByte(obj, f->byteOffset)); 465 } else if (size == 2) { 466 hprofAddU2ToRecord(rec, 467 (u2)dvmGetFieldChar(obj, f->byteOffset)); 468 } else if (size == 4) { 469 hprofAddU4ToRecord(rec, 470 (u4)dvmGetFieldInt(obj, f->byteOffset)); 471 } else if (size == 8) { 472 hprofAddU8ToRecord(rec, 473 (u8)dvmGetFieldLong(obj, f->byteOffset)); 474 } else { 475 assert(false); 476 } 477 } 478 479 sclass = sclass->super; 480 } 481 482 /* Patch the instance field length. 483 */ 484 savedLen = rec->length; 485 rec->length = sizePatchOffset; 486 hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4)); 487 rec->length = savedLen; 488 } 489 } 490 491 ctx->objectsInSegment++; 492 493 return 0; 494} 495