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