HprofHeap.cpp revision 7aa9563279627b2ff5413bc895381fc170df9f12
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
39int 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
48int hprofFinishHeapDump(hprof_context_t *ctx)
49{
50    return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
51}
52
53int 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
64static 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
92static 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 */
122int 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
209static int stackTraceSerialNumber(const void *obj)
210{
211    return HPROF_NULL_STACK_TRACE;
212}
213
214int 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