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
HeapProfiler(Heap * heap)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
DeleteAllSnapshots()24 void HeapProfiler::DeleteAllSnapshots() {
25 snapshots_.clear();
26 names_.reset(new StringsStorage());
27 }
28
29
RemoveSnapshot(HeapSnapshot * snapshot)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
DefineWrapperClass(uint16_t class_id,v8::HeapProfiler::WrapperInfoCallback callback)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
ExecuteWrapperClassCallback(uint16_t class_id,Object ** wrapper)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
SetGetRetainerInfosCallback(v8::HeapProfiler::GetRetainerInfosCallback callback)58 void HeapProfiler::SetGetRetainerInfosCallback(
59 v8::HeapProfiler::GetRetainerInfosCallback callback) {
60 get_retainer_infos_callback_ = callback;
61 }
62
GetRetainerInfos(Isolate * isolate)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
AddBuildEmbedderGraphCallback(v8::HeapProfiler::BuildEmbedderGraphCallback callback,void * data)72 void HeapProfiler::AddBuildEmbedderGraphCallback(
73 v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data) {
74 build_embedder_graph_callbacks_.push_back({callback, data});
75 }
76
RemoveBuildEmbedderGraphCallback(v8::HeapProfiler::BuildEmbedderGraphCallback callback,void * data)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
BuildEmbedderGraph(Isolate * isolate,v8::EmbedderGraph * graph)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
TakeSnapshot(v8::ActivityControl * control,v8::HeapProfiler::ObjectNameResolver * resolver)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
StartSamplingHeapProfiler(uint64_t sample_interval,int stack_depth,v8::HeapProfiler::SamplingFlags flags)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
StopSamplingHeapProfiler()127 void HeapProfiler::StopSamplingHeapProfiler() {
128 sampling_heap_profiler_.reset();
129 }
130
131
GetAllocationProfile()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
StartHeapObjectsTracking(bool track_allocations)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
PushHeapObjectsStats(OutputStream * stream,int64_t * timestamp_us)153 SnapshotObjectId HeapProfiler::PushHeapObjectsStats(OutputStream* stream,
154 int64_t* timestamp_us) {
155 return ids_->PushHeapObjectsStats(stream, timestamp_us);
156 }
157
StopHeapObjectsTracking()158 void HeapProfiler::StopHeapObjectsTracking() {
159 ids_->StopHeapObjectsTracking();
160 if (allocation_tracker_) {
161 allocation_tracker_.reset();
162 heap()->RemoveHeapObjectAllocationTracker(this);
163 }
164 }
165
GetSnapshotsCount()166 int HeapProfiler::GetSnapshotsCount() {
167 return static_cast<int>(snapshots_.size());
168 }
169
GetSnapshot(int index)170 HeapSnapshot* HeapProfiler::GetSnapshot(int index) {
171 return snapshots_.at(index).get();
172 }
173
GetSnapshotObjectId(Handle<Object> obj)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
ObjectMoveEvent(Address from,Address to,int size)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
AllocationEvent(Address addr,int size)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
UpdateObjectSizeEvent(Address addr,int size)196 void HeapProfiler::UpdateObjectSizeEvent(Address addr, int size) {
197 ids_->UpdateObjectSize(addr, size);
198 }
199
FindHeapObjectById(SnapshotObjectId id)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
ClearHeapObjectMap()217 void HeapProfiler::ClearHeapObjectMap() {
218 ids_.reset(new HeapObjectsMap(heap()));
219 if (!allocation_tracker_) is_tracking_object_moves_ = false;
220 }
221
222
heap() const223 Heap* HeapProfiler::heap() const { return ids_->heap(); }
224
isolate() const225 Isolate* HeapProfiler::isolate() const { return heap()->isolate(); }
226
QueryObjects(Handle<Context> context,debug::QueryObjectPredicate * predicate,PersistentValueVector<v8::Object> * objects)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