• 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 int 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 
stackTraceSerialNumber(const void * obj)209 static int stackTraceSerialNumber(const void *obj)
210 {
211     return HPROF_NULL_STACK_TRACE;
212 }
213 
hprofDumpHeapObject(hprof_context_t * ctx,const Object * obj)214 int 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