2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 #include "alloc/HeapInternal.h"
22 #include "alloc/HeapSource.h"
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.
28 #define DUMP_PRIM_DATA 1
30 #define OBJECTS_PER_SEGMENT ((size_t)128)
31 #define BYTES_PER_SEGMENT ((size_t)4096)
33 /* The static field-name for the synthetic object generated to account
34 * for class Static overhead.
36 #define STATIC_OVERHEAD_NAME "$staticOverhead"
37 /* The ID for the synthetic object generated to account for class
40 #define CLASS_STATICS_ID(clazz) ((hprof_object_id)(((u4)(clazz)) | 1))
43 hprofStartHeapDump(hprof_context_t *ctx)
45 UNUSED_PARAMETER(ctx);
47 ctx->objectsInSegment = OBJECTS_PER_SEGMENT;
48 ctx->currentHeap = HPROF_HEAP_DEFAULT;
53 hprofFinishHeapDump(hprof_context_t *ctx)
55 return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
59 hprofSetGcScanState(hprof_context_t *ctx,
60 hprof_heap_tag_t state, u4 threadSerialNumber)
62 /* Used by hprofMarkRootObject()
64 ctx->gcScanState = state;
65 ctx->gcThreadSerialNumber = threadSerialNumber;
69 static hprof_basic_type
70 signatureToBasicTypeAndSize(const char *sig, size_t *sizeOut)
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;
90 if (sizeOut != NULL) {
97 static hprof_basic_type
98 primitiveToBasicTypeAndSize(PrimitiveType prim, size_t *sizeOut)
100 hprof_basic_type ret;
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;
115 if (sizeOut != NULL) {
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.
128 hprofMarkRootObject(hprof_context_t *ctx, const Object *obj, jobject jniObj)
130 hprof_record_t *rec = &ctx->curRec;
132 hprof_heap_tag_t heapTag = ctx->gcScanState;
138 if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
139 rec->length >= BYTES_PER_SEGMENT)
141 /* This flushes the old segment and starts a new one.
143 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
144 ctx->objectsInSegment = 0;
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:
158 hprofAddU1ToRecord(rec, heapTag);
159 hprofAddIdToRecord(rec, (hprof_object_id)obj);
163 * ID: JNI global ref ID
165 case HPROF_ROOT_JNI_GLOBAL:
166 hprofAddU1ToRecord(rec, heapTag);
167 hprofAddIdToRecord(rec, (hprof_object_id)obj);
168 hprofAddIdToRecord(rec, (hprof_id)jniObj);
172 * u4: thread serial number
173 * u4: frame number in stack trace (-1 for empty)
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);
185 * u4: thread serial number
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);
194 /* ID: thread object ID
195 * u4: thread serial number
196 * u4: stack trace serial number
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
210 ctx->objectsInSegment++;
216 stackTraceSerialNumber(const void *obj)
220 DvmHeapChunk *chunk = ptr2chunk(obj);
221 return chunk->stackTraceSerialNumber;
223 return HPROF_NULL_STACK_TRACE;
228 hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
230 const ClassObject *clazz;
231 hprof_record_t *rec = &ctx->curRec;
232 HprofHeapId desiredHeap;
235 dvmHeapSourceGetPtrFlag(obj, HS_ALLOCATED_IN_ZYGOTE) ?
236 HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP;
238 if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
239 rec->length >= BYTES_PER_SEGMENT)
241 /* This flushes the old segment and starts a new one.
243 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
244 ctx->objectsInSegment = 0;
246 /* Starting a new HEAP_DUMP resets the heap to default.
248 ctx->currentHeap = HPROF_HEAP_DEFAULT;
251 if (desiredHeap != ctx->currentHeap) {
252 hprof_string_id nameId;
254 /* This object is in a different heap than the current one.
255 * Emit a HEAP_DUMP_INFO tag to change heaps.
257 hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
258 hprofAddU4ToRecord(rec, (u4)desiredHeap); // u4: heap id
259 switch (desiredHeap) {
261 nameId = hprofLookupStringId("app");
263 case HPROF_HEAP_ZYGOTE:
264 nameId = hprofLookupStringId("zygote");
267 /* Internal error. */
268 assert(!"Unexpected desiredHeap");
269 nameId = hprofLookupStringId("<ILLEGAL>");
272 hprofAddIdToRecord(rec, nameId);
273 ctx->currentHeap = desiredHeap;
279 /* This object will bother HprofReader, because it has a NULL
280 * class, so just don't dump it. It could be
281 * gDvm.unlinkedJavaLangClass or it could be an object just
282 * allocated which hasn't been initialized yet.
285 hprof_class_object_id clazzId;
287 clazzId = hprofLookupClassId(clazz);
289 if (clazz == gDvm.classJavaLangClass) {
290 const ClassObject *thisClass = (const ClassObject *)obj;
291 int i, sFieldCount, iFieldCount;
292 /* obj is a ClassObject.
294 sFieldCount = thisClass->sfieldCount;
295 if (sFieldCount != 0) {
296 int byteLength = sFieldCount*sizeof(StaticField);
297 /* Create a byte array to reflect the allocation of the
298 * StaticField array at the end of this class.
300 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
301 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
302 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
303 hprofAddU4ToRecord(rec, byteLength);
304 hprofAddU1ToRecord(rec, hprof_basic_byte);
305 for (i = 0; i < byteLength; i++) {
306 hprofAddU1ToRecord(rec, 0);
310 hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
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 // ClassObjects have their static fields appended, so
321 // aren't all the same size. But they're at least this
323 hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
325 hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
328 hprofAddU2ToRecord(rec, 0); // empty const pool
332 if (sFieldCount == 0) {
333 hprofAddU2ToRecord(rec, (u2)0);
335 hprofAddU2ToRecord(rec, (u2)(sFieldCount+1));
336 hprofAddIdToRecord(rec,
337 hprofLookupStringId(STATIC_OVERHEAD_NAME));
338 hprofAddU1ToRecord(rec, hprof_basic_object);
339 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
340 for (i = 0; i < sFieldCount; i++) {
343 const StaticField *f = &thisClass->sfields[i];
345 t = signatureToBasicTypeAndSize(f->field.signature, &size);
346 hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name));
347 hprofAddU1ToRecord(rec, t);
349 hprofAddU1ToRecord(rec, (u1)f->value.b);
350 } else if (size == 2) {
351 hprofAddU2ToRecord(rec, (u2)f->value.c);
352 } else if (size == 4) {
353 hprofAddU4ToRecord(rec, (u4)f->value.i);
354 } else if (size == 8) {
355 hprofAddU8ToRecord(rec, (u8)f->value.j);
362 /* Instance fields for this class (no superclass fields)
364 iFieldCount = thisClass->ifieldCount;
365 hprofAddU2ToRecord(rec, (u2)iFieldCount);
366 for (i = 0; i < iFieldCount; i++) {
367 const InstField *f = &thisClass->ifields[i];
370 t = signatureToBasicTypeAndSize(f->field.signature, NULL);
371 hprofAddIdToRecord(rec, hprofLookupStringId(f->field.name));
372 hprofAddU1ToRecord(rec, t);
374 } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
375 const ArrayObject *aobj = (const ArrayObject *)obj;
376 u4 length = aobj->length;
378 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
379 /* obj is an object array.
381 hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
383 hprofAddIdToRecord(rec, (hprof_object_id)obj);
384 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
385 hprofAddU4ToRecord(rec, length);
386 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
388 /* Dump the elements, which are always objects or NULL.
390 hprofAddIdListToRecord(rec,
391 (const hprof_object_id *)aobj->contents, length);
396 t = primitiveToBasicTypeAndSize(clazz->elementClass->
397 primitiveType, &size);
399 /* obj is a primitive array.
402 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
404 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
407 hprofAddIdToRecord(rec, (hprof_object_id)obj);
408 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
409 hprofAddU4ToRecord(rec, length);
410 hprofAddU1ToRecord(rec, t);
413 /* Dump the raw, packed element values.
416 hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents,
418 } else if (size == 2) {
419 hprofAddU2ListToRecord(rec, (const u2 *)aobj->contents,
421 } else if (size == 4) {
422 hprofAddU4ListToRecord(rec, (const u4 *)aobj->contents,
424 } else if (size == 8) {
425 hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents,
431 const ClassObject *sclass;
432 size_t sizePatchOffset, savedLen;
434 /* obj is an instance object.
436 hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
437 hprofAddIdToRecord(rec, (hprof_object_id)obj);
438 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
439 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
441 /* Reserve some space for the length of the instance
442 * data, which we won't know until we're done writing
445 sizePatchOffset = rec->length;
446 hprofAddU4ToRecord(rec, 0x77777777);
448 /* Write the instance data; fields for this
449 * class, followed by super class fields, and so on.
452 while (sclass != NULL) {
455 ifieldCount = sclass->ifieldCount;
456 for (i = 0; i < ifieldCount; i++) {
457 const InstField *f = &sclass->ifields[i];
461 t = signatureToBasicTypeAndSize(f->field.signature, &size);
463 hprofAddU1ToRecord(rec,
464 (u1)dvmGetFieldByte(obj, f->byteOffset));
465 } else if (size == 2) {
466 hprofAddU2ToRecord(rec,
467 (u2)dvmGetFieldChar(obj, f->byteOffset));
468 } else if (size == 4) {
469 hprofAddU4ToRecord(rec,
470 (u4)dvmGetFieldInt(obj, f->byteOffset));
471 } else if (size == 8) {
472 hprofAddU8ToRecord(rec,
473 (u8)dvmGetFieldLong(obj, f->byteOffset));
479 sclass = sclass->super;
482 /* Patch the instance field length.
484 savedLen = rec->length;
485 rec->length = sizePatchOffset;
486 hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
487 rec->length = savedLen;
491 ctx->objectsInSegment++;