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