blob: 571167ded0b9183ef053d2a47fb1a5fd9adde8c2 [file] [log] [blame]
Jesse Wilsonc4824e62011-11-01 14:39:04 -04001/*
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#include "primitive.h"
21#include "object.h"
22#include "logging.h"
23
24namespace art {
25
26namespace hprof {
27
28/* Set DUMP_PRIM_DATA to 1 if you want to include the contents
29 * of primitive arrays (byte arrays, character arrays, etc.)
30 * in heap dumps. This can be a large amount of data.
31 */
32#define DUMP_PRIM_DATA 1
33
34#define OBJECTS_PER_SEGMENT ((size_t)128)
35#define BYTES_PER_SEGMENT ((size_t)4096)
36
37/* The static field-name for the synthetic object generated to account
38 * for class Static overhead.
39 */
40#define STATIC_OVERHEAD_NAME "$staticOverhead"
41/* The ID for the synthetic object generated to account for class
42 * Static overhead.
43 */
44#define CLASS_STATICS_ID(clazz) ((hprof_object_id)(((uint32_t)(clazz)) | 1))
45
46int hprofStartHeapDump(hprof_context_t *ctx)
47{
48 ctx->objectsInSegment = OBJECTS_PER_SEGMENT;
49 ctx->currentHeap = HPROF_HEAP_DEFAULT;
50 return 0;
51}
52
53int hprofFinishHeapDump(hprof_context_t *ctx)
54{
55 return hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
56}
57
58int hprofSetGcScanState(hprof_context_t *ctx,
59 hprof_heap_tag_t state,
60 uint32_t threadSerialNumber)
61{
62 /* Used by hprofMarkRootObject()
63 */
64 ctx->gcScanState = state;
65 ctx->gcThreadSerialNumber = threadSerialNumber;
66 return 0;
67}
68
69static hprof_basic_type signatureToBasicTypeAndSize(const char *sig,
70 size_t *sizeOut)
71{
72 char c = sig[0];
73 hprof_basic_type ret;
74 size_t size;
75
76 switch (c) {
77 case '[':
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: CHECK(false);
86 case 'I': ret = hprof_basic_int; size = 4; break;
87 case 'J': ret = hprof_basic_long; size = 8; break;
88 }
89
90 if (sizeOut != NULL) {
91 *sizeOut = size;
92 }
93
94 return ret;
95}
96
97static hprof_basic_type primitiveToBasicTypeAndSize(Primitive::Type prim,
98 size_t *sizeOut)
99{
100 hprof_basic_type ret;
101 size_t size;
102
103 switch (prim) {
104 case Primitive::kPrimBoolean: ret = hprof_basic_boolean; size = 1; break;
105 case Primitive::kPrimChar: ret = hprof_basic_char; size = 2; break;
106 case Primitive::kPrimFloat: ret = hprof_basic_float; size = 4; break;
107 case Primitive::kPrimDouble: ret = hprof_basic_double; size = 8; break;
108 case Primitive::kPrimByte: ret = hprof_basic_byte; size = 1; break;
109 case Primitive::kPrimShort: ret = hprof_basic_short; size = 2; break;
110 default: CHECK(false);
111 case Primitive::kPrimInt: ret = hprof_basic_int; size = 4; break;
112 case Primitive::kPrimLong: ret = hprof_basic_long; size = 8; break;
113 }
114
115 if (sizeOut != NULL) {
116 *sizeOut = size;
117 }
118
119 return ret;
120}
121
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.
126 */
127int hprofMarkRootObject(hprof_context_t *ctx, const Object *obj, jobject jniObj)
128{
129 hprof_record_t *rec = &ctx->curRec;
130 int err;
131 hprof_heap_tag_t heapTag = (hprof_heap_tag_t)ctx->gcScanState;
132
133 if (heapTag == 0) {
134 return 0;
135 }
136
137 if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
138 rec->length >= BYTES_PER_SEGMENT)
139 {
140 /* This flushes the old segment and starts a new one.
141 */
142 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
143 ctx->objectsInSegment = 0;
144 }
145
146 switch (heapTag) {
147 /* ID: object ID
148 */
149 case HPROF_ROOT_UNKNOWN:
150 case HPROF_ROOT_STICKY_CLASS:
151 case HPROF_ROOT_MONITOR_USED:
152 case HPROF_ROOT_INTERNED_STRING:
153 case HPROF_ROOT_FINALIZING:
154 case HPROF_ROOT_DEBUGGER:
155 case HPROF_ROOT_REFERENCE_CLEANUP:
156 case HPROF_ROOT_VM_INTERNAL:
157 hprofAddU1ToRecord(rec, heapTag);
158 hprofAddIdToRecord(rec, (hprof_object_id)obj);
159 break;
160
161 /* ID: object ID
162 * ID: JNI global ref ID
163 */
164 case HPROF_ROOT_JNI_GLOBAL:
165 hprofAddU1ToRecord(rec, heapTag);
166 hprofAddIdToRecord(rec, (hprof_object_id)obj);
167 hprofAddIdToRecord(rec, (hprof_id)jniObj);
168 break;
169
170 /* ID: object ID
171 * uint32_t: thread serial number
172 * uint32_t: frame number in stack trace (-1 for empty)
173 */
174 case HPROF_ROOT_JNI_LOCAL:
175 case HPROF_ROOT_JNI_MONITOR:
176 case HPROF_ROOT_JAVA_FRAME:
177 hprofAddU1ToRecord(rec, heapTag);
178 hprofAddIdToRecord(rec, (hprof_object_id)obj);
179 hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
180 hprofAddU4ToRecord(rec, (uint32_t)-1);
181 break;
182
183 /* ID: object ID
184 * uint32_t: thread serial number
185 */
186 case HPROF_ROOT_NATIVE_STACK:
187 case HPROF_ROOT_THREAD_BLOCK:
188 hprofAddU1ToRecord(rec, heapTag);
189 hprofAddIdToRecord(rec, (hprof_object_id)obj);
190 hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
191 break;
192
193 /* ID: thread object ID
194 * uint32_t: thread serial number
195 * uint32_t: stack trace serial number
196 */
197 case HPROF_ROOT_THREAD_OBJECT:
198 hprofAddU1ToRecord(rec, heapTag);
199 hprofAddIdToRecord(rec, (hprof_object_id)obj);
200 hprofAddU4ToRecord(rec, ctx->gcThreadSerialNumber);
201 hprofAddU4ToRecord(rec, (uint32_t)-1); //xxx
202 break;
203
204 default:
205 err = 0;
206 break;
207 }
208
209 ctx->objectsInSegment++;
210
211 return err;
212}
213
214static int stackTraceSerialNumber(const void *obj)
215{
216 return HPROF_NULL_STACK_TRACE;
217}
218
219int hprofDumpHeapObject(hprof_context_t *ctx, const Object *obj)
220{
221 Class* clazz;
222 hprof_record_t *rec = &ctx->curRec;
223 HprofHeapId desiredHeap;
224
225 desiredHeap = false ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP; // TODO: zygote objects?
226
227 if (ctx->objectsInSegment >= OBJECTS_PER_SEGMENT ||
228 rec->length >= BYTES_PER_SEGMENT)
229 {
230 /* This flushes the old segment and starts a new one.
231 */
232 hprofStartNewRecord(ctx, HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
233 ctx->objectsInSegment = 0;
234
235 /* Starting a new HEAP_DUMP resets the heap to default.
236 */
237 ctx->currentHeap = HPROF_HEAP_DEFAULT;
238 }
239
240 if (desiredHeap != ctx->currentHeap) {
241 hprof_string_id nameId;
242
243 /* This object is in a different heap than the current one.
244 * Emit a HEAP_DUMP_INFO tag to change heaps.
245 */
246 hprofAddU1ToRecord(rec, HPROF_HEAP_DUMP_INFO);
247 hprofAddU4ToRecord(rec, (uint32_t)desiredHeap); // uint32_t: heap id
248 switch (desiredHeap) {
249 case HPROF_HEAP_APP:
250 nameId = hprofLookupStringId("app");
251 break;
252 case HPROF_HEAP_ZYGOTE:
253 nameId = hprofLookupStringId("zygote");
254 break;
255 default:
256 /* Internal error. */
257 LOG(ERROR) << "Unexpected desiredHeap";
258 nameId = hprofLookupStringId("<ILLEGAL>");
259 break;
260 }
261 hprofAddIdToRecord(rec, nameId);
262 ctx->currentHeap = desiredHeap;
263 }
264
265 clazz = obj->GetClass();
266
267 if (clazz == NULL) {
268 /* This object will bother HprofReader, because it has a NULL
269 * class, so just don't dump it. It could be
270 * gDvm.unlinkedJavaLangClass or it could be an object just
271 * allocated which hasn't been initialized yet.
272 */
273 } else {
274 if (obj->IsClass()) {
275 Class* thisClass = (Class*)obj;
276 /* obj is a ClassObject.
277 */
278 size_t sFieldCount = thisClass->NumStaticFields();
279 if (sFieldCount != 0) {
280 int byteLength = sFieldCount*sizeof(JValue); // TODO bogus; fields are packed
281 /* Create a byte array to reflect the allocation of the
282 * StaticField array at the end of this class.
283 */
284 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
285 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
286 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
287 hprofAddU4ToRecord(rec, byteLength);
288 hprofAddU1ToRecord(rec, hprof_basic_byte);
289 for (int i = 0; i < byteLength; i++) {
290 hprofAddU1ToRecord(rec, 0);
291 }
292 }
293
294 hprofAddU1ToRecord(rec, HPROF_CLASS_DUMP);
295 hprofAddIdToRecord(rec, hprofLookupClassId(thisClass));
296 hprofAddU4ToRecord(rec, stackTraceSerialNumber(thisClass));
297 hprofAddIdToRecord(rec, hprofLookupClassId(thisClass->GetSuperClass()));
298 hprofAddIdToRecord(rec, (hprof_object_id)thisClass->GetClassLoader());
299 hprofAddIdToRecord(rec, (hprof_object_id)0); // no signer
300 hprofAddIdToRecord(rec, (hprof_object_id)0); // no prot domain
301 hprofAddIdToRecord(rec, (hprof_id)0); // reserved
302 hprofAddIdToRecord(rec, (hprof_id)0); // reserved
303 if (obj->IsClassClass()) {
304 // ClassObjects have their static fields appended, so
305 // aren't all the same size. But they're at least this
306 // size.
307 hprofAddU4ToRecord(rec, sizeof(Class)); // instance size
Jesse Wilson1121e0b2011-11-07 15:37:42 -0500308 } else if (thisClass->IsArrayClass() || thisClass->IsPrimitive()) {
309 hprofAddU4ToRecord(rec, 0);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400310 } else {
311 hprofAddU4ToRecord(rec, thisClass->GetObjectSize()); // instance size
312 }
313
314 hprofAddU2ToRecord(rec, 0); // empty const pool
315
316 /* Static fields
317 */
318 if (sFieldCount == 0) {
319 hprofAddU2ToRecord(rec, (uint16_t)0);
320 } else {
321 hprofAddU2ToRecord(rec, (uint16_t)(sFieldCount+1));
322 hprofAddIdToRecord(rec,
323 hprofLookupStringId(STATIC_OVERHEAD_NAME));
324 hprofAddU1ToRecord(rec, hprof_basic_object);
325 hprofAddIdToRecord(rec, CLASS_STATICS_ID(obj));
326
327 for (size_t i = 0; i < sFieldCount; ++i) {
328 hprof_basic_type t;
329 size_t size;
330 Field* f = thisClass->GetStaticField(i);
331
332 t = signatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
333 hprofAddIdToRecord(rec, hprofLookupStringId(f->GetName()));
334 hprofAddU1ToRecord(rec, t);
335 if (size == 1) {
336 hprofAddU1ToRecord(rec, (uint8_t)f->GetByte(NULL));
337 } else if (size == 2) {
338 hprofAddU2ToRecord(rec, (uint16_t)f->GetShort(NULL));
339 } else if (size == 4) {
340 hprofAddU4ToRecord(rec, (uint32_t)f->GetInt(NULL));
341 } else if (size == 8) {
342 hprofAddU8ToRecord(rec, (uint64_t)f->GetLong(NULL));
343 } else {
344 CHECK(false);
345 }
346 }
347 }
348
349 /* Instance fields for this class (no superclass fields)
350 */
Jesse Wilson1121e0b2011-11-07 15:37:42 -0500351 int iFieldCount = thisClass->IsObjectClass() ? 0 : thisClass->NumInstanceFields();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400352 hprofAddU2ToRecord(rec, (uint16_t)iFieldCount);
353 for (int i = 0; i < iFieldCount; ++i) {
354 Field* f = thisClass->GetInstanceField(i);
355 hprof_basic_type t;
356
357 t = signatureToBasicTypeAndSize(f->GetTypeDescriptor(), NULL);
358 hprofAddIdToRecord(rec, hprofLookupStringId(f->GetName()));
359 hprofAddU1ToRecord(rec, t);
360 }
361 } else if (clazz->IsArrayClass()) {
362 Array *aobj = (Array *)obj;
363 uint32_t length = aobj->GetLength();
364
365 if (obj->IsObjectArray()) {
366 /* obj is an object array.
367 */
368 hprofAddU1ToRecord(rec, HPROF_OBJECT_ARRAY_DUMP);
369
370 hprofAddIdToRecord(rec, (hprof_object_id)obj);
371 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
372 hprofAddU4ToRecord(rec, length);
373 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
374
375 /* Dump the elements, which are always objects or NULL.
376 */
377 hprofAddIdListToRecord(rec,
378 (const hprof_object_id *)aobj->GetRawData(), length);
379 } else {
380 hprof_basic_type t;
381 size_t size;
382
383 t = primitiveToBasicTypeAndSize(clazz->GetComponentType()->
384 GetPrimitiveType(), &size);
385
386 /* obj is a primitive array.
387 */
388#if DUMP_PRIM_DATA
389 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_DUMP);
390#else
391 hprofAddU1ToRecord(rec, HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
392#endif
393
394 hprofAddIdToRecord(rec, (hprof_object_id)obj);
395 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
396 hprofAddU4ToRecord(rec, length);
397 hprofAddU1ToRecord(rec, t);
398
399#if DUMP_PRIM_DATA
400 /* Dump the raw, packed element values.
401 */
402 if (size == 1) {
403 hprofAddU1ListToRecord(rec, (const uint8_t *)aobj->GetRawData(),
404 length);
405 } else if (size == 2) {
406 hprofAddU2ListToRecord(rec, (const uint16_t *)(void *)aobj->GetRawData(),
407 length);
408 } else if (size == 4) {
409 hprofAddU4ListToRecord(rec, (const uint32_t *)(void *)aobj->GetRawData(),
410 length);
411 } else if (size == 8) {
412 hprofAddU8ListToRecord(rec, (const uint64_t *)aobj->GetRawData(),
413 length);
414 }
415#endif
416 }
417 } else {
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400418
419 /* obj is an instance object.
420 */
421 hprofAddU1ToRecord(rec, HPROF_INSTANCE_DUMP);
422 hprofAddIdToRecord(rec, (hprof_object_id)obj);
423 hprofAddU4ToRecord(rec, stackTraceSerialNumber(obj));
424 hprofAddIdToRecord(rec, hprofLookupClassId(clazz));
425
426 /* Reserve some space for the length of the instance
427 * data, which we won't know until we're done writing
428 * it.
429 */
Jesse Wilson1121e0b2011-11-07 15:37:42 -0500430 size_t sizePatchOffset = rec->length;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400431 hprofAddU4ToRecord(rec, 0x77777777);
432
433 /* Write the instance data; fields for this
434 * class, followed by super class fields, and so on.
Jesse Wilson1121e0b2011-11-07 15:37:42 -0500435 * Don't write the klass or monitor fields of Object.class.
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400436 */
Jesse Wilson1121e0b2011-11-07 15:37:42 -0500437 const Class* sclass = clazz;
438 while (!sclass->IsObjectClass()) {
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400439 int ifieldCount = sclass->NumInstanceFields();
440 for (int i = 0; i < ifieldCount; i++) {
441 Field* f = sclass->GetInstanceField(i);
442 size_t size;
443
444 (void) signatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size);
445 if (size == 1) {
446 hprofAddU1ToRecord(rec, f->GetByte(obj));
447 } else if (size == 2) {
448 hprofAddU2ToRecord(rec, f->GetChar(obj));
449 } else if (size == 4) {
450 hprofAddU4ToRecord(rec, f->GetInt(obj));
451 } else if (size == 8) {
452 hprofAddU8ToRecord(rec, f->GetLong(obj));
453 } else {
454 CHECK(false);
455 }
456 }
457
458 sclass = sclass->GetSuperClass();
459 }
460
461 /* Patch the instance field length.
462 */
Jesse Wilson1121e0b2011-11-07 15:37:42 -0500463 size_t savedLen = rec->length;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400464 rec->length = sizePatchOffset;
465 hprofAddU4ToRecord(rec, savedLen - (sizePatchOffset + 4));
466 rec->length = savedLen;
467 }
468 }
469
470 ctx->objectsInSegment++;
471
472 return 0;
473}
474
475} // namespace hprof
476
477} // namespace art