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 /* Set DUMP_PRIM_DATA to 1 if you want to include the contents
22 * of primitive arrays (byte arrays, character arrays, etc.)
23 * in heap dumps. This can be a large amount of data.
25 #define DUMP_PRIM_DATA 1
27 #define OBJECTS_PER_SEGMENT ((size_t)128)
28 #define BYTES_PER_SEGMENT ((size_t)4096)
30 /* The static field-name for the synthetic object generated to account
31 * for class Static overhead.
33 #define STATIC_OVERHEAD_NAME "$staticOverhead"
34 /* The ID for the synthetic object generated to account for class
37 #define CLASS_STATICS_ID(clazz) ((hprof_object_id)(((u4)(clazz)) | 1))
39 int hprofStartHeapDump(hprof_context_t *ctx)
41 UNUSED_PARAMETER(ctx);
43 ctx->objectsInSegment = OBJECTS_PER_SEGMENT;
44 ctx->currentHeap = HPROF_HEAP_DEFAULT;
48 int hprofFinishHeapDump(hprof_context_t *ctx)
50 return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
53 int hprofSetGcScanState(hprof_context_t *ctx,
54 hprof_heap_tag_t state,
55 u4 threadSerialNumber)
57 /* Used by hprofMarkRootObject()
59 ctx->gcScanState = state;
60 ctx->gcThreadSerialNumber = threadSerialNumber;
64 static hprof_basic_type signatureToBasicTypeAndSize(const char *sig,
73 case 'L': ret = hprof_basic_object; size = 4; break;
74 case 'Z': ret = hprof_basic_boolean; size = 1; break;
75 case 'C': ret = hprof_basic_char; size = 2; break;
76 case 'F': ret = hprof_basic_float; size = 4; break;
77 case 'D': ret = hprof_basic_double; size = 8; break;
78 case 'B': ret = hprof_basic_byte; size = 1; break;
79 case 'S': ret = hprof_basic_short; size = 2; break;
80 default: assert(false);
81 case 'I': ret = hprof_basic_int; size = 4; break;
82 case 'J': ret = hprof_basic_long; size = 8; break;
85 if (sizeOut != NULL) {
92 static hprof_basic_type primitiveToBasicTypeAndSize(PrimitiveType prim,
99 case PRIM_BOOLEAN: ret = hprof_basic_boolean; size = 1; break;
100 case PRIM_CHAR: ret = hprof_basic_char; size = 2; break;
101 case PRIM_FLOAT: ret = hprof_basic_float; size = 4; break;
102 case PRIM_DOUBLE: ret = hprof_basic_double; size = 8; break;
103 case PRIM_BYTE: ret = hprof_basic_byte; size = 1; break;
104 case PRIM_SHORT: ret = hprof_basic_short; size = 2; break;
105 default: assert(false);
106 case PRIM_INT: ret = hprof_basic_int; size = 4; break;
107 case PRIM_LONG: ret = hprof_basic_long; size = 8; break;
110 if (sizeOut != NULL) {
117 /* Always called when marking objects, but only does
118 * something when ctx->gcScanState is non-zero, which is usually
119 * only true when marking the root set or unreachable
120 * objects. Used to add rootset references to obj.
122 int hprofMarkRootObject(hprof_context_t *ctx, const Object *obj, jobject jniObj)
124 hprof_record_t *rec = &ctx->curRec;
126 hprof_heap_tag_t heapTag = (hprof_heap_tag_t)ctx->gcScanState;
132 if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
133 rec->length >= BYTES_PER_SEGMENT)
135 /* This flushes the old segment and starts a new one.
137 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
138 ctx->objectsInSegment = 0;
144 case HPROF_ROOT_UNKNOWN:
145 case HPROF_ROOT_STICKY_CLASS:
146 case HPROF_ROOT_MONITOR_USED:
147 case HPROF_ROOT_INTERNED_STRING:
148 case HPROF_ROOT_FINALIZING:
149 case HPROF_ROOT_DEBUGGER:
150 case HPROF_ROOT_REFERENCE_CLEANUP:
151 case HPROF_ROOT_VM_INTERNAL:
152 hprofAddU1ToRecord(rec, heapTag);
153 hprofAddIdToRecord(rec, (hprof_object_id)obj);
157 * ID: JNI global ref ID
159 case HPROF_ROOT_JNI_GLOBAL:
160 hprofAddU1ToRecord(rec, heapTag);
161 hprofAddIdToRecord(rec, (hprof_object_id)obj);
162 hprofAddIdToRecord(rec, (hprof_id)jniObj);
166 * u4: thread serial number
167 * u4: frame number in stack trace (-1 for empty)
169 case HPROF_ROOT_JNI_LOCAL:
170 case HPROF_ROOT_JNI_MONITOR:
171 case HPROF_ROOT_JAVA_FRAME:
172 hprofAddU1ToRecord(rec, heapTag);
173 hprofAddIdToRecord(rec, (hprof_object_id)obj);
174 hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
175 hprofAddU4ToRecord(rec, (u4)-1);
179 * u4: thread serial number
181 case HPROF_ROOT_NATIVE_STACK:
182 case HPROF_ROOT_THREAD_BLOCK:
183 hprofAddU1ToRecord(rec, heapTag);
184 hprofAddIdToRecord(rec, (hprof_object_id)obj);
185 hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
188 /* ID: thread object ID
189 * u4: thread serial number
190 * u4: stack trace serial number
192 case HPROF_ROOT_THREAD_OBJECT:
193 hprofAddU1ToRecord(rec, heapTag);
194 hprofAddIdToRecord(rec, (hprof_object_id)obj);
195 hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
196 hprofAddU4ToRecord(rec, (u4)-1); //xxx
204 ctx->objectsInSegment++;
209 static int stackTraceSerialNumber(const void *obj)
211 return HPROF_NULL_STACK_TRACE;
214 int hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
216 const ClassObject *clazz;
217 hprof_record_t *rec = &ctx->curRec;
218 HprofHeapId desiredHeap;
220 desiredHeap = dvmIsZygoteObject(obj) ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP;
222 if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
223 rec->length >= BYTES_PER_SEGMENT)
225 /* This flushes the old segment and starts a new one.
227 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
228 ctx->objectsInSegment = 0;
230 /* Starting a new HEAP_DUMP resets the heap to default.
232 ctx->currentHeap = HPROF_HEAP_DEFAULT;
235 if (desiredHeap != ctx->currentHeap) {
236 hprof_string_id nameId;
238 /* This object is in a different heap than the current one.
239 * Emit a HEAP_DUMP_INFO tag to change heaps.
241 hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
242 hprofAddU4ToRecord(rec, (u4)desiredHeap); // u4: heap id
243 switch (desiredHeap) {
245 nameId = hprofLookupStringId("app");
247 case HPROF_HEAP_ZYGOTE:
248 nameId = hprofLookupStringId("zygote");
251 /* Internal error. */
252 assert(!"Unexpected desiredHeap");
253 nameId = hprofLookupStringId("<ILLEGAL>");
256 hprofAddIdToRecord(rec, nameId);
257 ctx->currentHeap = desiredHeap;
263 /* This object will bother HprofReader, because it has a NULL
264 * class, so just don't dump it. It could be
265 * gDvm.unlinkedJavaLangClass or it could be an object just
266 * allocated which hasn't been initialized yet.
269 if (dvmIsClassObject(obj)) {
270 const ClassObject *thisClass = (const ClassObject *)obj;
271 /* obj is a ClassObject.
273 int sFieldCount = thisClass->sfieldCount;
274 if (sFieldCount != 0) {
275 int byteLength = sFieldCount*sizeof(StaticField);
276 /* Create a byte array to reflect the allocation of the
277 * StaticField array at the end of this class.
279 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
280 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
281 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
282 hprofAddU4ToRecord(rec, byteLength);
283 hprofAddU1ToRecord(rec, hprof_basic_byte);
284 for (int i = 0; i < byteLength; i++) {
285 hprofAddU1ToRecord(rec, 0);
289 hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
290 hprofAddIdToRecord(rec, hprofLookupClassId(thisClass));
291 hprofAddU4ToRecord(rec, stackTraceSerialNumber(thisClass));
292 hprofAddIdToRecord(rec, hprofLookupClassId(thisClass->super));
293 hprofAddIdToRecord(rec, (hprof_object_id)thisClass->classLoader);
294 hprofAddIdToRecord(rec, (hprof_object_id)0); // no signer
295 hprofAddIdToRecord(rec, (hprof_object_id)0); // no prot domain
296 hprofAddIdToRecord(rec, (hprof_id)0); // reserved
297 hprofAddIdToRecord(rec, (hprof_id)0); // reserved
298 if (obj == (Object *)gDvm.classJavaLangClass) {
299 // ClassObjects have their static fields appended, so
300 // aren't all the same size. But they're at least this
302 hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
304 hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
307 hprofAddU2ToRecord(rec, 0); // empty const pool
311 if (sFieldCount == 0) {
312 hprofAddU2ToRecord(rec, (u2)0);
314 hprofAddU2ToRecord(rec, (u2)(sFieldCount+1));
315 hprofAddIdToRecord(rec,
316 hprofLookupStringId(STATIC_OVERHEAD_NAME));
317 hprofAddU1ToRecord(rec, hprof_basic_object);
318 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
319 for (int i = 0; i < sFieldCount; i++) {
322 const StaticField *f = &thisClass->sfields[i];
324 t = signatureToBasicTypeAndSize(f->signature, &size);
325 hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
326 hprofAddU1ToRecord(rec, t);
328 hprofAddU1ToRecord(rec, (u1)f->value.b);
329 } else if (size == 2) {
330 hprofAddU2ToRecord(rec, (u2)f->value.c);
331 } else if (size == 4) {
332 hprofAddU4ToRecord(rec, (u4)f->value.i);
333 } else if (size == 8) {
334 hprofAddU8ToRecord(rec, (u8)f->value.j);
341 /* Instance fields for this class (no superclass fields)
343 int iFieldCount = thisClass->ifieldCount;
344 hprofAddU2ToRecord(rec, (u2)iFieldCount);
345 for (int i = 0; i < iFieldCount; i++) {
346 const InstField *f = &thisClass->ifields[i];
349 t = signatureToBasicTypeAndSize(f->signature, NULL);
350 hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
351 hprofAddU1ToRecord(rec, t);
353 } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
354 const ArrayObject *aobj = (const ArrayObject *)obj;
355 u4 length = aobj->length;
357 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
358 /* obj is an object array.
360 hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
362 hprofAddIdToRecord(rec, (hprof_object_id)obj);
363 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
364 hprofAddU4ToRecord(rec, length);
365 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
367 /* Dump the elements, which are always objects or NULL.
369 hprofAddIdListToRecord(rec,
370 (const hprof_object_id *)(void *)aobj->contents, length);
375 t = primitiveToBasicTypeAndSize(clazz->elementClass->
376 primitiveType, &size);
378 /* obj is a primitive array.
381 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
383 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
386 hprofAddIdToRecord(rec, (hprof_object_id)obj);
387 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
388 hprofAddU4ToRecord(rec, length);
389 hprofAddU1ToRecord(rec, t);
392 /* Dump the raw, packed element values.
395 hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents,
397 } else if (size == 2) {
398 hprofAddU2ListToRecord(rec, (const u2 *)(void *)aobj->contents,
400 } else if (size == 4) {
401 hprofAddU4ListToRecord(rec, (const u4 *)(void *)aobj->contents,
403 } else if (size == 8) {
404 hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents,
410 const ClassObject *sclass;
411 size_t sizePatchOffset, savedLen;
413 /* obj is an instance object.
415 hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
416 hprofAddIdToRecord(rec, (hprof_object_id)obj);
417 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
418 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
420 /* Reserve some space for the length of the instance
421 * data, which we won't know until we're done writing
424 sizePatchOffset = rec->length;
425 hprofAddU4ToRecord(rec, 0x77777777);
427 /* Write the instance data; fields for this
428 * class, followed by super class fields, and so on.
431 while (sclass != NULL) {
432 int ifieldCount = sclass->ifieldCount;
433 for (int i = 0; i < ifieldCount; i++) {
434 const InstField *f = &sclass->ifields[i];
437 (void) signatureToBasicTypeAndSize(f->signature, &size);
439 hprofAddU1ToRecord(rec,
440 (u1)dvmGetFieldByte(obj, f->byteOffset));
441 } else if (size == 2) {
442 hprofAddU2ToRecord(rec,
443 (u2)dvmGetFieldChar(obj, f->byteOffset));
444 } else if (size == 4) {
445 hprofAddU4ToRecord(rec,
446 (u4)dvmGetFieldInt(obj, f->byteOffset));
447 } else if (size == 8) {
448 hprofAddU8ToRecord(rec,
449 (u8)dvmGetFieldLong(obj, f->byteOffset));
455 sclass = sclass->super;
458 /* Patch the instance field length.
460 savedLen = rec->length;
461 rec->length = sizePatchOffset;
462 hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
463 rec->length = savedLen;
467 ctx->objectsInSegment++;