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