blob: 1738b57f508b850be4f4604dc043a72ca02ca31f [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) {
Barry Hayesb4bad122010-03-08 13:57:34 -0800270 /* This object will bother HprofReader, because it has a NULL
271 * class, so just don't dump it. It could be
272 * gDvm.unlinkedJavaLangClass or it could be an object just
273 * allocated which hasn't been initialized yet.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800274 */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800275 } else if (clazz == gDvm.unlinkedJavaLangClass) {
276 /* obj is a ClassObject that hasn't been linked yet.
277 */
278 hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
279
280 //TODO: use hprofLookupClassId() for this:
281 hprofAddIdToRecord(rec, (hprof_class_object_id)obj);
282 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
283 hprofAddIdToRecord(rec, (hprof_class_object_id)0); // no super class
284 hprofAddIdToRecord(rec, (hprof_object_id)0); // no class loader
285 hprofAddIdToRecord(rec, (hprof_object_id)0); // no signer
286 hprofAddIdToRecord(rec, (hprof_object_id)0); // no prot domain
287 hprofAddIdToRecord(rec, (hprof_id)0); // reserved
288 hprofAddIdToRecord(rec, (hprof_id)0); // reserved
289 hprofAddU4ToRecord(rec, 0); // zero instance size
290 hprofAddU2ToRecord(rec, 0); // empty const pool
291 hprofAddU2ToRecord(rec, 0); // no statics
292 hprofAddU2ToRecord(rec, 0); // no instance fields
293 } else {
294 hprof_class_object_id clazzId;
295
296 clazzId = hprofLookupClassId(clazz);
297
298 if (clazz == gDvm.classJavaLangClass) {
299 const ClassObject *thisClass = (const ClassObject *)obj;
300 int i, n;
301 /* obj is a ClassObject.
302 */
303 hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
304
305 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) {
314 hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
315 } else {
316 hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
317 }
318
319 hprofAddU2ToRecord(rec, 0); // empty const pool
320
321 /* Static fields
322 */
323 n = thisClass->sfieldCount;
324 hprofAddU2ToRecord(rec, (u2)n);
325 for (i = 0; i < n; i++) {
326 const StaticField *f = &thisClass->sfields[i];
327 hprof_basic_type t;
328 size_t size;
329
330 t = signatureToBasicTypeAndSize(f->field.signature, &size);
331 hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name));
332 hprofAddU1ToRecord(rec, t);
333 if (size == 1) {
334 hprofAddU1ToRecord(rec, (u1)f->value.b);
335 } else if (size == 2) {
336 hprofAddU2ToRecord(rec, (u2)f->value.c);
337 } else if (size == 4) {
338 hprofAddU4ToRecord(rec, (u4)f->value.i);
339 } else if (size == 8) {
340 hprofAddU8ToRecord(rec, (u8)f->value.j);
341 } else {
342 assert(false);
343 }
344 }
345
346 /* Instance fields for this class (no superclass fields)
347 */
348 n = thisClass->ifieldCount;
349 hprofAddU2ToRecord(rec, (u2)n);
350 for (i = 0; i < n; i++) {
351 const InstField *f = &thisClass->ifields[i];
352 hprof_basic_type t;
353
354 t = signatureToBasicTypeAndSize(f->field.signature, NULL);
355 hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name));
356 hprofAddU1ToRecord(rec, t);
357 }
358 } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
359 const ArrayObject *aobj = (const ArrayObject *)obj;
360 u4 length = aobj->length;
361
362 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
363 /* obj is an object array.
364 */
365 hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
366
367 hprofAddIdToRecord(rec, (hprof_object_id)obj);
368 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
369 hprofAddU4ToRecord(rec, length);
370 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
371
372 /* Dump the elements, which are always objects or NULL.
373 */
374 hprofAddIdListToRecord(rec,
375 (const hprof_object_id *)aobj->contents, length);
376 } else {
377 hprof_basic_type t;
378 size_t size;
379
380 t = primitiveToBasicTypeAndSize(clazz->elementClass->
381 primitiveType, &size);
382
383 /* obj is a primitive array.
384 */
385#if DUMP_PRIM_DATA
386 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
387#else
388 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
389#endif
390
391 hprofAddIdToRecord(rec, (hprof_object_id)obj);
392 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
393 hprofAddU4ToRecord(rec, length);
394 hprofAddU1ToRecord(rec, t);
395
396#if DUMP_PRIM_DATA
397 /* Dump the raw, packed element values.
398 */
399 if (size == 1) {
400 hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents,
401 length);
402 } else if (size == 2) {
403 hprofAddU2ListToRecord(rec, (const u2 *)aobj->contents,
404 length);
405 } else if (size == 4) {
406 hprofAddU4ListToRecord(rec, (const u4 *)aobj->contents,
407 length);
408 } else if (size == 8) {
409 hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents,
410 length);
411 }
412#endif
413 }
414 } else {
415 const ClassObject *sclass;
416 size_t sizePatchOffset, savedLen;
417
418 /* obj is an instance object.
419 */
420 hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
421 hprofAddIdToRecord(rec, (hprof_object_id)obj);
422 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
423 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
424
425 /* Reserve some space for the length of the instance
426 * data, which we won't know until we're done writing
427 * it.
428 */
429 sizePatchOffset = rec->length;
430 hprofAddU4ToRecord(rec, 0x77777777);
431
432 /* Write the instance data; fields for this
433 * class, followed by super class fields, and so on.
434 */
435 sclass = clazz;
436 while (sclass != NULL) {
437 int i, ifieldCount;
438
439 ifieldCount = sclass->ifieldCount;
440 for (i = 0; i < ifieldCount; i++) {
441 const InstField *f = &sclass->ifields[i];
442 hprof_basic_type t;
443 size_t size;
444
445 t = signatureToBasicTypeAndSize(f->field.signature, &size);
446 if (size == 1) {
447 hprofAddU1ToRecord(rec,
448 (u1)dvmGetFieldByte(obj, f->byteOffset));
449 } else if (size == 2) {
450 hprofAddU2ToRecord(rec,
451 (u2)dvmGetFieldChar(obj, f->byteOffset));
452 } else if (size == 4) {
453 hprofAddU4ToRecord(rec,
454 (u4)dvmGetFieldInt(obj, f->byteOffset));
455 } else if (size == 8) {
456 hprofAddU8ToRecord(rec,
457 (u8)dvmGetFieldLong(obj, f->byteOffset));
458 } else {
459 assert(false);
460 }
461 }
462
463 sclass = sclass->super;
464 }
465
466 /* Patch the instance field length.
467 */
468 savedLen = rec->length;
469 rec->length = sizePatchOffset;
470 hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
471 rec->length = savedLen;
472 }
473 }
474
475 ctx->objectsInSegment++;
476
477 return 0;
478}