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