| 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 |