• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
42 int
hprofStartHeapDump(hprof_context_t * ctx)43 hprofStartHeapDump(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 
52 int
hprofFinishHeapDump(hprof_context_t * ctx)53 hprofFinishHeapDump(hprof_context_t *ctx)
54 {
55     return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
56 }
57 
58 int
hprofSetGcScanState(hprof_context_t * ctx,hprof_heap_tag_t state,u4 threadSerialNumber)59 hprofSetGcScanState(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 
69 static hprof_basic_type
signatureToBasicTypeAndSize(const char * sig,size_t * sizeOut)70 signatureToBasicTypeAndSize(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 
97 static hprof_basic_type
primitiveToBasicTypeAndSize(PrimitiveType prim,size_t * sizeOut)98 primitiveToBasicTypeAndSize(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  */
127 int
hprofMarkRootObject(hprof_context_t * ctx,const Object * obj,jobject jniObj)128 hprofMarkRootObject(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 
215 static int
stackTraceSerialNumber(const void * obj)216 stackTraceSerialNumber(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 
227 int
hprofDumpHeapObject(hprof_context_t * ctx,const Object * obj)228 hprofDumpHeapObject(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