1 /*
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 /* 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.
24 */
25 #define DUMP_PRIM_DATA 1
26
27 #define OBJECTS_PER_SEGMENT ((size_t)128)
28 #define BYTES_PER_SEGMENT ((size_t)4096)
29
30 /* The static field-name for the synthetic object generated to account
31 * for class Static overhead.
32 */
33 #define STATIC_OVERHEAD_NAME "$staticOverhead"
34 /* The ID for the synthetic object generated to account for class
35 * Static overhead.
36 */
37 #define CLASS_STATICS_ID(clazz) ((hprof_object_id)(((u4)(clazz)) | 1))
38
hprofStartHeapDump(hprof_context_t * ctx)39 int hprofStartHeapDump(hprof_context_t *ctx)
40 {
41 UNUSED_PARAMETER(ctx);
42
43 ctx->objectsInSegment = OBJECTS_PER_SEGMENT;
44 ctx->currentHeap = HPROF_HEAP_DEFAULT;
45 return 0;
46 }
47
hprofFinishHeapDump(hprof_context_t * ctx)48 int hprofFinishHeapDump(hprof_context_t *ctx)
49 {
50 return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
51 }
52
hprofSetGcScanState(hprof_context_t * ctx,hprof_heap_tag_t state,u4 threadSerialNumber)53 int hprofSetGcScanState(hprof_context_t *ctx,
54 hprof_heap_tag_t state,
55 u4 threadSerialNumber)
56 {
57 /* Used by hprofMarkRootObject()
58 */
59 ctx->gcScanState = state;
60 ctx->gcThreadSerialNumber = threadSerialNumber;
61 return 0;
62 }
63
signatureToBasicTypeAndSize(const char * sig,size_t * sizeOut)64 static hprof_basic_type signatureToBasicTypeAndSize(const char *sig,
65 size_t *sizeOut)
66 {
67 char c = sig[0];
68 hprof_basic_type ret;
69 size_t size;
70
71 switch (c) {
72 case '[':
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;
83 }
84
85 if (sizeOut != NULL) {
86 *sizeOut = size;
87 }
88
89 return ret;
90 }
91
primitiveToBasicTypeAndSize(PrimitiveType prim,size_t * sizeOut)92 static hprof_basic_type primitiveToBasicTypeAndSize(PrimitiveType prim,
93 size_t *sizeOut)
94 {
95 hprof_basic_type ret;
96 size_t size;
97
98 switch (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;
108 }
109
110 if (sizeOut != NULL) {
111 *sizeOut = size;
112 }
113
114 return ret;
115 }
116
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.
121 */
hprofMarkRootObject(hprof_context_t * ctx,const Object * obj,jobject jniObj)122 int hprofMarkRootObject(hprof_context_t *ctx, const Object *obj, jobject jniObj)
123 {
124 hprof_record_t *rec = &ctx->curRec;
125 int err;
126 hprof_heap_tag_t heapTag = (hprof_heap_tag_t)ctx->gcScanState;
127
128 if (heapTag == 0) {
129 return 0;
130 }
131
132 if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
133 rec->length >= BYTES_PER_SEGMENT)
134 {
135 /* This flushes the old segment and starts a new one.
136 */
137 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
138 ctx->objectsInSegment = 0;
139 }
140
141 switch (heapTag) {
142 /* ID: object ID
143 */
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);
154 break;
155
156 /* ID: object ID
157 * ID: JNI global ref ID
158 */
159 case HPROF_ROOT_JNI_GLOBAL:
160 hprofAddU1ToRecord(rec, heapTag);
161 hprofAddIdToRecord(rec, (hprof_object_id)obj);
162 hprofAddIdToRecord(rec, (hprof_id)jniObj);
163 break;
164
165 /* ID: object ID
166 * u4: thread serial number
167 * u4: frame number in stack trace (-1 for empty)
168 */
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);
176 break;
177
178 /* ID: object ID
179 * u4: thread serial number
180 */
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);
186 break;
187
188 /* ID: thread object ID
189 * u4: thread serial number
190 * u4: stack trace serial number
191 */
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
197 break;
198
199 default:
200 err = 0;
201 break;
202 }
203
204 ctx->objectsInSegment++;
205
206 return err;
207 }
208
stackTraceSerialNumber(const void * obj)209 static int stackTraceSerialNumber(const void *obj)
210 {
211 return HPROF_NULL_STACK_TRACE;
212 }
213
hprofDumpHeapObject(hprof_context_t * ctx,const Object * obj)214 int hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
215 {
216 const ClassObject *clazz;
217 hprof_record_t *rec = &ctx->curRec;
218 HprofHeapId desiredHeap;
219
220 desiredHeap = dvmIsZygoteObject(obj) ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP;
221
222 if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
223 rec->length >= BYTES_PER_SEGMENT)
224 {
225 /* This flushes the old segment and starts a new one.
226 */
227 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
228 ctx->objectsInSegment = 0;
229
230 /* Starting a new HEAP_DUMP resets the heap to default.
231 */
232 ctx->currentHeap = HPROF_HEAP_DEFAULT;
233 }
234
235 if (desiredHeap != ctx->currentHeap) {
236 hprof_string_id nameId;
237
238 /* This object is in a different heap than the current one.
239 * Emit a HEAP_DUMP_INFO tag to change heaps.
240 */
241 hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
242 hprofAddU4ToRecord(rec, (u4)desiredHeap); // u4: heap id
243 switch (desiredHeap) {
244 case HPROF_HEAP_APP:
245 nameId = hprofLookupStringId("app");
246 break;
247 case HPROF_HEAP_ZYGOTE:
248 nameId = hprofLookupStringId("zygote");
249 break;
250 default:
251 /* Internal error. */
252 assert(!"Unexpected desiredHeap");
253 nameId = hprofLookupStringId("<ILLEGAL>");
254 break;
255 }
256 hprofAddIdToRecord(rec, nameId);
257 ctx->currentHeap = desiredHeap;
258 }
259
260 clazz = obj->clazz;
261
262 if (clazz == NULL) {
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.
267 */
268 } else {
269 if (dvmIsClassObject(obj)) {
270 const ClassObject *thisClass = (const ClassObject *)obj;
271 /* obj is a ClassObject.
272 */
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.
278 */
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);
286 }
287 }
288
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
301 // size.
302 hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
303 } else {
304 hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
305 }
306
307 hprofAddU2ToRecord(rec, 0); // empty const pool
308
309 /* Static fields
310 */
311 if (sFieldCount == 0) {
312 hprofAddU2ToRecord(rec, (u2)0);
313 } else {
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++) {
320 hprof_basic_type t;
321 size_t size;
322 const StaticField *f = &thisClass->sfields[i];
323
324 t = signatureToBasicTypeAndSize(f->signature, &size);
325 hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
326 hprofAddU1ToRecord(rec, t);
327 if (size == 1) {
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);
335 } else {
336 assert(false);
337 }
338 }
339 }
340
341 /* Instance fields for this class (no superclass fields)
342 */
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];
347 hprof_basic_type t;
348
349 t = signatureToBasicTypeAndSize(f->signature, NULL);
350 hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
351 hprofAddU1ToRecord(rec, t);
352 }
353 } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
354 const ArrayObject *aobj = (const ArrayObject *)obj;
355 u4 length = aobj->length;
356
357 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
358 /* obj is an object array.
359 */
360 hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
361
362 hprofAddIdToRecord(rec, (hprof_object_id)obj);
363 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
364 hprofAddU4ToRecord(rec, length);
365 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
366
367 /* Dump the elements, which are always objects or NULL.
368 */
369 hprofAddIdListToRecord(rec,
370 (const hprof_object_id *)(void *)aobj->contents, length);
371 } else {
372 hprof_basic_type t;
373 size_t size;
374
375 t = primitiveToBasicTypeAndSize(clazz->elementClass->
376 primitiveType, &size);
377
378 /* obj is a primitive array.
379 */
380 #if DUMP_PRIM_DATA
381 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
382 #else
383 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
384 #endif
385
386 hprofAddIdToRecord(rec, (hprof_object_id)obj);
387 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
388 hprofAddU4ToRecord(rec, length);
389 hprofAddU1ToRecord(rec, t);
390
391 #if DUMP_PRIM_DATA
392 /* Dump the raw, packed element values.
393 */
394 if (size == 1) {
395 hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents,
396 length);
397 } else if (size == 2) {
398 hprofAddU2ListToRecord(rec, (const u2 *)(void *)aobj->contents,
399 length);
400 } else if (size == 4) {
401 hprofAddU4ListToRecord(rec, (const u4 *)(void *)aobj->contents,
402 length);
403 } else if (size == 8) {
404 hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents,
405 length);
406 }
407 #endif
408 }
409 } else {
410 const ClassObject *sclass;
411 size_t sizePatchOffset, savedLen;
412
413 /* obj is an instance object.
414 */
415 hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
416 hprofAddIdToRecord(rec, (hprof_object_id)obj);
417 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
418 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
419
420 /* Reserve some space for the length of the instance
421 * data, which we won't know until we're done writing
422 * it.
423 */
424 sizePatchOffset = rec->length;
425 hprofAddU4ToRecord(rec, 0x77777777);
426
427 /* Write the instance data; fields for this
428 * class, followed by super class fields, and so on.
429 */
430 sclass = clazz;
431 while (sclass != NULL) {
432 int ifieldCount = sclass->ifieldCount;
433 for (int i = 0; i < ifieldCount; i++) {
434 const InstField *f = &sclass->ifields[i];
435 size_t size;
436
437 (void) signatureToBasicTypeAndSize(f->signature, &size);
438 if (size == 1) {
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));
450 } else {
451 assert(false);
452 }
453 }
454
455 sclass = sclass->super;
456 }
457
458 /* Patch the instance field length.
459 */
460 savedLen = rec->length;
461 rec->length = sizePatchOffset;
462 hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
463 rec->length = savedLen;
464 }
465 }
466
467 ctx->objectsInSegment++;
468
469 return 0;
470 }
471