blob: 6de9c6487f19042d44ad5dd9f57e564ce6823658 [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"
Elliott Hughesb3bd5f02012-03-08 21:05:27 -080033#include "scoped_heap_lock.h"
Jesse Wilsonc4824e62011-11-01 14:39:04 -040034#include "stringprintf.h"
Jesse Wilsonc4824e62011-11-01 14:39:04 -040035
Jesse Wilson0c54ac12011-11-09 15:14:05 -050036#include <cutils/open_memstream.h>
Jesse Wilsonc4824e62011-11-01 14:39:04 -040037#include <sys/uio.h>
38#include <string.h>
39#include <unistd.h>
40#include <fcntl.h>
41#include <errno.h>
42#include <sys/time.h>
43#include <time.h>
44
45namespace art {
46
47namespace hprof {
48
Jesse Wilson0c54ac12011-11-09 15:14:05 -050049#define HPROF_MAGIC_STRING "JAVA PROFILE 1.0.3"
50
51/*
52 * Initialize an Hprof.
53 */
Elliott Hughesc1f143d2011-12-01 17:31:10 -080054Hprof::Hprof(const char* outputFileName, int fd, bool writeHeader, bool directToDdms)
Jesse Wilson0c54ac12011-11-09 15:14:05 -050055 : current_record_(),
56 gc_thread_serial_number_(0),
57 gc_scan_state_(0),
58 current_heap_(HPROF_HEAP_DEFAULT),
59 objects_in_segment_(0),
60 direct_to_ddms_(0),
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -080061 file_name_(outputFileName),
Jesse Wilson0c54ac12011-11-09 15:14:05 -050062 file_data_ptr_(NULL),
63 file_data_size_(0),
64 mem_fp_(NULL),
65 fd_(0),
66 next_string_id_(0x400000) {
67 // Have to do this here, because it must happen after we
68 // memset the struct (want to treat file_data_ptr_/file_data_size_
69 // as read-only while the file is open).
70 FILE *fp = open_memstream(&file_data_ptr_, &file_data_size_);
71 if (fp == NULL) {
72 // not expected
73 LOG(ERROR) << StringPrintf("hprof: open_memstream failed: %s", strerror(errno));
74 CHECK(false);
75 }
76
77 direct_to_ddms_ = directToDdms;
Jesse Wilson0c54ac12011-11-09 15:14:05 -050078 mem_fp_ = fp;
79 fd_ = fd;
80
81 current_record_.alloc_length_ = 128;
Elliott Hughesc1f143d2011-12-01 17:31:10 -080082 current_record_.body_ = (unsigned char*)malloc(current_record_.alloc_length_);
Jesse Wilson0c54ac12011-11-09 15:14:05 -050083 // TODO check for/return an error
84
85 if (writeHeader) {
86 char magic[] = HPROF_MAGIC_STRING;
87 unsigned char buf[4];
88
89 // Write the file header.
90 // U1: NUL-terminated magic string.
91 fwrite(magic, 1, sizeof(magic), fp);
92
93 // U4: size of identifiers. We're using addresses as IDs, so make sure a pointer fits.
94 U4_TO_BUF_BE(buf, 0, sizeof(void *));
95 fwrite(buf, 1, sizeof(uint32_t), fp);
96
97 // The current time, in milliseconds since 0:00 GMT, 1/1/70.
98 struct timeval now;
99 uint64_t nowMs;
100 if (gettimeofday(&now, NULL) < 0) {
101 nowMs = 0;
102 } else {
103 nowMs = (uint64_t)now.tv_sec * 1000 + now.tv_usec / 1000;
104 }
105
106 // U4: high word of the 64-bit time.
107 U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs >> 32));
108 fwrite(buf, 1, sizeof(uint32_t), fp);
109
110 // U4: low word of the 64-bit time.
111 U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs & 0xffffffffULL));
112 fwrite(buf, 1, sizeof(uint32_t), fp); //xxx fix the time
113 }
114}
115
116int Hprof::StartNewRecord(uint8_t tag, uint32_t time) {
117 HprofRecord *rec = &current_record_;
118
119 int err = rec->Flush(mem_fp_);
120 if (err != 0) {
121 return err;
122 } else if (rec->dirty_) {
123 return UNIQUE_ERROR();
124 }
125
126 rec->dirty_ = true;
127 rec->tag_ = tag;
128 rec->time_ = time;
129 rec->length_ = 0;
130 return 0;
131}
132
133int Hprof::FlushCurrentRecord() {
134 return current_record_.Flush(mem_fp_);
135}
136
137// Set DUMP_PRIM_DATA to 1 if you want to include the contents
138// of primitive arrays (byte arrays, character arrays, etc.)
139// in heap dumps. This can be a large amount of data.
140#define DUMP_PRIM_DATA 1
141
142#define OBJECTS_PER_SEGMENT ((size_t)128)
143#define BYTES_PER_SEGMENT ((size_t)4096)
144
145// The static field-name for the synthetic object generated to account
146// for class static overhead.
147#define STATIC_OVERHEAD_NAME "$staticOverhead"
148// The ID for the synthetic object generated to account for class static overhead.
149#define CLASS_STATICS_ID(clazz) ((HprofObjectId)(((uint32_t)(clazz)) | 1))
150
Elliott Hughesc1f143d2011-12-01 17:31:10 -0800151HprofBasicType Hprof::SignatureToBasicTypeAndSize(const char* sig, size_t* sizeOut) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500152 char c = sig[0];
153 HprofBasicType ret;
154 size_t size;
155
156 switch (c) {
157 case '[':
158 case 'L': ret = hprof_basic_object; size = 4; break;
159 case 'Z': ret = hprof_basic_boolean; size = 1; break;
160 case 'C': ret = hprof_basic_char; size = 2; break;
161 case 'F': ret = hprof_basic_float; size = 4; break;
162 case 'D': ret = hprof_basic_double; size = 8; break;
163 case 'B': ret = hprof_basic_byte; size = 1; break;
164 case 'S': ret = hprof_basic_short; size = 2; break;
165 default: CHECK(false);
166 case 'I': ret = hprof_basic_int; size = 4; break;
167 case 'J': ret = hprof_basic_long; size = 8; break;
168 }
169
170 if (sizeOut != NULL) {
171 *sizeOut = size;
172 }
173
174 return ret;
175}
176
177HprofBasicType Hprof::PrimitiveToBasicTypeAndSize(Primitive::Type prim, size_t *sizeOut) {
178 HprofBasicType ret;
179 size_t size;
180
181 switch (prim) {
182 case Primitive::kPrimBoolean: ret = hprof_basic_boolean; size = 1; break;
183 case Primitive::kPrimChar: ret = hprof_basic_char; size = 2; break;
184 case Primitive::kPrimFloat: ret = hprof_basic_float; size = 4; break;
185 case Primitive::kPrimDouble: ret = hprof_basic_double; size = 8; break;
186 case Primitive::kPrimByte: ret = hprof_basic_byte; size = 1; break;
187 case Primitive::kPrimShort: ret = hprof_basic_short; size = 2; break;
188 default: CHECK(false);
189 case Primitive::kPrimInt: ret = hprof_basic_int; size = 4; break;
190 case Primitive::kPrimLong: ret = hprof_basic_long; size = 8; break;
191 }
192
193 if (sizeOut != NULL) {
194 *sizeOut = size;
195 }
196
197 return ret;
198}
199
200// Always called when marking objects, but only does
201// something when ctx->gc_scan_state_ is non-zero, which is usually
202// only true when marking the root set or unreachable
203// objects. Used to add rootset references to obj.
204int Hprof::MarkRootObject(const Object *obj, jobject jniObj) {
205 HprofRecord *rec = &current_record_;
206 int err; // TODO: we may return this uninitialized
207 HprofHeapTag heapTag = (HprofHeapTag)gc_scan_state_;
208
209 if (heapTag == 0) {
210 return 0;
211 }
212
213 if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->length_ >= BYTES_PER_SEGMENT) {
214 // This flushes the old segment and starts a new one.
215 StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
216 objects_in_segment_ = 0;
217 }
218
219 switch (heapTag) {
220 // ID: object ID
221 case HPROF_ROOT_UNKNOWN:
222 case HPROF_ROOT_STICKY_CLASS:
223 case HPROF_ROOT_MONITOR_USED:
224 case HPROF_ROOT_INTERNED_STRING:
225 case HPROF_ROOT_FINALIZING:
226 case HPROF_ROOT_DEBUGGER:
227 case HPROF_ROOT_REFERENCE_CLEANUP:
228 case HPROF_ROOT_VM_INTERNAL:
229 rec->AddU1(heapTag);
230 rec->AddId((HprofObjectId)obj);
231 break;
232
233 // ID: object ID
234 // ID: JNI global ref ID
235 case HPROF_ROOT_JNI_GLOBAL:
236 rec->AddU1(heapTag);
237 rec->AddId((HprofObjectId)obj);
238 rec->AddId((HprofId)jniObj);
239 break;
240
241 // ID: object ID
242 // U4: thread serial number
243 // U4: frame number in stack trace (-1 for empty)
244 case HPROF_ROOT_JNI_LOCAL:
245 case HPROF_ROOT_JNI_MONITOR:
246 case HPROF_ROOT_JAVA_FRAME:
247 rec->AddU1(heapTag);
248 rec->AddId((HprofObjectId)obj);
249 rec->AddU4(gc_thread_serial_number_);
250 rec->AddU4((uint32_t)-1);
251 break;
252
253 // ID: object ID
254 // U4: thread serial number
255 case HPROF_ROOT_NATIVE_STACK:
256 case HPROF_ROOT_THREAD_BLOCK:
257 rec->AddU1(heapTag);
258 rec->AddId((HprofObjectId)obj);
259 rec->AddU4(gc_thread_serial_number_);
260 break;
261
262 // ID: thread object ID
263 // U4: thread serial number
264 // U4: stack trace serial number
265 case HPROF_ROOT_THREAD_OBJECT:
266 rec->AddU1(heapTag);
267 rec->AddId((HprofObjectId)obj);
268 rec->AddU4(gc_thread_serial_number_);
269 rec->AddU4((uint32_t)-1); //xxx
270 break;
271
272 default:
273 err = 0;
274 break;
275 }
276
277 objects_in_segment_++;
278 return err;
279}
280
281int Hprof::StackTraceSerialNumber(const void *obj) {
282 return HPROF_NULL_STACK_TRACE;
283}
284
285int Hprof::DumpHeapObject(const Object* obj) {
286 HprofRecord *rec = &current_record_;
287 HprofHeapId desiredHeap = false ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP; // TODO: zygote objects?
288
289 if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->length_ >= BYTES_PER_SEGMENT) {
290 // This flushes the old segment and starts a new one.
291 StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
292 objects_in_segment_ = 0;
293
294 // Starting a new HEAP_DUMP resets the heap to default.
295 current_heap_ = HPROF_HEAP_DEFAULT;
296 }
297
298 if (desiredHeap != current_heap_) {
299 HprofStringId nameId;
300
301 // This object is in a different heap than the current one.
302 // Emit a HEAP_DUMP_INFO tag to change heaps.
303 rec->AddU1(HPROF_HEAP_DUMP_INFO);
304 rec->AddU4((uint32_t)desiredHeap); // uint32_t: heap id
305 switch (desiredHeap) {
306 case HPROF_HEAP_APP:
307 nameId = LookupStringId("app");
308 break;
309 case HPROF_HEAP_ZYGOTE:
310 nameId = LookupStringId("zygote");
311 break;
312 default:
313 // Internal error
314 LOG(ERROR) << "Unexpected desiredHeap";
315 nameId = LookupStringId("<ILLEGAL>");
316 break;
317 }
318 rec->AddId(nameId);
319 current_heap_ = desiredHeap;
320 }
321
322 Class* clazz = obj->GetClass();
323 if (clazz == NULL) {
324 // This object will bother HprofReader, because it has a NULL
325 // class, so just don't dump it. It could be
326 // gDvm.unlinkedJavaLangClass or it could be an object just
327 // allocated which hasn't been initialized yet.
328 } else {
329 if (obj->IsClass()) {
330 Class* thisClass = (Class*)obj;
331 // obj is a ClassObject.
332 size_t sFieldCount = thisClass->NumStaticFields();
333 if (sFieldCount != 0) {
334 int byteLength = sFieldCount*sizeof(JValue); // TODO bogus; fields are packed
335 // Create a byte array to reflect the allocation of the
336 // StaticField array at the end of this class.
337 rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
338 rec->AddId(CLASS_STATICS_ID(obj));
339 rec->AddU4(StackTraceSerialNumber(obj));
340 rec->AddU4(byteLength);
341 rec->AddU1(hprof_basic_byte);
342 for (int i = 0; i < byteLength; i++) {
343 rec->AddU1(0);
344 }
345 }
346
347 rec->AddU1(HPROF_CLASS_DUMP);
348 rec->AddId(LookupClassId(thisClass));
349 rec->AddU4(StackTraceSerialNumber(thisClass));
350 rec->AddId(LookupClassId(thisClass->GetSuperClass()));
351 rec->AddId((HprofObjectId)thisClass->GetClassLoader());
352 rec->AddId((HprofObjectId)0); // no signer
353 rec->AddId((HprofObjectId)0); // no prot domain
354 rec->AddId((HprofId)0); // reserved
355 rec->AddId((HprofId)0); // reserved
Elliott Hughesdbb40792011-11-18 17:05:22 -0800356 if (thisClass->IsClassClass()) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500357 // ClassObjects have their static fields appended, so aren't all the same size.
358 // But they're at least this size.
359 rec->AddU4(sizeof(Class)); // instance size
360 } else if (thisClass->IsArrayClass() || thisClass->IsPrimitive()) {
361 rec->AddU4(0);
362 } else {
363 rec->AddU4(thisClass->GetObjectSize()); // instance size
364 }
365
366 rec->AddU2(0); // empty const pool
367
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800368 FieldHelper fh;
369
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500370 // Static fields
371 if (sFieldCount == 0) {
372 rec->AddU2((uint16_t)0);
373 } else {
374 rec->AddU2((uint16_t)(sFieldCount+1));
375 rec->AddId(LookupStringId(STATIC_OVERHEAD_NAME));
376 rec->AddU1(hprof_basic_object);
377 rec->AddId(CLASS_STATICS_ID(obj));
378
379 for (size_t i = 0; i < sFieldCount; ++i) {
380 Field* f = thisClass->GetStaticField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800381 fh.ChangeField(f);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500382
383 size_t size;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800384 HprofBasicType t = SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), &size);
385 rec->AddId(LookupStringId(fh.GetName()));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500386 rec->AddU1(t);
387 if (size == 1) {
388 rec->AddU1(static_cast<uint8_t>(f->Get32(NULL)));
389 } else if (size == 2) {
390 rec->AddU2(static_cast<uint16_t>(f->Get32(NULL)));
391 } else if (size == 4) {
392 rec->AddU4(f->Get32(NULL));
393 } else if (size == 8) {
394 rec->AddU8(f->Get64(NULL));
395 } else {
396 CHECK(false);
397 }
398 }
399 }
400
401 // Instance fields for this class (no superclass fields)
402 int iFieldCount = thisClass->IsObjectClass() ? 0 : thisClass->NumInstanceFields();
403 rec->AddU2((uint16_t)iFieldCount);
404 for (int i = 0; i < iFieldCount; ++i) {
405 Field* f = thisClass->GetInstanceField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800406 fh.ChangeField(f);
407 HprofBasicType t = SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), NULL);
408 rec->AddId(LookupStringId(fh.GetName()));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500409 rec->AddU1(t);
410 }
411 } else if (clazz->IsArrayClass()) {
412 Array *aobj = (Array *)obj;
413 uint32_t length = aobj->GetLength();
414
415 if (obj->IsObjectArray()) {
416 // obj is an object array.
417 rec->AddU1(HPROF_OBJECT_ARRAY_DUMP);
418
419 rec->AddId((HprofObjectId)obj);
420 rec->AddU4(StackTraceSerialNumber(obj));
421 rec->AddU4(length);
422 rec->AddId(LookupClassId(clazz));
423
424 // Dump the elements, which are always objects or NULL.
Ian Rogersa15e67d2012-02-28 13:51:55 -0800425 rec->AddIdList((const HprofObjectId *)aobj->GetRawData(sizeof(Object*)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500426 } else {
427 size_t size;
428 HprofBasicType t = PrimitiveToBasicTypeAndSize(clazz->GetComponentType()->GetPrimitiveType(), &size);
429
430 // obj is a primitive array.
431#if DUMP_PRIM_DATA
432 rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
433#else
434 rec->AddU1(HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
435#endif
436
437 rec->AddId((HprofObjectId)obj);
438 rec->AddU4(StackTraceSerialNumber(obj));
439 rec->AddU4(length);
440 rec->AddU1(t);
441
442#if DUMP_PRIM_DATA
443 // Dump the raw, packed element values.
444 if (size == 1) {
Ian Rogersa15e67d2012-02-28 13:51:55 -0800445 rec->AddU1List((const uint8_t *)aobj->GetRawData(sizeof(uint8_t)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500446 } else if (size == 2) {
Ian Rogersa15e67d2012-02-28 13:51:55 -0800447 rec->AddU2List((const uint16_t *)(void *)aobj->GetRawData(sizeof(uint16_t)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500448 } else if (size == 4) {
Ian Rogersa15e67d2012-02-28 13:51:55 -0800449 rec->AddU4List((const uint32_t *)(void *)aobj->GetRawData(sizeof(uint32_t)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500450 } else if (size == 8) {
Ian Rogersa15e67d2012-02-28 13:51:55 -0800451 rec->AddU8List((const uint64_t *)aobj->GetRawData(sizeof(uint64_t)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500452 }
453#endif
454 }
455 } else {
456
457 // obj is an instance object.
458 rec->AddU1(HPROF_INSTANCE_DUMP);
459 rec->AddId((HprofObjectId)obj);
460 rec->AddU4(StackTraceSerialNumber(obj));
461 rec->AddId(LookupClassId(clazz));
462
463 // Reserve some space for the length of the instance data, which we won't
464 // know until we're done writing it.
465 size_t sizePatchOffset = rec->length_;
466 rec->AddU4(0x77777777);
467
468 // Write the instance data; fields for this class, followed by super class fields,
469 // and so on. Don't write the klass or monitor fields of Object.class.
470 const Class* sclass = clazz;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800471 FieldHelper fh;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500472 while (!sclass->IsObjectClass()) {
473 int ifieldCount = sclass->NumInstanceFields();
474 for (int i = 0; i < ifieldCount; i++) {
475 Field* f = sclass->GetInstanceField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800476 fh.ChangeField(f);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500477 size_t size;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800478 SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), &size);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500479 if (size == 1) {
480 rec->AddU1(f->Get32(obj));
481 } else if (size == 2) {
482 rec->AddU2(f->Get32(obj));
483 } else if (size == 4) {
484 rec->AddU4(f->Get32(obj));
485 } else if (size == 8) {
486 rec->AddU8(f->Get64(obj));
487 } else {
488 CHECK(false);
489 }
490 }
491
492 sclass = sclass->GetSuperClass();
493 }
494
495 // Patch the instance field length.
496 size_t savedLen = rec->length_;
497 rec->length_ = sizePatchOffset;
498 rec->AddU4(savedLen - (sizePatchOffset + 4));
499 rec->length_ = savedLen;
500 }
501 }
502
503 objects_in_segment_++;
504 return 0;
505}
506
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400507#define kHeadSuffix "-hptemp"
508
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400509// TODO: use File::WriteFully
Jesse Wilson0b075f12011-11-09 10:57:41 -0500510int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg) {
511 while (count != 0) {
512 ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
513 if (actual < 0) {
514 int err = errno;
515 LOG(ERROR) << StringPrintf("%s: write failed: %s", logMsg, strerror(err));
516 return err;
517 } else if (actual != (ssize_t) count) {
518 LOG(DEBUG) << StringPrintf("%s: partial write (will retry): (%d of %zd)",
519 logMsg, (int) actual, count);
520 buf = (const void*) (((const uint8_t*) buf) + actual);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400521 }
Jesse Wilson0b075f12011-11-09 10:57:41 -0500522 count -= actual;
523 }
524 return 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400525}
526
527/*
528 * Finish up the hprof dump. Returns true on success.
529 */
Jesse Wilson0b075f12011-11-09 10:57:41 -0500530bool Hprof::Finish() {
531 // flush the "tail" portion of the output
532 StartNewRecord(HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
533 FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400534
Jesse Wilson0b075f12011-11-09 10:57:41 -0500535 // 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 -0800536 Hprof headCtx(file_name_.c_str(), fd_, true, direct_to_ddms_);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500537 headCtx.classes_ = classes_;
538 headCtx.strings_ = strings_;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400539
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800540 LOG(INFO) << StringPrintf("hprof: dumping heap strings to \"%s\".", file_name_.c_str());
Jesse Wilson0b075f12011-11-09 10:57:41 -0500541 headCtx.DumpStrings();
542 headCtx.DumpClasses();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400543
Jesse Wilson0b075f12011-11-09 10:57:41 -0500544 // write a dummy stack trace record so the analysis tools don't freak out
545 headCtx.StartNewRecord(HPROF_TAG_STACK_TRACE, HPROF_TIME);
546 headCtx.current_record_.AddU4(HPROF_NULL_STACK_TRACE);
547 headCtx.current_record_.AddU4(HPROF_NULL_THREAD);
548 headCtx.current_record_.AddU4(0); // no frames
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400549
Jesse Wilson0b075f12011-11-09 10:57:41 -0500550 headCtx.FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400551
Jesse Wilson0b075f12011-11-09 10:57:41 -0500552 // flush to ensure memstream pointer and size are updated
553 fflush(headCtx.mem_fp_);
554 fflush(mem_fp_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400555
Jesse Wilson0b075f12011-11-09 10:57:41 -0500556 if (direct_to_ddms_) {
557 // send the data off to DDMS
558 struct iovec iov[2];
559 iov[0].iov_base = headCtx.file_data_ptr_;
560 iov[0].iov_len = headCtx.file_data_size_;
561 iov[1].iov_base = file_data_ptr_;
562 iov[1].iov_len = file_data_size_;
563 Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
564 } else {
565 // open the output file, and copy the head and tail to it.
566 CHECK_EQ(headCtx.fd_, fd_);
567
568 int outFd;
569 if (headCtx.fd_ >= 0) {
570 outFd = dup(headCtx.fd_);
571 if (outFd < 0) {
572 LOG(ERROR) << StringPrintf("dup(%d) failed: %s", headCtx.fd_, strerror(errno));
573 // continue to fail-handler below
574 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400575 } else {
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800576 outFd = open(file_name_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500577 if (outFd < 0) {
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800578 LOG(ERROR) << StringPrintf("can't open %s: %s", headCtx.file_name_.c_str(), strerror(errno));
Jesse Wilson0b075f12011-11-09 10:57:41 -0500579 // continue to fail-handler below
580 }
581 }
582 if (outFd < 0) {
583 return false;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400584 }
585
Jesse Wilson0b075f12011-11-09 10:57:41 -0500586 int result = sysWriteFully(outFd, headCtx.file_data_ptr_,
587 headCtx.file_data_size_, "hprof-head");
588 result |= sysWriteFully(outFd, file_data_ptr_, file_data_size_, "hprof-tail");
589 close(outFd);
590 if (result != 0) {
591 return false;
592 }
593 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400594
Jesse Wilson0b075f12011-11-09 10:57:41 -0500595 // throw out a log message for the benefit of "runhat"
Elliott Hughes5d78d392011-12-13 16:53:05 -0800596 LOG(INFO) << "hprof: heap dump completed (" << ((headCtx.file_data_size_ + file_data_size_ + 1023) / KB) << "KiB)";
Jesse Wilson0b075f12011-11-09 10:57:41 -0500597
598 return true;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400599}
600
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500601Hprof::~Hprof() {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500602 // we don't own ctx->fd_, do not close
603 if (mem_fp_ != NULL) {
604 fclose(mem_fp_);
605 }
606 free(current_record_.body_);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500607 free(file_data_ptr_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400608}
609
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500610void Hprof::VisitRoot(const Object* obj) {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500611 uint32_t threadId = 0; // TODO
612 /*RootType */ size_t type = 0; // TODO
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400613
Jesse Wilson0b075f12011-11-09 10:57:41 -0500614 static const HprofHeapTag xlate[] = {
615 HPROF_ROOT_UNKNOWN,
616 HPROF_ROOT_JNI_GLOBAL,
617 HPROF_ROOT_JNI_LOCAL,
618 HPROF_ROOT_JAVA_FRAME,
619 HPROF_ROOT_NATIVE_STACK,
620 HPROF_ROOT_STICKY_CLASS,
621 HPROF_ROOT_THREAD_BLOCK,
622 HPROF_ROOT_MONITOR_USED,
623 HPROF_ROOT_THREAD_OBJECT,
624 HPROF_ROOT_INTERNED_STRING,
625 HPROF_ROOT_FINALIZING,
626 HPROF_ROOT_DEBUGGER,
627 HPROF_ROOT_REFERENCE_CLEANUP,
628 HPROF_ROOT_VM_INTERNAL,
629 HPROF_ROOT_JNI_MONITOR,
630 };
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400631
Jesse Wilson0b075f12011-11-09 10:57:41 -0500632 CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag));
633 if (obj == NULL) {
634 return;
635 }
636 gc_scan_state_ = xlate[type];
637 gc_thread_serial_number_ = threadId;
638 MarkRootObject(obj, 0);
639 gc_scan_state_ = 0;
640 gc_thread_serial_number_ = 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400641}
642
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500643HprofStringId Hprof::LookupStringId(String* string) {
644 return LookupStringId(string->ToModifiedUtf8());
645}
646
647HprofStringId Hprof::LookupStringId(const char* string) {
648 return LookupStringId(std::string(string));
649}
650
651HprofStringId Hprof::LookupStringId(std::string string) {
652 if (strings_.find(string) == strings_.end()) {
653 strings_[string] = next_string_id_++;
654 }
655 return strings_[string];
656}
657
658int Hprof::DumpStrings() {
659 HprofRecord *rec = &current_record_;
660
661 for (StringMapIterator it = strings_.begin(); it != strings_.end(); ++it) {
Elliott Hughes95572412011-12-13 18:14:20 -0800662 std::string string((*it).first);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500663 size_t id = (*it).second;
664
665 int err = StartNewRecord(HPROF_TAG_STRING, HPROF_TIME);
666 if (err != 0) {
667 return err;
668 }
669
670 // STRING format:
671 // ID: ID for this string
672 // U1*: UTF8 characters for string (NOT NULL terminated)
673 // (the record format encodes the length)
674 err = rec->AddU4(id);
675 if (err != 0) {
676 return err;
677 }
678 err = rec->AddUtf8String(string.c_str());
679 if (err != 0) {
680 return err;
681 }
682 }
683
684 return 0;
685}
686
687HprofStringId Hprof::LookupClassNameId(Class* clazz) {
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800688 return LookupStringId(PrettyDescriptor(clazz));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500689}
690
691HprofClassObjectId Hprof::LookupClassId(Class* clazz) {
692 if (clazz == NULL) {
693 // clazz is the superclass of java.lang.Object or a primitive
694 return (HprofClassObjectId)0;
695 }
696
697 std::pair<ClassSetIterator, bool> result = classes_.insert(clazz);
698 Class* present = *result.first;
699
700 // Make sure that we've assigned a string ID for this class' name
701 LookupClassNameId(clazz);
702
703 CHECK_EQ(present, clazz);
704 return (HprofStringId) present;
705}
706
707int Hprof::DumpClasses() {
708 HprofRecord *rec = &current_record_;
709 uint32_t nextSerialNumber = 1;
710
711 for (ClassSetIterator it = classes_.begin(); it != classes_.end(); ++it) {
712 Class* clazz = *it;
713 CHECK(clazz != NULL);
714
715 int err = StartNewRecord(HPROF_TAG_LOAD_CLASS, HPROF_TIME);
716 if (err != 0) {
717 return err;
718 }
719
720 // LOAD CLASS format:
721 // U4: class serial number (always > 0)
722 // ID: class object ID. We use the address of the class object structure as its ID.
723 // U4: stack trace serial number
724 // ID: class name string ID
725 rec->AddU4(nextSerialNumber++);
726 rec->AddId((HprofClassObjectId) clazz);
727 rec->AddU4(HPROF_NULL_STACK_TRACE);
728 rec->AddId(LookupClassNameId(clazz));
729 }
730
731 return 0;
732}
733
734void HprofRootVisitor(const Object* obj, void* arg) {
735 CHECK(arg != NULL);
736 Hprof* hprof = (Hprof*)arg;
737 hprof->VisitRoot(obj);
738}
739
740void HprofBitmapCallback(Object *obj, void *arg) {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500741 CHECK(obj != NULL);
742 CHECK(arg != NULL);
743 Hprof *hprof = (Hprof*)arg;
744 hprof->DumpHeapObject(obj);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400745}
746
747/*
748 * Walk the roots and heap writing heap information to the specified
749 * file.
750 *
751 * If "fd" is >= 0, the output will be written to that file descriptor.
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500752 * Otherwise, "file_name_" is used to create an output file.
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400753 *
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500754 * If "direct_to_ddms_" is set, the other arguments are ignored, and data is
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400755 * sent directly to DDMS.
756 *
757 * Returns 0 on success, or an error code on failure.
758 */
Jesse Wilson0b075f12011-11-09 10:57:41 -0500759int DumpHeap(const char* fileName, int fd, bool directToDdms) {
760 CHECK(fileName != NULL);
Elliott Hughesffb465f2012-03-01 18:46:05 -0800761 ScopedHeapLock heap_lock;
Jesse Wilson0b075f12011-11-09 10:57:41 -0500762 ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400763
Jesse Wilson0b075f12011-11-09 10:57:41 -0500764 ThreadList* thread_list = Runtime::Current()->GetThreadList();
765 thread_list->SuspendAll();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400766
Elliott Hughesb3bd5f02012-03-08 21:05:27 -0800767 Runtime* runtime = Runtime::Current();
Jesse Wilson0b075f12011-11-09 10:57:41 -0500768 Hprof hprof(fileName, fd, false, directToDdms);
Elliott Hughesb3bd5f02012-03-08 21:05:27 -0800769 runtime->VisitRoots(HprofRootVisitor, &hprof);
770 runtime->GetHeap()->GetLiveBits()->Walk(HprofBitmapCallback, &hprof);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500771 // 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