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