Chromium Code Reviews| 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..8c18931e73d28df3fbee41d6710b646d01c42d5f |
| --- /dev/null |
| +++ b/runtime/vm/heap_trace.cc |
| @@ -0,0 +1,481 @@ |
| +// Copyright 2012 Google Inc. All Rights Reserved. |
| +// Author: nricci@google.com (Nathan Ricci) |
|
siva
2012/12/05 16:06:40
This copyright doesn't seem to be standard.
cshapiro
2012/12/08 03:23:08
Done.
|
| + |
| +#include "include/dart_api.h" |
|
siva
2012/12/05 16:06:40
blank line.
cshapiro
2012/12/08 03:23:08
Done.
|
| +#include "vm/dart_api_state.h" |
| +#include "vm/debugger.h" |
| +#include "vm/heap_trace.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 { |
| + |
| +Dart_FileOpenCallback HeapTrace::open_callback_ = NULL; |
| +Dart_FileWriteCallback HeapTrace::write_callback_ = NULL; |
| +Dart_FileCloseCallback HeapTrace::close_callback_ = NULL; |
| +bool HeapTrace::tracing_enabled_ = false; |
| +const char* HeapTrace::file_name_prefix_ = NULL; |
| + |
| + |
| +class HeapTraceVisitor : public ObjectPointerVisitor { |
| + public: |
| + HeapTraceVisitor(Isolate* isolate, HeapTrace* tracer, ObjectSet* object_set) |
| + : ObjectPointerVisitor(isolate), |
| + tracer_(tracer), |
| + 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); |
| + tracer_->TraceSingleRoot(addr); |
| + } |
| + } |
| + } |
| + |
| + private: |
| + HeapTrace* tracer_; |
| + Isolate* vm_isolate_; |
| + // TODO(nricc): replace this with a map or something else sparse. |
| + ObjectSet* object_set_; |
| +}; |
| + |
| + |
| +class HeapTraceScopedHandleVisitor : public ObjectPointerVisitor { |
| + public: |
| + HeapTraceScopedHandleVisitor(Isolate* isolate, HeapTrace* tracer) |
| + : ObjectPointerVisitor(isolate), tracer_(tracer) { |
| + } |
| + |
| + 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); |
| + tracer_->TraceScopedHandle(addr); |
| + } |
| + } |
| + } |
| + |
| + private: |
| + HeapTrace* tracer_; |
| +}; |
| + |
| + |
| +class HeapTraceObjectStoreVisitor : public ObjectPointerVisitor { |
| + public: |
| + HeapTraceObjectStoreVisitor(Isolate* isolate, HeapTrace* tracer) |
| + : ObjectPointerVisitor(isolate), tracer_(tracer) {} |
| + |
| + 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); |
| + tracer_->TraceObjectStorePointer(addr); |
| + } |
| + } |
| + } |
| + |
| + private: |
| + HeapTrace* tracer_; |
| +}; |
| + |
| + |
| +class HeapTraceInitialHeapVisitor : public ObjectVisitor { |
| + public: |
| + HeapTraceInitialHeapVisitor(Isolate* isolate, HeapTrace* tracer) |
| + : ObjectVisitor(isolate), tracer_(tracer) {} |
| + |
| + void VisitObject(RawObject *raw_obj) { |
| + tracer_->TraceSnapshotAlloc(raw_obj, raw_obj->Size()); |
| + } |
| + |
| + private: |
| + HeapTrace* tracer_; |
| +}; |
| + |
| + |
| + |
| +HeapTrace::HeapTrace() : isolate_initialized_(false), |
| + output_stream_(NULL) { |
| +} |
| + |
| + |
| +HeapTrace::~HeapTrace() { |
| + if (isolate_initialized_) { |
| + (*close_callback_)(output_stream_); |
| + } |
| +} |
| + |
| + |
| +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)); |
| +} |
| + |
| + |
| +// InitializeIsolate or ...? |
| +void HeapTrace::InitializeIsolateTracing(Isolate* isolate) { |
| + // Do not trace the VM isolate |
| + if (isolate == Dart::vm_isolate()) { |
| + return; |
| + } |
| + ASSERT(isolate_initialized_ == false); |
| + const char* format = "%s_%p.trace"; |
| + intptr_t len = OS::SNPrint(NULL, 0, format, file_name_prefix_, isolate); |
|
siva
2012/12/05 16:06:40
This seems kind of ad-hoc to use the isolate point
cshapiro
2012/12/08 03:23:08
Good idea. Fixed.
|
| + char* filename = new char[len + 1]; |
| + OS::SNPrint(filename, len + 1, format, file_name_prefix_, isolate); |
| + 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(RawClass* klass) { |
| + if (isolate_initialized_) { |
| + RegisterClassRecord rec(this); |
| + rec.Write(RawObject::ToAddr(klass)); |
| + } |
| +} |
| + |
| +// 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(); // TODO(nricci): map or something? |
| + 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); |
| + } |
| +} |
| + |
| + |
| +void HeapTrace::InitTracing(Dart_FileOpenCallback open_callback, |
| + Dart_FileWriteCallback write_callback, |
| + Dart_FileCloseCallback close_callback, |
| + const char* prefix) { |
| + HeapTrace::open_callback_ = open_callback; |
| + HeapTrace::write_callback_ = write_callback; |
| + HeapTrace::close_callback_ = close_callback; |
| + HeapTrace::file_name_prefix_ = prefix; |
| + HeapTrace::tracing_enabled_ = true; |
| +} |
| + |
| + |
| +} // namespace dart |