OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 #include "vm/heap_profiler.h" | |
6 | |
7 #include "vm/dart_api_state.h" | |
8 #include "vm/object.h" | |
9 #include "vm/raw_object.h" | |
10 #include "vm/stack_frame.h" | |
11 #include "vm/unicode.h" | |
12 | |
13 namespace dart { | |
14 | |
15 HeapProfiler::Buffer::~Buffer() { | |
16 delete[] data_; | |
17 } | |
18 | |
19 | |
20 void HeapProfiler::Buffer::Write(const uint8_t* data, intptr_t size) { | |
21 EnsureCapacity(size); | |
22 memmove(&data_[size_], data, size); | |
23 size_ += size; | |
24 } | |
25 | |
26 | |
27 void HeapProfiler::Buffer::EnsureCapacity(intptr_t size) { | |
28 if ((size + size_) > capacity_) { | |
29 intptr_t new_capacity = Utils::RoundUpToPowerOfTwo(capacity_ + size); | |
30 uint8_t* new_data = new uint8_t[new_capacity]; | |
31 memmove(new_data, data_, size_); | |
32 capacity_ = new_capacity; | |
33 delete[] data_; | |
34 data_ = new_data; | |
35 } | |
36 } | |
37 | |
38 | |
39 void HeapProfiler::Record::Write(const uint8_t* value, intptr_t size) { | |
40 body_.Write(value, size); | |
41 } | |
42 | |
43 | |
44 void HeapProfiler::Record::Write8(uint8_t value) { | |
45 body_.Write(&value, sizeof(value)); | |
46 } | |
47 | |
48 | |
49 void HeapProfiler::Record::Write16(uint16_t value) { | |
50 value = htons(value); | |
51 body_.Write(reinterpret_cast<uint8_t*>(&value), sizeof(value)); | |
52 } | |
53 | |
54 | |
55 void HeapProfiler::Record::Write32(uint32_t value) { | |
56 value = htonl(value); | |
57 body_.Write(reinterpret_cast<uint8_t*>(&value), sizeof(value)); | |
58 } | |
59 | |
60 | |
61 void HeapProfiler::Record::Write64(uint64_t value) { | |
62 uint16_t x = 0xFF; | |
63 if (*reinterpret_cast<uint8_t*>(&x) == 0xFF) { | |
64 uint64_t hi = static_cast<uint64_t>(htonl(value & 0xFFFFFFFF)) << 32; | |
65 uint64_t lo = htonl(value >> 32); | |
66 value = hi | lo; | |
67 } | |
68 body_.Write(reinterpret_cast<uint8_t*>(&value), sizeof(value)); | |
69 } | |
70 | |
71 | |
72 void HeapProfiler::Record::WriteObjectId(const void* value) { | |
73 Write64(reinterpret_cast<uint64_t>(value)); | |
74 } | |
75 | |
76 | |
77 HeapProfiler::SubRecord::SubRecord(uint8_t sub_tag, HeapProfiler* profiler) | |
78 : record_(profiler->heap_dump_record_) { | |
79 record_->Write8(sub_tag); | |
80 } | |
81 | |
82 | |
83 HeapProfiler::SubRecord::~SubRecord() { | |
84 } | |
85 | |
86 | |
87 void HeapProfiler::SubRecord::Write(const uint8_t* value, intptr_t size) { | |
88 record_->Write(value, size); | |
89 } | |
90 | |
91 | |
92 void HeapProfiler::SubRecord::Write8(uint8_t value) { | |
93 record_->Write8(value); | |
94 } | |
95 | |
96 | |
97 void HeapProfiler::SubRecord::Write16(uint16_t value) { | |
98 record_->Write16(value); | |
99 } | |
100 | |
101 | |
102 void HeapProfiler::SubRecord::Write32(uint32_t value) { | |
103 record_->Write32(value); | |
104 } | |
105 | |
106 | |
107 void HeapProfiler::SubRecord::Write64(uint64_t value) { | |
108 record_->Write64(value); | |
109 } | |
110 | |
111 | |
112 void HeapProfiler::SubRecord::WriteObjectId(const void* value) { | |
113 record_->WriteObjectId(value); | |
114 } | |
115 | |
116 | |
117 HeapProfiler::HeapProfiler(Dart_FileWriteCallback callback, void* stream) | |
118 : write_callback_(callback), | |
119 output_stream_(stream), | |
120 heap_dump_record_(NULL) { | |
121 WriteHeader(); | |
122 WriteStackTrace(); | |
123 WriteFakeLoadClass(kJavaLangClass, "java.lang.Class"); | |
124 WriteFakeLoadClass(kJavaLangClassLoader, "java.lang.ClassLoader"); | |
125 WriteFakeLoadClass(kJavaLangObject, "java.lang.Object"); | |
126 WriteFakeLoadClass(kJavaLangString, "java.lang.String"); | |
127 WriteFakeLoadClass(kArrayObject, "Object[]"); | |
128 WriteFakeLoadClass(kArrayBoolean, "bool[]"); | |
129 WriteFakeLoadClass(kArrayChar, "char[]"); | |
130 WriteFakeLoadClass(kArrayFloat, "float[]"); | |
131 WriteFakeLoadClass(kArrayDouble, "double[]"); | |
132 WriteFakeLoadClass(kArrayByte, "byte[]"); | |
133 WriteFakeLoadClass(kArrayShort, "short[]"); | |
134 WriteFakeLoadClass(kArrayInt, "int[]"); | |
135 WriteFakeLoadClass(kArrayLong, "long[]"); | |
136 heap_dump_record_ = new Record(kHeapDump, this); | |
137 WriteFakeClassDump(kJavaLangClass, static_cast<FakeClass>(0)); | |
138 WriteFakeClassDump(kJavaLangClassLoader, kJavaLangObject); | |
139 WriteFakeClassDump(kJavaLangObject, static_cast<FakeClass>(0)); | |
140 WriteFakeClassDump(kJavaLangString, kJavaLangObject); | |
141 } | |
142 | |
143 | |
144 HeapProfiler::~HeapProfiler() { | |
145 for (std::set<const RawSmi*>::iterator it = smi_table_.begin(); | |
146 it != smi_table_.end(); | |
147 ++it) { | |
148 WriteSmiInstanceDump(*it); | |
149 } | |
150 delete heap_dump_record_; | |
151 } | |
152 | |
153 | |
154 const RawObject* HeapProfiler::ObjectId(const RawObject* raw_obj) { | |
155 if (!raw_obj->IsHeapObject()) { | |
156 // To describe an immediate object in HPROF we record its value | |
157 // and write fake INSTANCE_DUMP subrecord in the HEAP_DUMP record. | |
158 const RawSmi* raw_smi = reinterpret_cast<const RawSmi*>(raw_obj); | |
159 if (smi_table_.find(raw_smi) == smi_table_.end()) { | |
160 smi_table_.insert(raw_smi); | |
161 } | |
162 } else if (raw_obj->GetClassId() == kNullCid) { | |
163 // Instances of the Null type are translated to NULL so they can | |
164 // be printed as "null" in HAT. | |
165 return NULL; | |
166 } | |
167 return raw_obj; | |
168 } | |
169 | |
170 | |
171 const RawClass* HeapProfiler::ClassId(const RawClass* raw_class) { | |
172 // A unique LOAD_CLASS record must be written for each class object. | |
173 if (class_table_.find(raw_class) == class_table_.end()) { | |
174 class_table_.insert(raw_class); | |
175 WriteLoadClass(raw_class); | |
176 } | |
177 return raw_class; | |
178 } | |
179 | |
180 | |
181 // A built-in class may have its name encoded in a C-string. These | |
182 // strings should only be found in class objects. We emit a unique | |
183 // STRING_IN_UTF8 so HAT will properly display the class name. | |
184 const char* HeapProfiler::StringId(const char* c_string) { | |
185 const RawString* ptr = reinterpret_cast<const RawString*>(c_string); | |
186 if (string_table_.find(ptr) == string_table_.end()) { | |
187 string_table_.insert(ptr); | |
188 WriteStringInUtf8(c_string); | |
189 } | |
190 return c_string; | |
191 } | |
192 | |
193 | |
194 const RawString* HeapProfiler::StringId(const RawString* raw_string) { | |
195 // A unique STRING_IN_UTF8 record must be written for each string | |
196 // object. | |
197 if (string_table_.find(raw_string) == string_table_.end()) { | |
198 string_table_.insert(raw_string); | |
199 WriteStringInUtf8(raw_string); | |
200 } | |
201 return raw_string; | |
202 } | |
203 | |
204 | |
205 const RawClass* HeapProfiler::GetClass(const RawObject* raw_obj) { | |
206 return Isolate::Current()->class_table()->At(raw_obj->GetClassId()); | |
207 } | |
208 | |
209 | |
210 const RawClass* HeapProfiler::GetSuperClass(const RawClass* raw_class) { | |
211 ASSERT(raw_class != Class::null()); | |
212 const RawAbstractType* super_type = raw_class->ptr()->super_type_; | |
213 if (super_type == AbstractType::null()) { | |
214 return Class::null(); | |
215 } | |
216 while (super_type->GetClassId() == kBoundedTypeCid) { | |
217 super_type = | |
218 reinterpret_cast<const RawBoundedType*>(super_type)->ptr()->type_; | |
219 } | |
220 ASSERT(super_type->GetClassId() == kTypeCid); | |
221 return reinterpret_cast<const RawClass*>( | |
222 reinterpret_cast<const RawType*>(super_type)->ptr()->type_class_); | |
223 } | |
224 | |
225 | |
226 void HeapProfiler::WriteRoot(const RawObject* raw_obj) { | |
227 SubRecord sub(kRootUnknown, this); | |
228 sub.WriteObjectId(ObjectId(raw_obj)); | |
229 } | |
230 | |
231 | |
232 void HeapProfiler::WriteObject(const RawObject* raw_obj) { | |
233 ASSERT(raw_obj->IsHeapObject()); | |
234 intptr_t class_id = raw_obj->GetClassId(); | |
235 switch (class_id) { | |
236 case kFreeListElement: { | |
237 // Free space has an object-like encoding. Heap profiles only | |
238 // care about live objects so we skip over these records. | |
239 break; | |
240 } | |
241 case kClassCid: { | |
242 const RawClass* raw_class = reinterpret_cast<const RawClass*>(raw_obj); | |
243 if (raw_class->ptr()->id_ == kFreeListElement) { | |
244 // Skip over the FreeListElement class. This class exists to | |
245 // describe free space. | |
246 break; | |
247 } | |
248 WriteClassDump(raw_class); | |
249 break; | |
250 } | |
251 case kArrayCid: | |
252 case kImmutableArrayCid: { | |
253 WriteObjectArrayDump(reinterpret_cast<const RawArray*>(raw_obj)); | |
254 break; | |
255 } | |
256 case kTypedDataInt8ArrayCid: | |
257 case kTypedDataUint8ArrayCid: | |
258 case kTypedDataUint8ClampedArrayCid: { | |
259 const RawTypedData* raw_int8_array = | |
260 reinterpret_cast<const RawTypedData*>(raw_obj); | |
261 WritePrimitiveArrayDump(raw_int8_array, kByte); | |
262 break; | |
263 } | |
264 case kTypedDataInt16ArrayCid: | |
265 case kTypedDataUint16ArrayCid: { | |
266 const RawTypedData* raw_int16_array = | |
267 reinterpret_cast<const RawTypedData*>(raw_obj); | |
268 WritePrimitiveArrayDump(raw_int16_array, kShort); | |
269 break; | |
270 } | |
271 case kTypedDataInt32ArrayCid: | |
272 case kTypedDataUint32ArrayCid: { | |
273 const RawTypedData* raw_int32_array = | |
274 reinterpret_cast<const RawTypedData*>(raw_obj); | |
275 WritePrimitiveArrayDump(raw_int32_array, kInt); | |
276 break; | |
277 } | |
278 case kTypedDataInt64ArrayCid: | |
279 case kTypedDataUint64ArrayCid: { | |
280 const RawTypedData* raw_int64_array = | |
281 reinterpret_cast<const RawTypedData*>(raw_obj); | |
282 WritePrimitiveArrayDump(raw_int64_array, kLong); | |
283 break; | |
284 } | |
285 case kTypedDataFloat32ArrayCid: { | |
286 const RawTypedData* raw_float32_array = | |
287 reinterpret_cast<const RawTypedData*>(raw_obj); | |
288 WritePrimitiveArrayDump(raw_float32_array, kFloat); | |
289 break; | |
290 } | |
291 case kTypedDataFloat64ArrayCid: { | |
292 const RawTypedData* raw_float64_array = | |
293 reinterpret_cast<const RawTypedData*>(raw_obj); | |
294 WritePrimitiveArrayDump(raw_float64_array, kDouble); | |
295 break; | |
296 } | |
297 case kOneByteStringCid: | |
298 case kTwoByteStringCid: | |
299 case kExternalOneByteStringCid: | |
300 case kExternalTwoByteStringCid: { | |
301 WriteInstanceDump(StringId(reinterpret_cast<const RawString*>(raw_obj))); | |
302 break; | |
303 } | |
304 default: | |
305 WriteInstanceDump(raw_obj); | |
306 } | |
307 } | |
308 | |
309 | |
310 void HeapProfiler::Write(const void* data, intptr_t size) { | |
311 (*write_callback_)(data, size, output_stream_); | |
312 } | |
313 | |
314 | |
315 // Header | |
316 // | |
317 // Format: | |
318 // [u1]* - format name | |
319 // u4 - size of identifiers | |
320 // u4 - high word of number of milliseconds since 0:00 GMT, 1/1/70 | |
321 // u4 - low word of number of milliseconds since 0:00 GMT, 1/1/70 | |
322 void HeapProfiler::WriteHeader() { | |
323 const char magic[] = "JAVA PROFILE 1.0.1"; | |
324 Write(magic, sizeof(magic)); | |
325 uint32_t size = htonl(kObjectIdSize); | |
326 Write(&size, sizeof(size)); | |
327 uint64_t milliseconds = OS::GetCurrentTimeMillis(); | |
328 uint32_t hi = htonl( | |
329 static_cast<uint32_t>((milliseconds >> 32) & 0x00000000FFFFFFFF)); | |
330 Write(&hi, sizeof(hi)); | |
331 uint32_t lo = htonl( | |
332 static_cast<uint32_t>(milliseconds & 0x00000000FFFFFFFF)); | |
333 Write(&lo, sizeof(lo)); | |
334 } | |
335 | |
336 | |
337 // Record | |
338 // | |
339 // Format: | |
340 // u1 - TAG: denoting the type of the record | |
341 // u4 - TIME: number of microseconds since the time stamp in the header | |
342 // u4 - LENGTH: number of bytes that follow this u4 field and belong | |
343 // to this record | |
344 // [u1]* - BODY: as many bytes as specified in the above u4 field | |
345 void HeapProfiler::WriteRecord(const Record& record) { | |
346 uint8_t tag = record.Tag(); | |
347 Write(&tag, sizeof(tag)); | |
348 uint32_t time = htonl(record.Time()); | |
349 Write(&time, sizeof(time)); | |
350 uint32_t length = htonl(record.Length()); | |
351 Write(&length, sizeof(length)); | |
352 Write(record.Body(), record.Length()); | |
353 } | |
354 | |
355 | |
356 // STRING IN UTF8 - 0x01 | |
357 // | |
358 // Format: | |
359 // ID - ID for this string | |
360 // [u1]* - UTF8 characters for string (NOT NULL terminated) | |
361 void HeapProfiler::WriteStringInUtf8(const RawString* raw_string) { | |
362 intptr_t length = 0; | |
363 char* characters = NULL; | |
364 intptr_t class_id = raw_string->GetClassId(); | |
365 if (class_id == kOneByteStringCid) { | |
366 const RawOneByteString* onestr = | |
367 reinterpret_cast<const RawOneByteString*>(raw_string); | |
368 for (intptr_t i = 0; i < Smi::Value(onestr->ptr()->length_); ++i) { | |
369 length += Utf8::Length(onestr->ptr()->data_[i]); | |
370 } | |
371 characters = new char[length]; | |
372 for (intptr_t i = 0, j = 0; i < Smi::Value(onestr->ptr()->length_); ++i) { | |
373 int32_t ch = onestr->ptr()->data_[i]; | |
374 j += Utf8::Encode(ch, &characters[j]); | |
375 } | |
376 } else { | |
377 ASSERT(class_id == kTwoByteStringCid); | |
378 const RawTwoByteString* twostr = | |
379 reinterpret_cast<const RawTwoByteString*>(raw_string); | |
380 for (intptr_t i = 0; i < Smi::Value(twostr->ptr()->length_); ++i) { | |
381 length += Utf8::Length(twostr->ptr()->data_[i]); | |
382 } | |
383 characters = new char[length]; | |
384 for (intptr_t i = 0, j = 0; i < Smi::Value(twostr->ptr()->length_); ++i) { | |
385 int32_t ch = twostr->ptr()->data_[i]; | |
386 j += Utf8::Encode(ch, &characters[j]); | |
387 } | |
388 } | |
389 Record record(kStringInUtf8, this); | |
390 record.WriteObjectId(ObjectId(raw_string)); | |
391 for (intptr_t i = 0; i < length; ++i) { | |
392 record.Write8(characters[i]); | |
393 } | |
394 delete[] characters; | |
395 } | |
396 | |
397 | |
398 void HeapProfiler::WriteStringInUtf8(const char* c_string) { | |
399 Record record(kStringInUtf8, this); | |
400 record.WriteObjectId(c_string); | |
401 for (; *c_string != '\0'; ++c_string) { | |
402 record.Write8(*c_string); | |
403 } | |
404 } | |
405 | |
406 | |
407 // LOAD CLASS - 0x02 | |
408 // | |
409 // Format: | |
410 // u4 - class serial number (always > 0) | |
411 // ID - class object ID | |
412 // u4 - stack trace serial number | |
413 // ID - class name string ID | |
414 void HeapProfiler::WriteLoadClass(const RawClass* raw_class) { | |
415 Record record(kLoadClass, this); | |
416 // class serial number (always > 0) | |
417 record.Write32(1); | |
418 // class object ID | |
419 record.WriteObjectId(raw_class); | |
420 // stack trace serial number | |
421 record.Write32(0); | |
422 // class name string ID | |
423 if (raw_class->ptr()->name_ != String::null()) { | |
424 record.WriteObjectId(StringId(raw_class->ptr()->name_)); | |
425 } else { | |
426 const char* format = "<an unnamed class with id %d>"; | |
427 intptr_t len = OS::SNPrint(NULL, 0, format, raw_class->ptr()->id_); | |
428 char* str = new char[len + 1]; | |
429 OS::SNPrint(str, len + 1, format, raw_class->ptr()->id_); | |
430 record.WriteObjectId(StringId(str)); | |
431 delete[] str; | |
432 } | |
433 } | |
434 | |
435 | |
436 void HeapProfiler::WriteFakeLoadClass(FakeClass fake_class, | |
437 const char* class_name) { | |
438 Record record(kLoadClass, this); | |
439 // class serial number (always > 0) | |
440 record.Write32(1); | |
441 // class object ID | |
442 record.WriteObjectId(reinterpret_cast<void*>(fake_class)); | |
443 // stack trace serial number | |
444 record.Write32(0); | |
445 // class name string ID | |
446 record.WriteObjectId(StringId(class_name)); | |
447 } | |
448 | |
449 | |
450 // STACK TRACE - 0x05 | |
451 // | |
452 // u4 - stack trace serial number | |
453 // u4 - thread serial number | |
454 // u4 - number of frames | |
455 // [ID]* - series of stack frame ID's | |
456 void HeapProfiler::WriteStackTrace() { | |
457 Record record(kStackTrace, this); | |
458 // stack trace serial number | |
459 record.Write32(0); | |
460 // thread serial number | |
461 record.Write32(0); | |
462 // number of frames | |
463 record.Write32(0); | |
464 } | |
465 | |
466 | |
467 // HEAP SUMMARY - 0x07 | |
468 // | |
469 // Format: | |
470 // u4 - total live bytes | |
471 // u4 - total live instances | |
472 // u8 - total bytes allocated | |
473 // u8 - total instances allocated | |
474 void HeapProfiler::WriteHeapSummary(uint32_t total_live_bytes, | |
475 uint32_t total_live_instances, | |
476 uint64_t total_bytes_allocated, | |
477 uint64_t total_instances_allocated) { | |
478 Record record(kHeapSummary, this); | |
479 record.Write32(total_live_bytes); | |
480 record.Write32(total_live_instances); | |
481 record.Write32(total_bytes_allocated); | |
482 record.Write32(total_instances_allocated); | |
483 } | |
484 | |
485 | |
486 // HEAP DUMP - 0x0C | |
487 // | |
488 // Format: | |
489 // []* | |
490 void HeapProfiler::WriteHeapDump() { | |
491 Record record(kHeapDump, this); | |
492 } | |
493 | |
494 | |
495 // CLASS DUMP - 0x20 | |
496 // | |
497 // Format: | |
498 // ID - class object ID | |
499 // u4 - stack trace serial number | |
500 // ID - super class object ID | |
501 // ID - class loader object ID | |
502 // ID - signers object ID | |
503 // ID - protection domain object ID | |
504 // ID - reserved | |
505 // ID - reserved | |
506 // u4 - instance size (in bytes) | |
507 // u2 - size of constant pool and number of records that follow: | |
508 // u2 - constant pool index | |
509 // u1 - type of entry: (See Basic Type) | |
510 // value - value of entry (u1, u2, u4, or u8 based on type of entry) | |
511 // u2 - Number of static fields: | |
512 // ID - static field name string ID | |
513 // u1 - type of field: (See Basic Type) | |
514 // value - value of entry (u1, u2, u4, or u8 based on type of field) | |
515 // u2 - Number of instance fields (not including super class's) | |
516 // ID - field name string ID | |
517 // u1 - type of field: (See Basic Type) | |
518 void HeapProfiler::WriteClassDump(const RawClass* raw_class) { | |
519 SubRecord sub(kClassDump, this); | |
520 // class object ID | |
521 sub.WriteObjectId(ClassId(raw_class)); | |
522 // stack trace serial number | |
523 sub.Write32(0); | |
524 // super class object ID | |
525 const RawClass* super_class = GetSuperClass(raw_class); | |
526 if (super_class == Class::null()) { | |
527 sub.WriteObjectId(NULL); | |
528 } else { | |
529 sub.WriteObjectId(ClassId(super_class)); | |
530 } | |
531 // class loader object ID | |
532 sub.WriteObjectId(NULL); | |
533 // signers object ID | |
534 sub.WriteObjectId(NULL); | |
535 // protection domain object ID | |
536 sub.WriteObjectId(NULL); | |
537 // reserved | |
538 sub.WriteObjectId(NULL); | |
539 // reserved | |
540 sub.WriteObjectId(NULL); | |
541 | |
542 intptr_t num_static_fields = 0; | |
543 intptr_t num_instance_fields = 0; | |
544 | |
545 RawArray* raw_array = raw_class->ptr()->fields_; | |
546 if (raw_array != Array::null()) { | |
547 for (intptr_t i = 0; i < Smi::Value(raw_array->ptr()->length_); ++i) { | |
548 RawField* raw_field = | |
549 reinterpret_cast<RawField*>(raw_array->ptr()->data()[i]); | |
550 if (Field::StaticBit::decode(raw_field->ptr()->kind_bits_)) { | |
551 ++num_static_fields; | |
552 } else { | |
553 ++num_instance_fields; | |
554 } | |
555 } | |
556 } | |
557 // instance size (in bytes) | |
558 intptr_t instance_size_in_words = raw_class->ptr()->instance_size_in_words_; | |
559 if (instance_size_in_words == 0) { | |
560 // TODO(iposva): Better accounting of variable sized VM classes. | |
561 instance_size_in_words = num_instance_fields; | |
562 } | |
563 sub.Write32(instance_size_in_words * kWordSize); | |
564 // size of constant pool and number of records that follow: | |
565 sub.Write16(0); | |
566 // Number of static fields | |
567 sub.Write16(num_static_fields); | |
568 // Static fields: | |
569 if (raw_array != Array::null()) { | |
570 for (intptr_t i = 0; i < Smi::Value(raw_array->ptr()->length_); ++i) { | |
571 RawField* raw_field = | |
572 reinterpret_cast<RawField*>(raw_array->ptr()->data()[i]); | |
573 if (Field::StaticBit::decode(raw_field->ptr()->kind_bits_)) { | |
574 ASSERT(raw_field->ptr()->name_ != String::null()); | |
575 // static field name string ID | |
576 sub.WriteObjectId(StringId(raw_field->ptr()->name_)); | |
577 // type of static field | |
578 sub.Write8(kObject); | |
579 // value of entry | |
580 sub.WriteObjectId(ObjectId(raw_field->ptr()->value_)); | |
581 } | |
582 } | |
583 } | |
584 // Number of instance fields (not include super class's) | |
585 sub.Write16(num_instance_fields); | |
586 // Instance fields: | |
587 if (raw_array != Array::null()) { | |
588 for (intptr_t i = 0; i < Smi::Value(raw_array->ptr()->length_); ++i) { | |
589 RawField* raw_field = | |
590 reinterpret_cast<RawField*>(raw_array->ptr()->data()[i]); | |
591 if (!Field::StaticBit::decode(raw_field->ptr()->kind_bits_)) { | |
592 ASSERT(raw_field->ptr()->name_ != String::null()); | |
593 // field name string ID | |
594 sub.WriteObjectId(StringId(raw_field->ptr()->name_)); | |
595 // type of field | |
596 sub.Write8(kObject); | |
597 } | |
598 } | |
599 } | |
600 } | |
601 | |
602 void HeapProfiler::WriteFakeClassDump(FakeClass fake_class, | |
603 FakeClass fake_super_class) { | |
604 SubRecord sub(kClassDump, this); | |
605 // class object ID | |
606 sub.WriteObjectId(reinterpret_cast<void*>(fake_class)); | |
607 // stack trace serial number | |
608 sub.Write32(0); | |
609 // super class object ID | |
610 sub.WriteObjectId(reinterpret_cast<void*>(fake_super_class)); | |
611 // class loader object ID | |
612 sub.WriteObjectId(NULL); | |
613 // signers object ID | |
614 sub.WriteObjectId(NULL); | |
615 // protection domain object ID | |
616 sub.WriteObjectId(NULL); | |
617 // reserved | |
618 sub.WriteObjectId(NULL); | |
619 // reserved | |
620 sub.WriteObjectId(NULL); | |
621 // instance size (in bytes) | |
622 sub.Write32(0); | |
623 // size of constant pool and number of records that follow: | |
624 sub.Write16(0); | |
625 // Number of static fields | |
626 sub.Write16(0); | |
627 // Number of instance fields (not include super class's) | |
628 sub.Write16(0); | |
629 } | |
630 | |
631 | |
632 | |
633 // INSTANCE DUMP - 0x21 | |
634 // | |
635 // Format: | |
636 // ID - object ID | |
637 // u4 - stack trace serial number | |
638 // ID - class object ID | |
639 // u4 - number of bytes that follow | |
640 // [value]* - instance field values (this class, followed by super class, etc) | |
641 void HeapProfiler::WriteInstanceDump(const RawObject* raw_obj) { | |
642 ASSERT(raw_obj->IsHeapObject()); | |
643 SubRecord sub(kInstanceDump, this); | |
644 // object ID | |
645 sub.WriteObjectId(raw_obj); | |
646 // stack trace serial number | |
647 sub.Write32(0); | |
648 // class object ID | |
649 sub.WriteObjectId(ClassId(GetClass(raw_obj))); | |
650 // number of bytes that follow | |
651 intptr_t num_instance_fields = 0; | |
652 for (const RawClass* cls = GetClass(raw_obj); | |
653 cls != Class::null(); | |
654 cls = GetSuperClass(cls)) { | |
655 RawArray* raw_array = cls->ptr()->fields_; | |
656 if (raw_array != Array::null()) { | |
657 intptr_t length = Smi::Value(raw_array->ptr()->length_); | |
658 for (intptr_t i = 0; i < length; ++i) { | |
659 RawField* raw_field = | |
660 reinterpret_cast<RawField*>(raw_array->ptr()->data()[i]); | |
661 if (!Field::StaticBit::decode(raw_field->ptr()->kind_bits_)) { | |
662 ++num_instance_fields; | |
663 } | |
664 } | |
665 } | |
666 } | |
667 int64_t num_instance_bytes = num_instance_fields * kObjectIdSize; | |
668 ASSERT(num_instance_bytes <= kMaxUint32); | |
669 sub.Write32(num_instance_bytes); | |
670 // instance field values (this class, followed by super class, etc) | |
671 for (const RawClass* cls = GetClass(raw_obj); | |
672 cls != Class::null(); | |
673 cls = GetSuperClass(cls)) { | |
674 RawArray* raw_array = cls->ptr()->fields_; | |
675 if (raw_array != Array::null()) { | |
676 intptr_t length = Smi::Value(raw_array->ptr()->length_); | |
677 uint8_t* base = reinterpret_cast<uint8_t*>(raw_obj->ptr()); | |
678 for (intptr_t i = 0; i < length; ++i) { | |
679 RawField* raw_field = | |
680 reinterpret_cast<RawField*>(raw_array->ptr()->data()[i]); | |
681 if (!Field::StaticBit::decode(raw_field->ptr()->kind_bits_)) { | |
682 intptr_t offset_in_words = | |
683 Smi::Value(reinterpret_cast<RawSmi*>(raw_field->ptr()->value_)); | |
684 intptr_t offset = offset_in_words * kWordSize; | |
685 RawObject* ptr = *reinterpret_cast<RawObject**>(base + offset); | |
686 sub.WriteObjectId(ObjectId(ptr)); | |
687 } | |
688 } | |
689 } | |
690 } | |
691 } | |
692 | |
693 | |
694 // Write a specialized instance dump for "referenced" Smi objects. | |
695 void HeapProfiler::WriteSmiInstanceDump(const RawSmi* raw_smi) { | |
696 ASSERT(!raw_smi->IsHeapObject()); | |
697 SubRecord sub(kInstanceDump, this); | |
698 // object ID | |
699 sub.WriteObjectId(raw_smi); | |
700 // stack trace serial number | |
701 sub.Write32(0); | |
702 // class object ID | |
703 sub.WriteObjectId(Isolate::Current()->class_table()->At(kSmiCid)); | |
704 // number of bytes that follow | |
705 sub.Write32(0); | |
706 } | |
707 | |
708 | |
709 // OBJECT ARRAY DUMP - 0x22 | |
710 // | |
711 // Format: | |
712 // ID - array object ID | |
713 // u4 - stack trace serial number | |
714 // u4 - number of elements | |
715 // ID - array class object ID | |
716 // [ID]* - elements | |
717 void HeapProfiler::WriteObjectArrayDump(const RawArray* raw_array) { | |
718 SubRecord sub(kObjectArrayDump, this); | |
719 // array object ID | |
720 sub.WriteObjectId(raw_array); | |
721 // stack trace serial number | |
722 sub.Write32(0); | |
723 // number of elements | |
724 intptr_t length = Smi::Value(raw_array->ptr()->length_); | |
725 sub.Write32(length); | |
726 // array class object ID | |
727 sub.WriteObjectId(reinterpret_cast<void*>(kArrayObject)); | |
728 // elements | |
729 for (intptr_t i = 0; i < length; ++i) { | |
730 sub.WriteObjectId(ObjectId(raw_array->ptr()->data()[i])); | |
731 } | |
732 } | |
733 | |
734 | |
735 // PRIMITIVE ARRAY DUMP - 0x23 | |
736 // | |
737 // Format: | |
738 // ID - array object ID | |
739 // u4 - stack trace serial number | |
740 // u4 - number of elements | |
741 // u1 - element type | |
742 // [u1]* - elements | |
743 void HeapProfiler::WritePrimitiveArrayDump(const RawTypedData* raw_byte_array, | |
744 uint8_t tag) { | |
745 SubRecord sub(kPrimitiveArrayDump, this); | |
746 // array object ID | |
747 sub.WriteObjectId(raw_byte_array); | |
748 // stack trace serial number | |
749 sub.Write32(0); | |
750 // number of elements | |
751 intptr_t length = Smi::Value(raw_byte_array->ptr()->length_); | |
752 const void* data = &(raw_byte_array->ptr()->data_[0]); | |
753 sub.Write32(length); | |
754 // element type | |
755 sub.Write8(tag); | |
756 // elements (packed) | |
757 for (intptr_t i = 0; i < length; ++i) { | |
758 if (tag == kByte) { | |
759 sub.Write8(reinterpret_cast<const int8_t*>(data)[i]); | |
760 } else if (tag == kShort) { | |
761 sub.Write16(reinterpret_cast<const int16_t*>(data)[i]); | |
762 break; | |
763 } else if (tag == kInt || tag == kFloat) { | |
764 sub.Write32(reinterpret_cast<const int32_t*>(data)[i]); | |
765 } else { | |
766 ASSERT(tag == kLong || tag == kDouble); | |
767 sub.Write64(reinterpret_cast<const int64_t*>(data)[i]); | |
768 } | |
769 } | |
770 } | |
771 | |
772 | |
773 void HeapProfilerRootVisitor::VisitPointers(RawObject** first, | |
774 RawObject** last) { | |
775 for (RawObject** current = first; current <= last; current++) { | |
776 RawObject* raw_obj = *current; | |
777 if (raw_obj->IsHeapObject()) { | |
778 // Skip visits of FreeListElements. | |
779 if (raw_obj->GetClassId() == kFreeListElement) { | |
780 // Only the class of the free list element should ever be visited. | |
781 ASSERT(first == last); | |
782 return; | |
783 } | |
784 uword obj_addr = RawObject::ToAddr(raw_obj); | |
785 if (!Isolate::Current()->heap()->Contains(obj_addr) && | |
786 !Dart::vm_isolate()->heap()->Contains(obj_addr)) { | |
787 FATAL1("Invalid object pointer encountered %#" Px "\n", obj_addr); | |
788 } | |
789 } | |
790 profiler_->WriteRoot(raw_obj); | |
791 } | |
792 } | |
793 | |
794 | |
795 void HeapProfilerWeakRootVisitor::VisitHandle(uword addr) { | |
796 FinalizablePersistentHandle* handle = | |
797 reinterpret_cast<FinalizablePersistentHandle*>(addr); | |
798 RawObject* raw_obj = handle->raw(); | |
799 visitor_->VisitPointer(&raw_obj); | |
800 } | |
801 | |
802 | |
803 void HeapProfilerObjectVisitor::VisitObject(RawObject* raw_obj) { | |
804 profiler_->WriteObject(raw_obj); | |
805 } | |
806 | |
807 } // namespace dart | |
OLD | NEW |