Home | History | Annotate | Download | only in hprof
      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 
     42 int
     43 hprofStartHeapDump(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 
     52 int
     53 hprofFinishHeapDump(hprof_context_t *ctx)
     54 {
     55     return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
     56 }
     57 
     58 int
     59 hprofSetGcScanState(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 
     69 static hprof_basic_type
     70 signatureToBasicTypeAndSize(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 
     97 static hprof_basic_type
     98 primitiveToBasicTypeAndSize(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  */
    127 int
    128 hprofMarkRootObject(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 
    215 static int
    216 stackTraceSerialNumber(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 
    227 int
    228 hprofDumpHeapObject(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