Index: runtime/vm/heap_trace.cc |
diff --git a/runtime/vm/heap_trace.cc b/runtime/vm/heap_trace.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..56282c22cc013f110b9cdead17f932e50396c9aa |
--- /dev/null |
+++ b/runtime/vm/heap_trace.cc |
@@ -0,0 +1,488 @@ |
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+#include "vm/heap_trace.h" |
+ |
+#include "include/dart_api.h" |
+#include "vm/dart_api_state.h" |
+#include "vm/debugger.h" |
+#include "vm/isolate.h" |
+#include "vm/object.h" |
+#include "vm/object_set.h" |
+#include "vm/object_store.h" |
+#include "vm/os.h" |
+#include "vm/stack_frame.h" |
+#include "vm/unicode.h" |
+ |
+namespace dart { |
+ |
+DEFINE_FLAG(bool, heap_trace, false, "Enable heap tracing."); |
+ |
+Dart_FileOpenCallback HeapTrace::open_callback_ = NULL; |
+Dart_FileWriteCallback HeapTrace::write_callback_ = NULL; |
+Dart_FileCloseCallback HeapTrace::close_callback_ = NULL; |
+bool HeapTrace::is_enabled_ = false; |
+ |
+class HeapTraceVisitor : public ObjectPointerVisitor { |
+ public: |
+ HeapTraceVisitor(Isolate* isolate, |
+ HeapTrace* heap_trace, |
+ ObjectSet* object_set) |
+ : ObjectPointerVisitor(isolate), |
+ heap_trace_(heap_trace), |
+ vm_isolate_(Dart::vm_isolate()), |
+ object_set_(object_set) { |
+ } |
+ |
+ void VisitPointers(RawObject** first, RawObject** last) { |
+ for (RawObject** current = first; current <= last; current++) { |
+ RawObject* raw_obj = *current; |
+ |
+ // We only care about objects in the heap |
+ // Also, since this visitor will frequently be encountering redudant |
+ // roots, we use an object_set to skip the duplicates. |
+ if (raw_obj->IsHeapObject() && |
+ raw_obj != reinterpret_cast<RawObject*>(0x1) && |
+ raw_obj != reinterpret_cast<RawObject*>(0xabababab) && |
+ !object_set_->Contains(raw_obj) && |
+ !vm_isolate_->heap()->Contains(RawObject::ToAddr(raw_obj))) { |
+ object_set_->Add(raw_obj); |
+ uword addr = RawObject::ToAddr(raw_obj); |
+ heap_trace_->TraceSingleRoot(addr); |
+ } |
+ } |
+ } |
+ |
+ private: |
+ HeapTrace* heap_trace_; |
+ Isolate* vm_isolate_; |
+ // TODO(cshapiro): replace with a sparse data structure. |
+ ObjectSet* object_set_; |
+ DISALLOW_COPY_AND_ASSIGN(HeapTraceVisitor); |
+}; |
+ |
+ |
+class HeapTraceScopedHandleVisitor : public ObjectPointerVisitor { |
+ public: |
+ HeapTraceScopedHandleVisitor(Isolate* isolate, HeapTrace* heap_trace) |
+ : ObjectPointerVisitor(isolate), heap_trace_(heap_trace) { |
+ } |
+ |
+ void VisitPointers(RawObject** first, RawObject** last) { |
+ for (RawObject** current = first; current <= last; current++) { |
+ RawObject* raw_obj = *current; |
+ Heap* heap = isolate()->heap(); |
+ |
+ // We only care about objects in the heap |
+ if (raw_obj->IsHeapObject() && |
+ raw_obj != reinterpret_cast<RawObject*>(0x1) && |
+ raw_obj != reinterpret_cast<RawObject*>(0xabababab) && |
+ heap->Contains(RawObject::ToAddr(raw_obj))) { |
+ uword addr = RawObject::ToAddr(raw_obj); |
+ heap_trace_->TraceScopedHandle(addr); |
+ } |
+ } |
+ } |
+ |
+ private: |
+ HeapTrace* heap_trace_; |
+ DISALLOW_COPY_AND_ASSIGN(HeapTraceScopedHandleVisitor); |
+}; |
+ |
+ |
+class HeapTraceObjectStoreVisitor : public ObjectPointerVisitor { |
+ public: |
+ HeapTraceObjectStoreVisitor(Isolate* isolate, HeapTrace* heap_trace) |
+ : ObjectPointerVisitor(isolate), heap_trace_(heap_trace) { |
+ } |
+ |
+ void VisitPointers(RawObject** first, RawObject** last) { |
+ for (RawObject** current = first; current <= last; current++) { |
+ RawObject* raw_obj = *current; |
+ |
+ // We only care about obects in the heap. |
+ if (raw_obj->IsHeapObject() && |
+ raw_obj != reinterpret_cast<RawObject*>(0x1) && |
+ raw_obj != reinterpret_cast<RawObject*>(0xabababab)) { |
+ uword addr = RawObject::ToAddr(raw_obj); |
+ heap_trace_->TraceObjectStorePointer(addr); |
+ } |
+ } |
+ } |
+ |
+ private: |
+ HeapTrace* heap_trace_; |
+ DISALLOW_COPY_AND_ASSIGN(HeapTraceObjectStoreVisitor); |
+}; |
+ |
+ |
+class HeapTraceInitialHeapVisitor : public ObjectVisitor { |
+ public: |
+ HeapTraceInitialHeapVisitor(Isolate* isolate, HeapTrace* heap_trace) |
+ : ObjectVisitor(isolate), heap_trace_(heap_trace) {} |
+ |
+ void VisitObject(RawObject* raw_obj) { |
+ heap_trace_->TraceSnapshotAlloc(raw_obj, raw_obj->Size()); |
+ } |
+ |
+ private: |
+ HeapTrace* heap_trace_; |
+ DISALLOW_COPY_AND_ASSIGN(HeapTraceInitialHeapVisitor); |
+}; |
+ |
+ |
+HeapTrace::HeapTrace() : isolate_initialized_(false), output_stream_(NULL) { |
+} |
+ |
+ |
+HeapTrace::~HeapTrace() { |
+ if (isolate_initialized_) { |
+ (*close_callback_)(output_stream_); |
+ } |
+} |
+ |
+ |
+void HeapTrace::InitOnce(Dart_FileOpenCallback open_callback, |
+ Dart_FileWriteCallback write_callback, |
+ Dart_FileCloseCallback close_callback) { |
+ ASSERT(open_callback != NULL); |
+ ASSERT(write_callback != NULL); |
+ ASSERT(close_callback != NULL); |
+ HeapTrace::open_callback_ = open_callback; |
+ HeapTrace::write_callback_ = write_callback; |
+ HeapTrace::close_callback_ = close_callback; |
+ HeapTrace::is_enabled_ = true; |
+} |
+ |
+ |
+ObjectSet* HeapTrace::CreateEmptyObjectSet() const { |
+ Isolate* isolate = Isolate::Current(); |
+ uword start, end; |
+ isolate->heap()->StartEndAddress(&start, &end); |
+ |
+ Isolate* vm_isolate = Dart::vm_isolate(); |
+ uword vm_start, vm_end; |
+ vm_isolate->heap()->StartEndAddress(&vm_start, &vm_end); |
+ |
+ ObjectSet* allocated_set = new ObjectSet(Utils::Minimum(start, vm_start), |
+ Utils::Maximum(end, vm_end)); |
+ |
+ return allocated_set; |
+} |
+ |
+ |
+void HeapTrace::ResizeObjectSet() { |
+ Isolate* isolate = Isolate::Current(); |
+ uword start, end; |
+ isolate->heap()->StartEndAddress(&start, &end); |
+ Isolate* vm_isolate = Dart::vm_isolate(); |
+ uword vm_start, vm_end; |
+ vm_isolate->heap()->StartEndAddress(&vm_start, &vm_end); |
+ object_set_.Resize(Utils::Minimum(start, vm_start), |
+ Utils::Maximum(end, vm_end)); |
+} |
+ |
+ |
+void HeapTrace::Init(Isolate* isolate) { |
+ // Do not trace the VM isolate |
+ if (isolate == Dart::vm_isolate()) { |
+ return; |
+ } |
+ ASSERT(isolate_initialized_ == false); |
+ const char* format = "%s.htrace"; |
+ intptr_t len = OS::SNPrint(NULL, 0, format, isolate->name()); |
+ char* filename = new char[len + 1]; |
+ OS::SNPrint(filename, len + 1, format, isolate->name()); |
+ output_stream_ = (*open_callback_)(filename); |
+ ASSERT(output_stream_ != NULL); |
+ delete[] filename; |
+ isolate_initialized_ = true; |
+ |
+ HeapTraceObjectStoreVisitor object_store_visitor(isolate, this); |
+ isolate->object_store()->VisitObjectPointers(&object_store_visitor); |
+ |
+ // Visit any objects that may have been allocated during startup, |
+ // before we started tracing. |
+ HeapTraceInitialHeapVisitor heap_visitor(isolate, this); |
+ isolate->heap()->IterateObjects(&heap_visitor); |
+ TraceRoots(isolate); |
+} |
+ |
+ |
+// Allocation Record - 'A' (0x41) |
+// |
+// Format: |
+// 'A' |
+// uword - address of allocated object |
+// uword - size of allocated object |
+void HeapTrace::TraceAllocation(uword addr, intptr_t size) { |
+ if (isolate_initialized_) { |
+ { |
+ AllocationRecord rec(this); |
+ rec.Write(addr); |
+ rec.Write(size); |
+ } |
+ TraceRoots(Isolate::Current()); |
+ } |
+} |
+ |
+ |
+// Snapshot Allocation Record - 'B' (0x41) |
+// |
+// Format: |
+// 'B' |
+// uword - address of allocated object |
+// uword - size of allocated object |
+void HeapTrace::TraceSnapshotAlloc(RawObject* obj, intptr_t size) { |
+ if (isolate_initialized_) { |
+ SnapshotAllocationRecord rec(this); |
+ rec.Write(RawObject::ToAddr(obj)); |
+ rec.Write(static_cast<uword>(size)); |
+ } |
+} |
+ |
+ |
+// Allocate Zone Handle Record - 'Z' (0x5a) |
+// |
+// Format: |
+// 'Z' |
+// uword - handle address (where the handle is pointing) |
+// uword - zone address (address of the zone the handle is in) |
+void HeapTrace::TraceAllocateZoneHandle(uword handle, uword zone_addr) { |
+ if (isolate_initialized_) { |
+ AllocZoneHandleRecord rec(this); |
+ rec.Write(handle); |
+ rec.Write(zone_addr); |
+ } |
+} |
+ |
+ |
+// Delete Zone Record - 'z' (0x7a) |
+// |
+// Format: |
+// 'z' |
+// uword - zone address (all the handles in that zone are now gone) |
+void HeapTrace::TraceDeleteZone(Zone* zone) { |
+ if (isolate_initialized_) { |
+ DeleteZoneRecord rec(this); |
+ rec.Write(reinterpret_cast<uword>(zone)); |
+ } |
+} |
+ |
+ |
+// Delete Scoped Hanldes Record - 's' (0x73) |
+// |
+// Format: |
+// 's' |
+void HeapTrace::TraceDeleteScopedHandles() { |
+ if (isolate_initialized_) { |
+ DeleteScopedHandlesRecord rec(this); |
+ } |
+} |
+ |
+ |
+// Copy Record - 'C' (0x43) |
+// |
+// Format: |
+// 'C' |
+// uword - old address |
+// uword - new address |
+void HeapTrace::TraceCopy(uword from_addr, uword to_addr) { |
+ if (isolate_initialized_) { |
+ CopyRecord rec(this); |
+ rec.Write(from_addr); |
+ rec.Write(to_addr); |
+ } |
+} |
+ |
+ |
+// Object Store Recorda - 'O'(0x4f) |
+// |
+// Format: |
+// 'O' |
+// uword - address |
+void HeapTrace::TraceObjectStorePointer(uword addr) { |
+ if (isolate_initialized_) { |
+ ObjectStoreRecord rec(this); |
+ rec.Write(addr); |
+ } |
+} |
+ |
+ |
+// Promotion Records - 'P' (0x50) |
+// |
+// Format: |
+// 'P' |
+// uword - old address |
+// uword - new address |
+void HeapTrace::TracePromotion(uword old_addr, uword promoted_addr) { |
+ if (isolate_initialized_) { |
+ PromotionRecord rec(this); |
+ rec.Write(old_addr); |
+ rec.Write(promoted_addr); |
+ } |
+} |
+ |
+ |
+// Death Range Record - 'L' (0x4c) |
+// |
+// Format: |
+// 'L' |
+// uword - inclusive start address of the space being left |
+// uword - exclusive end address of the space being left |
+void HeapTrace::TraceDeathRange(uword inclusive_start, uword exclusive_end) { |
+ if (isolate_initialized_) { |
+ DeathRangeRecord rec(this); |
+ rec.Write(inclusive_start); |
+ rec.Write(exclusive_end); |
+ } |
+} |
+ |
+ |
+// Register Class Record - 'K' (0x4b) |
+// |
+// Format: |
+// 'K' |
+// uword - address ( the address of the class) |
+void HeapTrace::TraceRegisterClass(const Class& cls) { |
+ if (isolate_initialized_) { |
+ RegisterClassRecord rec(this); |
+ rec.Write(RawObject::ToAddr(cls.raw())); |
+ } |
+} |
+ |
+ |
+// Scoped Handle Record - 'H' (0x48) |
+// |
+// Format: |
+// 'H' |
+// uword - adress of the scoped handle (where it is pointing) |
+void HeapTrace::TraceScopedHandle(uword handle) { |
+ if (isolate_initialized_) { |
+ AllocScopedHandleRecord rec(this); |
+ rec.Write(handle); |
+ } |
+} |
+ |
+ |
+// Root Record - 'R' (0x52) |
+// |
+// Format: |
+// 'R' |
+// uword - address |
+void HeapTrace::TraceSingleRoot(uword root_addr) { |
+ if (isolate_initialized_) { |
+ RootRecord rec(this); |
+ rec.Write(root_addr); |
+ } |
+} |
+ |
+ |
+// Sweep Record - 'S' |
+// |
+// Format: |
+// 'S' |
+// uword - address |
+void HeapTrace::TraceSweep(uword sweept_addr) { |
+ if (isolate_initialized_) { |
+ SweepRecord rec(this); |
+ rec.Write(sweept_addr); |
+ } |
+} |
+ |
+ |
+// Does not output any records directly, |
+// but does call TraceSingleRoot |
+void HeapTrace::TraceRoots(Isolate* isolate) { |
+ if (isolate_initialized_) { |
+ ResizeObjectSet(); |
+ HeapTraceVisitor visitor(isolate, this, &object_set_); |
+ HeapTraceScopedHandleVisitor handle_visitor(isolate, this); |
+ |
+ bool visit_prologue_weak_handles = true; |
+ bool validate_frames = false; |
+ |
+ // Visit objects in per isolate stubs. |
+ StubCode::VisitObjectPointers(&visitor); |
+ |
+ // stack |
+ StackFrameIterator frames_iterator(validate_frames); |
+ StackFrame* frame = frames_iterator.NextFrame(); |
+ while (frame != NULL) { |
+ frame->VisitObjectPointers(&visitor); |
+ frame = frames_iterator.NextFrame(); |
+ } |
+ |
+ if (isolate->api_state() != NULL) { |
+ isolate->api_state()->VisitObjectPointers(&visitor, |
+ visit_prologue_weak_handles); |
+ } |
+ |
+ // Visit the top context which is stored in the isolate. |
+ RawContext* top_context = isolate->top_context(); |
+ visitor.VisitPointer(reinterpret_cast<RawObject**>(&top_context)); |
+ |
+ // Visit the currently active IC data array. |
+ RawArray* ic_data_array = isolate->ic_data_array(); |
+ visitor.VisitPointer(reinterpret_cast<RawObject**>(&ic_data_array)); |
+ |
+ // Visit objects in the debugger. |
+ isolate->debugger()->VisitObjectPointers(&visitor); |
+ |
+ isolate->current_zone()->handles()-> |
+ VisitUnvisitedScopedHandles(&handle_visitor); |
+ |
+ object_set_.FastClear(); |
+ } |
+} |
+ |
+ |
+// Store Record - 'U' (0x55) |
+// |
+// Format: |
+// 'U' |
+// uword - originating object address (where a pointer is being stored) |
+// uword - byte offset into origin where the pointer is being stored |
+// uword - value of the pointer being stored |
+void HeapTrace::TraceStoreIntoObject(uword object, |
+ uword field_addr, |
+ uword value) { |
+ if (isolate_initialized_) { |
+ // We don't care about pointers into the VM_Islate heap, so skip them. |
+ // There should not be any pointers /out/ of the VM isolate; so we |
+ // do not check object. |
+ if (Isolate::Current()->heap()->Contains(value)) { |
+ StoreRecord rec(this); |
+ uword slot_offset = field_addr - object; |
+ |
+ rec.Write(object); |
+ rec.Write(slot_offset); |
+ rec.Write(value); |
+ } |
+ } |
+} |
+ |
+ |
+// Mark Sweep Start Record - '{' (0x7b) |
+// |
+// Format: |
+// '{' |
+void HeapTrace::TraceMarkSweepStart() { |
+ if (isolate_initialized_) { |
+ MarkSweepStartRecord rec(this); |
+ } |
+} |
+ |
+ |
+// Mark Sweep Finish Record - '}' (0x7d) |
+// |
+// Format: |
+// '}' |
+void HeapTrace::TraceMarkSweepFinish() { |
+ if (isolate_initialized_) { |
+ MarkSweepFinishRecord rec(this); |
+ } |
+} |
+ |
+} // namespace dart |