blob: 64cd7dbf515aa9354951117865ef87eae9747e20 [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),
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -080062 file_name_(outputFileName),
Jesse Wilson0c54ac12011-11-09 15:14:05 -050063 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;
Jesse Wilson0c54ac12011-11-09 15:14:05 -050079 mem_fp_ = fp;
80 fd_ = fd;
81
82 current_record_.alloc_length_ = 128;
Elliott Hughesc1f143d2011-12-01 17:31:10 -080083 current_record_.body_ = (unsigned char*)malloc(current_record_.alloc_length_);
Jesse Wilson0c54ac12011-11-09 15:14:05 -050084 // TODO check for/return an error
85
86 if (writeHeader) {
87 char magic[] = HPROF_MAGIC_STRING;
88 unsigned char buf[4];
89
90 // Write the file header.
91 // U1: NUL-terminated magic string.
92 fwrite(magic, 1, sizeof(magic), fp);
93
94 // U4: size of identifiers. We're using addresses as IDs, so make sure a pointer fits.
95 U4_TO_BUF_BE(buf, 0, sizeof(void *));
96 fwrite(buf, 1, sizeof(uint32_t), fp);
97
98 // The current time, in milliseconds since 0:00 GMT, 1/1/70.
99 struct timeval now;
100 uint64_t nowMs;
101 if (gettimeofday(&now, NULL) < 0) {
102 nowMs = 0;
103 } else {
104 nowMs = (uint64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
105 }
106
107 // U4: high word of the 64-bit time.
108 U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs >> 32));
109 fwrite(buf, 1, sizeof(uint32_t), fp);
110
111 // U4: low word of the 64-bit time.
112 U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs & 0xffffffffULL));
113 fwrite(buf, 1, sizeof(uint32_t), fp); //xxx fix the time
114 }
115}
116
117int Hprof::StartNewRecord(uint8_t tag, uint32_t time) {
118 HprofRecord *rec = &current_record_;
119
120 int err = rec->Flush(mem_fp_);
121 if (err != 0) {
122 return err;
123 } else if (rec->dirty_) {
124 return UNIQUE_ERROR();
125 }
126
127 rec->dirty_ = true;
128 rec->tag_ = tag;
129 rec->time_ = time;
130 rec->length_ = 0;
131 return 0;
132}
133
134int Hprof::FlushCurrentRecord() {
135 return current_record_.Flush(mem_fp_);
136}
137
138// Set DUMP_PRIM_DATA to 1 if you want to include the contents
139// of primitive arrays (byte arrays, character arrays, etc.)
140// in heap dumps. This can be a large amount of data.
141#define DUMP_PRIM_DATA 1
142
143#define OBJECTS_PER_SEGMENT ((size_t)128)
144#define BYTES_PER_SEGMENT ((size_t)4096)
145
146// The static field-name for the synthetic object generated to account
147// for class static overhead.
148#define STATIC_OVERHEAD_NAME "$staticOverhead"
149// The ID for the synthetic object generated to account for class static overhead.
150#define CLASS_STATICS_ID(clazz) ((HprofObjectId)(((uint32_t)(clazz)) | 1))
151
Elliott Hughesc1f143d2011-12-01 17:31:10 -0800152HprofBasicType Hprof::SignatureToBasicTypeAndSize(const char* sig, size_t* sizeOut) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500153 char c = sig[0];
154 HprofBasicType ret;
155 size_t size;
156
157 switch (c) {
158 case '[':
159 case 'L': ret = hprof_basic_object; size = 4; break;
160 case 'Z': ret = hprof_basic_boolean; size = 1; break;
161 case 'C': ret = hprof_basic_char; size = 2; break;
162 case 'F': ret = hprof_basic_float; size = 4; break;
163 case 'D': ret = hprof_basic_double; size = 8; break;
164 case 'B': ret = hprof_basic_byte; size = 1; break;
165 case 'S': ret = hprof_basic_short; size = 2; break;
166 default: CHECK(false);
167 case 'I': ret = hprof_basic_int; size = 4; break;
168 case 'J': ret = hprof_basic_long; size = 8; break;
169 }
170
171 if (sizeOut != NULL) {
172 *sizeOut = size;
173 }
174
175 return ret;
176}
177
178HprofBasicType Hprof::PrimitiveToBasicTypeAndSize(Primitive::Type prim, size_t *sizeOut) {
179 HprofBasicType ret;
180 size_t size;
181
182 switch (prim) {
183 case Primitive::kPrimBoolean: ret = hprof_basic_boolean; size = 1; break;
184 case Primitive::kPrimChar: ret = hprof_basic_char; size = 2; break;
185 case Primitive::kPrimFloat: ret = hprof_basic_float; size = 4; break;
186 case Primitive::kPrimDouble: ret = hprof_basic_double; size = 8; break;
187 case Primitive::kPrimByte: ret = hprof_basic_byte; size = 1; break;
188 case Primitive::kPrimShort: ret = hprof_basic_short; size = 2; break;
189 default: CHECK(false);
190 case Primitive::kPrimInt: ret = hprof_basic_int; size = 4; break;
191 case Primitive::kPrimLong: ret = hprof_basic_long; size = 8; break;
192 }
193
194 if (sizeOut != NULL) {
195 *sizeOut = size;
196 }
197
198 return ret;
199}
200
201// Always called when marking objects, but only does
202// something when ctx->gc_scan_state_ is non-zero, which is usually
203// only true when marking the root set or unreachable
204// objects. Used to add rootset references to obj.
205int Hprof::MarkRootObject(const Object *obj, jobject jniObj) {
206 HprofRecord *rec = &current_record_;
207 int err; // TODO: we may return this uninitialized
208 HprofHeapTag heapTag = (HprofHeapTag)gc_scan_state_;
209
210 if (heapTag == 0) {
211 return 0;
212 }
213
214 if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->length_ >= BYTES_PER_SEGMENT) {
215 // This flushes the old segment and starts a new one.
216 StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
217 objects_in_segment_ = 0;
218 }
219
220 switch (heapTag) {
221 // ID: object ID
222 case HPROF_ROOT_UNKNOWN:
223 case HPROF_ROOT_STICKY_CLASS:
224 case HPROF_ROOT_MONITOR_USED:
225 case HPROF_ROOT_INTERNED_STRING:
226 case HPROF_ROOT_FINALIZING:
227 case HPROF_ROOT_DEBUGGER:
228 case HPROF_ROOT_REFERENCE_CLEANUP:
229 case HPROF_ROOT_VM_INTERNAL:
230 rec->AddU1(heapTag);
231 rec->AddId((HprofObjectId)obj);
232 break;
233
234 // ID: object ID
235 // ID: JNI global ref ID
236 case HPROF_ROOT_JNI_GLOBAL:
237 rec->AddU1(heapTag);
238 rec->AddId((HprofObjectId)obj);
239 rec->AddId((HprofId)jniObj);
240 break;
241
242 // ID: object ID
243 // U4: thread serial number
244 // U4: frame number in stack trace (-1 for empty)
245 case HPROF_ROOT_JNI_LOCAL:
246 case HPROF_ROOT_JNI_MONITOR:
247 case HPROF_ROOT_JAVA_FRAME:
248 rec->AddU1(heapTag);
249 rec->AddId((HprofObjectId)obj);
250 rec->AddU4(gc_thread_serial_number_);
251 rec->AddU4((uint32_t)-1);
252 break;
253
254 // ID: object ID
255 // U4: thread serial number
256 case HPROF_ROOT_NATIVE_STACK:
257 case HPROF_ROOT_THREAD_BLOCK:
258 rec->AddU1(heapTag);
259 rec->AddId((HprofObjectId)obj);
260 rec->AddU4(gc_thread_serial_number_);
261 break;
262
263 // ID: thread object ID
264 // U4: thread serial number
265 // U4: stack trace serial number
266 case HPROF_ROOT_THREAD_OBJECT:
267 rec->AddU1(heapTag);
268 rec->AddId((HprofObjectId)obj);
269 rec->AddU4(gc_thread_serial_number_);
270 rec->AddU4((uint32_t)-1); //xxx
271 break;
272
273 default:
274 err = 0;
275 break;
276 }
277
278 objects_in_segment_++;
279 return err;
280}
281
282int Hprof::StackTraceSerialNumber(const void *obj) {
283 return HPROF_NULL_STACK_TRACE;
284}
285
286int Hprof::DumpHeapObject(const Object* obj) {
287 HprofRecord *rec = &current_record_;
288 HprofHeapId desiredHeap = false ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP; // TODO: zygote objects?
289
290 if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->length_ >= BYTES_PER_SEGMENT) {
291 // This flushes the old segment and starts a new one.
292 StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
293 objects_in_segment_ = 0;
294
295 // Starting a new HEAP_DUMP resets the heap to default.
296 current_heap_ = HPROF_HEAP_DEFAULT;
297 }
298
299 if (desiredHeap != current_heap_) {
300 HprofStringId nameId;
301
302 // This object is in a different heap than the current one.
303 // Emit a HEAP_DUMP_INFO tag to change heaps.
304 rec->AddU1(HPROF_HEAP_DUMP_INFO);
305 rec->AddU4((uint32_t)desiredHeap); // uint32_t: heap id
306 switch (desiredHeap) {
307 case HPROF_HEAP_APP:
308 nameId = LookupStringId("app");
309 break;
310 case HPROF_HEAP_ZYGOTE:
311 nameId = LookupStringId("zygote");
312 break;
313 default:
314 // Internal error
315 LOG(ERROR) << "Unexpected desiredHeap";
316 nameId = LookupStringId("<ILLEGAL>");
317 break;
318 }
319 rec->AddId(nameId);
320 current_heap_ = desiredHeap;
321 }
322
323 Class* clazz = obj->GetClass();
324 if (clazz == NULL) {
325 // This object will bother HprofReader, because it has a NULL
326 // class, so just don't dump it. It could be
327 // gDvm.unlinkedJavaLangClass or it could be an object just
328 // allocated which hasn't been initialized yet.
329 } else {
330 if (obj->IsClass()) {
331 Class* thisClass = (Class*)obj;
332 // obj is a ClassObject.
333 size_t sFieldCount = thisClass->NumStaticFields();
334 if (sFieldCount != 0) {
335 int byteLength = sFieldCount*sizeof(JValue); // TODO bogus; fields are packed
336 // Create a byte array to reflect the allocation of the
337 // StaticField array at the end of this class.
338 rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
339 rec->AddId(CLASS_STATICS_ID(obj));
340 rec->AddU4(StackTraceSerialNumber(obj));
341 rec->AddU4(byteLength);
342 rec->AddU1(hprof_basic_byte);
343 for (int i = 0; i < byteLength; i++) {
344 rec->AddU1(0);
345 }
346 }
347
348 rec->AddU1(HPROF_CLASS_DUMP);
349 rec->AddId(LookupClassId(thisClass));
350 rec->AddU4(StackTraceSerialNumber(thisClass));
351 rec->AddId(LookupClassId(thisClass->GetSuperClass()));
352 rec->AddId((HprofObjectId)thisClass->GetClassLoader());
353 rec->AddId((HprofObjectId)0); // no signer
354 rec->AddId((HprofObjectId)0); // no prot domain
355 rec->AddId((HprofId)0); // reserved
356 rec->AddId((HprofId)0); // reserved
Elliott Hughesdbb40792011-11-18 17:05:22 -0800357 if (thisClass->IsClassClass()) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500358 // ClassObjects have their static fields appended, so aren't all the same size.
359 // But they're at least this size.
360 rec->AddU4(sizeof(Class)); // instance size
361 } else if (thisClass->IsArrayClass() || thisClass->IsPrimitive()) {
362 rec->AddU4(0);
363 } else {
364 rec->AddU4(thisClass->GetObjectSize()); // instance size
365 }
366
367 rec->AddU2(0); // empty const pool
368
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800369 FieldHelper fh;
370
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500371 // Static fields
372 if (sFieldCount == 0) {
373 rec->AddU2((uint16_t)0);
374 } else {
375 rec->AddU2((uint16_t)(sFieldCount+1));
376 rec->AddId(LookupStringId(STATIC_OVERHEAD_NAME));
377 rec->AddU1(hprof_basic_object);
378 rec->AddId(CLASS_STATICS_ID(obj));
379
380 for (size_t i = 0; i < sFieldCount; ++i) {
381 Field* f = thisClass->GetStaticField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800382 fh.ChangeField(f);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500383
384 size_t size;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800385 HprofBasicType t = SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), &size);
386 rec->AddId(LookupStringId(fh.GetName()));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500387 rec->AddU1(t);
388 if (size == 1) {
389 rec->AddU1(static_cast<uint8_t>(f->Get32(NULL)));
390 } else if (size == 2) {
391 rec->AddU2(static_cast<uint16_t>(f->Get32(NULL)));
392 } else if (size == 4) {
393 rec->AddU4(f->Get32(NULL));
394 } else if (size == 8) {
395 rec->AddU8(f->Get64(NULL));
396 } else {
397 CHECK(false);
398 }
399 }
400 }
401
402 // Instance fields for this class (no superclass fields)
403 int iFieldCount = thisClass->IsObjectClass() ? 0 : thisClass->NumInstanceFields();
404 rec->AddU2((uint16_t)iFieldCount);
405 for (int i = 0; i < iFieldCount; ++i) {
406 Field* f = thisClass->GetInstanceField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800407 fh.ChangeField(f);
408 HprofBasicType t = SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), NULL);
409 rec->AddId(LookupStringId(fh.GetName()));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500410 rec->AddU1(t);
411 }
412 } else if (clazz->IsArrayClass()) {
413 Array *aobj = (Array *)obj;
414 uint32_t length = aobj->GetLength();
415
416 if (obj->IsObjectArray()) {
417 // obj is an object array.
418 rec->AddU1(HPROF_OBJECT_ARRAY_DUMP);
419
420 rec->AddId((HprofObjectId)obj);
421 rec->AddU4(StackTraceSerialNumber(obj));
422 rec->AddU4(length);
423 rec->AddId(LookupClassId(clazz));
424
425 // Dump the elements, which are always objects or NULL.
426 rec->AddIdList((const HprofObjectId *)aobj->GetRawData(), length);
427 } else {
428 size_t size;
429 HprofBasicType t = PrimitiveToBasicTypeAndSize(clazz->GetComponentType()->GetPrimitiveType(), &size);
430
431 // obj is a primitive array.
432#if DUMP_PRIM_DATA
433 rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
434#else
435 rec->AddU1(HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
436#endif
437
438 rec->AddId((HprofObjectId)obj);
439 rec->AddU4(StackTraceSerialNumber(obj));
440 rec->AddU4(length);
441 rec->AddU1(t);
442
443#if DUMP_PRIM_DATA
444 // Dump the raw, packed element values.
445 if (size == 1) {
446 rec->AddU1List((const uint8_t *)aobj->GetRawData(), length);
447 } else if (size == 2) {
448 rec->AddU2List((const uint16_t *)(void *)aobj->GetRawData(), length);
449 } else if (size == 4) {
450 rec->AddU4List((const uint32_t *)(void *)aobj->GetRawData(), length);
451 } else if (size == 8) {
452 rec->AddU8List((const uint64_t *)aobj->GetRawData(), length);
453 }
454#endif
455 }
456 } else {
457
458 // obj is an instance object.
459 rec->AddU1(HPROF_INSTANCE_DUMP);
460 rec->AddId((HprofObjectId)obj);
461 rec->AddU4(StackTraceSerialNumber(obj));
462 rec->AddId(LookupClassId(clazz));
463
464 // Reserve some space for the length of the instance data, which we won't
465 // know until we're done writing it.
466 size_t sizePatchOffset = rec->length_;
467 rec->AddU4(0x77777777);
468
469 // Write the instance data; fields for this class, followed by super class fields,
470 // and so on. Don't write the klass or monitor fields of Object.class.
471 const Class* sclass = clazz;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800472 FieldHelper fh;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500473 while (!sclass->IsObjectClass()) {
474 int ifieldCount = sclass->NumInstanceFields();
475 for (int i = 0; i < ifieldCount; i++) {
476 Field* f = sclass->GetInstanceField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800477 fh.ChangeField(f);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500478 size_t size;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800479 SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), &size);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500480 if (size == 1) {
481 rec->AddU1(f->Get32(obj));
482 } else if (size == 2) {
483 rec->AddU2(f->Get32(obj));
484 } else if (size == 4) {
485 rec->AddU4(f->Get32(obj));
486 } else if (size == 8) {
487 rec->AddU8(f->Get64(obj));
488 } else {
489 CHECK(false);
490 }
491 }
492
493 sclass = sclass->GetSuperClass();
494 }
495
496 // Patch the instance field length.
497 size_t savedLen = rec->length_;
498 rec->length_ = sizePatchOffset;
499 rec->AddU4(savedLen - (sizePatchOffset + 4));
500 rec->length_ = savedLen;
501 }
502 }
503
504 objects_in_segment_++;
505 return 0;
506}
507
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400508#define kHeadSuffix "-hptemp"
509
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400510// TODO: use File::WriteFully
Jesse Wilson0b075f12011-11-09 10:57:41 -0500511int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg) {
512 while (count != 0) {
513 ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
514 if (actual < 0) {
515 int err = errno;
516 LOG(ERROR) << StringPrintf("%s: write failed: %s", logMsg, strerror(err));
517 return err;
518 } else if (actual != (ssize_t) count) {
519 LOG(DEBUG) << StringPrintf("%s: partial write (will retry): (%d of %zd)",
520 logMsg, (int) actual, count);
521 buf = (const void*) (((const uint8_t*) buf) + actual);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400522 }
Jesse Wilson0b075f12011-11-09 10:57:41 -0500523 count -= actual;
524 }
525 return 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400526}
527
528/*
529 * Finish up the hprof dump. Returns true on success.
530 */
Jesse Wilson0b075f12011-11-09 10:57:41 -0500531bool Hprof::Finish() {
532 // flush the "tail" portion of the output
533 StartNewRecord(HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
534 FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400535
Jesse Wilson0b075f12011-11-09 10:57:41 -0500536 // create a new Hprof for the start of the file (as opposed to this, which is the tail)
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800537 Hprof headCtx(file_name_.c_str(), fd_, true, direct_to_ddms_);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500538 headCtx.classes_ = classes_;
539 headCtx.strings_ = strings_;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400540
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800541 LOG(INFO) << StringPrintf("hprof: dumping heap strings to \"%s\".", file_name_.c_str());
Jesse Wilson0b075f12011-11-09 10:57:41 -0500542 headCtx.DumpStrings();
543 headCtx.DumpClasses();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400544
Jesse Wilson0b075f12011-11-09 10:57:41 -0500545 // write a dummy stack trace record so the analysis tools don't freak out
546 headCtx.StartNewRecord(HPROF_TAG_STACK_TRACE, HPROF_TIME);
547 headCtx.current_record_.AddU4(HPROF_NULL_STACK_TRACE);
548 headCtx.current_record_.AddU4(HPROF_NULL_THREAD);
549 headCtx.current_record_.AddU4(0); // no frames
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400550
Jesse Wilson0b075f12011-11-09 10:57:41 -0500551 headCtx.FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400552
Jesse Wilson0b075f12011-11-09 10:57:41 -0500553 // flush to ensure memstream pointer and size are updated
554 fflush(headCtx.mem_fp_);
555 fflush(mem_fp_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400556
Jesse Wilson0b075f12011-11-09 10:57:41 -0500557 if (direct_to_ddms_) {
558 // send the data off to DDMS
559 struct iovec iov[2];
560 iov[0].iov_base = headCtx.file_data_ptr_;
561 iov[0].iov_len = headCtx.file_data_size_;
562 iov[1].iov_base = file_data_ptr_;
563 iov[1].iov_len = file_data_size_;
564 Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
565 } else {
566 // open the output file, and copy the head and tail to it.
567 CHECK_EQ(headCtx.fd_, fd_);
568
569 int outFd;
570 if (headCtx.fd_ >= 0) {
571 outFd = dup(headCtx.fd_);
572 if (outFd < 0) {
573 LOG(ERROR) << StringPrintf("dup(%d) failed: %s", headCtx.fd_, strerror(errno));
574 // continue to fail-handler below
575 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400576 } else {
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800577 outFd = open(file_name_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500578 if (outFd < 0) {
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800579 LOG(ERROR) << StringPrintf("can't open %s: %s", headCtx.file_name_.c_str(), strerror(errno));
Jesse Wilson0b075f12011-11-09 10:57:41 -0500580 // continue to fail-handler below
581 }
582 }
583 if (outFd < 0) {
584 return false;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400585 }
586
Jesse Wilson0b075f12011-11-09 10:57:41 -0500587 int result = sysWriteFully(outFd, headCtx.file_data_ptr_,
588 headCtx.file_data_size_, "hprof-head");
589 result |= sysWriteFully(outFd, file_data_ptr_, file_data_size_, "hprof-tail");
590 close(outFd);
591 if (result != 0) {
592 return false;
593 }
594 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400595
Jesse Wilson0b075f12011-11-09 10:57:41 -0500596 // throw out a log message for the benefit of "runhat"
Elliott Hughes5d78d392011-12-13 16:53:05 -0800597 LOG(INFO) << "hprof: heap dump completed (" << ((headCtx.file_data_size_ + file_data_size_ + 1023) / KB) << "KiB)";
Jesse Wilson0b075f12011-11-09 10:57:41 -0500598
599 return true;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400600}
601
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500602Hprof::~Hprof() {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500603 // we don't own ctx->fd_, do not close
604 if (mem_fp_ != NULL) {
605 fclose(mem_fp_);
606 }
607 free(current_record_.body_);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500608 free(file_data_ptr_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400609}
610
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500611void Hprof::VisitRoot(const Object* obj) {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500612 uint32_t threadId = 0; // TODO
613 /*RootType */ size_t type = 0; // TODO
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400614
Jesse Wilson0b075f12011-11-09 10:57:41 -0500615 static const HprofHeapTag xlate[] = {
616 HPROF_ROOT_UNKNOWN,
617 HPROF_ROOT_JNI_GLOBAL,
618 HPROF_ROOT_JNI_LOCAL,
619 HPROF_ROOT_JAVA_FRAME,
620 HPROF_ROOT_NATIVE_STACK,
621 HPROF_ROOT_STICKY_CLASS,
622 HPROF_ROOT_THREAD_BLOCK,
623 HPROF_ROOT_MONITOR_USED,
624 HPROF_ROOT_THREAD_OBJECT,
625 HPROF_ROOT_INTERNED_STRING,
626 HPROF_ROOT_FINALIZING,
627 HPROF_ROOT_DEBUGGER,
628 HPROF_ROOT_REFERENCE_CLEANUP,
629 HPROF_ROOT_VM_INTERNAL,
630 HPROF_ROOT_JNI_MONITOR,
631 };
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400632
Jesse Wilson0b075f12011-11-09 10:57:41 -0500633 CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag));
634 if (obj == NULL) {
635 return;
636 }
637 gc_scan_state_ = xlate[type];
638 gc_thread_serial_number_ = threadId;
639 MarkRootObject(obj, 0);
640 gc_scan_state_ = 0;
641 gc_thread_serial_number_ = 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400642}
643
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500644HprofStringId Hprof::LookupStringId(String* string) {
645 return LookupStringId(string->ToModifiedUtf8());
646}
647
648HprofStringId Hprof::LookupStringId(const char* string) {
649 return LookupStringId(std::string(string));
650}
651
652HprofStringId Hprof::LookupStringId(std::string string) {
653 if (strings_.find(string) == strings_.end()) {
654 strings_[string] = next_string_id_++;
655 }
656 return strings_[string];
657}
658
659int Hprof::DumpStrings() {
660 HprofRecord *rec = &current_record_;
661
662 for (StringMapIterator it = strings_.begin(); it != strings_.end(); ++it) {
663 std::string string = (*it).first;
664 size_t id = (*it).second;
665
666 int err = StartNewRecord(HPROF_TAG_STRING, HPROF_TIME);
667 if (err != 0) {
668 return err;
669 }
670
671 // STRING format:
672 // ID: ID for this string
673 // U1*: UTF8 characters for string (NOT NULL terminated)
674 // (the record format encodes the length)
675 err = rec->AddU4(id);
676 if (err != 0) {
677 return err;
678 }
679 err = rec->AddUtf8String(string.c_str());
680 if (err != 0) {
681 return err;
682 }
683 }
684
685 return 0;
686}
687
688HprofStringId Hprof::LookupClassNameId(Class* clazz) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800689 return LookupStringId(PrettyDescriptor(clazz));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500690}
691
692HprofClassObjectId Hprof::LookupClassId(Class* clazz) {
693 if (clazz == NULL) {
694 // clazz is the superclass of java.lang.Object or a primitive
695 return (HprofClassObjectId)0;
696 }
697
698 std::pair<ClassSetIterator, bool> result = classes_.insert(clazz);
699 Class* present = *result.first;
700
701 // Make sure that we've assigned a string ID for this class' name
702 LookupClassNameId(clazz);
703
704 CHECK_EQ(present, clazz);
705 return (HprofStringId) present;
706}
707
708int Hprof::DumpClasses() {
709 HprofRecord *rec = &current_record_;
710 uint32_t nextSerialNumber = 1;
711
712 for (ClassSetIterator it = classes_.begin(); it != classes_.end(); ++it) {
713 Class* clazz = *it;
714 CHECK(clazz != NULL);
715
716 int err = StartNewRecord(HPROF_TAG_LOAD_CLASS, HPROF_TIME);
717 if (err != 0) {
718 return err;
719 }
720
721 // LOAD CLASS format:
722 // U4: class serial number (always > 0)
723 // ID: class object ID. We use the address of the class object structure as its ID.
724 // U4: stack trace serial number
725 // ID: class name string ID
726 rec->AddU4(nextSerialNumber++);
727 rec->AddId((HprofClassObjectId) clazz);
728 rec->AddU4(HPROF_NULL_STACK_TRACE);
729 rec->AddId(LookupClassNameId(clazz));
730 }
731
732 return 0;
733}
734
735void HprofRootVisitor(const Object* obj, void* arg) {
736 CHECK(arg != NULL);
737 Hprof* hprof = (Hprof*)arg;
738 hprof->VisitRoot(obj);
739}
740
741void HprofBitmapCallback(Object *obj, void *arg) {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500742 CHECK(obj != NULL);
743 CHECK(arg != NULL);
744 Hprof *hprof = (Hprof*)arg;
745 hprof->DumpHeapObject(obj);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400746}
747
748/*
749 * Walk the roots and heap writing heap information to the specified
750 * file.
751 *
752 * If "fd" is >= 0, the output will be written to that file descriptor.
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500753 * Otherwise, "file_name_" is used to create an output file.
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400754 *
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500755 * If "direct_to_ddms_" is set, the other arguments are ignored, and data is
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400756 * sent directly to DDMS.
757 *
758 * Returns 0 on success, or an error code on failure.
759 */
Jesse Wilson0b075f12011-11-09 10:57:41 -0500760int DumpHeap(const char* fileName, int fd, bool directToDdms) {
761 CHECK(fileName != NULL);
762 ScopedHeapLock lock;
763 ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400764
Jesse Wilson0b075f12011-11-09 10:57:41 -0500765 ThreadList* thread_list = Runtime::Current()->GetThreadList();
766 thread_list->SuspendAll();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400767
Jesse Wilson0b075f12011-11-09 10:57:41 -0500768 Hprof hprof(fileName, fd, false, directToDdms);
769 Runtime::Current()->VisitRoots(HprofRootVisitor, &hprof);
770 Heap::GetLiveBits()->Walk(HprofBitmapCallback, &hprof);
771 // TODO: write a HEAP_SUMMARY record
772 int success = hprof.Finish() ? 0 : -1;
773 thread_list->ResumeAll();
774 return success;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400775}
776
777} // namespace hprof
778
779} // namespace art