blob: 02f80c1646c54071f4df16df1f18e38bc0b29b0d [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
Elliott Hughes7b9d9962012-04-20 18:48:18 -070073 PLOG(FATAL) << "open_memstream failed";
Jesse Wilson0c54ac12011-11-09 15:14:05 -050074 }
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.
Elliott Hughes7b9d9962012-04-20 18:48:18 -070097 timeval now;
Jesse Wilson0c54ac12011-11-09 15:14:05 -050098 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.
Elliott Hughese84278b2012-03-22 10:06:53 -0700148#define CLASS_STATICS_ID(c) ((HprofObjectId)(((uint32_t)(c)) | 1))
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500149
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) {
Elliott Hughes73e66f72012-05-09 09:34:45 -0700204 HprofRecord* rec = &current_record_;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500205 HprofHeapTag heapTag = (HprofHeapTag)gc_scan_state_;
206
207 if (heapTag == 0) {
208 return 0;
209 }
210
211 if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->length_ >= BYTES_PER_SEGMENT) {
212 // This flushes the old segment and starts a new one.
213 StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
214 objects_in_segment_ = 0;
215 }
216
217 switch (heapTag) {
218 // ID: object ID
219 case HPROF_ROOT_UNKNOWN:
220 case HPROF_ROOT_STICKY_CLASS:
221 case HPROF_ROOT_MONITOR_USED:
222 case HPROF_ROOT_INTERNED_STRING:
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500223 case HPROF_ROOT_DEBUGGER:
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500224 case HPROF_ROOT_VM_INTERNAL:
225 rec->AddU1(heapTag);
226 rec->AddId((HprofObjectId)obj);
227 break;
228
229 // ID: object ID
230 // ID: JNI global ref ID
231 case HPROF_ROOT_JNI_GLOBAL:
232 rec->AddU1(heapTag);
233 rec->AddId((HprofObjectId)obj);
234 rec->AddId((HprofId)jniObj);
235 break;
236
237 // ID: object ID
238 // U4: thread serial number
239 // U4: frame number in stack trace (-1 for empty)
240 case HPROF_ROOT_JNI_LOCAL:
241 case HPROF_ROOT_JNI_MONITOR:
242 case HPROF_ROOT_JAVA_FRAME:
243 rec->AddU1(heapTag);
244 rec->AddId((HprofObjectId)obj);
245 rec->AddU4(gc_thread_serial_number_);
246 rec->AddU4((uint32_t)-1);
247 break;
248
249 // ID: object ID
250 // U4: thread serial number
251 case HPROF_ROOT_NATIVE_STACK:
252 case HPROF_ROOT_THREAD_BLOCK:
253 rec->AddU1(heapTag);
254 rec->AddId((HprofObjectId)obj);
255 rec->AddU4(gc_thread_serial_number_);
256 break;
257
258 // ID: thread object ID
259 // U4: thread serial number
260 // U4: stack trace serial number
261 case HPROF_ROOT_THREAD_OBJECT:
262 rec->AddU1(heapTag);
263 rec->AddId((HprofObjectId)obj);
264 rec->AddU4(gc_thread_serial_number_);
265 rec->AddU4((uint32_t)-1); //xxx
266 break;
267
Elliott Hughes73e66f72012-05-09 09:34:45 -0700268 case HPROF_CLASS_DUMP:
269 case HPROF_INSTANCE_DUMP:
270 case HPROF_OBJECT_ARRAY_DUMP:
271 case HPROF_PRIMITIVE_ARRAY_DUMP:
272 case HPROF_HEAP_DUMP_INFO:
273 case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP:
274 // Ignored.
275 break;
276
277 case HPROF_ROOT_FINALIZING:
278 case HPROF_ROOT_REFERENCE_CLEANUP:
279 case HPROF_UNREACHABLE:
280 LOG(FATAL) << "obsolete tag " << static_cast<int>(heapTag);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500281 break;
282 }
283
284 objects_in_segment_++;
Elliott Hughes73e66f72012-05-09 09:34:45 -0700285 return 0;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500286}
287
Elliott Hughes1bac54f2012-03-16 12:48:31 -0700288int Hprof::StackTraceSerialNumber(const void* /*obj*/) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500289 return HPROF_NULL_STACK_TRACE;
290}
291
292int Hprof::DumpHeapObject(const Object* obj) {
293 HprofRecord *rec = &current_record_;
294 HprofHeapId desiredHeap = false ? HPROF_HEAP_ZYGOTE : HPROF_HEAP_APP; // TODO: zygote objects?
295
296 if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->length_ >= BYTES_PER_SEGMENT) {
297 // This flushes the old segment and starts a new one.
298 StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, HPROF_TIME);
299 objects_in_segment_ = 0;
300
301 // Starting a new HEAP_DUMP resets the heap to default.
302 current_heap_ = HPROF_HEAP_DEFAULT;
303 }
304
305 if (desiredHeap != current_heap_) {
306 HprofStringId nameId;
307
308 // This object is in a different heap than the current one.
309 // Emit a HEAP_DUMP_INFO tag to change heaps.
310 rec->AddU1(HPROF_HEAP_DUMP_INFO);
311 rec->AddU4((uint32_t)desiredHeap); // uint32_t: heap id
312 switch (desiredHeap) {
313 case HPROF_HEAP_APP:
314 nameId = LookupStringId("app");
315 break;
316 case HPROF_HEAP_ZYGOTE:
317 nameId = LookupStringId("zygote");
318 break;
319 default:
320 // Internal error
321 LOG(ERROR) << "Unexpected desiredHeap";
322 nameId = LookupStringId("<ILLEGAL>");
323 break;
324 }
325 rec->AddId(nameId);
326 current_heap_ = desiredHeap;
327 }
328
Elliott Hughese84278b2012-03-22 10:06:53 -0700329 Class* c = obj->GetClass();
330 if (c == NULL) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500331 // This object will bother HprofReader, because it has a NULL
332 // class, so just don't dump it. It could be
333 // gDvm.unlinkedJavaLangClass or it could be an object just
334 // allocated which hasn't been initialized yet.
335 } else {
336 if (obj->IsClass()) {
337 Class* thisClass = (Class*)obj;
338 // obj is a ClassObject.
339 size_t sFieldCount = thisClass->NumStaticFields();
340 if (sFieldCount != 0) {
341 int byteLength = sFieldCount*sizeof(JValue); // TODO bogus; fields are packed
342 // Create a byte array to reflect the allocation of the
343 // StaticField array at the end of this class.
344 rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
345 rec->AddId(CLASS_STATICS_ID(obj));
346 rec->AddU4(StackTraceSerialNumber(obj));
347 rec->AddU4(byteLength);
348 rec->AddU1(hprof_basic_byte);
349 for (int i = 0; i < byteLength; i++) {
350 rec->AddU1(0);
351 }
352 }
353
354 rec->AddU1(HPROF_CLASS_DUMP);
355 rec->AddId(LookupClassId(thisClass));
356 rec->AddU4(StackTraceSerialNumber(thisClass));
357 rec->AddId(LookupClassId(thisClass->GetSuperClass()));
358 rec->AddId((HprofObjectId)thisClass->GetClassLoader());
359 rec->AddId((HprofObjectId)0); // no signer
360 rec->AddId((HprofObjectId)0); // no prot domain
361 rec->AddId((HprofId)0); // reserved
362 rec->AddId((HprofId)0); // reserved
Elliott Hughesdbb40792011-11-18 17:05:22 -0800363 if (thisClass->IsClassClass()) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500364 // ClassObjects have their static fields appended, so aren't all the same size.
365 // But they're at least this size.
366 rec->AddU4(sizeof(Class)); // instance size
367 } else if (thisClass->IsArrayClass() || thisClass->IsPrimitive()) {
368 rec->AddU4(0);
369 } else {
370 rec->AddU4(thisClass->GetObjectSize()); // instance size
371 }
372
373 rec->AddU2(0); // empty const pool
374
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800375 FieldHelper fh;
376
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500377 // Static fields
378 if (sFieldCount == 0) {
379 rec->AddU2((uint16_t)0);
380 } else {
381 rec->AddU2((uint16_t)(sFieldCount+1));
382 rec->AddId(LookupStringId(STATIC_OVERHEAD_NAME));
383 rec->AddU1(hprof_basic_object);
384 rec->AddId(CLASS_STATICS_ID(obj));
385
386 for (size_t i = 0; i < sFieldCount; ++i) {
387 Field* f = thisClass->GetStaticField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800388 fh.ChangeField(f);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500389
390 size_t size;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800391 HprofBasicType t = SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), &size);
392 rec->AddId(LookupStringId(fh.GetName()));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500393 rec->AddU1(t);
394 if (size == 1) {
395 rec->AddU1(static_cast<uint8_t>(f->Get32(NULL)));
396 } else if (size == 2) {
397 rec->AddU2(static_cast<uint16_t>(f->Get32(NULL)));
398 } else if (size == 4) {
399 rec->AddU4(f->Get32(NULL));
400 } else if (size == 8) {
401 rec->AddU8(f->Get64(NULL));
402 } else {
403 CHECK(false);
404 }
405 }
406 }
407
408 // Instance fields for this class (no superclass fields)
409 int iFieldCount = thisClass->IsObjectClass() ? 0 : thisClass->NumInstanceFields();
410 rec->AddU2((uint16_t)iFieldCount);
411 for (int i = 0; i < iFieldCount; ++i) {
412 Field* f = thisClass->GetInstanceField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800413 fh.ChangeField(f);
414 HprofBasicType t = SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), NULL);
415 rec->AddId(LookupStringId(fh.GetName()));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500416 rec->AddU1(t);
417 }
Elliott Hughese84278b2012-03-22 10:06:53 -0700418 } else if (c->IsArrayClass()) {
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500419 Array *aobj = (Array *)obj;
420 uint32_t length = aobj->GetLength();
421
422 if (obj->IsObjectArray()) {
423 // obj is an object array.
424 rec->AddU1(HPROF_OBJECT_ARRAY_DUMP);
425
426 rec->AddId((HprofObjectId)obj);
427 rec->AddU4(StackTraceSerialNumber(obj));
428 rec->AddU4(length);
Elliott Hughese84278b2012-03-22 10:06:53 -0700429 rec->AddId(LookupClassId(c));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500430
431 // Dump the elements, which are always objects or NULL.
Ian Rogersa15e67d2012-02-28 13:51:55 -0800432 rec->AddIdList((const HprofObjectId *)aobj->GetRawData(sizeof(Object*)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500433 } else {
434 size_t size;
Elliott Hughese84278b2012-03-22 10:06:53 -0700435 HprofBasicType t = PrimitiveToBasicTypeAndSize(c->GetComponentType()->GetPrimitiveType(), &size);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500436
437 // obj is a primitive array.
438#if DUMP_PRIM_DATA
439 rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP);
440#else
441 rec->AddU1(HPROF_PRIMITIVE_ARRAY_NODATA_DUMP);
442#endif
443
444 rec->AddId((HprofObjectId)obj);
445 rec->AddU4(StackTraceSerialNumber(obj));
446 rec->AddU4(length);
447 rec->AddU1(t);
448
449#if DUMP_PRIM_DATA
450 // Dump the raw, packed element values.
451 if (size == 1) {
Ian Rogersa15e67d2012-02-28 13:51:55 -0800452 rec->AddU1List((const uint8_t *)aobj->GetRawData(sizeof(uint8_t)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500453 } else if (size == 2) {
Ian Rogersa15e67d2012-02-28 13:51:55 -0800454 rec->AddU2List((const uint16_t *)(void *)aobj->GetRawData(sizeof(uint16_t)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500455 } else if (size == 4) {
Ian Rogersa15e67d2012-02-28 13:51:55 -0800456 rec->AddU4List((const uint32_t *)(void *)aobj->GetRawData(sizeof(uint32_t)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500457 } else if (size == 8) {
Ian Rogersa15e67d2012-02-28 13:51:55 -0800458 rec->AddU8List((const uint64_t *)aobj->GetRawData(sizeof(uint64_t)), length);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500459 }
460#endif
461 }
462 } else {
463
464 // obj is an instance object.
465 rec->AddU1(HPROF_INSTANCE_DUMP);
466 rec->AddId((HprofObjectId)obj);
467 rec->AddU4(StackTraceSerialNumber(obj));
Elliott Hughese84278b2012-03-22 10:06:53 -0700468 rec->AddId(LookupClassId(c));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500469
470 // Reserve some space for the length of the instance data, which we won't
471 // know until we're done writing it.
472 size_t sizePatchOffset = rec->length_;
473 rec->AddU4(0x77777777);
474
475 // Write the instance data; fields for this class, followed by super class fields,
476 // and so on. Don't write the klass or monitor fields of Object.class.
Elliott Hughese84278b2012-03-22 10:06:53 -0700477 const Class* sclass = c;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800478 FieldHelper fh;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500479 while (!sclass->IsObjectClass()) {
480 int ifieldCount = sclass->NumInstanceFields();
481 for (int i = 0; i < ifieldCount; i++) {
482 Field* f = sclass->GetInstanceField(i);
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800483 fh.ChangeField(f);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500484 size_t size;
Ian Rogers6d4d9fc2011-11-30 16:24:48 -0800485 SignatureToBasicTypeAndSize(fh.GetTypeDescriptor(), &size);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500486 if (size == 1) {
487 rec->AddU1(f->Get32(obj));
488 } else if (size == 2) {
489 rec->AddU2(f->Get32(obj));
490 } else if (size == 4) {
491 rec->AddU4(f->Get32(obj));
492 } else if (size == 8) {
493 rec->AddU8(f->Get64(obj));
494 } else {
495 CHECK(false);
496 }
497 }
498
499 sclass = sclass->GetSuperClass();
500 }
501
502 // Patch the instance field length.
503 size_t savedLen = rec->length_;
504 rec->length_ = sizePatchOffset;
505 rec->AddU4(savedLen - (sizePatchOffset + 4));
506 rec->length_ = savedLen;
507 }
508 }
509
510 objects_in_segment_++;
511 return 0;
512}
513
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400514#define kHeadSuffix "-hptemp"
515
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400516// TODO: use File::WriteFully
Jesse Wilson0b075f12011-11-09 10:57:41 -0500517int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg) {
518 while (count != 0) {
519 ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
520 if (actual < 0) {
521 int err = errno;
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700522 PLOG(ERROR) << StringPrintf("%s: write failed", logMsg);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500523 return err;
524 } else if (actual != (ssize_t) count) {
525 LOG(DEBUG) << StringPrintf("%s: partial write (will retry): (%d of %zd)",
526 logMsg, (int) actual, count);
527 buf = (const void*) (((const uint8_t*) buf) + actual);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400528 }
Jesse Wilson0b075f12011-11-09 10:57:41 -0500529 count -= actual;
530 }
531 return 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400532}
533
534/*
535 * Finish up the hprof dump. Returns true on success.
536 */
Jesse Wilson0b075f12011-11-09 10:57:41 -0500537bool Hprof::Finish() {
538 // flush the "tail" portion of the output
539 StartNewRecord(HPROF_TAG_HEAP_DUMP_END, HPROF_TIME);
540 FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400541
Jesse Wilson0b075f12011-11-09 10:57:41 -0500542 // 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 -0800543 Hprof headCtx(file_name_.c_str(), fd_, true, direct_to_ddms_);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500544 headCtx.classes_ = classes_;
545 headCtx.strings_ = strings_;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400546
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800547 LOG(INFO) << StringPrintf("hprof: dumping heap strings to \"%s\".", file_name_.c_str());
Jesse Wilson0b075f12011-11-09 10:57:41 -0500548 headCtx.DumpStrings();
549 headCtx.DumpClasses();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400550
Jesse Wilson0b075f12011-11-09 10:57:41 -0500551 // write a dummy stack trace record so the analysis tools don't freak out
552 headCtx.StartNewRecord(HPROF_TAG_STACK_TRACE, HPROF_TIME);
553 headCtx.current_record_.AddU4(HPROF_NULL_STACK_TRACE);
554 headCtx.current_record_.AddU4(HPROF_NULL_THREAD);
555 headCtx.current_record_.AddU4(0); // no frames
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400556
Jesse Wilson0b075f12011-11-09 10:57:41 -0500557 headCtx.FlushCurrentRecord();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400558
Jesse Wilson0b075f12011-11-09 10:57:41 -0500559 // flush to ensure memstream pointer and size are updated
560 fflush(headCtx.mem_fp_);
561 fflush(mem_fp_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400562
Jesse Wilson0b075f12011-11-09 10:57:41 -0500563 if (direct_to_ddms_) {
564 // send the data off to DDMS
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700565 iovec iov[2];
Jesse Wilson0b075f12011-11-09 10:57:41 -0500566 iov[0].iov_base = headCtx.file_data_ptr_;
567 iov[0].iov_len = headCtx.file_data_size_;
568 iov[1].iov_base = file_data_ptr_;
569 iov[1].iov_len = file_data_size_;
570 Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2);
571 } else {
572 // open the output file, and copy the head and tail to it.
573 CHECK_EQ(headCtx.fd_, fd_);
574
575 int outFd;
576 if (headCtx.fd_ >= 0) {
577 outFd = dup(headCtx.fd_);
578 if (outFd < 0) {
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700579 PLOG(ERROR) << StringPrintf("dup(%d) failed", headCtx.fd_);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500580 // continue to fail-handler below
581 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400582 } else {
Elliott Hughes7b3cdfc2011-12-08 21:28:17 -0800583 outFd = open(file_name_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500584 if (outFd < 0) {
Elliott Hughes7b9d9962012-04-20 18:48:18 -0700585 PLOG(ERROR) << StringPrintf("can't open \"%s\"", headCtx.file_name_.c_str());
Jesse Wilson0b075f12011-11-09 10:57:41 -0500586 // continue to fail-handler below
587 }
588 }
589 if (outFd < 0) {
590 return false;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400591 }
592
Jesse Wilson0b075f12011-11-09 10:57:41 -0500593 int result = sysWriteFully(outFd, headCtx.file_data_ptr_,
594 headCtx.file_data_size_, "hprof-head");
595 result |= sysWriteFully(outFd, file_data_ptr_, file_data_size_, "hprof-tail");
596 close(outFd);
597 if (result != 0) {
598 return false;
599 }
600 }
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400601
Jesse Wilson0b075f12011-11-09 10:57:41 -0500602 // throw out a log message for the benefit of "runhat"
Elliott Hughes409d2732012-04-03 13:34:44 -0700603 LOG(INFO) << "hprof: heap dump completed (" << PrettySize(headCtx.file_data_size_ + file_data_size_ + 1023) << ")";
Jesse Wilson0b075f12011-11-09 10:57:41 -0500604
605 return true;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400606}
607
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500608Hprof::~Hprof() {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500609 // we don't own ctx->fd_, do not close
610 if (mem_fp_ != NULL) {
611 fclose(mem_fp_);
612 }
613 free(current_record_.body_);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500614 free(file_data_ptr_);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400615}
616
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500617void Hprof::VisitRoot(const Object* obj) {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500618 uint32_t threadId = 0; // TODO
619 /*RootType */ size_t type = 0; // TODO
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400620
Jesse Wilson0b075f12011-11-09 10:57:41 -0500621 static const HprofHeapTag xlate[] = {
622 HPROF_ROOT_UNKNOWN,
623 HPROF_ROOT_JNI_GLOBAL,
624 HPROF_ROOT_JNI_LOCAL,
625 HPROF_ROOT_JAVA_FRAME,
626 HPROF_ROOT_NATIVE_STACK,
627 HPROF_ROOT_STICKY_CLASS,
628 HPROF_ROOT_THREAD_BLOCK,
629 HPROF_ROOT_MONITOR_USED,
630 HPROF_ROOT_THREAD_OBJECT,
631 HPROF_ROOT_INTERNED_STRING,
632 HPROF_ROOT_FINALIZING,
633 HPROF_ROOT_DEBUGGER,
634 HPROF_ROOT_REFERENCE_CLEANUP,
635 HPROF_ROOT_VM_INTERNAL,
636 HPROF_ROOT_JNI_MONITOR,
637 };
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400638
Jesse Wilson0b075f12011-11-09 10:57:41 -0500639 CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag));
640 if (obj == NULL) {
641 return;
642 }
643 gc_scan_state_ = xlate[type];
644 gc_thread_serial_number_ = threadId;
645 MarkRootObject(obj, 0);
646 gc_scan_state_ = 0;
647 gc_thread_serial_number_ = 0;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400648}
649
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500650HprofStringId Hprof::LookupStringId(String* string) {
651 return LookupStringId(string->ToModifiedUtf8());
652}
653
654HprofStringId Hprof::LookupStringId(const char* string) {
655 return LookupStringId(std::string(string));
656}
657
Elliott Hughesae80b492012-04-24 10:43:17 -0700658HprofStringId Hprof::LookupStringId(const std::string& string) {
Elliott Hughesa0e18062012-04-13 15:59:59 -0700659 StringMapIterator it = strings_.find(string);
660 if (it != strings_.end()) {
661 return it->second;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500662 }
Elliott Hughesa0e18062012-04-13 15:59:59 -0700663 HprofStringId id = next_string_id_++;
664 strings_.Put(string, id);
665 return id;
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500666}
667
668int Hprof::DumpStrings() {
669 HprofRecord *rec = &current_record_;
670
671 for (StringMapIterator it = strings_.begin(); it != strings_.end(); ++it) {
Elliott Hughes95572412011-12-13 18:14:20 -0800672 std::string string((*it).first);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500673 size_t id = (*it).second;
674
675 int err = StartNewRecord(HPROF_TAG_STRING, HPROF_TIME);
676 if (err != 0) {
677 return err;
678 }
679
680 // STRING format:
681 // ID: ID for this string
682 // U1*: UTF8 characters for string (NOT NULL terminated)
683 // (the record format encodes the length)
684 err = rec->AddU4(id);
685 if (err != 0) {
686 return err;
687 }
688 err = rec->AddUtf8String(string.c_str());
689 if (err != 0) {
690 return err;
691 }
692 }
693
694 return 0;
695}
696
Elliott Hughese84278b2012-03-22 10:06:53 -0700697HprofStringId Hprof::LookupClassNameId(Class* c) {
698 return LookupStringId(PrettyDescriptor(c));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500699}
700
Elliott Hughese84278b2012-03-22 10:06:53 -0700701HprofClassObjectId Hprof::LookupClassId(Class* c) {
702 if (c == NULL) {
703 // c is the superclass of java.lang.Object or a primitive
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500704 return (HprofClassObjectId)0;
705 }
706
Elliott Hughese84278b2012-03-22 10:06:53 -0700707 std::pair<ClassSetIterator, bool> result = classes_.insert(c);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500708 Class* present = *result.first;
709
710 // Make sure that we've assigned a string ID for this class' name
Elliott Hughese84278b2012-03-22 10:06:53 -0700711 LookupClassNameId(c);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500712
Elliott Hughese84278b2012-03-22 10:06:53 -0700713 CHECK_EQ(present, c);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500714 return (HprofStringId) present;
715}
716
717int Hprof::DumpClasses() {
718 HprofRecord *rec = &current_record_;
719 uint32_t nextSerialNumber = 1;
720
721 for (ClassSetIterator it = classes_.begin(); it != classes_.end(); ++it) {
Elliott Hughese84278b2012-03-22 10:06:53 -0700722 Class* c = *it;
723 CHECK(c != NULL);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500724
725 int err = StartNewRecord(HPROF_TAG_LOAD_CLASS, HPROF_TIME);
726 if (err != 0) {
727 return err;
728 }
729
730 // LOAD CLASS format:
731 // U4: class serial number (always > 0)
732 // ID: class object ID. We use the address of the class object structure as its ID.
733 // U4: stack trace serial number
734 // ID: class name string ID
735 rec->AddU4(nextSerialNumber++);
Elliott Hughese84278b2012-03-22 10:06:53 -0700736 rec->AddId((HprofClassObjectId) c);
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500737 rec->AddU4(HPROF_NULL_STACK_TRACE);
Elliott Hughese84278b2012-03-22 10:06:53 -0700738 rec->AddId(LookupClassNameId(c));
Jesse Wilson0c54ac12011-11-09 15:14:05 -0500739 }
740
741 return 0;
742}
743
744void HprofRootVisitor(const Object* obj, void* arg) {
745 CHECK(arg != NULL);
746 Hprof* hprof = (Hprof*)arg;
747 hprof->VisitRoot(obj);
748}
749
750void HprofBitmapCallback(Object *obj, void *arg) {
Jesse Wilson0b075f12011-11-09 10:57:41 -0500751 CHECK(obj != NULL);
752 CHECK(arg != NULL);
753 Hprof *hprof = (Hprof*)arg;
754 hprof->DumpHeapObject(obj);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400755}
756
757/*
758 * Walk the roots and heap writing heap information to the specified
759 * file.
760 *
761 * If "fd" is >= 0, the output will be written to that file descriptor.
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500762 * Otherwise, "file_name_" is used to create an output file.
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400763 *
Jesse Wilson3aa66fd2011-11-08 20:53:40 -0500764 * If "direct_to_ddms_" is set, the other arguments are ignored, and data is
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400765 * sent directly to DDMS.
766 *
767 * Returns 0 on success, or an error code on failure.
768 */
Jesse Wilson0b075f12011-11-09 10:57:41 -0500769int DumpHeap(const char* fileName, int fd, bool directToDdms) {
770 CHECK(fileName != NULL);
Elliott Hughesffb465f2012-03-01 18:46:05 -0800771 ScopedHeapLock heap_lock;
Elliott Hughes34e06962012-04-09 13:55:55 -0700772 ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400773
Jesse Wilson0b075f12011-11-09 10:57:41 -0500774 ThreadList* thread_list = Runtime::Current()->GetThreadList();
775 thread_list->SuspendAll();
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400776
Elliott Hughesb3bd5f02012-03-08 21:05:27 -0800777 Runtime* runtime = Runtime::Current();
Jesse Wilson0b075f12011-11-09 10:57:41 -0500778 Hprof hprof(fileName, fd, false, directToDdms);
Elliott Hughesb3bd5f02012-03-08 21:05:27 -0800779 runtime->VisitRoots(HprofRootVisitor, &hprof);
780 runtime->GetHeap()->GetLiveBits()->Walk(HprofBitmapCallback, &hprof);
Jesse Wilson0b075f12011-11-09 10:57:41 -0500781 // TODO: write a HEAP_SUMMARY record
782 int success = hprof.Finish() ? 0 : -1;
783 thread_list->ResumeAll();
784 return success;
Jesse Wilsonc4824e62011-11-01 14:39:04 -0400785}
786
787} // namespace hprof
788
789} // namespace art