blob: d2134e0148955ced71bf480b220259f608b4107c [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/*
18 * Preparation and completion of hprof data generation. The output is
19 * written into two files and then combined. This is necessary because
20 * we generate some of the data (strings and classes) while we dump the
21 * heap, and some analysis tools require that the class and string data
22 * appear first.
23 */
24
25#include "hprof.h"
Ian Rogers6d4d9fc2011-11-30 16:24:48 -080026
27#include "class_linker.h"
Jesse Wilsonc4824e62011-11-01 14:39:04 -040028#include "debugger.h"
Ian Rogers6d4d9fc2011-11-30 16:24:48 -080029#include "heap.h"
30#include "logging.h"
Jesse Wilson0c54ac12011-11-09 15:14:05 -050031#include "object.h"
Ian Rogers6d4d9fc2011-11-30 16:24:48 -080032#include "object_utils.h"
Jesse Wilsonc4824e62011-11-01 14:39:04 -040033#include "stringprintf.h"
Jesse Wilson0c54ac12011-11-09 15:14:05 -050034#include "unordered_map.h"
35#include "unordered_set.h"
Jesse Wilsonc4824e62011-11-01 14:39:04 -040036
Jesse Wilson0c54ac12011-11-09 15:14:05 -050037#include <cutils/open_memstream.h>
Jesse Wilsonc4824e62011-11-01 14:39:04 -040038#include <sys/uio.h>
39#include <string.h>
40#include <unistd.h>
41#include <fcntl.h>
42#include <errno.h>
43#include <sys/time.h>
44#include <time.h>
45
46namespace art {
47
48namespace hprof {
49
Jesse Wilson0c54ac12011-11-09 15:14:05 -050050#define HPROF_MAGIC_STRING "JAVA PROFILE 1.0.3"
51
52/*
53 * Initialize an Hprof.
54 */
Elliott Hughesc1f143d2011-12-01 17:31:10 -080055Hprof::Hprof(const char* outputFileName, int fd, bool writeHeader, bool directToDdms)
Jesse Wilson0c54ac12011-11-09 15:14:05 -050056 : current_record_(),
57 gc_thread_serial_number_(0),
58 gc_scan_state_(0),
59 current_heap_(HPROF_HEAP_DEFAULT),
60 objects_in_segment_(0),
61 direct_to_ddms_(0),
62 file_name_(NULL),
63 file_data_ptr_(NULL),
64 file_data_size_(0),
65 mem_fp_(NULL),
66 fd_(0),
67 next_string_id_(0x400000) {
68 // Have to do this here, because it must happen after we
69 // memset the struct (want to treat file_data_ptr_/file_data_size_
70 // as read-only while the file is open).
71 FILE *fp = open_memstream(&file_data_ptr_, &file_data_size_);
72 if (fp == NULL) {
73 // not expected
74 LOG(ERROR) << StringPrintf("hprof: open_memstream failed: %s", strerror(errno));
75 CHECK(false);
76 }
77
78 direct_to_ddms_ = directToDdms;
79 file_name_ = strdup(outputFileName);
80 mem_fp_ = fp;
81 fd_ = fd;
82
83 current_record_.alloc_length_ = 128;
Elliott Hughesc1f143d2011-12-01 17:31:10 -080084 current_record_.body_ = (unsigned char*)malloc(current_record_.alloc_length_);
Jesse Wilson0c54ac12011-11-09 15:14:05 -050085 // TODO check for/return an error
86
87 if (writeHeader) {
88 char magic[] = HPROF_MAGIC_STRING;
89 unsigned char buf[4];
90
91 // Write the file header.
92 // U1: NUL-terminated magic string.
93 fwrite(magic, 1, sizeof(magic), fp);
94
95 // U4: size of identifiers. We're using addresses as IDs, so make sure a pointer fits.
96 U4_TO_BUF_BE(buf, 0, sizeof(void *));
97 fwrite(buf, 1, sizeof(uint32_t), fp);
98
99 // The current time, in milliseconds since 0:00 GMT, 1/1/70.
100 struct timeval now;
101 uint64_t nowMs;
102 if (gettimeofday(&now, NULL) < 0) {
103 nowMs = 0;
104 } else {
105 nowMs = (uint64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
106 }
107
108 // U4: high word of the 64-bit time.
109 U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs >> 32));
110 fwrite(buf, 1, sizeof(uint32_t), fp);
111
112 // U4: low word of the 64-bit time.
113 U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs & 0xffffffffULL));
114 fwrite(buf, 1, sizeof(uint32_t), fp); //xxx fix the time
115 }
116}
117
118int Hprof::StartNewRecord(uint8_t tag, uint32_t time) {
119 HprofRecord *rec = &current_record_;
120
121 int err = rec->Flush(mem_fp_);
122 if (err != 0) {
123 return err;
124 } else if (rec->dirty_) {
125 return UNIQUE_ERROR();
126 }
127
128 rec->dirty_ = true;
129 rec->tag_ = tag;
130 rec->time_ = time;
131 rec->length_ = 0;
132 return 0;
133}
134
135int Hprof::FlushCurrentRecord() {
136 return current_record_.Flush(mem_fp_);
137}
138
139// Set DUMP_PRIM_DATA to 1 if you want to include the contents
140// of primitive arrays (byte arrays, character arrays, etc.)
141// in heap dumps. This can be a large amount of data.
142#define DUMP_PRIM_DATA 1
143
144#define OBJECTS_PER_SEGMENT ((size_t)128)
145#define BYTES_PER_SEGMENT ((size_t)4096)
146
147// The static field-name for the synthetic object generated to account
148// for class static overhead.
149#define STATIC_OVERHEAD_NAME "$staticOverhead"
150// The ID for the synthetic object generated to account for class static overhead.
151#define CLASS_STATICS_ID(clazz) ((HprofObjectId)(((uint32_t)(clazz)) | 1))
152
Elliott Hughesc1f143d2011-12-01 17:31:10 -0800153HprofBasicType Hprof::SignatureToBasicTypeAndSize(const char* sig, size_t* sizeOut) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500154 char c = sig[0];
155 HprofBasicType ret;
156 size_t size;
157
158 switch (c) {
159 case '[':
160 case 'L': ret = hprof_basic_object; size = 4; break;
161 case 'Z': ret = hprof_basic_boolean; size = 1; break;
162 case 'C': ret = hprof_basic_char; size = 2; break;
163 case 'F': ret = hprof_basic_float; size = 4; break;
164 case 'D': ret = hprof_basic_double; size = 8; break;
165 case 'B': ret = hprof_basic_byte; size = 1; break;
166 case 'S': ret = hprof_basic_short; size = 2; break;
167 default: CHECK(false);
168 case 'I': ret = hprof_basic_int; size = 4; break;
169 case 'J': ret = hprof_basic_long; size = 8; break;
170 }
171
172 if (sizeOut != NULL) {
173 *sizeOut = size;
174 }
175
176 return ret;
177}
178
179HprofBasicType Hprof::PrimitiveToBasicTypeAndSize(Primitive::Type prim, size_t *sizeOut) {
180 HprofBasicType ret;
181 size_t size;
182
183 switch (prim) {
184 case Primitive::kPrimBoolean: ret = hprof_basic_boolean; size = 1; break;
185 case Primitive::kPrimChar: ret = hprof_basic_char; size = 2; break;
186 case Primitive::kPrimFloat: ret = hprof_basic_float; size = 4; break;
187 case Primitive::kPrimDouble: ret = hprof_basic_double; size = 8; break;
188 case Primitive::kPrimByte: ret = hprof_basic_byte; size = 1; break;
189 case Primitive::kPrimShort: ret = hprof_basic_short; size = 2; break;
190 default: CHECK(false);
191 case Primitive::kPrimInt: ret = hprof_basic_int; size = 4; break;
192 case Primitive::kPrimLong: ret = hprof_basic_long; size = 8; break;
193 }
194
195 if (sizeOut != NULL) {
196 *sizeOut = size;
197 }
198
199 return ret;
200}
201
202// Always called when marking objects, but only does
203// something when ctx->gc_scan_state_ is non-zero, which is usually
204// only true when marking the root set or unreachable
205// objects. Used to add rootset references to obj.
206int Hprof::MarkRootObject(const Object *obj, jobject jniObj) {
207 HprofRecord *rec = &current_record_;
208 int err; // TODO: we may return this uninitialized
209 HprofHeapTag heapTag = (HprofHeapTag)gc_scan_state_;
210
211 if (heapTag == 0) {
212 return 0;
213 }
214
215 if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->length_ >= BYTES_PER_SEGMENT) {
216 // This flushes the old segment and starts a new one.
217 StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
218 objects_in_segment_ = 0;
219 }
220
221 switch (heapTag) {
222 // ID: object ID
223 case HPROF_ROOT_UNKNOWN:
224 case HPROF_ROOT_STICKY_CLASS:
225 case HPROF_ROOT_MONITOR_USED:
226 case HPROF_ROOT_INTERNED_STRING:
227 case HPROF_ROOT_FINALIZING:
228 case HPROF_ROOT_DEBUGGER:
229 case HPROF_ROOT_REFERENCE_CLEANUP:
230 case HPROF_ROOT_VM_INTERNAL:
231 rec->AddU1(heapTag);
232 rec->AddId((HprofObjectId)obj);
233 break;
234
235 // ID: object ID
236 // ID: JNI global ref ID
237 case HPROF_ROOT_JNI_GLOBAL:
238 rec->AddU1(heapTag);
239 rec->AddId((HprofObjectId)obj);
240 rec->AddId((HprofId)jniObj);
241 break;
242
243 // ID: object ID
244 // U4: thread serial number
245 // U4: frame number in stack trace (-1 for empty)
246 case HPROF_ROOT_JNI_LOCAL:
247 case HPROF_ROOT_JNI_MONITOR:
248 case HPROF_ROOT_JAVA_FRAME:
249 rec->AddU1(heapTag);
250 rec->AddId((HprofObjectId)obj);
251 rec->AddU4(gc_thread_serial_number_);
252 rec->AddU4((uint32_t)-1);
253 break;
254
255 // ID: object ID
256 // U4: thread serial number
257 case HPROF_ROOT_NATIVE_STACK:
258 case HPROF_ROOT_THREAD_BLOCK:
259 rec->AddU1(heapTag);
260 rec->AddId((HprofObjectId)obj);
261 rec->AddU4(gc_thread_serial_number_);
262 break;
263
264 // ID: thread object ID
265 // U4: thread serial number
266 // U4: stack trace serial number
267 case HPROF_ROOT_THREAD_OBJECT:
268 rec->AddU1(heapTag);
269 rec->AddId((HprofObjectId)obj);
270 rec->AddU4(gc_thread_serial_number_);
271 rec->AddU4((uint32_t)-1); //xxx
272 break;
273
274 default:
275 err = 0;
276 break;
277 }
278
279 objects_in_segment_++;
280 return err;
281}
282
283int Hprof::StackTraceSerialNumber(const void *obj) {
284 return HPROF_NULL_STACK_TRACE;
285}
286
287int Hprof::DumpHeapObject(const Object* obj) {
288 HprofRecord *rec = &current_record_;
289 HprofHeapId desiredHeap = false ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP; // TODO: zygote objects?
290
291 if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->length_ >= BYTES_PER_SEGMENT) {
292 // This flushes the old segment and starts a new one.
293 StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
294 objects_in_segment_ = 0;
295
296 // Starting a new HEAP_DUMP resets the heap to default.
297 current_heap_ = HPROF_HEAP_DEFAULT;
298 }
299
300 if (desiredHeap != current_heap_) {
301 HprofStringId nameId;
302
303 // This object is in a different heap than the current one.
304 // Emit a HEAP_DUMP_INFO tag to change heaps.
305 rec->AddU1(HPROF_HEAP_DUMP_INFO);
306 rec->AddU4((uint32_t)desiredHeap); // uint32_t: heap id
307 switch (desiredHeap) {
308 case HPROF_HEAP_APP:
309 nameId = LookupStringId("app");
310 break;
311 case HPROF_HEAP_ZYGOTE:
312 nameId = LookupStringId("zygote");
313 break;
314 default:
315 // Internal error
316 LOG(ERROR) << "Unexpected desiredHeap";
317 nameId = LookupStringId("<ILLEGAL>");
318 break;
319 }
320 rec->AddId(nameId);
321 current_heap_ = desiredHeap;
322 }
323
324 Class* clazz = obj->GetClass();
325 if (clazz == NULL) {
326 // This object will bother HprofReader, because it has a NULL
327 // class, so just don't dump it. It could be
328 // gDvm.unlinkedJavaLangClass or it could be an object just
329 // allocated which hasn't been initialized yet.
330 } else {
331 if (obj->IsClass()) {
332 Class* thisClass = (Class*)obj;
333 // obj is a ClassObject.
334 size_t sFieldCount = thisClass->NumStaticFields();
335 if (sFieldCount != 0) {
336 int byteLength = sFieldCount*sizeof(JValue); // TODO bogus; fields are packed
337 // Create a byte array to reflect the allocation of the
338 // StaticField array at the end of this class.
339 rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
340 rec->AddId(CLASS_STATICS_ID(obj));
341 rec->AddU4(StackTraceSerialNumber(obj));
342 rec->AddU4(byteLength);
343 rec->AddU1(hprof_basic_byte);
344 for (int i = 0; i < byteLength; i++) {
345 rec->AddU1(0);
346 }
347 }
348
349 rec->AddU1(HPROF_CLASS_DUMP);
350 rec->AddId(LookupClassId(thisClass));
351 rec->AddU4(StackTraceSerialNumber(thisClass));
352 rec->AddId(LookupClassId(thisClass->GetSuperClass()));
353 rec->AddId((HprofObjectId)thisClass->GetClassLoader());
354 rec->AddId((HprofObjectId)0); // no signer
355 rec->AddId((HprofObjectId)0); // no prot domain
356 rec->AddId((HprofId)0); // reserved
357 rec->AddId((HprofId)0); // reserved
Elliott Hughesdbb40792011-11-18 17:05:22 -0800358 if (thisClass->IsClassClass()) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500359 // ClassObjects have their static fields appended, so aren't all the same size.
360 // But they're at least this size.
361 rec->AddU4(sizeof(Class)); // instance size
362 } else if (thisClass->IsArrayClass() || thisClass->IsPrimitive()) {
363 rec->AddU4(0);
364 } else {
365 rec->AddU4(thisClass->GetObjectSize()); // instance size
366 }
367
368 rec->AddU2(0); // empty const pool
369
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800370 FieldHelper fh;
371
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500372 // Static fields
373 if (sFieldCount == 0) {
374 rec->AddU2((uint16_t)0);
375 } else {
376 rec->AddU2((uint16_t)(sFieldCount+1));
377 rec->AddId(LookupStringId(STATIC_OVERHEAD_NAME));
378 rec->AddU1(hprof_basic_object);
379 rec->AddId(CLASS_STATICS_ID(obj));
380
381 for (size_t i = 0; i < sFieldCount; ++i) {
382 Field* f = thisClass->GetStaticField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800383 fh.ChangeField(f);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500384
385 size_t size;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800386 HprofBasicType t = SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), &size);
387 rec->AddId(LookupStringId(fh.GetName()));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500388 rec->AddU1(t);
389 if (size == 1) {
390 rec->AddU1(static_cast<uint8_t>(f->Get32(NULL)));
391 } else if (size == 2) {
392 rec->AddU2(static_cast<uint16_t>(f->Get32(NULL)));
393 } else if (size == 4) {
394 rec->AddU4(f->Get32(NULL));
395 } else if (size == 8) {
396 rec->AddU8(f->Get64(NULL));
397 } else {
398 CHECK(false);
399 }
400 }
401 }
402
403 // Instance fields for this class (no superclass fields)
404 int iFieldCount = thisClass->IsObjectClass() ? 0 : thisClass->NumInstanceFields();
405 rec->AddU2((uint16_t)iFieldCount);
406 for (int i = 0; i < iFieldCount; ++i) {
407 Field* f = thisClass->GetInstanceField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800408 fh.ChangeField(f);
409 HprofBasicType t = SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), NULL);
410 rec->AddId(LookupStringId(fh.GetName()));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500411 rec->AddU1(t);
412 }
413 } else if (clazz->IsArrayClass()) {
414 Array *aobj = (Array *)obj;
415 uint32_t length = aobj->GetLength();
416
417 if (obj->IsObjectArray()) {
418 // obj is an object array.
419 rec->AddU1(HPROF_OBJECT_ARRAY_DUMP);
420
421 rec->AddId((HprofObjectId)obj);
422 rec->AddU4(StackTraceSerialNumber(obj));
423 rec->AddU4(length);
424 rec->AddId(LookupClassId(clazz));
425
426 // Dump the elements, which are always objects or NULL.
427 rec->AddIdList((const HprofObjectId *)aobj->GetRawData(), length);
428 } else {
429 size_t size;
430 HprofBasicType t = PrimitiveToBasicTypeAndSize(clazz->GetComponentType()->GetPrimitiveType(), &size);
431
432 // obj is a primitive array.
433#if DUMP_PRIM_DATA
434 rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
435#else
436 rec->AddU1(HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
437#endif
438
439 rec->AddId((HprofObjectId)obj);
440 rec->AddU4(StackTraceSerialNumber(obj));
441 rec->AddU4(length);
442 rec->AddU1(t);
443
444#if DUMP_PRIM_DATA
445 // Dump the raw, packed element values.
446 if (size == 1) {
447 rec->AddU1List((const uint8_t *)aobj->GetRawData(), length);
448 } else if (size == 2) {
449 rec->AddU2List((const uint16_t *)(void *)aobj->GetRawData(), length);
450 } else if (size == 4) {
451 rec->AddU4List((const uint32_t *)(void *)aobj->GetRawData(), length);
452 } else if (size == 8) {
453 rec->AddU8List((const uint64_t *)aobj->GetRawData(), length);
454 }
455#endif
456 }
457 } else {
458
459 // obj is an instance object.
460 rec->AddU1(HPROF_INSTANCE_DUMP);
461 rec->AddId((HprofObjectId)obj);
462 rec->AddU4(StackTraceSerialNumber(obj));
463 rec->AddId(LookupClassId(clazz));
464
465 // Reserve some space for the length of the instance data, which we won't
466 // know until we're done writing it.
467 size_t sizePatchOffset = rec->length_;
468 rec->AddU4(0x77777777);
469
470 // Write the instance data; fields for this class, followed by super class fields,
471 // and so on. Don't write the klass or monitor fields of Object.class.
472 const Class* sclass = clazz;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800473 FieldHelper fh;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500474 while (!sclass->IsObjectClass()) {
475 int ifieldCount = sclass->NumInstanceFields();
476 for (int i = 0; i < ifieldCount; i++) {
477 Field* f = sclass->GetInstanceField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800478 fh.ChangeField(f);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500479 size_t size;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800480 SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), &size);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500481 if (size == 1) {
482 rec->AddU1(f->Get32(obj));
483 } else if (size == 2) {
484 rec->AddU2(f->Get32(obj));
485 } else if (size == 4) {
486 rec->AddU4(f->Get32(obj));
487 } else if (size == 8) {
488 rec->AddU8(f->Get64(obj));
489 } else {
490 CHECK(false);
491 }
492 }
493
494 sclass = sclass->GetSuperClass();
495 }
496
497 // Patch the instance field length.
498 size_t savedLen = rec->length_;
499 rec->length_ = sizePatchOffset;
500 rec->AddU4(savedLen - (sizePatchOffset + 4));
501 rec->length_ = savedLen;
502 }
503 }
504
505 objects_in_segment_++;
506 return 0;
507}
508
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400509#define kHeadSuffix "-hptemp"
510
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400511// TODO: use File::WriteFully
Jesse Wilson0b075f12011-11-09 10:57:41 -0500512int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg) {
513 while (count != 0) {
514 ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
515 if (actual < 0) {
516 int err = errno;
517 LOG(ERROR) << StringPrintf("%s: write failed: %s", logMsg, strerror(err));
518 return err;
519 } else if (actual != (ssize_t) count) {
520 LOG(DEBUG) << StringPrintf("%s: partial write (will retry): (%d of %zd)",
521 logMsg, (int) actual, count);
522 buf = (const void*) (((const uint8_t*) buf) + actual);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400523 }
Jesse Wilson0b075f12011-11-09 10:57:41 -0500524 count -= actual;
525 }
526 return 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400527}
528
529/*
530 * Finish up the hprof dump. Returns true on success.
531 */
Jesse Wilson0b075f12011-11-09 10:57:41 -0500532bool Hprof::Finish() {
533 // flush the "tail" portion of the output
534 StartNewRecord(HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
535 FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400536
Jesse Wilson0b075f12011-11-09 10:57:41 -0500537 // create a new Hprof for the start of the file (as opposed to this, which is the tail)
538 Hprof headCtx(file_name_, fd_, true, direct_to_ddms_);
539 headCtx.classes_ = classes_;
540 headCtx.strings_ = strings_;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400541
Jesse Wilson0b075f12011-11-09 10:57:41 -0500542 LOG(INFO) << StringPrintf("hprof: dumping heap strings to \"%s\".", file_name_);
543 headCtx.DumpStrings();
544 headCtx.DumpClasses();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400545
Jesse Wilson0b075f12011-11-09 10:57:41 -0500546 // write a dummy stack trace record so the analysis tools don't freak out
547 headCtx.StartNewRecord(HPROF_TAG_STACK_TRACE, HPROF_TIME);
548 headCtx.current_record_.AddU4(HPROF_NULL_STACK_TRACE);
549 headCtx.current_record_.AddU4(HPROF_NULL_THREAD);
550 headCtx.current_record_.AddU4(0); // no frames
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400551
Jesse Wilson0b075f12011-11-09 10:57:41 -0500552 headCtx.FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400553
Jesse Wilson0b075f12011-11-09 10:57:41 -0500554 // flush to ensure memstream pointer and size are updated
555 fflush(headCtx.mem_fp_);
556 fflush(mem_fp_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400557
Jesse Wilson0b075f12011-11-09 10:57:41 -0500558 if (direct_to_ddms_) {
559 // send the data off to DDMS
560 struct iovec iov[2];
561 iov[0].iov_base = headCtx.file_data_ptr_;
562 iov[0].iov_len = headCtx.file_data_size_;
563 iov[1].iov_base = file_data_ptr_;
564 iov[1].iov_len = file_data_size_;
565 Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
566 } else {
567 // open the output file, and copy the head and tail to it.
568 CHECK_EQ(headCtx.fd_, fd_);
569
570 int outFd;
571 if (headCtx.fd_ >= 0) {
572 outFd = dup(headCtx.fd_);
573 if (outFd < 0) {
574 LOG(ERROR) << StringPrintf("dup(%d) failed: %s", headCtx.fd_, strerror(errno));
575 // continue to fail-handler below
576 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400577 } else {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500578 outFd = open(file_name_, O_WRONLY|O_CREAT|O_TRUNC, 0644);
579 if (outFd < 0) {
580 LOG(ERROR) << StringPrintf("can't open %s: %s", headCtx.file_name_, strerror(errno));
581 // continue to fail-handler below
582 }
583 }
584 if (outFd < 0) {
585 return false;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400586 }
587
Jesse Wilson0b075f12011-11-09 10:57:41 -0500588 int result = sysWriteFully(outFd, headCtx.file_data_ptr_,
589 headCtx.file_data_size_, "hprof-head");
590 result |= sysWriteFully(outFd, file_data_ptr_, file_data_size_, "hprof-tail");
591 close(outFd);
592 if (result != 0) {
593 return false;
594 }
595 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400596
Jesse Wilson0b075f12011-11-09 10:57:41 -0500597 // throw out a log message for the benefit of "runhat"
598 LOG(INFO) << StringPrintf("hprof: heap dump completed (%dKB)",
599 (headCtx.file_data_size_ + file_data_size_ + 1023) / 1024);
600
601 return true;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400602}
603
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500604Hprof::~Hprof() {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500605 // we don't own ctx->fd_, do not close
606 if (mem_fp_ != NULL) {
607 fclose(mem_fp_);
608 }
609 free(current_record_.body_);
610 free(file_name_);
611 free(file_data_ptr_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400612}
613
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500614void Hprof::VisitRoot(const Object* obj) {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500615 uint32_t threadId = 0; // TODO
616 /*RootType */ size_t type = 0; // TODO
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400617
Jesse Wilson0b075f12011-11-09 10:57:41 -0500618 static const HprofHeapTag xlate[] = {
619 HPROF_ROOT_UNKNOWN,
620 HPROF_ROOT_JNI_GLOBAL,
621 HPROF_ROOT_JNI_LOCAL,
622 HPROF_ROOT_JAVA_FRAME,
623 HPROF_ROOT_NATIVE_STACK,
624 HPROF_ROOT_STICKY_CLASS,
625 HPROF_ROOT_THREAD_BLOCK,
626 HPROF_ROOT_MONITOR_USED,
627 HPROF_ROOT_THREAD_OBJECT,
628 HPROF_ROOT_INTERNED_STRING,
629 HPROF_ROOT_FINALIZING,
630 HPROF_ROOT_DEBUGGER,
631 HPROF_ROOT_REFERENCE_CLEANUP,
632 HPROF_ROOT_VM_INTERNAL,
633 HPROF_ROOT_JNI_MONITOR,
634 };
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400635
Jesse Wilson0b075f12011-11-09 10:57:41 -0500636 CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag));
637 if (obj == NULL) {
638 return;
639 }
640 gc_scan_state_ = xlate[type];
641 gc_thread_serial_number_ = threadId;
642 MarkRootObject(obj, 0);
643 gc_scan_state_ = 0;
644 gc_thread_serial_number_ = 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400645}
646
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500647HprofStringId Hprof::LookupStringId(String* string) {
648 return LookupStringId(string->ToModifiedUtf8());
649}
650
651HprofStringId Hprof::LookupStringId(const char* string) {
652 return LookupStringId(std::string(string));
653}
654
655HprofStringId Hprof::LookupStringId(std::string string) {
656 if (strings_.find(string) == strings_.end()) {
657 strings_[string] = next_string_id_++;
658 }
659 return strings_[string];
660}
661
662int Hprof::DumpStrings() {
663 HprofRecord *rec = &current_record_;
664
665 for (StringMapIterator it = strings_.begin(); it != strings_.end(); ++it) {
666 std::string string = (*it).first;
667 size_t id = (*it).second;
668
669 int err = StartNewRecord(HPROF_TAG_STRING, HPROF_TIME);
670 if (err != 0) {
671 return err;
672 }
673
674 // STRING format:
675 // ID: ID for this string
676 // U1*: UTF8 characters for string (NOT NULL terminated)
677 // (the record format encodes the length)
678 err = rec->AddU4(id);
679 if (err != 0) {
680 return err;
681 }
682 err = rec->AddUtf8String(string.c_str());
683 if (err != 0) {
684 return err;
685 }
686 }
687
688 return 0;
689}
690
691HprofStringId Hprof::LookupClassNameId(Class* clazz) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800692 return LookupStringId(PrettyDescriptor(clazz));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500693}
694
695HprofClassObjectId Hprof::LookupClassId(Class* clazz) {
696 if (clazz == NULL) {
697 // clazz is the superclass of java.lang.Object or a primitive
698 return (HprofClassObjectId)0;
699 }
700
701 std::pair<ClassSetIterator, bool> result = classes_.insert(clazz);
702 Class* present = *result.first;
703
704 // Make sure that we've assigned a string ID for this class' name
705 LookupClassNameId(clazz);
706
707 CHECK_EQ(present, clazz);
708 return (HprofStringId) present;
709}
710
711int Hprof::DumpClasses() {
712 HprofRecord *rec = &current_record_;
713 uint32_t nextSerialNumber = 1;
714
715 for (ClassSetIterator it = classes_.begin(); it != classes_.end(); ++it) {
716 Class* clazz = *it;
717 CHECK(clazz != NULL);
718
719 int err = StartNewRecord(HPROF_TAG_LOAD_CLASS, HPROF_TIME);
720 if (err != 0) {
721 return err;
722 }
723
724 // LOAD CLASS format:
725 // U4: class serial number (always > 0)
726 // ID: class object ID. We use the address of the class object structure as its ID.
727 // U4: stack trace serial number
728 // ID: class name string ID
729 rec->AddU4(nextSerialNumber++);
730 rec->AddId((HprofClassObjectId) clazz);
731 rec->AddU4(HPROF_NULL_STACK_TRACE);
732 rec->AddId(LookupClassNameId(clazz));
733 }
734
735 return 0;
736}
737
738void HprofRootVisitor(const Object* obj, void* arg) {
739 CHECK(arg != NULL);
740 Hprof* hprof = (Hprof*)arg;
741 hprof->VisitRoot(obj);
742}
743
744void HprofBitmapCallback(Object *obj, void *arg) {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500745 CHECK(obj != NULL);
746 CHECK(arg != NULL);
747 Hprof *hprof = (Hprof*)arg;
748 hprof->DumpHeapObject(obj);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400749}
750
751/*
752 * Walk the roots and heap writing heap information to the specified
753 * file.
754 *
755 * If "fd" is >= 0, the output will be written to that file descriptor.
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500756 * Otherwise, "file_name_" is used to create an output file.
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400757 *
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500758 * If "direct_to_ddms_" is set, the other arguments are ignored, and data is
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400759 * sent directly to DDMS.
760 *
761 * Returns 0 on success, or an error code on failure.
762 */
Jesse Wilson0b075f12011-11-09 10:57:41 -0500763int DumpHeap(const char* fileName, int fd, bool directToDdms) {
764 CHECK(fileName != NULL);
765 ScopedHeapLock lock;
766 ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400767
Jesse Wilson0b075f12011-11-09 10:57:41 -0500768 ThreadList* thread_list = Runtime::Current()->GetThreadList();
769 thread_list->SuspendAll();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400770
Jesse Wilson0b075f12011-11-09 10:57:41 -0500771 Hprof hprof(fileName, fd, false, directToDdms);
772 Runtime::Current()->VisitRoots(HprofRootVisitor, &hprof);
773 Heap::GetLiveBits()->Walk(HprofBitmapCallback, &hprof);
774 // TODO: write a HEAP_SUMMARY record
775 int success = hprof.Finish() ? 0 : -1;
776 thread_list->ResumeAll();
777 return success;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400778}
779
780} // namespace hprof
781
782} // namespace art