| 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_trace.h" | |
| 6 | |
| 7 #include "include/dart_api.h" | |
| 8 #include "vm/dart_api_state.h" | |
| 9 #include "vm/debugger.h" | |
| 10 #include "vm/isolate.h" | |
| 11 #include "vm/object.h" | |
| 12 #include "vm/object_set.h" | |
| 13 #include "vm/object_store.h" | |
| 14 #include "vm/os.h" | |
| 15 #include "vm/stack_frame.h" | |
| 16 #include "vm/unicode.h" | |
| 17 | |
| 18 namespace dart { | |
| 19 | |
| 20 DEFINE_FLAG(bool, heap_trace, false, "Enable heap tracing."); | |
| 21 | |
| 22 Dart_FileOpenCallback HeapTrace::open_callback_ = NULL; | |
| 23 Dart_FileWriteCallback HeapTrace::write_callback_ = NULL; | |
| 24 Dart_FileCloseCallback HeapTrace::close_callback_ = NULL; | |
| 25 bool HeapTrace::is_enabled_ = false; | |
| 26 | |
| 27 class HeapTraceVisitor : public ObjectPointerVisitor { | |
| 28 public: | |
| 29 HeapTraceVisitor(Isolate* isolate, | |
| 30 HeapTrace* heap_trace, | |
| 31 ObjectSet* object_set) | |
| 32 : ObjectPointerVisitor(isolate), | |
| 33 heap_trace_(heap_trace), | |
| 34 vm_isolate_(Dart::vm_isolate()), | |
| 35 object_set_(object_set) { | |
| 36 } | |
| 37 | |
| 38 void VisitPointers(RawObject** first, RawObject** last) { | |
| 39 for (RawObject** current = first; current <= last; current++) { | |
| 40 RawObject* raw_obj = *current; | |
| 41 | |
| 42 // We only care about objects in the heap | |
| 43 // Also, since this visitor will frequently be encountering redudant | |
| 44 // roots, we use an object_set to skip the duplicates. | |
| 45 if (raw_obj->IsHeapObject() && | |
| 46 raw_obj != reinterpret_cast<RawObject*>(0x1) && | |
| 47 raw_obj != reinterpret_cast<RawObject*>(0xabababab) && | |
| 48 !object_set_->Contains(raw_obj) && | |
| 49 !vm_isolate_->heap()->Contains(RawObject::ToAddr(raw_obj))) { | |
| 50 object_set_->Add(raw_obj); | |
| 51 uword addr = RawObject::ToAddr(raw_obj); | |
| 52 heap_trace_->TraceSingleRoot(addr); | |
| 53 } | |
| 54 } | |
| 55 } | |
| 56 | |
| 57 private: | |
| 58 HeapTrace* heap_trace_; | |
| 59 Isolate* vm_isolate_; | |
| 60 // TODO(cshapiro): replace with a sparse data structure. | |
| 61 ObjectSet* object_set_; | |
| 62 DISALLOW_COPY_AND_ASSIGN(HeapTraceVisitor); | |
| 63 }; | |
| 64 | |
| 65 | |
| 66 class HeapTraceScopedHandleVisitor : public ObjectPointerVisitor { | |
| 67 public: | |
| 68 HeapTraceScopedHandleVisitor(Isolate* isolate, HeapTrace* heap_trace) | |
| 69 : ObjectPointerVisitor(isolate), heap_trace_(heap_trace) { | |
| 70 } | |
| 71 | |
| 72 void VisitPointers(RawObject** first, RawObject** last) { | |
| 73 for (RawObject** current = first; current <= last; current++) { | |
| 74 RawObject* raw_obj = *current; | |
| 75 Heap* heap = isolate()->heap(); | |
| 76 | |
| 77 // We only care about objects in the heap | |
| 78 if (raw_obj->IsHeapObject() && | |
| 79 raw_obj != reinterpret_cast<RawObject*>(0x1) && | |
| 80 raw_obj != reinterpret_cast<RawObject*>(0xabababab) && | |
| 81 heap->Contains(RawObject::ToAddr(raw_obj))) { | |
| 82 uword addr = RawObject::ToAddr(raw_obj); | |
| 83 heap_trace_->TraceScopedHandle(addr); | |
| 84 } | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 private: | |
| 89 HeapTrace* heap_trace_; | |
| 90 DISALLOW_COPY_AND_ASSIGN(HeapTraceScopedHandleVisitor); | |
| 91 }; | |
| 92 | |
| 93 | |
| 94 class HeapTraceObjectStoreVisitor : public ObjectPointerVisitor { | |
| 95 public: | |
| 96 HeapTraceObjectStoreVisitor(Isolate* isolate, HeapTrace* heap_trace) | |
| 97 : ObjectPointerVisitor(isolate), heap_trace_(heap_trace) { | |
| 98 } | |
| 99 | |
| 100 void VisitPointers(RawObject** first, RawObject** last) { | |
| 101 for (RawObject** current = first; current <= last; current++) { | |
| 102 RawObject* raw_obj = *current; | |
| 103 | |
| 104 // We only care about obects in the heap. | |
| 105 if (raw_obj->IsHeapObject() && | |
| 106 raw_obj != reinterpret_cast<RawObject*>(0x1) && | |
| 107 raw_obj != reinterpret_cast<RawObject*>(0xabababab)) { | |
| 108 uword addr = RawObject::ToAddr(raw_obj); | |
| 109 heap_trace_->TraceObjectStorePointer(addr); | |
| 110 } | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 private: | |
| 115 HeapTrace* heap_trace_; | |
| 116 DISALLOW_COPY_AND_ASSIGN(HeapTraceObjectStoreVisitor); | |
| 117 }; | |
| 118 | |
| 119 | |
| 120 class HeapTraceInitialHeapVisitor : public ObjectVisitor { | |
| 121 public: | |
| 122 HeapTraceInitialHeapVisitor(Isolate* isolate, HeapTrace* heap_trace) | |
| 123 : ObjectVisitor(isolate), heap_trace_(heap_trace) {} | |
| 124 | |
| 125 void VisitObject(RawObject* raw_obj) { | |
| 126 heap_trace_->TraceSnapshotAlloc(raw_obj, raw_obj->Size()); | |
| 127 } | |
| 128 | |
| 129 private: | |
| 130 HeapTrace* heap_trace_; | |
| 131 DISALLOW_COPY_AND_ASSIGN(HeapTraceInitialHeapVisitor); | |
| 132 }; | |
| 133 | |
| 134 | |
| 135 HeapTrace::HeapTrace() : isolate_initialized_(false), output_stream_(NULL) { | |
| 136 } | |
| 137 | |
| 138 | |
| 139 HeapTrace::~HeapTrace() { | |
| 140 if (isolate_initialized_) { | |
| 141 (*close_callback_)(output_stream_); | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 | |
| 146 void HeapTrace::InitOnce(Dart_FileOpenCallback open_callback, | |
| 147 Dart_FileWriteCallback write_callback, | |
| 148 Dart_FileCloseCallback close_callback) { | |
| 149 ASSERT(open_callback != NULL); | |
| 150 ASSERT(write_callback != NULL); | |
| 151 ASSERT(close_callback != NULL); | |
| 152 HeapTrace::open_callback_ = open_callback; | |
| 153 HeapTrace::write_callback_ = write_callback; | |
| 154 HeapTrace::close_callback_ = close_callback; | |
| 155 HeapTrace::is_enabled_ = true; | |
| 156 } | |
| 157 | |
| 158 | |
| 159 ObjectSet* HeapTrace::CreateEmptyObjectSet() const { | |
| 160 Isolate* isolate = Isolate::Current(); | |
| 161 uword start, end; | |
| 162 isolate->heap()->StartEndAddress(&start, &end); | |
| 163 | |
| 164 Isolate* vm_isolate = Dart::vm_isolate(); | |
| 165 uword vm_start, vm_end; | |
| 166 vm_isolate->heap()->StartEndAddress(&vm_start, &vm_end); | |
| 167 | |
| 168 ObjectSet* allocated_set = new ObjectSet(Utils::Minimum(start, vm_start), | |
| 169 Utils::Maximum(end, vm_end)); | |
| 170 | |
| 171 return allocated_set; | |
| 172 } | |
| 173 | |
| 174 | |
| 175 void HeapTrace::ResizeObjectSet() { | |
| 176 Isolate* isolate = Isolate::Current(); | |
| 177 uword start, end; | |
| 178 isolate->heap()->StartEndAddress(&start, &end); | |
| 179 Isolate* vm_isolate = Dart::vm_isolate(); | |
| 180 uword vm_start, vm_end; | |
| 181 vm_isolate->heap()->StartEndAddress(&vm_start, &vm_end); | |
| 182 object_set_.Resize(Utils::Minimum(start, vm_start), | |
| 183 Utils::Maximum(end, vm_end)); | |
| 184 } | |
| 185 | |
| 186 | |
| 187 void HeapTrace::Init(Isolate* isolate) { | |
| 188 // Do not trace the VM isolate | |
| 189 if (isolate == Dart::vm_isolate()) { | |
| 190 return; | |
| 191 } | |
| 192 ASSERT(isolate_initialized_ == false); | |
| 193 const char* format = "%s.htrace"; | |
| 194 intptr_t len = OS::SNPrint(NULL, 0, format, isolate->name()); | |
| 195 char* filename = new char[len + 1]; | |
| 196 OS::SNPrint(filename, len + 1, format, isolate->name()); | |
| 197 output_stream_ = (*open_callback_)(filename); | |
| 198 ASSERT(output_stream_ != NULL); | |
| 199 delete[] filename; | |
| 200 isolate_initialized_ = true; | |
| 201 | |
| 202 HeapTraceObjectStoreVisitor object_store_visitor(isolate, this); | |
| 203 isolate->object_store()->VisitObjectPointers(&object_store_visitor); | |
| 204 | |
| 205 // Visit any objects that may have been allocated during startup, | |
| 206 // before we started tracing. | |
| 207 HeapTraceInitialHeapVisitor heap_visitor(isolate, this); | |
| 208 isolate->heap()->IterateObjects(&heap_visitor); | |
| 209 TraceRoots(isolate); | |
| 210 } | |
| 211 | |
| 212 | |
| 213 // Allocation Record - 'A' (0x41) | |
| 214 // | |
| 215 // Format: | |
| 216 // 'A' | |
| 217 // uword - address of allocated object | |
| 218 // uword - size of allocated object | |
| 219 void HeapTrace::TraceAllocation(uword addr, intptr_t size) { | |
| 220 if (isolate_initialized_) { | |
| 221 { | |
| 222 AllocationRecord rec(this); | |
| 223 rec.Write(addr); | |
| 224 rec.Write(size); | |
| 225 } | |
| 226 TraceRoots(Isolate::Current()); | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 | |
| 231 // Snapshot Allocation Record - 'B' (0x41) | |
| 232 // | |
| 233 // Format: | |
| 234 // 'B' | |
| 235 // uword - address of allocated object | |
| 236 // uword - size of allocated object | |
| 237 void HeapTrace::TraceSnapshotAlloc(RawObject* obj, intptr_t size) { | |
| 238 if (isolate_initialized_) { | |
| 239 SnapshotAllocationRecord rec(this); | |
| 240 rec.Write(RawObject::ToAddr(obj)); | |
| 241 rec.Write(static_cast<uword>(size)); | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 | |
| 246 // Allocate Zone Handle Record - 'Z' (0x5a) | |
| 247 // | |
| 248 // Format: | |
| 249 // 'Z' | |
| 250 // uword - handle address (where the handle is pointing) | |
| 251 // uword - zone address (address of the zone the handle is in) | |
| 252 void HeapTrace::TraceAllocateZoneHandle(uword handle, uword zone_addr) { | |
| 253 if (isolate_initialized_) { | |
| 254 AllocZoneHandleRecord rec(this); | |
| 255 rec.Write(handle); | |
| 256 rec.Write(zone_addr); | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 | |
| 261 // Delete Zone Record - 'z' (0x7a) | |
| 262 // | |
| 263 // Format: | |
| 264 // 'z' | |
| 265 // uword - zone address (all the handles in that zone are now gone) | |
| 266 void HeapTrace::TraceDeleteZone(Zone* zone) { | |
| 267 if (isolate_initialized_) { | |
| 268 DeleteZoneRecord rec(this); | |
| 269 rec.Write(reinterpret_cast<uword>(zone)); | |
| 270 } | |
| 271 } | |
| 272 | |
| 273 | |
| 274 // Delete Scoped Hanldes Record - 's' (0x73) | |
| 275 // | |
| 276 // Format: | |
| 277 // 's' | |
| 278 void HeapTrace::TraceDeleteScopedHandles() { | |
| 279 if (isolate_initialized_) { | |
| 280 DeleteScopedHandlesRecord rec(this); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 | |
| 285 // Copy Record - 'C' (0x43) | |
| 286 // | |
| 287 // Format: | |
| 288 // 'C' | |
| 289 // uword - old address | |
| 290 // uword - new address | |
| 291 void HeapTrace::TraceCopy(uword from_addr, uword to_addr) { | |
| 292 if (isolate_initialized_) { | |
| 293 CopyRecord rec(this); | |
| 294 rec.Write(from_addr); | |
| 295 rec.Write(to_addr); | |
| 296 } | |
| 297 } | |
| 298 | |
| 299 | |
| 300 // Object Store Recorda - 'O'(0x4f) | |
| 301 // | |
| 302 // Format: | |
| 303 // 'O' | |
| 304 // uword - address | |
| 305 void HeapTrace::TraceObjectStorePointer(uword addr) { | |
| 306 if (isolate_initialized_) { | |
| 307 ObjectStoreRecord rec(this); | |
| 308 rec.Write(addr); | |
| 309 } | |
| 310 } | |
| 311 | |
| 312 | |
| 313 // Promotion Records - 'P' (0x50) | |
| 314 // | |
| 315 // Format: | |
| 316 // 'P' | |
| 317 // uword - old address | |
| 318 // uword - new address | |
| 319 void HeapTrace::TracePromotion(uword old_addr, uword promoted_addr) { | |
| 320 if (isolate_initialized_) { | |
| 321 PromotionRecord rec(this); | |
| 322 rec.Write(old_addr); | |
| 323 rec.Write(promoted_addr); | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 | |
| 328 // Death Range Record - 'L' (0x4c) | |
| 329 // | |
| 330 // Format: | |
| 331 // 'L' | |
| 332 // uword - inclusive start address of the space being left | |
| 333 // uword - exclusive end address of the space being left | |
| 334 void HeapTrace::TraceDeathRange(uword inclusive_start, uword exclusive_end) { | |
| 335 if (isolate_initialized_) { | |
| 336 DeathRangeRecord rec(this); | |
| 337 rec.Write(inclusive_start); | |
| 338 rec.Write(exclusive_end); | |
| 339 } | |
| 340 } | |
| 341 | |
| 342 | |
| 343 // Register Class Record - 'K' (0x4b) | |
| 344 // | |
| 345 // Format: | |
| 346 // 'K' | |
| 347 // uword - address ( the address of the class) | |
| 348 void HeapTrace::TraceRegisterClass(const Class& cls) { | |
| 349 if (isolate_initialized_) { | |
| 350 RegisterClassRecord rec(this); | |
| 351 rec.Write(RawObject::ToAddr(cls.raw())); | |
| 352 } | |
| 353 } | |
| 354 | |
| 355 | |
| 356 // Scoped Handle Record - 'H' (0x48) | |
| 357 // | |
| 358 // Format: | |
| 359 // 'H' | |
| 360 // uword - adress of the scoped handle (where it is pointing) | |
| 361 void HeapTrace::TraceScopedHandle(uword handle) { | |
| 362 if (isolate_initialized_) { | |
| 363 AllocScopedHandleRecord rec(this); | |
| 364 rec.Write(handle); | |
| 365 } | |
| 366 } | |
| 367 | |
| 368 | |
| 369 // Root Record - 'R' (0x52) | |
| 370 // | |
| 371 // Format: | |
| 372 // 'R' | |
| 373 // uword - address | |
| 374 void HeapTrace::TraceSingleRoot(uword root_addr) { | |
| 375 if (isolate_initialized_) { | |
| 376 RootRecord rec(this); | |
| 377 rec.Write(root_addr); | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 | |
| 382 // Sweep Record - 'S' | |
| 383 // | |
| 384 // Format: | |
| 385 // 'S' | |
| 386 // uword - address | |
| 387 void HeapTrace::TraceSweep(uword sweept_addr) { | |
| 388 if (isolate_initialized_) { | |
| 389 SweepRecord rec(this); | |
| 390 rec.Write(sweept_addr); | |
| 391 } | |
| 392 } | |
| 393 | |
| 394 | |
| 395 // Does not output any records directly, | |
| 396 // but does call TraceSingleRoot | |
| 397 void HeapTrace::TraceRoots(Isolate* isolate) { | |
| 398 if (isolate_initialized_) { | |
| 399 ResizeObjectSet(); | |
| 400 HeapTraceVisitor visitor(isolate, this, &object_set_); | |
| 401 HeapTraceScopedHandleVisitor handle_visitor(isolate, this); | |
| 402 | |
| 403 bool visit_prologue_weak_handles = true; | |
| 404 bool validate_frames = false; | |
| 405 | |
| 406 // Visit objects in per isolate stubs. | |
| 407 StubCode::VisitObjectPointers(&visitor); | |
| 408 | |
| 409 // stack | |
| 410 StackFrameIterator frames_iterator(validate_frames); | |
| 411 StackFrame* frame = frames_iterator.NextFrame(); | |
| 412 while (frame != NULL) { | |
| 413 frame->VisitObjectPointers(&visitor); | |
| 414 frame = frames_iterator.NextFrame(); | |
| 415 } | |
| 416 | |
| 417 if (isolate->api_state() != NULL) { | |
| 418 isolate->api_state()->VisitObjectPointers(&visitor, | |
| 419 visit_prologue_weak_handles); | |
| 420 } | |
| 421 | |
| 422 // Visit the top context which is stored in the isolate. | |
| 423 RawContext* top_context = isolate->top_context(); | |
| 424 visitor.VisitPointer(reinterpret_cast<RawObject**>(&top_context)); | |
| 425 | |
| 426 // Visit the currently active IC data array. | |
| 427 RawArray* ic_data_array = isolate->ic_data_array(); | |
| 428 visitor.VisitPointer(reinterpret_cast<RawObject**>(&ic_data_array)); | |
| 429 | |
| 430 // Visit objects in the debugger. | |
| 431 isolate->debugger()->VisitObjectPointers(&visitor); | |
| 432 | |
| 433 isolate->current_zone()->handles()-> | |
| 434 VisitUnvisitedScopedHandles(&handle_visitor); | |
| 435 | |
| 436 object_set_.FastClear(); | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 | |
| 441 // Store Record - 'U' (0x55) | |
| 442 // | |
| 443 // Format: | |
| 444 // 'U' | |
| 445 // uword - originating object address (where a pointer is being stored) | |
| 446 // uword - byte offset into origin where the pointer is being stored | |
| 447 // uword - value of the pointer being stored | |
| 448 void HeapTrace::TraceStoreIntoObject(uword object, | |
| 449 uword field_addr, | |
| 450 uword value) { | |
| 451 if (isolate_initialized_) { | |
| 452 // We don't care about pointers into the VM_Islate heap, so skip them. | |
| 453 // There should not be any pointers /out/ of the VM isolate; so we | |
| 454 // do not check object. | |
| 455 if (Isolate::Current()->heap()->Contains(value)) { | |
| 456 StoreRecord rec(this); | |
| 457 uword slot_offset = field_addr - object; | |
| 458 | |
| 459 rec.Write(object); | |
| 460 rec.Write(slot_offset); | |
| 461 rec.Write(value); | |
| 462 } | |
| 463 } | |
| 464 } | |
| 465 | |
| 466 | |
| 467 // Mark Sweep Start Record - '{' (0x7b) | |
| 468 // | |
| 469 // Format: | |
| 470 // '{' | |
| 471 void HeapTrace::TraceMarkSweepStart() { | |
| 472 if (isolate_initialized_) { | |
| 473 MarkSweepStartRecord rec(this); | |
| 474 } | |
| 475 } | |
| 476 | |
| 477 | |
| 478 // Mark Sweep Finish Record - '}' (0x7d) | |
| 479 // | |
| 480 // Format: | |
| 481 // '}' | |
| 482 void HeapTrace::TraceMarkSweepFinish() { | |
| 483 if (isolate_initialized_) { | |
| 484 MarkSweepFinishRecord rec(this); | |
| 485 } | |
| 486 } | |
| 487 | |
| 488 } // namespace dart | |
| OLD | NEW |