• 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 /* 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 
hprofStartHeapDump(hprof_context_t * ctx)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 
hprofFinishHeapDump(hprof_context_t * ctx)48 int hprofFinishHeapDump(hprof_context_t *ctx)
49 {
50     return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
51 }
52 
hprofSetGcScanState(hprof_context_t * ctx,hprof_heap_tag_t state,u4 threadSerialNumber)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 
signatureToBasicTypeAndSize(const char * sig,size_t * sizeOut)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 
primitiveToBasicTypeAndSize(PrimitiveType prim,size_t * sizeOut)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  */
hprofMarkRootObject(hprof_context_t * ctx,const Object * obj,jobject jniObj)122 void hprofMarkRootObject(hprof_context_t *ctx, const Object *obj,
123                          jobject jniObj)
124 {
125     hprof_record_t *rec = &ctx->curRec;
126     hprof_heap_tag_t heapTag = (hprof_heap_tag_t)ctx->gcScanState;
127 
128     if (heapTag == 0) {
129         return;
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         break;
201     }
202 
203     ctx->objectsInSegment++;
204 }
205 
stackTraceSerialNumber(const void * obj)206 static int stackTraceSerialNumber(const void *obj)
207 {
208     return HPROF_NULL_STACK_TRACE;
209 }
210 
hprofDumpHeapObject(hprof_context_t * ctx,const Object * obj)211 int hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
212 {
213     const ClassObject *clazz;
214     hprof_record_t *rec = &ctx->curRec;
215     HprofHeapId desiredHeap;
216 
217     desiredHeap = dvmIsZygoteObject(obj) ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP;
218 
219     if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
220         rec->length >= BYTES_PER_SEGMENT)
221     {
222         /* This flushes the old segment and starts a new one.
223          */
224         hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
225         ctx->objectsInSegment = 0;
226 
227         /* Starting a new HEAP_DUMP resets the heap to default.
228          */
229         ctx->currentHeap = HPROF_HEAP_DEFAULT;
230     }
231 
232     if (desiredHeap != ctx->currentHeap) {
233         hprof_string_id nameId;
234 
235         /* This object is in a different heap than the current one.
236          * Emit a HEAP_DUMP_INFO tag to change heaps.
237          */
238         hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
239         hprofAddU4ToRecord(rec, (u4)desiredHeap);   // u4: heap id
240         switch (desiredHeap) {
241         case HPROF_HEAP_APP:
242             nameId = hprofLookupStringId("app");
243             break;
244         case HPROF_HEAP_ZYGOTE:
245             nameId = hprofLookupStringId("zygote");
246             break;
247         default:
248             /* Internal error. */
249             assert(!"Unexpected desiredHeap");
250             nameId = hprofLookupStringId("<ILLEGAL>");
251             break;
252         }
253         hprofAddIdToRecord(rec, nameId);
254         ctx->currentHeap = desiredHeap;
255     }
256 
257     clazz = obj->clazz;
258 
259     if (clazz == NULL) {
260         /* This object will bother HprofReader, because it has a NULL
261          * class, so just don't dump it. It could be
262          * gDvm.unlinkedJavaLangClass or it could be an object just
263          * allocated which hasn't been initialized yet.
264          */
265     } else {
266         if (dvmIsClassObject(obj)) {
267             const ClassObject *thisClass = (const ClassObject *)obj;
268             /* obj is a ClassObject.
269              */
270             int sFieldCount = thisClass->sfieldCount;
271             if (sFieldCount != 0) {
272                 int byteLength = sFieldCount*sizeof(StaticField);
273                 /* Create a byte array to reflect the allocation of the
274                  * StaticField array at the end of this class.
275                  */
276                 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
277                 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
278                 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
279                 hprofAddU4ToRecord(rec, byteLength);
280                 hprofAddU1ToRecord(rec, hprof_basic_byte);
281                 for (int i = 0; i < byteLength; i++) {
282                     hprofAddU1ToRecord(rec, 0);
283                 }
284             }
285 
286             hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
287             hprofAddIdToRecord(rec, hprofLookupClassId(thisClass));
288             hprofAddU4ToRecord(rec, stackTraceSerialNumber(thisClass));
289             hprofAddIdToRecord(rec, hprofLookupClassId(thisClass->super));
290             hprofAddIdToRecord(rec, (hprof_object_id)thisClass->classLoader);
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             if (obj == (Object *)gDvm.classJavaLangClass) {
296                 // ClassObjects have their static fields appended, so
297                 // aren't all the same size. But they're at least this
298                 // size.
299                 hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
300             } else {
301                 hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
302             }
303 
304             hprofAddU2ToRecord(rec, 0);                     // empty const pool
305 
306             /* Static fields
307              */
308             if (sFieldCount == 0) {
309                 hprofAddU2ToRecord(rec, (u2)0);
310             } else {
311                 hprofAddU2ToRecord(rec, (u2)(sFieldCount+1));
312                 hprofAddIdToRecord(rec,
313                                    hprofLookupStringId(STATIC_OVERHEAD_NAME));
314                 hprofAddU1ToRecord(rec, hprof_basic_object);
315                 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
316                 for (int i = 0; i < sFieldCount; i++) {
317                     hprof_basic_type t;
318                     size_t size;
319                     const StaticField *f = &thisClass->sfields[i];
320 
321                     t = signatureToBasicTypeAndSize(f->signature, &size);
322                     hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
323                     hprofAddU1ToRecord(rec, t);
324                     if (size == 1) {
325                         hprofAddU1ToRecord(rec, (u1)f->value.b);
326                     } else if (size == 2) {
327                         hprofAddU2ToRecord(rec, (u2)f->value.c);
328                     } else if (size == 4) {
329                         hprofAddU4ToRecord(rec, (u4)f->value.i);
330                     } else if (size == 8) {
331                         hprofAddU8ToRecord(rec, (u8)f->value.j);
332                     } else {
333                         assert(false);
334                     }
335                 }
336             }
337 
338             /* Instance fields for this class (no superclass fields)
339              */
340             int iFieldCount = thisClass->ifieldCount;
341             hprofAddU2ToRecord(rec, (u2)iFieldCount);
342             for (int i = 0; i < iFieldCount; i++) {
343                 const InstField *f = &thisClass->ifields[i];
344                 hprof_basic_type t;
345 
346                 t = signatureToBasicTypeAndSize(f->signature, NULL);
347                 hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
348                 hprofAddU1ToRecord(rec, t);
349             }
350         } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
351             const ArrayObject *aobj = (const ArrayObject *)obj;
352             u4 length = aobj->length;
353 
354             if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
355                 /* obj is an object array.
356                  */
357                 hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
358 
359                 hprofAddIdToRecord(rec, (hprof_object_id)obj);
360                 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
361                 hprofAddU4ToRecord(rec, length);
362                 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
363 
364                 /* Dump the elements, which are always objects or NULL.
365                  */
366                 hprofAddIdListToRecord(rec,
367                         (const hprof_object_id *)(void *)aobj->contents, length);
368             } else {
369                 hprof_basic_type t;
370                 size_t size;
371 
372                 t = primitiveToBasicTypeAndSize(clazz->elementClass->
373                                                 primitiveType, &size);
374 
375                 /* obj is a primitive array.
376                  */
377 #if DUMP_PRIM_DATA
378                 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
379 #else
380                 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
381 #endif
382 
383                 hprofAddIdToRecord(rec, (hprof_object_id)obj);
384                 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
385                 hprofAddU4ToRecord(rec, length);
386                 hprofAddU1ToRecord(rec, t);
387 
388 #if DUMP_PRIM_DATA
389                 /* Dump the raw, packed element values.
390                  */
391                 if (size == 1) {
392                     hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents,
393                             length);
394                 } else if (size == 2) {
395                     hprofAddU2ListToRecord(rec, (const u2 *)(void *)aobj->contents,
396                             length);
397                 } else if (size == 4) {
398                     hprofAddU4ListToRecord(rec, (const u4 *)(void *)aobj->contents,
399                             length);
400                 } else if (size == 8) {
401                     hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents,
402                             length);
403                 }
404 #endif
405             }
406         } else {
407             const ClassObject *sclass;
408             size_t sizePatchOffset, savedLen;
409 
410             /* obj is an instance object.
411              */
412             hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
413             hprofAddIdToRecord(rec, (hprof_object_id)obj);
414             hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
415             hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
416 
417             /* Reserve some space for the length of the instance
418              * data, which we won't know until we're done writing
419              * it.
420              */
421             sizePatchOffset = rec->length;
422             hprofAddU4ToRecord(rec, 0x77777777);
423 
424             /* Write the instance data;  fields for this
425              * class, followed by super class fields, and so on.
426              */
427             sclass = clazz;
428             while (sclass != NULL) {
429                 int ifieldCount = sclass->ifieldCount;
430                 for (int i = 0; i < ifieldCount; i++) {
431                     const InstField *f = &sclass->ifields[i];
432                     size_t size;
433 
434                     (void) signatureToBasicTypeAndSize(f->signature, &size);
435                     if (size == 1) {
436                         hprofAddU1ToRecord(rec,
437                                 (u1)dvmGetFieldByte(obj, f->byteOffset));
438                     } else if (size == 2) {
439                         hprofAddU2ToRecord(rec,
440                                 (u2)dvmGetFieldChar(obj, f->byteOffset));
441                     } else if (size == 4) {
442                         hprofAddU4ToRecord(rec,
443                                 (u4)dvmGetFieldInt(obj, f->byteOffset));
444                     } else if (size == 8) {
445                         hprofAddU8ToRecord(rec,
446                                 (u8)dvmGetFieldLong(obj, f->byteOffset));
447                     } else {
448                         assert(false);
449                     }
450                 }
451 
452                 sclass = sclass->super;
453             }
454 
455             /* Patch the instance field length.
456              */
457             savedLen = rec->length;
458             rec->length = sizePatchOffset;
459             hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
460             rec->length = savedLen;
461         }
462     }
463 
464     ctx->objectsInSegment++;
465 
466     return 0;
467 }
468