1 // Copyright 2009-2010 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/profiler/heap-profiler.h" 6 7 #include "src/api.h" 8 #include "src/debug/debug.h" 9 #include "src/profiler/allocation-tracker.h" 10 #include "src/profiler/heap-snapshot-generator-inl.h" 11 12 namespace v8 { 13 namespace internal { 14 15 HeapProfiler::HeapProfiler(Heap* heap) 16 : ids_(new HeapObjectsMap(heap)), 17 names_(new StringsStorage(heap)), 18 is_tracking_object_moves_(false) { 19 } 20 21 22 static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) { 23 delete *snapshot_ptr; 24 } 25 26 27 HeapProfiler::~HeapProfiler() { 28 snapshots_.Iterate(DeleteHeapSnapshot); 29 snapshots_.Clear(); 30 } 31 32 33 void HeapProfiler::DeleteAllSnapshots() { 34 snapshots_.Iterate(DeleteHeapSnapshot); 35 snapshots_.Clear(); 36 names_.Reset(new StringsStorage(heap())); 37 } 38 39 40 void HeapProfiler::RemoveSnapshot(HeapSnapshot* snapshot) { 41 snapshots_.RemoveElement(snapshot); 42 } 43 44 45 void HeapProfiler::DefineWrapperClass( 46 uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback) { 47 DCHECK(class_id != v8::HeapProfiler::kPersistentHandleNoClassId); 48 if (wrapper_callbacks_.length() <= class_id) { 49 wrapper_callbacks_.AddBlock( 50 NULL, class_id - wrapper_callbacks_.length() + 1); 51 } 52 wrapper_callbacks_[class_id] = callback; 53 } 54 55 56 v8::RetainedObjectInfo* HeapProfiler::ExecuteWrapperClassCallback( 57 uint16_t class_id, Object** wrapper) { 58 if (wrapper_callbacks_.length() <= class_id) return NULL; 59 return wrapper_callbacks_[class_id]( 60 class_id, Utils::ToLocal(Handle<Object>(wrapper))); 61 } 62 63 64 HeapSnapshot* HeapProfiler::TakeSnapshot( 65 v8::ActivityControl* control, 66 v8::HeapProfiler::ObjectNameResolver* resolver) { 67 HeapSnapshot* result = new HeapSnapshot(this); 68 { 69 HeapSnapshotGenerator generator(result, control, resolver, heap()); 70 if (!generator.GenerateSnapshot()) { 71 delete result; 72 result = NULL; 73 } else { 74 snapshots_.Add(result); 75 } 76 } 77 ids_->RemoveDeadEntries(); 78 is_tracking_object_moves_ = true; 79 80 heap()->isolate()->debug()->feature_tracker()->Track( 81 DebugFeatureTracker::kHeapSnapshot); 82 83 return result; 84 } 85 86 87 void HeapProfiler::StartHeapObjectsTracking(bool track_allocations) { 88 ids_->UpdateHeapObjectsMap(); 89 is_tracking_object_moves_ = true; 90 DCHECK(!is_tracking_allocations()); 91 if (track_allocations) { 92 allocation_tracker_.Reset(new AllocationTracker(ids_.get(), names_.get())); 93 heap()->DisableInlineAllocation(); 94 heap()->isolate()->debug()->feature_tracker()->Track( 95 DebugFeatureTracker::kAllocationTracking); 96 } 97 } 98 99 100 SnapshotObjectId HeapProfiler::PushHeapObjectsStats(OutputStream* stream, 101 int64_t* timestamp_us) { 102 return ids_->PushHeapObjectsStats(stream, timestamp_us); 103 } 104 105 106 void HeapProfiler::StopHeapObjectsTracking() { 107 ids_->StopHeapObjectsTracking(); 108 if (is_tracking_allocations()) { 109 allocation_tracker_.Reset(NULL); 110 heap()->EnableInlineAllocation(); 111 } 112 } 113 114 115 size_t HeapProfiler::GetMemorySizeUsedByProfiler() { 116 size_t size = sizeof(*this); 117 size += names_->GetUsedMemorySize(); 118 size += ids_->GetUsedMemorySize(); 119 size += GetMemoryUsedByList(snapshots_); 120 for (int i = 0; i < snapshots_.length(); ++i) { 121 size += snapshots_[i]->RawSnapshotSize(); 122 } 123 return size; 124 } 125 126 127 int HeapProfiler::GetSnapshotsCount() { 128 return snapshots_.length(); 129 } 130 131 132 HeapSnapshot* HeapProfiler::GetSnapshot(int index) { 133 return snapshots_.at(index); 134 } 135 136 137 SnapshotObjectId HeapProfiler::GetSnapshotObjectId(Handle<Object> obj) { 138 if (!obj->IsHeapObject()) 139 return v8::HeapProfiler::kUnknownObjectId; 140 return ids_->FindEntry(HeapObject::cast(*obj)->address()); 141 } 142 143 144 void HeapProfiler::ObjectMoveEvent(Address from, Address to, int size) { 145 base::LockGuard<base::Mutex> guard(&profiler_mutex_); 146 bool known_object = ids_->MoveObject(from, to, size); 147 if (!known_object && !allocation_tracker_.is_empty()) { 148 allocation_tracker_->address_to_trace()->MoveObject(from, to, size); 149 } 150 } 151 152 153 void HeapProfiler::AllocationEvent(Address addr, int size) { 154 DisallowHeapAllocation no_allocation; 155 if (!allocation_tracker_.is_empty()) { 156 allocation_tracker_->AllocationEvent(addr, size); 157 } 158 } 159 160 161 void HeapProfiler::UpdateObjectSizeEvent(Address addr, int size) { 162 ids_->UpdateObjectSize(addr, size); 163 } 164 165 166 void HeapProfiler::SetRetainedObjectInfo(UniqueId id, 167 RetainedObjectInfo* info) { 168 // TODO(yurus, marja): Don't route this information through GlobalHandles. 169 heap()->isolate()->global_handles()->SetRetainedObjectInfo(id, info); 170 } 171 172 173 Handle<HeapObject> HeapProfiler::FindHeapObjectById(SnapshotObjectId id) { 174 HeapObject* object = NULL; 175 HeapIterator iterator(heap(), HeapIterator::kFilterUnreachable); 176 // Make sure that object with the given id is still reachable. 177 for (HeapObject* obj = iterator.next(); 178 obj != NULL; 179 obj = iterator.next()) { 180 if (ids_->FindEntry(obj->address()) == id) { 181 DCHECK(object == NULL); 182 object = obj; 183 // Can't break -- kFilterUnreachable requires full heap traversal. 184 } 185 } 186 return object != NULL ? Handle<HeapObject>(object) : Handle<HeapObject>(); 187 } 188 189 190 void HeapProfiler::ClearHeapObjectMap() { 191 ids_.Reset(new HeapObjectsMap(heap())); 192 if (!is_tracking_allocations()) is_tracking_object_moves_ = false; 193 } 194 195 196 Heap* HeapProfiler::heap() const { return ids_->heap(); } 197 198 199 } // namespace internal 200 } // namespace v8 201