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