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