blob: 31063e4150c418616272bd6b63fad02168cca281 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
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
33int
34hprofStartHeapDump(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
43int
44hprofFinishHeapDump(hprof_context_t *ctx)
45{
46 return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
47}
48
49int
50hprofSetGcScanState(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
60static hprof_basic_type
61signatureToBasicTypeAndSize(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
88static hprof_basic_type
89primitiveToBasicTypeAndSize(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 */
118int
119hprofMarkRootObject(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:
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800149 hprofAddU1ToRecord(rec, heapTag);
150 hprofAddIdToRecord(rec, (hprof_object_id)obj);
151 break;
152
153 /* ID: object ID
154 * ID: JNI global ref ID
155 */
156 case HPROF_ROOT_JNI_GLOBAL:
157 hprofAddU1ToRecord(rec, heapTag);
158 hprofAddIdToRecord(rec, (hprof_object_id)obj);
159 hprofAddIdToRecord(rec, (hprof_id)jniObj);
160 break;
161
162 /* ID: object ID
163 * u4: thread serial number
164 * u4: frame number in stack trace (-1 for empty)
165 */
166 case HPROF_ROOT_JNI_LOCAL:
167 case HPROF_ROOT_JNI_MONITOR:
168 case HPROF_ROOT_JAVA_FRAME:
169 hprofAddU1ToRecord(rec, heapTag);
170 hprofAddIdToRecord(rec, (hprof_object_id)obj);
171 hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
172 hprofAddU4ToRecord(rec, (u4)-1);
173 break;
174
175 /* ID: object ID
176 * u4: thread serial number
177 */
178 case HPROF_ROOT_NATIVE_STACK:
179 case HPROF_ROOT_THREAD_BLOCK:
180 hprofAddU1ToRecord(rec, heapTag);
181 hprofAddIdToRecord(rec, (hprof_object_id)obj);
182 hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
183 break;
184
185 /* ID: thread object ID
186 * u4: thread serial number
187 * u4: stack trace serial number
188 */
189 case HPROF_ROOT_THREAD_OBJECT:
190 hprofAddU1ToRecord(rec, heapTag);
191 hprofAddIdToRecord(rec, (hprof_object_id)obj);
192 hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
193 hprofAddU4ToRecord(rec, (u4)-1); //xxx
194 break;
195
196 default:
197 err = 0;
198 break;
199 }
200
201 ctx->objectsInSegment++;
202
203 return err;
204}
205
206static int
207stackTraceSerialNumber(const void *obj)
208
209{
210#if WITH_HPROF_STACK
211 DvmHeapChunk *chunk = ptr2chunk(obj);
212 return chunk->stackTraceSerialNumber;
213#else
214 return HPROF_NULL_STACK_TRACE;
215#endif
216}
217
218int
219hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
220{
221 const ClassObject *clazz;
222 hprof_record_t *rec = &ctx->curRec;
223 HprofHeapId desiredHeap;
224
225 desiredHeap =
Carl Shapiro6343bd02010-02-16 17:40:19 -0800226 dvmHeapSourceGetPtrFlag(obj, HS_ALLOCATED_IN_ZYGOTE) ?
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800227 HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP;
228
229 if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
230 rec->length >= BYTES_PER_SEGMENT)
231 {
232 /* This flushes the old segment and starts a new one.
233 */
234 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
235 ctx->objectsInSegment = 0;
236
237 /* Starting a new HEAP_DUMP resets the heap to default.
238 */
239 ctx->currentHeap = HPROF_HEAP_DEFAULT;
240 }
241
242 if (desiredHeap != ctx->currentHeap) {
243 hprof_string_id nameId;
244
245 /* This object is in a different heap than the current one.
246 * Emit a HEAP_DUMP_INFO tag to change heaps.
247 */
248 hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
249 hprofAddU4ToRecord(rec, (u4)desiredHeap); // u4: heap id
250 switch (desiredHeap) {
251 case HPROF_HEAP_APP:
252 nameId = hprofLookupStringId("app");
253 break;
254 case HPROF_HEAP_ZYGOTE:
255 nameId = hprofLookupStringId("zygote");
256 break;
257 default:
258 /* Internal error. */
259 assert(!"Unexpected desiredHeap");
260 nameId = hprofLookupStringId("<ILLEGAL>");
261 break;
262 }
263 hprofAddIdToRecord(rec, nameId);
264 ctx->currentHeap = desiredHeap;
265 }
266
267 clazz = obj->clazz;
268
269 if (clazz == NULL) {
270 /* This object was probably just allocated and hasn't been
271 * initialized yet. Add an instance entry to make a note of
272 * it; there's not much else that we can do.
273 */
274 hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
275
276 hprofAddIdToRecord(rec, (hprof_object_id)obj);
277 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
278 hprofAddIdToRecord(rec, (hprof_class_object_id)clazz); // NULL
279 hprofAddIdToRecord(rec, 0); // no instance data
280 } else if (clazz == gDvm.unlinkedJavaLangClass) {
281 /* obj is a ClassObject that hasn't been linked yet.
282 */
283 hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
284
285 //TODO: use hprofLookupClassId() for this:
286 hprofAddIdToRecord(rec, (hprof_class_object_id)obj);
287 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
288 hprofAddIdToRecord(rec, (hprof_class_object_id)0); // no super class
289 hprofAddIdToRecord(rec, (hprof_object_id)0); // no class loader
290 hprofAddIdToRecord(rec, (hprof_object_id)0); // no signer
291 hprofAddIdToRecord(rec, (hprof_object_id)0); // no prot domain
292 hprofAddIdToRecord(rec, (hprof_id)0); // reserved
293 hprofAddIdToRecord(rec, (hprof_id)0); // reserved
294 hprofAddU4ToRecord(rec, 0); // zero instance size
295 hprofAddU2ToRecord(rec, 0); // empty const pool
296 hprofAddU2ToRecord(rec, 0); // no statics
297 hprofAddU2ToRecord(rec, 0); // no instance fields
298 } else {
299 hprof_class_object_id clazzId;
300
301 clazzId = hprofLookupClassId(clazz);
302
303 if (clazz == gDvm.classJavaLangClass) {
304 const ClassObject *thisClass = (const ClassObject *)obj;
305 int i, n;
306 /* obj is a ClassObject.
307 */
308 hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
309
310 hprofAddIdToRecord(rec, hprofLookupClassId(thisClass));
311 hprofAddU4ToRecord(rec, stackTraceSerialNumber(thisClass));
312 hprofAddIdToRecord(rec, hprofLookupClassId(thisClass->super));
313 hprofAddIdToRecord(rec, (hprof_object_id)thisClass->classLoader);
314 hprofAddIdToRecord(rec, (hprof_object_id)0); // no signer
315 hprofAddIdToRecord(rec, (hprof_object_id)0); // no prot domain
316 hprofAddIdToRecord(rec, (hprof_id)0); // reserved
317 hprofAddIdToRecord(rec, (hprof_id)0); // reserved
318 if (obj == (Object *)gDvm.classJavaLangClass) {
319 hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
320 } else {
321 hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
322 }
323
324 hprofAddU2ToRecord(rec, 0); // empty const pool
325
326 /* Static fields
327 */
328 n = thisClass->sfieldCount;
329 hprofAddU2ToRecord(rec, (u2)n);
330 for (i = 0; i < n; i++) {
331 const StaticField *f = &thisClass->sfields[i];
332 hprof_basic_type t;
333 size_t size;
334
335 t = signatureToBasicTypeAndSize(f->field.signature, &size);
336 hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name));
337 hprofAddU1ToRecord(rec, t);
338 if (size == 1) {
339 hprofAddU1ToRecord(rec, (u1)f->value.b);
340 } else if (size == 2) {
341 hprofAddU2ToRecord(rec, (u2)f->value.c);
342 } else if (size == 4) {
343 hprofAddU4ToRecord(rec, (u4)f->value.i);
344 } else if (size == 8) {
345 hprofAddU8ToRecord(rec, (u8)f->value.j);
346 } else {
347 assert(false);
348 }
349 }
350
351 /* Instance fields for this class (no superclass fields)
352 */
353 n = thisClass->ifieldCount;
354 hprofAddU2ToRecord(rec, (u2)n);
355 for (i = 0; i < n; i++) {
356 const InstField *f = &thisClass->ifields[i];
357 hprof_basic_type t;
358
359 t = signatureToBasicTypeAndSize(f->field.signature, NULL);
360 hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name));
361 hprofAddU1ToRecord(rec, t);
362 }
363 } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
364 const ArrayObject *aobj = (const ArrayObject *)obj;
365 u4 length = aobj->length;
366
367 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
368 /* obj is an object array.
369 */
370 hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
371
372 hprofAddIdToRecord(rec, (hprof_object_id)obj);
373 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
374 hprofAddU4ToRecord(rec, length);
375 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
376
377 /* Dump the elements, which are always objects or NULL.
378 */
379 hprofAddIdListToRecord(rec,
380 (const hprof_object_id *)aobj->contents, length);
381 } else {
382 hprof_basic_type t;
383 size_t size;
384
385 t = primitiveToBasicTypeAndSize(clazz->elementClass->
386 primitiveType, &size);
387
388 /* obj is a primitive array.
389 */
390#if DUMP_PRIM_DATA
391 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
392#else
393 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
394#endif
395
396 hprofAddIdToRecord(rec, (hprof_object_id)obj);
397 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
398 hprofAddU4ToRecord(rec, length);
399 hprofAddU1ToRecord(rec, t);
400
401#if DUMP_PRIM_DATA
402 /* Dump the raw, packed element values.
403 */
404 if (size == 1) {
405 hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents,
406 length);
407 } else if (size == 2) {
408 hprofAddU2ListToRecord(rec, (const u2 *)aobj->contents,
409 length);
410 } else if (size == 4) {
411 hprofAddU4ListToRecord(rec, (const u4 *)aobj->contents,
412 length);
413 } else if (size == 8) {
414 hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents,
415 length);
416 }
417#endif
418 }
419 } else {
420 const ClassObject *sclass;
421 size_t sizePatchOffset, savedLen;
422
423 /* obj is an instance object.
424 */
425 hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
426 hprofAddIdToRecord(rec, (hprof_object_id)obj);
427 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
428 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
429
430 /* Reserve some space for the length of the instance
431 * data, which we won't know until we're done writing
432 * it.
433 */
434 sizePatchOffset = rec->length;
435 hprofAddU4ToRecord(rec, 0x77777777);
436
437 /* Write the instance data; fields for this
438 * class, followed by super class fields, and so on.
439 */
440 sclass = clazz;
441 while (sclass != NULL) {
442 int i, ifieldCount;
443
444 ifieldCount = sclass->ifieldCount;
445 for (i = 0; i < ifieldCount; i++) {
446 const InstField *f = &sclass->ifields[i];
447 hprof_basic_type t;
448 size_t size;
449
450 t = signatureToBasicTypeAndSize(f->field.signature, &size);
451 if (size == 1) {
452 hprofAddU1ToRecord(rec,
453 (u1)dvmGetFieldByte(obj, f->byteOffset));
454 } else if (size == 2) {
455 hprofAddU2ToRecord(rec,
456 (u2)dvmGetFieldChar(obj, f->byteOffset));
457 } else if (size == 4) {
458 hprofAddU4ToRecord(rec,
459 (u4)dvmGetFieldInt(obj, f->byteOffset));
460 } else if (size == 8) {
461 hprofAddU8ToRecord(rec,
462 (u8)dvmGetFieldLong(obj, f->byteOffset));
463 } else {
464 assert(false);
465 }
466 }
467
468 sclass = sclass->super;
469 }
470
471 /* Patch the instance field length.
472 */
473 savedLen = rec->length;
474 rec->length = sizePatchOffset;
475 hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
476 rec->length = savedLen;
477 }
478 }
479
480 ctx->objectsInSegment++;
481
482 return 0;
483}