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 /* 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 void hprofMarkRootObject(hprof_context_t *ctx, const Object *obj,
    123                          jobject jniObj)
    124 {
    125     hprof_record_t *rec = &ctx->curRec;
    126     hprof_heap_tag_t heapTag = (hprof_heap_tag_t)ctx->gcScanState;
    127 
    128     if (heapTag == 0) {
    129         return;
    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         break;
    201     }
    202 
    203     ctx->objectsInSegment++;
    204 }
    205 
    206 static int stackTraceSerialNumber(const void *obj)
    207 {
    208     return HPROF_NULL_STACK_TRACE;
    209 }
    210 
    211 int hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
    212 {
    213     const ClassObject *clazz;
    214     hprof_record_t *rec = &ctx->curRec;
    215     HprofHeapId desiredHeap;
    216 
    217     desiredHeap = dvmIsZygoteObject(obj) ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP;
    218 
    219     if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
    220         rec->length >= BYTES_PER_SEGMENT)
    221     {
    222         /* This flushes the old segment and starts a new one.
    223          */
    224         hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
    225         ctx->objectsInSegment = 0;
    226 
    227         /* Starting a new HEAP_DUMP resets the heap to default.
    228          */
    229         ctx->currentHeap = HPROF_HEAP_DEFAULT;
    230     }
    231 
    232     if (desiredHeap != ctx->currentHeap) {
    233         hprof_string_id nameId;
    234 
    235         /* This object is in a different heap than the current one.
    236          * Emit a HEAP_DUMP_INFO tag to change heaps.
    237          */
    238         hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
    239         hprofAddU4ToRecord(rec, (u4)desiredHeap);   // u4: heap id
    240         switch (desiredHeap) {
    241         case HPROF_HEAP_APP:
    242             nameId = hprofLookupStringId("app");
    243             break;
    244         case HPROF_HEAP_ZYGOTE:
    245             nameId = hprofLookupStringId("zygote");
    246             break;
    247         default:
    248             /* Internal error. */
    249             assert(!"Unexpected desiredHeap");
    250             nameId = hprofLookupStringId("<ILLEGAL>");
    251             break;
    252         }
    253         hprofAddIdToRecord(rec, nameId);
    254         ctx->currentHeap = desiredHeap;
    255     }
    256 
    257     clazz = obj->clazz;
    258 
    259     if (clazz == NULL) {
    260         /* This object will bother HprofReader, because it has a NULL
    261          * class, so just don't dump it. It could be
    262          * gDvm.unlinkedJavaLangClass or it could be an object just
    263          * allocated which hasn't been initialized yet.
    264          */
    265     } else {
    266         if (dvmIsClassObject(obj)) {
    267             const ClassObject *thisClass = (const ClassObject *)obj;
    268             /* obj is a ClassObject.
    269              */
    270             int sFieldCount = thisClass->sfieldCount;
    271             if (sFieldCount != 0) {
    272                 int byteLength = sFieldCount*sizeof(StaticField);
    273                 /* Create a byte array to reflect the allocation of the
    274                  * StaticField array at the end of this class.
    275                  */
    276                 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
    277                 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
    278                 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
    279                 hprofAddU4ToRecord(rec, byteLength);
    280                 hprofAddU1ToRecord(rec, hprof_basic_byte);
    281                 for (int i = 0; i < byteLength; i++) {
    282                     hprofAddU1ToRecord(rec, 0);
    283                 }
    284             }
    285 
    286             hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
    287             hprofAddIdToRecord(rec, hprofLookupClassId(thisClass));
    288             hprofAddU4ToRecord(rec, stackTraceSerialNumber(thisClass));
    289             hprofAddIdToRecord(rec, hprofLookupClassId(thisClass->super));
    290             hprofAddIdToRecord(rec, (hprof_object_id)thisClass->classLoader);
    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             if (obj == (Object *)gDvm.classJavaLangClass) {
    296                 // ClassObjects have their static fields appended, so
    297                 // aren't all the same size. But they're at least this
    298                 // size.
    299                 hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
    300             } else {
    301                 hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
    302             }
    303 
    304             hprofAddU2ToRecord(rec, 0);                     // empty const pool
    305 
    306             /* Static fields
    307              */
    308             if (sFieldCount == 0) {
    309                 hprofAddU2ToRecord(rec, (u2)0);
    310             } else {
    311                 hprofAddU2ToRecord(rec, (u2)(sFieldCount+1));
    312                 hprofAddIdToRecord(rec,
    313                                    hprofLookupStringId(STATIC_OVERHEAD_NAME));
    314                 hprofAddU1ToRecord(rec, hprof_basic_object);
    315                 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
    316                 for (int i = 0; i < sFieldCount; i++) {
    317                     hprof_basic_type t;
    318                     size_t size;
    319                     const StaticField *f = &thisClass->sfields[i];
    320 
    321                     t = signatureToBasicTypeAndSize(f->signature, &size);
    322                     hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
    323                     hprofAddU1ToRecord(rec, t);
    324                     if (size == 1) {
    325                         hprofAddU1ToRecord(rec, (u1)f->value.b);
    326                     } else if (size == 2) {
    327                         hprofAddU2ToRecord(rec, (u2)f->value.c);
    328                     } else if (size == 4) {
    329                         hprofAddU4ToRecord(rec, (u4)f->value.i);
    330                     } else if (size == 8) {
    331                         hprofAddU8ToRecord(rec, (u8)f->value.j);
    332                     } else {
    333                         assert(false);
    334                     }
    335                 }
    336             }
    337 
    338             /* Instance fields for this class (no superclass fields)
    339              */
    340             int iFieldCount = thisClass->ifieldCount;
    341             hprofAddU2ToRecord(rec, (u2)iFieldCount);
    342             for (int i = 0; i < iFieldCount; i++) {
    343                 const InstField *f = &thisClass->ifields[i];
    344                 hprof_basic_type t;
    345 
    346                 t = signatureToBasicTypeAndSize(f->signature, NULL);
    347                 hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
    348                 hprofAddU1ToRecord(rec, t);
    349             }
    350         } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
    351             const ArrayObject *aobj = (const ArrayObject *)obj;
    352             u4 length = aobj->length;
    353 
    354             if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
    355                 /* obj is an object array.
    356                  */
    357                 hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
    358 
    359                 hprofAddIdToRecord(rec, (hprof_object_id)obj);
    360                 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
    361                 hprofAddU4ToRecord(rec, length);
    362                 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
    363 
    364                 /* Dump the elements, which are always objects or NULL.
    365                  */
    366                 hprofAddIdListToRecord(rec,
    367                         (const hprof_object_id *)(void *)aobj->contents, length);
    368             } else {
    369                 hprof_basic_type t;
    370                 size_t size;
    371 
    372                 t = primitiveToBasicTypeAndSize(clazz->elementClass->
    373                                                 primitiveType, &size);
    374 
    375                 /* obj is a primitive array.
    376                  */
    377 #if DUMP_PRIM_DATA
    378                 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
    379 #else
    380                 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
    381 #endif
    382 
    383                 hprofAddIdToRecord(rec, (hprof_object_id)obj);
    384                 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
    385                 hprofAddU4ToRecord(rec, length);
    386                 hprofAddU1ToRecord(rec, t);
    387 
    388 #if DUMP_PRIM_DATA
    389                 /* Dump the raw, packed element values.
    390                  */
    391                 if (size == 1) {
    392                     hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents,
    393                             length);
    394                 } else if (size == 2) {
    395                     hprofAddU2ListToRecord(rec, (const u2 *)(void *)aobj->contents,
    396                             length);
    397                 } else if (size == 4) {
    398                     hprofAddU4ListToRecord(rec, (const u4 *)(void *)aobj->contents,
    399                             length);
    400                 } else if (size == 8) {
    401                     hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents,
    402                             length);
    403                 }
    404 #endif
    405             }
    406         } else {
    407             const ClassObject *sclass;
    408             size_t sizePatchOffset, savedLen;
    409 
    410             /* obj is an instance object.
    411              */
    412             hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
    413             hprofAddIdToRecord(rec, (hprof_object_id)obj);
    414             hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
    415             hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
    416 
    417             /* Reserve some space for the length of the instance
    418              * data, which we won't know until we're done writing
    419              * it.
    420              */
    421             sizePatchOffset = rec->length;
    422             hprofAddU4ToRecord(rec, 0x77777777);
    423 
    424             /* Write the instance data;  fields for this
    425              * class, followed by super class fields, and so on.
    426              */
    427             sclass = clazz;
    428             while (sclass != NULL) {
    429                 int ifieldCount = sclass->ifieldCount;
    430                 for (int i = 0; i < ifieldCount; i++) {
    431                     const InstField *f = &sclass->ifields[i];
    432                     size_t size;
    433 
    434                     (void) signatureToBasicTypeAndSize(f->signature, &size);
    435                     if (size == 1) {
    436                         hprofAddU1ToRecord(rec,
    437                                 (u1)dvmGetFieldByte(obj, f->byteOffset));
    438                     } else if (size == 2) {
    439                         hprofAddU2ToRecord(rec,
    440                                 (u2)dvmGetFieldChar(obj, f->byteOffset));
    441                     } else if (size == 4) {
    442                         hprofAddU4ToRecord(rec,
    443                                 (u4)dvmGetFieldInt(obj, f->byteOffset));
    444                     } else if (size == 8) {
    445                         hprofAddU8ToRecord(rec,
    446                                 (u8)dvmGetFieldLong(obj, f->byteOffset));
    447                     } else {
    448                         assert(false);
    449                     }
    450                 }
    451 
    452                 sclass = sclass->super;
    453             }
    454 
    455             /* Patch the instance field length.
    456              */
    457             savedLen = rec->length;
    458             rec->length = sizePatchOffset;
    459             hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
    460             rec->length = savedLen;
    461         }
    462     }
    463 
    464     ctx->objectsInSegment++;
    465 
    466     return 0;
    467 }
    468