• 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 int
hprofStartHeapDump(hprof_context_t * ctx)34 hprofStartHeapDump(hprof_context_t *ctx)
35 {
36     UNUSED_PARAMETER(ctx);
37 
38     ctx->objectsInSegment = OBJECTS_PER_SEGMENT;
39     ctx->currentHeap = HPROF_HEAP_DEFAULT;
40     return 0;
41 }
42 
43 int
hprofFinishHeapDump(hprof_context_t * ctx)44 hprofFinishHeapDump(hprof_context_t *ctx)
45 {
46     return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
47 }
48 
49 int
hprofSetGcScanState(hprof_context_t * ctx,hprof_heap_tag_t state,u4 threadSerialNumber)50 hprofSetGcScanState(hprof_context_t *ctx,
51                     hprof_heap_tag_t state, u4 threadSerialNumber)
52 {
53     /* Used by hprofMarkRootObject()
54      */
55     ctx->gcScanState = state;
56     ctx->gcThreadSerialNumber = threadSerialNumber;
57     return 0;
58 }
59 
60 static hprof_basic_type
signatureToBasicTypeAndSize(const char * sig,size_t * sizeOut)61 signatureToBasicTypeAndSize(const char *sig, size_t *sizeOut)
62 {
63     char c = sig[0];
64     hprof_basic_type ret;
65     size_t size;
66 
67     switch (c) {
68     case '[':
69     case 'L': ret = hprof_basic_object;  size = 4; break;
70     case 'Z': ret = hprof_basic_boolean; size = 1; break;
71     case 'C': ret = hprof_basic_char;    size = 2; break;
72     case 'F': ret = hprof_basic_float;   size = 4; break;
73     case 'D': ret = hprof_basic_double;  size = 8; break;
74     case 'B': ret = hprof_basic_byte;    size = 1; break;
75     case 'S': ret = hprof_basic_short;   size = 2; break;
76     default: assert(false);
77     case 'I': ret = hprof_basic_int;     size = 4; break;
78     case 'J': ret = hprof_basic_long;    size = 8; break;
79     }
80 
81     if (sizeOut != NULL) {
82         *sizeOut = size;
83     }
84 
85     return ret;
86 }
87 
88 static hprof_basic_type
primitiveToBasicTypeAndSize(PrimitiveType prim,size_t * sizeOut)89 primitiveToBasicTypeAndSize(PrimitiveType prim, size_t *sizeOut)
90 {
91     hprof_basic_type ret;
92     size_t size;
93 
94     switch (prim) {
95     case PRIM_BOOLEAN: ret = hprof_basic_boolean; size = 1; break;
96     case PRIM_CHAR:    ret = hprof_basic_char;    size = 2; break;
97     case PRIM_FLOAT:   ret = hprof_basic_float;   size = 4; break;
98     case PRIM_DOUBLE:  ret = hprof_basic_double;  size = 8; break;
99     case PRIM_BYTE:    ret = hprof_basic_byte;    size = 1; break;
100     case PRIM_SHORT:   ret = hprof_basic_short;   size = 2; break;
101     default: assert(false);
102     case PRIM_INT:     ret = hprof_basic_int;     size = 4; break;
103     case PRIM_LONG:    ret = hprof_basic_long;    size = 8; break;
104     }
105 
106     if (sizeOut != NULL) {
107         *sizeOut = size;
108     }
109 
110     return ret;
111 }
112 
113 /* Always called when marking objects, but only does
114  * something when ctx->gcScanState is non-zero, which is usually
115  * only true when marking the root set or unreachable
116  * objects.  Used to add rootset references to obj.
117  */
118 int
hprofMarkRootObject(hprof_context_t * ctx,const Object * obj,jobject jniObj)119 hprofMarkRootObject(hprof_context_t *ctx, const Object *obj, jobject jniObj)
120 {
121     hprof_record_t *rec = &ctx->curRec;
122     int err;
123     hprof_heap_tag_t heapTag = ctx->gcScanState;
124 
125     if (heapTag == 0) {
126         return 0;
127     }
128 
129     if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
130         rec->length >= BYTES_PER_SEGMENT)
131     {
132         /* This flushes the old segment and starts a new one.
133          */
134         hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
135         ctx->objectsInSegment = 0;
136     }
137 
138     switch (heapTag) {
139     /* ID: object ID
140      */
141     case HPROF_ROOT_UNKNOWN:
142     case HPROF_ROOT_STICKY_CLASS:
143     case HPROF_ROOT_MONITOR_USED:
144     case HPROF_ROOT_INTERNED_STRING:
145     case HPROF_ROOT_FINALIZING:
146     case HPROF_ROOT_DEBUGGER:
147     case HPROF_ROOT_REFERENCE_CLEANUP:
148     case HPROF_ROOT_VM_INTERNAL:
149     case HPROF_UNREACHABLE:
150         hprofAddU1ToRecord(rec, heapTag);
151         hprofAddIdToRecord(rec, (hprof_object_id)obj);
152         break;
153 
154     /* ID: object ID
155      * ID: JNI global ref ID
156      */
157     case HPROF_ROOT_JNI_GLOBAL:
158         hprofAddU1ToRecord(rec, heapTag);
159         hprofAddIdToRecord(rec, (hprof_object_id)obj);
160         hprofAddIdToRecord(rec, (hprof_id)jniObj);
161         break;
162 
163     /* ID: object ID
164      * u4: thread serial number
165      * u4: frame number in stack trace (-1 for empty)
166      */
167     case HPROF_ROOT_JNI_LOCAL:
168     case HPROF_ROOT_JNI_MONITOR:
169     case HPROF_ROOT_JAVA_FRAME:
170         hprofAddU1ToRecord(rec, heapTag);
171         hprofAddIdToRecord(rec, (hprof_object_id)obj);
172         hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
173         hprofAddU4ToRecord(rec, (u4)-1);
174         break;
175 
176     /* ID: object ID
177      * u4: thread serial number
178      */
179     case HPROF_ROOT_NATIVE_STACK:
180     case HPROF_ROOT_THREAD_BLOCK:
181         hprofAddU1ToRecord(rec, heapTag);
182         hprofAddIdToRecord(rec, (hprof_object_id)obj);
183         hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
184         break;
185 
186     /* ID: thread object ID
187      * u4: thread serial number
188      * u4: stack trace serial number
189      */
190     case HPROF_ROOT_THREAD_OBJECT:
191         hprofAddU1ToRecord(rec, heapTag);
192         hprofAddIdToRecord(rec, (hprof_object_id)obj);
193         hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
194         hprofAddU4ToRecord(rec, (u4)-1);    //xxx
195         break;
196 
197     default:
198         err = 0;
199         break;
200     }
201 
202     ctx->objectsInSegment++;
203 
204     return err;
205 }
206 
207 static int
stackTraceSerialNumber(const void * obj)208 stackTraceSerialNumber(const void *obj)
209 
210 {
211 #if WITH_HPROF_STACK
212     DvmHeapChunk *chunk = ptr2chunk(obj);
213     return chunk->stackTraceSerialNumber;
214 #else
215     return HPROF_NULL_STACK_TRACE;
216 #endif
217 }
218 
219 int
hprofDumpHeapObject(hprof_context_t * ctx,const Object * obj)220 hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
221 {
222     const ClassObject *clazz;
223     hprof_record_t *rec = &ctx->curRec;
224     HprofHeapId desiredHeap;
225 
226     desiredHeap =
227             dvmHeapSourceGetPtrFlag(ptr2chunk(obj), HS_ALLOCATED_IN_ZYGOTE) ?
228             HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP;
229 
230     if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
231         rec->length >= BYTES_PER_SEGMENT)
232     {
233         /* This flushes the old segment and starts a new one.
234          */
235         hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
236         ctx->objectsInSegment = 0;
237 
238         /* Starting a new HEAP_DUMP resets the heap to default.
239          */
240         ctx->currentHeap = HPROF_HEAP_DEFAULT;
241     }
242 
243     if (desiredHeap != ctx->currentHeap) {
244         hprof_string_id nameId;
245 
246         /* This object is in a different heap than the current one.
247          * Emit a HEAP_DUMP_INFO tag to change heaps.
248          */
249         hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
250         hprofAddU4ToRecord(rec, (u4)desiredHeap);   // u4: heap id
251         switch (desiredHeap) {
252         case HPROF_HEAP_APP:
253             nameId = hprofLookupStringId("app");
254             break;
255         case HPROF_HEAP_ZYGOTE:
256             nameId = hprofLookupStringId("zygote");
257             break;
258         default:
259             /* Internal error. */
260             assert(!"Unexpected desiredHeap");
261             nameId = hprofLookupStringId("<ILLEGAL>");
262             break;
263         }
264         hprofAddIdToRecord(rec, nameId);
265         ctx->currentHeap = desiredHeap;
266     }
267 
268     clazz = obj->clazz;
269 
270     if (clazz == NULL) {
271         /* This object was probably just allocated and hasn't been
272          * initialized yet.  Add an instance entry to make a note of
273          * it;  there's not much else that we can do.
274          */
275         hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
276 
277         hprofAddIdToRecord(rec, (hprof_object_id)obj);
278         hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
279         hprofAddIdToRecord(rec, (hprof_class_object_id)clazz);  // NULL
280         hprofAddIdToRecord(rec, 0);    // no instance data
281     } else if (clazz == gDvm.unlinkedJavaLangClass) {
282         /* obj is a ClassObject that hasn't been linked yet.
283          */
284         hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
285 
286     //TODO: use hprofLookupClassId() for this:
287         hprofAddIdToRecord(rec, (hprof_class_object_id)obj);
288         hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
289         hprofAddIdToRecord(rec, (hprof_class_object_id)0); // no super class
290         hprofAddIdToRecord(rec, (hprof_object_id)0);       // no class loader
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         hprofAddU4ToRecord(rec, 0);                        // zero instance size
296         hprofAddU2ToRecord(rec, 0);                        // empty const pool
297         hprofAddU2ToRecord(rec, 0);                        // no statics
298         hprofAddU2ToRecord(rec, 0);                        // no instance fields
299     } else {
300         hprof_class_object_id clazzId;
301 
302         clazzId = hprofLookupClassId(clazz);
303 
304         if (clazz == gDvm.classJavaLangClass) {
305             const ClassObject *thisClass = (const ClassObject *)obj;
306             int i, n;
307             /* obj is a ClassObject.
308              */
309             hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
310 
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                 hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
321             } else {
322                 hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
323             }
324 
325             hprofAddU2ToRecord(rec, 0);                     // empty const pool
326 
327             /* Static fields
328              */
329             n = thisClass->sfieldCount;
330             hprofAddU2ToRecord(rec, (u2)n);
331             for (i = 0; i < n; i++) {
332                 const StaticField *f = &thisClass->sfields[i];
333                 hprof_basic_type t;
334                 size_t size;
335 
336                 t = signatureToBasicTypeAndSize(f->field.signature, &size);
337                 hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name));
338                 hprofAddU1ToRecord(rec, t);
339                 if (size == 1) {
340                     hprofAddU1ToRecord(rec, (u1)f->value.b);
341                 } else if (size == 2) {
342                     hprofAddU2ToRecord(rec, (u2)f->value.c);
343                 } else if (size == 4) {
344                     hprofAddU4ToRecord(rec, (u4)f->value.i);
345                 } else if (size == 8) {
346                     hprofAddU8ToRecord(rec, (u8)f->value.j);
347                 } else {
348                     assert(false);
349                 }
350             }
351 
352             /* Instance fields for this class (no superclass fields)
353              */
354             n = thisClass->ifieldCount;
355             hprofAddU2ToRecord(rec, (u2)n);
356             for (i = 0; i < n; i++) {
357                 const InstField *f = &thisClass->ifields[i];
358                 hprof_basic_type t;
359 
360                 t = signatureToBasicTypeAndSize(f->field.signature, NULL);
361                 hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name));
362                 hprofAddU1ToRecord(rec, t);
363             }
364         } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
365             const ArrayObject *aobj = (const ArrayObject *)obj;
366             u4 length = aobj->length;
367 
368             if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
369                 /* obj is an object array.
370                  */
371                 hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
372 
373                 hprofAddIdToRecord(rec, (hprof_object_id)obj);
374                 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
375                 hprofAddU4ToRecord(rec, length);
376                 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
377 
378                 /* Dump the elements, which are always objects or NULL.
379                  */
380                 hprofAddIdListToRecord(rec,
381                         (const hprof_object_id *)aobj->contents, length);
382             } else {
383                 hprof_basic_type t;
384                 size_t size;
385 
386                 t = primitiveToBasicTypeAndSize(clazz->elementClass->
387                                                 primitiveType, &size);
388 
389                 /* obj is a primitive array.
390                  */
391 #if DUMP_PRIM_DATA
392                 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
393 #else
394                 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
395 #endif
396 
397                 hprofAddIdToRecord(rec, (hprof_object_id)obj);
398                 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
399                 hprofAddU4ToRecord(rec, length);
400                 hprofAddU1ToRecord(rec, t);
401 
402 #if DUMP_PRIM_DATA
403                 /* Dump the raw, packed element values.
404                  */
405                 if (size == 1) {
406                     hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents,
407                             length);
408                 } else if (size == 2) {
409                     hprofAddU2ListToRecord(rec, (const u2 *)aobj->contents,
410                             length);
411                 } else if (size == 4) {
412                     hprofAddU4ListToRecord(rec, (const u4 *)aobj->contents,
413                             length);
414                 } else if (size == 8) {
415                     hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents,
416                             length);
417                 }
418 #endif
419             }
420         } else {
421             const ClassObject *sclass;
422             size_t sizePatchOffset, savedLen;
423 
424             /* obj is an instance object.
425              */
426             hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
427             hprofAddIdToRecord(rec, (hprof_object_id)obj);
428             hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
429             hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
430 
431             /* Reserve some space for the length of the instance
432              * data, which we won't know until we're done writing
433              * it.
434              */
435             sizePatchOffset = rec->length;
436             hprofAddU4ToRecord(rec, 0x77777777);
437 
438             /* Write the instance data;  fields for this
439              * class, followed by super class fields, and so on.
440              */
441             sclass = clazz;
442             while (sclass != NULL) {
443                 int i, ifieldCount;
444 
445                 ifieldCount = sclass->ifieldCount;
446                 for (i = 0; i < ifieldCount; i++) {
447                     const InstField *f = &sclass->ifields[i];
448                     hprof_basic_type t;
449                     size_t size;
450 
451                     t = signatureToBasicTypeAndSize(f->field.signature, &size);
452                     if (size == 1) {
453                         hprofAddU1ToRecord(rec,
454                                 (u1)dvmGetFieldByte(obj, f->byteOffset));
455                     } else if (size == 2) {
456                         hprofAddU2ToRecord(rec,
457                                 (u2)dvmGetFieldChar(obj, f->byteOffset));
458                     } else if (size == 4) {
459                         hprofAddU4ToRecord(rec,
460                                 (u4)dvmGetFieldInt(obj, f->byteOffset));
461                     } else if (size == 8) {
462                         hprofAddU8ToRecord(rec,
463                                 (u8)dvmGetFieldLong(obj, f->byteOffset));
464                     } else {
465                         assert(false);
466                     }
467                 }
468 
469                 sclass = sclass->super;
470             }
471 
472             /* Patch the instance field length.
473              */
474             savedLen = rec->length;
475             rec->length = sizePatchOffset;
476             hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
477             rec->length = savedLen;
478         }
479     }
480 
481     ctx->objectsInSegment++;
482 
483     return 0;
484 }
485