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-inl.h" 8 #include "src/debug/debug.h" 9 #include "src/heap/heap-inl.h" 10 #include "src/profiler/allocation-tracker.h" 11 #include "src/profiler/heap-snapshot-generator-inl.h" 12 #include "src/profiler/sampling-heap-profiler.h" 13 14 namespace v8 { 15 namespace internal { 16 17 HeapProfiler::HeapProfiler(Heap* heap) 18 : ids_(new HeapObjectsMap(heap)), 19 names_(new StringsStorage()), 20 is_tracking_object_moves_(false) {} 21 22 HeapProfiler::~HeapProfiler() = default; 23 24 void HeapProfiler::DeleteAllSnapshots() { 25 snapshots_.clear(); 26 names_.reset(new StringsStorage()); 27 } 28 29 30 void HeapProfiler::RemoveSnapshot(HeapSnapshot* snapshot) { 31 snapshots_.erase( 32 std::find_if(snapshots_.begin(), snapshots_.end(), 33 [&](const std::unique_ptr<HeapSnapshot>& entry) { 34 return entry.get() == snapshot; 35 })); 36 } 37 38 39 void HeapProfiler::DefineWrapperClass( 40 uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback) { 41 DCHECK_NE(class_id, v8::HeapProfiler::kPersistentHandleNoClassId); 42 if (wrapper_callbacks_.size() <= class_id) { 43 wrapper_callbacks_.insert(wrapper_callbacks_.end(), 44 class_id - wrapper_callbacks_.size() + 1, 45 nullptr); 46 } 47 wrapper_callbacks_[class_id] = callback; 48 } 49 50 51 v8::RetainedObjectInfo* HeapProfiler::ExecuteWrapperClassCallback( 52 uint16_t class_id, Object** wrapper) { 53 if (wrapper_callbacks_.size() <= class_id) return nullptr; 54 return wrapper_callbacks_[class_id]( 55 class_id, Utils::ToLocal(Handle<Object>(wrapper))); 56 } 57 58 void HeapProfiler::SetGetRetainerInfosCallback( 59 v8::HeapProfiler::GetRetainerInfosCallback callback) { 60 get_retainer_infos_callback_ = callback; 61 } 62 63 v8::HeapProfiler::RetainerInfos HeapProfiler::GetRetainerInfos( 64 Isolate* isolate) { 65 v8::HeapProfiler::RetainerInfos infos; 66 if (get_retainer_infos_callback_ != nullptr) 67 infos = 68 get_retainer_infos_callback_(reinterpret_cast<v8::Isolate*>(isolate)); 69 return infos; 70 } 71 72 void HeapProfiler::AddBuildEmbedderGraphCallback( 73 v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data) { 74 build_embedder_graph_callbacks_.push_back({callback, data}); 75 } 76 77 void HeapProfiler::RemoveBuildEmbedderGraphCallback( 78 v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data) { 79 auto it = std::find(build_embedder_graph_callbacks_.begin(), 80 build_embedder_graph_callbacks_.end(), 81 std::make_pair(callback, data)); 82 if (it != build_embedder_graph_callbacks_.end()) 83 build_embedder_graph_callbacks_.erase(it); 84 } 85 86 void HeapProfiler::BuildEmbedderGraph(Isolate* isolate, 87 v8::EmbedderGraph* graph) { 88 for (const auto& cb : build_embedder_graph_callbacks_) { 89 cb.first(reinterpret_cast<v8::Isolate*>(isolate), graph, cb.second); 90 } 91 } 92 93 HeapSnapshot* HeapProfiler::TakeSnapshot( 94 v8::ActivityControl* control, 95 v8::HeapProfiler::ObjectNameResolver* resolver) { 96 HeapSnapshot* result = new HeapSnapshot(this); 97 { 98 HeapSnapshotGenerator generator(result, control, resolver, heap()); 99 if (!generator.GenerateSnapshot()) { 100 delete result; 101 result = nullptr; 102 } else { 103 snapshots_.emplace_back(result); 104 } 105 } 106 ids_->RemoveDeadEntries(); 107 is_tracking_object_moves_ = true; 108 109 heap()->isolate()->debug()->feature_tracker()->Track( 110 DebugFeatureTracker::kHeapSnapshot); 111 112 return result; 113 } 114 115 bool HeapProfiler::StartSamplingHeapProfiler( 116 uint64_t sample_interval, int stack_depth, 117 v8::HeapProfiler::SamplingFlags flags) { 118 if (sampling_heap_profiler_.get()) { 119 return false; 120 } 121 sampling_heap_profiler_.reset(new SamplingHeapProfiler( 122 heap(), names_.get(), sample_interval, stack_depth, flags)); 123 return true; 124 } 125 126 127 void HeapProfiler::StopSamplingHeapProfiler() { 128 sampling_heap_profiler_.reset(); 129 } 130 131 132 v8::AllocationProfile* HeapProfiler::GetAllocationProfile() { 133 if (sampling_heap_profiler_.get()) { 134 return sampling_heap_profiler_->GetAllocationProfile(); 135 } else { 136 return nullptr; 137 } 138 } 139 140 141 void HeapProfiler::StartHeapObjectsTracking(bool track_allocations) { 142 ids_->UpdateHeapObjectsMap(); 143 is_tracking_object_moves_ = true; 144 DCHECK(!allocation_tracker_); 145 if (track_allocations) { 146 allocation_tracker_.reset(new AllocationTracker(ids_.get(), names_.get())); 147 heap()->AddHeapObjectAllocationTracker(this); 148 heap()->isolate()->debug()->feature_tracker()->Track( 149 DebugFeatureTracker::kAllocationTracking); 150 } 151 } 152 153 SnapshotObjectId HeapProfiler::PushHeapObjectsStats(OutputStream* stream, 154 int64_t* timestamp_us) { 155 return ids_->PushHeapObjectsStats(stream, timestamp_us); 156 } 157 158 void HeapProfiler::StopHeapObjectsTracking() { 159 ids_->StopHeapObjectsTracking(); 160 if (allocation_tracker_) { 161 allocation_tracker_.reset(); 162 heap()->RemoveHeapObjectAllocationTracker(this); 163 } 164 } 165 166 int HeapProfiler::GetSnapshotsCount() { 167 return static_cast<int>(snapshots_.size()); 168 } 169 170 HeapSnapshot* HeapProfiler::GetSnapshot(int index) { 171 return snapshots_.at(index).get(); 172 } 173 174 SnapshotObjectId HeapProfiler::GetSnapshotObjectId(Handle<Object> obj) { 175 if (!obj->IsHeapObject()) 176 return v8::HeapProfiler::kUnknownObjectId; 177 return ids_->FindEntry(HeapObject::cast(*obj)->address()); 178 } 179 180 void HeapProfiler::ObjectMoveEvent(Address from, Address to, int size) { 181 base::LockGuard<base::Mutex> guard(&profiler_mutex_); 182 bool known_object = ids_->MoveObject(from, to, size); 183 if (!known_object && allocation_tracker_) { 184 allocation_tracker_->address_to_trace()->MoveObject(from, to, size); 185 } 186 } 187 188 void HeapProfiler::AllocationEvent(Address addr, int size) { 189 DisallowHeapAllocation no_allocation; 190 if (allocation_tracker_) { 191 allocation_tracker_->AllocationEvent(addr, size); 192 } 193 } 194 195 196 void HeapProfiler::UpdateObjectSizeEvent(Address addr, int size) { 197 ids_->UpdateObjectSize(addr, size); 198 } 199 200 Handle<HeapObject> HeapProfiler::FindHeapObjectById(SnapshotObjectId id) { 201 HeapObject* object = nullptr; 202 HeapIterator iterator(heap(), HeapIterator::kFilterUnreachable); 203 // Make sure that object with the given id is still reachable. 204 for (HeapObject* obj = iterator.next(); obj != nullptr; 205 obj = iterator.next()) { 206 if (ids_->FindEntry(obj->address()) == id) { 207 DCHECK_NULL(object); 208 object = obj; 209 // Can't break -- kFilterUnreachable requires full heap traversal. 210 } 211 } 212 return object != nullptr ? Handle<HeapObject>(object, isolate()) 213 : Handle<HeapObject>(); 214 } 215 216 217 void HeapProfiler::ClearHeapObjectMap() { 218 ids_.reset(new HeapObjectsMap(heap())); 219 if (!allocation_tracker_) is_tracking_object_moves_ = false; 220 } 221 222 223 Heap* HeapProfiler::heap() const { return ids_->heap(); } 224 225 Isolate* HeapProfiler::isolate() const { return heap()->isolate(); } 226 227 void HeapProfiler::QueryObjects(Handle<Context> context, 228 debug::QueryObjectPredicate* predicate, 229 PersistentValueVector<v8::Object>* objects) { 230 // We should return accurate information about live objects, so we need to 231 // collect all garbage first. 232 heap()->CollectAllAvailableGarbage( 233 GarbageCollectionReason::kLowMemoryNotification); 234 heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask, 235 GarbageCollectionReason::kHeapProfiler); 236 HeapIterator heap_iterator(heap()); 237 HeapObject* heap_obj; 238 while ((heap_obj = heap_iterator.next()) != nullptr) { 239 if (!heap_obj->IsJSObject() || heap_obj->IsExternal(isolate())) continue; 240 v8::Local<v8::Object> v8_obj( 241 Utils::ToLocal(handle(JSObject::cast(heap_obj), isolate()))); 242 if (!predicate->Filter(v8_obj)) continue; 243 objects->Append(v8_obj); 244 } 245 } 246 247 } // namespace internal 248 } // namespace v8 249