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 void hprofMarkRootObject(hprof_context_t *ctx, const Object *obj,
123 jobject jniObj)
124 {
125 hprof_record_t *rec = &ctx->curRec;
126 hprof_heap_tag_t heapTag = (hprof_heap_tag_t)ctx->gcScanState;
127
128 if (heapTag == 0) {
129 return;
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 break;
201 }
202
203 ctx->objectsInSegment++;
204 }
205
stackTraceSerialNumber(const void * obj)206 static int stackTraceSerialNumber(const void *obj)
207 {
208 return HPROF_NULL_STACK_TRACE;
209 }
210
hprofDumpHeapObject(hprof_context_t * ctx,const Object * obj)211 int hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
212 {
213 const ClassObject *clazz;
214 hprof_record_t *rec = &ctx->curRec;
215 HprofHeapId desiredHeap;
216
217 desiredHeap = dvmIsZygoteObject(obj) ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP;
218
219 if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
220 rec->length >= BYTES_PER_SEGMENT)
221 {
222 /* This flushes the old segment and starts a new one.
223 */
224 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
225 ctx->objectsInSegment = 0;
226
227 /* Starting a new HEAP_DUMP resets the heap to default.
228 */
229 ctx->currentHeap = HPROF_HEAP_DEFAULT;
230 }
231
232 if (desiredHeap != ctx->currentHeap) {
233 hprof_string_id nameId;
234
235 /* This object is in a different heap than the current one.
236 * Emit a HEAP_DUMP_INFO tag to change heaps.
237 */
238 hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
239 hprofAddU4ToRecord(rec, (u4)desiredHeap); // u4: heap id
240 switch (desiredHeap) {
241 case HPROF_HEAP_APP:
242 nameId = hprofLookupStringId("app");
243 break;
244 case HPROF_HEAP_ZYGOTE:
245 nameId = hprofLookupStringId("zygote");
246 break;
247 default:
248 /* Internal error. */
249 assert(!"Unexpected desiredHeap");
250 nameId = hprofLookupStringId("<ILLEGAL>");
251 break;
252 }
253 hprofAddIdToRecord(rec, nameId);
254 ctx->currentHeap = desiredHeap;
255 }
256
257 clazz = obj->clazz;
258
259 if (clazz == NULL) {
260 /* This object will bother HprofReader, because it has a NULL
261 * class, so just don't dump it. It could be
262 * gDvm.unlinkedJavaLangClass or it could be an object just
263 * allocated which hasn't been initialized yet.
264 */
265 } else {
266 if (dvmIsClassObject(obj)) {
267 const ClassObject *thisClass = (const ClassObject *)obj;
268 /* obj is a ClassObject.
269 */
270 int sFieldCount = thisClass->sfieldCount;
271 if (sFieldCount != 0) {
272 int byteLength = sFieldCount*sizeof(StaticField);
273 /* Create a byte array to reflect the allocation of the
274 * StaticField array at the end of this class.
275 */
276 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
277 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
278 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
279 hprofAddU4ToRecord(rec, byteLength);
280 hprofAddU1ToRecord(rec, hprof_basic_byte);
281 for (int i = 0; i < byteLength; i++) {
282 hprofAddU1ToRecord(rec, 0);
283 }
284 }
285
286 hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
287 hprofAddIdToRecord(rec, hprofLookupClassId(thisClass));
288 hprofAddU4ToRecord(rec, stackTraceSerialNumber(thisClass));
289 hprofAddIdToRecord(rec, hprofLookupClassId(thisClass->super));
290 hprofAddIdToRecord(rec, (hprof_object_id)thisClass->classLoader);
291 hprofAddIdToRecord(rec, (hprof_object_id)0); // no signer
292 hprofAddIdToRecord(rec, (hprof_object_id)0); // no prot domain
293 hprofAddIdToRecord(rec, (hprof_id)0); // reserved
294 hprofAddIdToRecord(rec, (hprof_id)0); // reserved
295 if (obj == (Object *)gDvm.classJavaLangClass) {
296 // ClassObjects have their static fields appended, so
297 // aren't all the same size. But they're at least this
298 // size.
299 hprofAddU4ToRecord(rec, sizeof(ClassObject)); // instance size
300 } else {
301 hprofAddU4ToRecord(rec, thisClass->objectSize); // instance size
302 }
303
304 hprofAddU2ToRecord(rec, 0); // empty const pool
305
306 /* Static fields
307 */
308 if (sFieldCount == 0) {
309 hprofAddU2ToRecord(rec, (u2)0);
310 } else {
311 hprofAddU2ToRecord(rec, (u2)(sFieldCount+1));
312 hprofAddIdToRecord(rec,
313 hprofLookupStringId(STATIC_OVERHEAD_NAME));
314 hprofAddU1ToRecord(rec, hprof_basic_object);
315 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
316 for (int i = 0; i < sFieldCount; i++) {
317 hprof_basic_type t;
318 size_t size;
319 const StaticField *f = &thisClass->sfields[i];
320
321 t = signatureToBasicTypeAndSize(f->signature, &size);
322 hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
323 hprofAddU1ToRecord(rec, t);
324 if (size == 1) {
325 hprofAddU1ToRecord(rec, (u1)f->value.b);
326 } else if (size == 2) {
327 hprofAddU2ToRecord(rec, (u2)f->value.c);
328 } else if (size == 4) {
329 hprofAddU4ToRecord(rec, (u4)f->value.i);
330 } else if (size == 8) {
331 hprofAddU8ToRecord(rec, (u8)f->value.j);
332 } else {
333 assert(false);
334 }
335 }
336 }
337
338 /* Instance fields for this class (no superclass fields)
339 */
340 int iFieldCount = thisClass->ifieldCount;
341 hprofAddU2ToRecord(rec, (u2)iFieldCount);
342 for (int i = 0; i < iFieldCount; i++) {
343 const InstField *f = &thisClass->ifields[i];
344 hprof_basic_type t;
345
346 t = signatureToBasicTypeAndSize(f->signature, NULL);
347 hprofAddIdToRecord(rec, hprofLookupStringId(f->name));
348 hprofAddU1ToRecord(rec, t);
349 }
350 } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
351 const ArrayObject *aobj = (const ArrayObject *)obj;
352 u4 length = aobj->length;
353
354 if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
355 /* obj is an object array.
356 */
357 hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
358
359 hprofAddIdToRecord(rec, (hprof_object_id)obj);
360 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
361 hprofAddU4ToRecord(rec, length);
362 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
363
364 /* Dump the elements, which are always objects or NULL.
365 */
366 hprofAddIdListToRecord(rec,
367 (const hprof_object_id *)(void *)aobj->contents, length);
368 } else {
369 hprof_basic_type t;
370 size_t size;
371
372 t = primitiveToBasicTypeAndSize(clazz->elementClass->
373 primitiveType, &size);
374
375 /* obj is a primitive array.
376 */
377 #if DUMP_PRIM_DATA
378 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
379 #else
380 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
381 #endif
382
383 hprofAddIdToRecord(rec, (hprof_object_id)obj);
384 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
385 hprofAddU4ToRecord(rec, length);
386 hprofAddU1ToRecord(rec, t);
387
388 #if DUMP_PRIM_DATA
389 /* Dump the raw, packed element values.
390 */
391 if (size == 1) {
392 hprofAddU1ListToRecord(rec, (const u1 *)aobj->contents,
393 length);
394 } else if (size == 2) {
395 hprofAddU2ListToRecord(rec, (const u2 *)(void *)aobj->contents,
396 length);
397 } else if (size == 4) {
398 hprofAddU4ListToRecord(rec, (const u4 *)(void *)aobj->contents,
399 length);
400 } else if (size == 8) {
401 hprofAddU8ListToRecord(rec, (const u8 *)aobj->contents,
402 length);
403 }
404 #endif
405 }
406 } else {
407 const ClassObject *sclass;
408 size_t sizePatchOffset, savedLen;
409
410 /* obj is an instance object.
411 */
412 hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
413 hprofAddIdToRecord(rec, (hprof_object_id)obj);
414 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
415 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
416
417 /* Reserve some space for the length of the instance
418 * data, which we won't know until we're done writing
419 * it.
420 */
421 sizePatchOffset = rec->length;
422 hprofAddU4ToRecord(rec, 0x77777777);
423
424 /* Write the instance data; fields for this
425 * class, followed by super class fields, and so on.
426 */
427 sclass = clazz;
428 while (sclass != NULL) {
429 int ifieldCount = sclass->ifieldCount;
430 for (int i = 0; i < ifieldCount; i++) {
431 const InstField *f = &sclass->ifields[i];
432 size_t size;
433
434 (void) signatureToBasicTypeAndSize(f->signature, &size);
435 if (size == 1) {
436 hprofAddU1ToRecord(rec,
437 (u1)dvmGetFieldByte(obj, f->byteOffset));
438 } else if (size == 2) {
439 hprofAddU2ToRecord(rec,
440 (u2)dvmGetFieldChar(obj, f->byteOffset));
441 } else if (size == 4) {
442 hprofAddU4ToRecord(rec,
443 (u4)dvmGetFieldInt(obj, f->byteOffset));
444 } else if (size == 8) {
445 hprofAddU8ToRecord(rec,
446 (u8)dvmGetFieldLong(obj, f->byteOffset));
447 } else {
448 assert(false);
449 }
450 }
451
452 sclass = sclass->super;
453 }
454
455 /* Patch the instance field length.
456 */
457 savedLen = rec->length;
458 rec->length = sizePatchOffset;
459 hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
460 rec->length = savedLen;
461 }
462 }
463
464 ctx->objectsInSegment++;
465
466 return 0;
467 }
468