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 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