blob: 4d5cae10a31570766d3b15453ce22f9642a86c51 [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 Wilsonc4824e62011-11-01 14:39:04 -040034
Jesse Wilson0c54ac12011-11-09 15:14:05 -050035#include <cutils/open_memstream.h>
Jesse Wilsonc4824e62011-11-01 14:39:04 -040036#include <sys/uio.h>
37#include <string.h>
38#include <unistd.h>
39#include <fcntl.h>
40#include <errno.h>
41#include <sys/time.h>
42#include <time.h>
43
44namespace art {
45
46namespace hprof {
47
Jesse Wilson0c54ac12011-11-09 15:14:05 -050048#define HPROF_MAGIC_STRING "JAVA PROFILE 1.0.3"
49
50/*
51 * Initialize an Hprof.
52 */
Elliott Hughesc1f143d2011-12-01 17:31:10 -080053Hprof::Hprof(const char* outputFileName, int fd, bool writeHeader, bool directToDdms)
Jesse Wilson0c54ac12011-11-09 15:14:05 -050054 : current_record_(),
55 gc_thread_serial_number_(0),
56 gc_scan_state_(0),
57 current_heap_(HPROF_HEAP_DEFAULT),
58 objects_in_segment_(0),
59 direct_to_ddms_(0),
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -080060 file_name_(outputFileName),
Jesse Wilson0c54ac12011-11-09 15:14:05 -050061 file_data_ptr_(NULL),
62 file_data_size_(0),
63 mem_fp_(NULL),
64 fd_(0),
65 next_string_id_(0x400000) {
66 // Have to do this here, because it must happen after we
67 // memset the struct (want to treat file_data_ptr_/file_data_size_
68 // as read-only while the file is open).
69 FILE *fp = open_memstream(&file_data_ptr_, &file_data_size_);
70 if (fp == NULL) {
71 // not expected
72 LOG(ERROR) << StringPrintf("hprof: open_memstream failed: %s", strerror(errno));
73 CHECK(false);
74 }
75
76 direct_to_ddms_ = directToDdms;
Jesse Wilson0c54ac12011-11-09 15:14:05 -050077 mem_fp_ = fp;
78 fd_ = fd;
79
80 current_record_.alloc_length_ = 128;
Elliott Hughesc1f143d2011-12-01 17:31:10 -080081 current_record_.body_ = (unsigned char*)malloc(current_record_.alloc_length_);
Jesse Wilson0c54ac12011-11-09 15:14:05 -050082 // 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
Elliott Hughesc1f143d2011-12-01 17:31:10 -0800150HprofBasicType Hprof::SignatureToBasicTypeAndSize(const char* sig, size_t* sizeOut) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500151 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
Elliott Hughesdbb40792011-11-18 17:05:22 -0800355 if (thisClass->IsClassClass()) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500356 // 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
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800367 FieldHelper fh;
368
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500369 // Static fields
370 if (sFieldCount == 0) {
371 rec->AddU2((uint16_t)0);
372 } else {
373 rec->AddU2((uint16_t)(sFieldCount+1));
374 rec->AddId(LookupStringId(STATIC_OVERHEAD_NAME));
375 rec->AddU1(hprof_basic_object);
376 rec->AddId(CLASS_STATICS_ID(obj));
377
378 for (size_t i = 0; i < sFieldCount; ++i) {
379 Field* f = thisClass->GetStaticField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800380 fh.ChangeField(f);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500381
382 size_t size;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800383 HprofBasicType t = SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), &size);
384 rec->AddId(LookupStringId(fh.GetName()));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500385 rec->AddU1(t);
386 if (size == 1) {
387 rec->AddU1(static_cast<uint8_t>(f->Get32(NULL)));
388 } else if (size == 2) {
389 rec->AddU2(static_cast<uint16_t>(f->Get32(NULL)));
390 } else if (size == 4) {
391 rec->AddU4(f->Get32(NULL));
392 } else if (size == 8) {
393 rec->AddU8(f->Get64(NULL));
394 } else {
395 CHECK(false);
396 }
397 }
398 }
399
400 // Instance fields for this class (no superclass fields)
401 int iFieldCount = thisClass->IsObjectClass() ? 0 : thisClass->NumInstanceFields();
402 rec->AddU2((uint16_t)iFieldCount);
403 for (int i = 0; i < iFieldCount; ++i) {
404 Field* f = thisClass->GetInstanceField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800405 fh.ChangeField(f);
406 HprofBasicType t = SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), NULL);
407 rec->AddId(LookupStringId(fh.GetName()));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500408 rec->AddU1(t);
409 }
410 } else if (clazz->IsArrayClass()) {
411 Array *aobj = (Array *)obj;
412 uint32_t length = aobj->GetLength();
413
414 if (obj->IsObjectArray()) {
415 // obj is an object array.
416 rec->AddU1(HPROF_OBJECT_ARRAY_DUMP);
417
418 rec->AddId((HprofObjectId)obj);
419 rec->AddU4(StackTraceSerialNumber(obj));
420 rec->AddU4(length);
421 rec->AddId(LookupClassId(clazz));
422
423 // Dump the elements, which are always objects or NULL.
Ian Rogersa15e67d2012-02-28 13:51:55 -0800424 rec->AddIdList((const HprofObjectId *)aobj->GetRawData(sizeof(Object*)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500425 } else {
426 size_t size;
427 HprofBasicType t = PrimitiveToBasicTypeAndSize(clazz->GetComponentType()->GetPrimitiveType(), &size);
428
429 // obj is a primitive array.
430#if DUMP_PRIM_DATA
431 rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
432#else
433 rec->AddU1(HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
434#endif
435
436 rec->AddId((HprofObjectId)obj);
437 rec->AddU4(StackTraceSerialNumber(obj));
438 rec->AddU4(length);
439 rec->AddU1(t);
440
441#if DUMP_PRIM_DATA
442 // Dump the raw, packed element values.
443 if (size == 1) {
Ian Rogersa15e67d2012-02-28 13:51:55 -0800444 rec->AddU1List((const uint8_t *)aobj->GetRawData(sizeof(uint8_t)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500445 } else if (size == 2) {
Ian Rogersa15e67d2012-02-28 13:51:55 -0800446 rec->AddU2List((const uint16_t *)(void *)aobj->GetRawData(sizeof(uint16_t)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500447 } else if (size == 4) {
Ian Rogersa15e67d2012-02-28 13:51:55 -0800448 rec->AddU4List((const uint32_t *)(void *)aobj->GetRawData(sizeof(uint32_t)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500449 } else if (size == 8) {
Ian Rogersa15e67d2012-02-28 13:51:55 -0800450 rec->AddU8List((const uint64_t *)aobj->GetRawData(sizeof(uint64_t)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500451 }
452#endif
453 }
454 } else {
455
456 // obj is an instance object.
457 rec->AddU1(HPROF_INSTANCE_DUMP);
458 rec->AddId((HprofObjectId)obj);
459 rec->AddU4(StackTraceSerialNumber(obj));
460 rec->AddId(LookupClassId(clazz));
461
462 // Reserve some space for the length of the instance data, which we won't
463 // know until we're done writing it.
464 size_t sizePatchOffset = rec->length_;
465 rec->AddU4(0x77777777);
466
467 // Write the instance data; fields for this class, followed by super class fields,
468 // and so on. Don't write the klass or monitor fields of Object.class.
469 const Class* sclass = clazz;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800470 FieldHelper fh;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500471 while (!sclass->IsObjectClass()) {
472 int ifieldCount = sclass->NumInstanceFields();
473 for (int i = 0; i < ifieldCount; i++) {
474 Field* f = sclass->GetInstanceField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800475 fh.ChangeField(f);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500476 size_t size;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800477 SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), &size);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500478 if (size == 1) {
479 rec->AddU1(f->Get32(obj));
480 } else if (size == 2) {
481 rec->AddU2(f->Get32(obj));
482 } else if (size == 4) {
483 rec->AddU4(f->Get32(obj));
484 } else if (size == 8) {
485 rec->AddU8(f->Get64(obj));
486 } else {
487 CHECK(false);
488 }
489 }
490
491 sclass = sclass->GetSuperClass();
492 }
493
494 // Patch the instance field length.
495 size_t savedLen = rec->length_;
496 rec->length_ = sizePatchOffset;
497 rec->AddU4(savedLen - (sizePatchOffset + 4));
498 rec->length_ = savedLen;
499 }
500 }
501
502 objects_in_segment_++;
503 return 0;
504}
505
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400506#define kHeadSuffix "-hptemp"
507
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400508// TODO: use File::WriteFully
Jesse Wilson0b075f12011-11-09 10:57:41 -0500509int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg) {
510 while (count != 0) {
511 ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
512 if (actual < 0) {
513 int err = errno;
514 LOG(ERROR) << StringPrintf("%s: write failed: %s", logMsg, strerror(err));
515 return err;
516 } else if (actual != (ssize_t) count) {
517 LOG(DEBUG) << StringPrintf("%s: partial write (will retry): (%d of %zd)",
518 logMsg, (int) actual, count);
519 buf = (const void*) (((const uint8_t*) buf) + actual);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400520 }
Jesse Wilson0b075f12011-11-09 10:57:41 -0500521 count -= actual;
522 }
523 return 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400524}
525
526/*
527 * Finish up the hprof dump. Returns true on success.
528 */
Jesse Wilson0b075f12011-11-09 10:57:41 -0500529bool Hprof::Finish() {
530 // flush the "tail" portion of the output
531 StartNewRecord(HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
532 FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400533
Jesse Wilson0b075f12011-11-09 10:57:41 -0500534 // 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 -0800535 Hprof headCtx(file_name_.c_str(), fd_, true, direct_to_ddms_);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500536 headCtx.classes_ = classes_;
537 headCtx.strings_ = strings_;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400538
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800539 LOG(INFO) << StringPrintf("hprof: dumping heap strings to \"%s\".", file_name_.c_str());
Jesse Wilson0b075f12011-11-09 10:57:41 -0500540 headCtx.DumpStrings();
541 headCtx.DumpClasses();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400542
Jesse Wilson0b075f12011-11-09 10:57:41 -0500543 // write a dummy stack trace record so the analysis tools don't freak out
544 headCtx.StartNewRecord(HPROF_TAG_STACK_TRACE, HPROF_TIME);
545 headCtx.current_record_.AddU4(HPROF_NULL_STACK_TRACE);
546 headCtx.current_record_.AddU4(HPROF_NULL_THREAD);
547 headCtx.current_record_.AddU4(0); // no frames
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400548
Jesse Wilson0b075f12011-11-09 10:57:41 -0500549 headCtx.FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400550
Jesse Wilson0b075f12011-11-09 10:57:41 -0500551 // flush to ensure memstream pointer and size are updated
552 fflush(headCtx.mem_fp_);
553 fflush(mem_fp_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400554
Jesse Wilson0b075f12011-11-09 10:57:41 -0500555 if (direct_to_ddms_) {
556 // send the data off to DDMS
557 struct iovec iov[2];
558 iov[0].iov_base = headCtx.file_data_ptr_;
559 iov[0].iov_len = headCtx.file_data_size_;
560 iov[1].iov_base = file_data_ptr_;
561 iov[1].iov_len = file_data_size_;
562 Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
563 } else {
564 // open the output file, and copy the head and tail to it.
565 CHECK_EQ(headCtx.fd_, fd_);
566
567 int outFd;
568 if (headCtx.fd_ >= 0) {
569 outFd = dup(headCtx.fd_);
570 if (outFd < 0) {
571 LOG(ERROR) << StringPrintf("dup(%d) failed: %s", headCtx.fd_, strerror(errno));
572 // continue to fail-handler below
573 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400574 } else {
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800575 outFd = open(file_name_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500576 if (outFd < 0) {
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800577 LOG(ERROR) << StringPrintf("can't open %s: %s", headCtx.file_name_.c_str(), strerror(errno));
Jesse Wilson0b075f12011-11-09 10:57:41 -0500578 // continue to fail-handler below
579 }
580 }
581 if (outFd < 0) {
582 return false;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400583 }
584
Jesse Wilson0b075f12011-11-09 10:57:41 -0500585 int result = sysWriteFully(outFd, headCtx.file_data_ptr_,
586 headCtx.file_data_size_, "hprof-head");
587 result |= sysWriteFully(outFd, file_data_ptr_, file_data_size_, "hprof-tail");
588 close(outFd);
589 if (result != 0) {
590 return false;
591 }
592 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400593
Jesse Wilson0b075f12011-11-09 10:57:41 -0500594 // throw out a log message for the benefit of "runhat"
Elliott Hughes5d78d392011-12-13 16:53:05 -0800595 LOG(INFO) << "hprof: heap dump completed (" << ((headCtx.file_data_size_ + file_data_size_ + 1023) / KB) << "KiB)";
Jesse Wilson0b075f12011-11-09 10:57:41 -0500596
597 return true;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400598}
599
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500600Hprof::~Hprof() {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500601 // we don't own ctx->fd_, do not close
602 if (mem_fp_ != NULL) {
603 fclose(mem_fp_);
604 }
605 free(current_record_.body_);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500606 free(file_data_ptr_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400607}
608
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500609void Hprof::VisitRoot(const Object* obj) {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500610 uint32_t threadId = 0; // TODO
611 /*RootType */ size_t type = 0; // TODO
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400612
Jesse Wilson0b075f12011-11-09 10:57:41 -0500613 static const HprofHeapTag xlate[] = {
614 HPROF_ROOT_UNKNOWN,
615 HPROF_ROOT_JNI_GLOBAL,
616 HPROF_ROOT_JNI_LOCAL,
617 HPROF_ROOT_JAVA_FRAME,
618 HPROF_ROOT_NATIVE_STACK,
619 HPROF_ROOT_STICKY_CLASS,
620 HPROF_ROOT_THREAD_BLOCK,
621 HPROF_ROOT_MONITOR_USED,
622 HPROF_ROOT_THREAD_OBJECT,
623 HPROF_ROOT_INTERNED_STRING,
624 HPROF_ROOT_FINALIZING,
625 HPROF_ROOT_DEBUGGER,
626 HPROF_ROOT_REFERENCE_CLEANUP,
627 HPROF_ROOT_VM_INTERNAL,
628 HPROF_ROOT_JNI_MONITOR,
629 };
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400630
Jesse Wilson0b075f12011-11-09 10:57:41 -0500631 CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag));
632 if (obj == NULL) {
633 return;
634 }
635 gc_scan_state_ = xlate[type];
636 gc_thread_serial_number_ = threadId;
637 MarkRootObject(obj, 0);
638 gc_scan_state_ = 0;
639 gc_thread_serial_number_ = 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400640}
641
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500642HprofStringId Hprof::LookupStringId(String* string) {
643 return LookupStringId(string->ToModifiedUtf8());
644}
645
646HprofStringId Hprof::LookupStringId(const char* string) {
647 return LookupStringId(std::string(string));
648}
649
650HprofStringId Hprof::LookupStringId(std::string string) {
651 if (strings_.find(string) == strings_.end()) {
652 strings_[string] = next_string_id_++;
653 }
654 return strings_[string];
655}
656
657int Hprof::DumpStrings() {
658 HprofRecord *rec = &current_record_;
659
660 for (StringMapIterator it = strings_.begin(); it != strings_.end(); ++it) {
Elliott Hughes95572412011-12-13 18:14:20 -0800661 std::string string((*it).first);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500662 size_t id = (*it).second;
663
664 int err = StartNewRecord(HPROF_TAG_STRING, HPROF_TIME);
665 if (err != 0) {
666 return err;
667 }
668
669 // STRING format:
670 // ID: ID for this string
671 // U1*: UTF8 characters for string (NOT NULL terminated)
672 // (the record format encodes the length)
673 err = rec->AddU4(id);
674 if (err != 0) {
675 return err;
676 }
677 err = rec->AddUtf8String(string.c_str());
678 if (err != 0) {
679 return err;
680 }
681 }
682
683 return 0;
684}
685
686HprofStringId Hprof::LookupClassNameId(Class* clazz) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800687 return LookupStringId(PrettyDescriptor(clazz));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500688}
689
690HprofClassObjectId Hprof::LookupClassId(Class* clazz) {
691 if (clazz == NULL) {
692 // clazz is the superclass of java.lang.Object or a primitive
693 return (HprofClassObjectId)0;
694 }
695
696 std::pair<ClassSetIterator, bool> result = classes_.insert(clazz);
697 Class* present = *result.first;
698
699 // Make sure that we've assigned a string ID for this class' name
700 LookupClassNameId(clazz);
701
702 CHECK_EQ(present, clazz);
703 return (HprofStringId) present;
704}
705
706int Hprof::DumpClasses() {
707 HprofRecord *rec = &current_record_;
708 uint32_t nextSerialNumber = 1;
709
710 for (ClassSetIterator it = classes_.begin(); it != classes_.end(); ++it) {
711 Class* clazz = *it;
712 CHECK(clazz != NULL);
713
714 int err = StartNewRecord(HPROF_TAG_LOAD_CLASS, HPROF_TIME);
715 if (err != 0) {
716 return err;
717 }
718
719 // LOAD CLASS format:
720 // U4: class serial number (always > 0)
721 // ID: class object ID. We use the address of the class object structure as its ID.
722 // U4: stack trace serial number
723 // ID: class name string ID
724 rec->AddU4(nextSerialNumber++);
725 rec->AddId((HprofClassObjectId) clazz);
726 rec->AddU4(HPROF_NULL_STACK_TRACE);
727 rec->AddId(LookupClassNameId(clazz));
728 }
729
730 return 0;
731}
732
733void HprofRootVisitor(const Object* obj, void* arg) {
734 CHECK(arg != NULL);
735 Hprof* hprof = (Hprof*)arg;
736 hprof->VisitRoot(obj);
737}
738
739void HprofBitmapCallback(Object *obj, void *arg) {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500740 CHECK(obj != NULL);
741 CHECK(arg != NULL);
742 Hprof *hprof = (Hprof*)arg;
743 hprof->DumpHeapObject(obj);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400744}
745
746/*
747 * Walk the roots and heap writing heap information to the specified
748 * file.
749 *
750 * If "fd" is >= 0, the output will be written to that file descriptor.
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500751 * Otherwise, "file_name_" is used to create an output file.
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400752 *
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500753 * If "direct_to_ddms_" is set, the other arguments are ignored, and data is
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400754 * sent directly to DDMS.
755 *
756 * Returns 0 on success, or an error code on failure.
757 */
Jesse Wilson0b075f12011-11-09 10:57:41 -0500758int DumpHeap(const char* fileName, int fd, bool directToDdms) {
759 CHECK(fileName != NULL);
Elliott Hughesffb465f2012-03-01 18:46:05 -0800760 ScopedHeapLock heap_lock;
Jesse Wilson0b075f12011-11-09 10:57:41 -0500761 ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400762
Jesse Wilson0b075f12011-11-09 10:57:41 -0500763 ThreadList* thread_list = Runtime::Current()->GetThreadList();
764 thread_list->SuspendAll();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400765
Jesse Wilson0b075f12011-11-09 10:57:41 -0500766 Hprof hprof(fileName, fd, false, directToDdms);
767 Runtime::Current()->VisitRoots(HprofRootVisitor, &hprof);
768 Heap::GetLiveBits()->Walk(HprofBitmapCallback, &hprof);
769 // TODO: write a HEAP_SUMMARY record
770 int success = hprof.Finish() ? 0 : -1;
771 thread_list->ResumeAll();
772 return success;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400773}
774
775} // namespace hprof
776
777} // namespace art