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/api-inl.h"
8 #include "src/debug/debug.h"
9 #include "src/heap/combined-heap.h"
10 #include "src/heap/heap-inl.h"
11 #include "src/objects/js-array-buffer-inl.h"
12 #include "src/profiler/allocation-tracker.h"
13 #include "src/profiler/heap-snapshot-generator-inl.h"
14 #include "src/profiler/sampling-heap-profiler.h"
15
16 namespace v8 {
17 namespace internal {
18
HeapProfiler(Heap * heap)19 HeapProfiler::HeapProfiler(Heap* heap)
20 : ids_(new HeapObjectsMap(heap)),
21 names_(new StringsStorage()),
22 is_tracking_object_moves_(false),
23 is_taking_snapshot_(false) {}
24
25 HeapProfiler::~HeapProfiler() = default;
26
DeleteAllSnapshots()27 void HeapProfiler::DeleteAllSnapshots() {
28 snapshots_.clear();
29 MaybeClearStringsStorage();
30 }
31
MaybeClearStringsStorage()32 void HeapProfiler::MaybeClearStringsStorage() {
33 if (snapshots_.empty() && !sampling_heap_profiler_ && !allocation_tracker_ &&
34 !is_taking_snapshot_) {
35 names_.reset(new StringsStorage());
36 }
37 }
38
RemoveSnapshot(HeapSnapshot * snapshot)39 void HeapProfiler::RemoveSnapshot(HeapSnapshot* snapshot) {
40 snapshots_.erase(
41 std::find_if(snapshots_.begin(), snapshots_.end(),
42 [&](const std::unique_ptr<HeapSnapshot>& entry) {
43 return entry.get() == snapshot;
44 }));
45 }
46
AddBuildEmbedderGraphCallback(v8::HeapProfiler::BuildEmbedderGraphCallback callback,void * data)47 void HeapProfiler::AddBuildEmbedderGraphCallback(
48 v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data) {
49 build_embedder_graph_callbacks_.push_back({callback, data});
50 }
51
RemoveBuildEmbedderGraphCallback(v8::HeapProfiler::BuildEmbedderGraphCallback callback,void * data)52 void HeapProfiler::RemoveBuildEmbedderGraphCallback(
53 v8::HeapProfiler::BuildEmbedderGraphCallback callback, void* data) {
54 auto it = std::find(build_embedder_graph_callbacks_.begin(),
55 build_embedder_graph_callbacks_.end(),
56 std::make_pair(callback, data));
57 if (it != build_embedder_graph_callbacks_.end())
58 build_embedder_graph_callbacks_.erase(it);
59 }
60
BuildEmbedderGraph(Isolate * isolate,v8::EmbedderGraph * graph)61 void HeapProfiler::BuildEmbedderGraph(Isolate* isolate,
62 v8::EmbedderGraph* graph) {
63 for (const auto& cb : build_embedder_graph_callbacks_) {
64 cb.first(reinterpret_cast<v8::Isolate*>(isolate), graph, cb.second);
65 }
66 }
67
SetGetDetachednessCallback(v8::HeapProfiler::GetDetachednessCallback callback,void * data)68 void HeapProfiler::SetGetDetachednessCallback(
69 v8::HeapProfiler::GetDetachednessCallback callback, void* data) {
70 get_detachedness_callback_ = {callback, data};
71 }
72
GetDetachedness(const v8::Local<v8::Value> v8_value,uint16_t class_id)73 v8::EmbedderGraph::Node::Detachedness HeapProfiler::GetDetachedness(
74 const v8::Local<v8::Value> v8_value, uint16_t class_id) {
75 DCHECK(HasGetDetachednessCallback());
76 return get_detachedness_callback_.first(
77 reinterpret_cast<v8::Isolate*>(heap()->isolate()), v8_value, class_id,
78 get_detachedness_callback_.second);
79 }
80
TakeSnapshot(v8::ActivityControl * control,v8::HeapProfiler::ObjectNameResolver * resolver,bool treat_global_objects_as_roots,bool capture_numeric_value)81 HeapSnapshot* HeapProfiler::TakeSnapshot(
82 v8::ActivityControl* control,
83 v8::HeapProfiler::ObjectNameResolver* resolver,
84 bool treat_global_objects_as_roots, bool capture_numeric_value) {
85 is_taking_snapshot_ = true;
86 HeapSnapshot* result = new HeapSnapshot(this, treat_global_objects_as_roots,
87 capture_numeric_value);
88 {
89 HeapSnapshotGenerator generator(result, control, resolver, heap());
90 if (!generator.GenerateSnapshot()) {
91 delete result;
92 result = nullptr;
93 } else {
94 snapshots_.emplace_back(result);
95 }
96 }
97 ids_->RemoveDeadEntries();
98 is_tracking_object_moves_ = true;
99 is_taking_snapshot_ = false;
100
101 heap()->isolate()->debug()->feature_tracker()->Track(
102 DebugFeatureTracker::kHeapSnapshot);
103
104 return result;
105 }
106
StartSamplingHeapProfiler(uint64_t sample_interval,int stack_depth,v8::HeapProfiler::SamplingFlags flags)107 bool HeapProfiler::StartSamplingHeapProfiler(
108 uint64_t sample_interval, int stack_depth,
109 v8::HeapProfiler::SamplingFlags flags) {
110 if (sampling_heap_profiler_.get()) {
111 return false;
112 }
113 sampling_heap_profiler_.reset(new SamplingHeapProfiler(
114 heap(), names_.get(), sample_interval, stack_depth, flags));
115 return true;
116 }
117
118
StopSamplingHeapProfiler()119 void HeapProfiler::StopSamplingHeapProfiler() {
120 sampling_heap_profiler_.reset();
121 MaybeClearStringsStorage();
122 }
123
124
GetAllocationProfile()125 v8::AllocationProfile* HeapProfiler::GetAllocationProfile() {
126 if (sampling_heap_profiler_.get()) {
127 return sampling_heap_profiler_->GetAllocationProfile();
128 } else {
129 return nullptr;
130 }
131 }
132
133
StartHeapObjectsTracking(bool track_allocations)134 void HeapProfiler::StartHeapObjectsTracking(bool track_allocations) {
135 ids_->UpdateHeapObjectsMap();
136 is_tracking_object_moves_ = true;
137 DCHECK(!allocation_tracker_);
138 if (track_allocations) {
139 allocation_tracker_.reset(new AllocationTracker(ids_.get(), names_.get()));
140 heap()->AddHeapObjectAllocationTracker(this);
141 heap()->isolate()->debug()->feature_tracker()->Track(
142 DebugFeatureTracker::kAllocationTracking);
143 }
144 }
145
PushHeapObjectsStats(OutputStream * stream,int64_t * timestamp_us)146 SnapshotObjectId HeapProfiler::PushHeapObjectsStats(OutputStream* stream,
147 int64_t* timestamp_us) {
148 return ids_->PushHeapObjectsStats(stream, timestamp_us);
149 }
150
StopHeapObjectsTracking()151 void HeapProfiler::StopHeapObjectsTracking() {
152 ids_->StopHeapObjectsTracking();
153 if (allocation_tracker_) {
154 allocation_tracker_.reset();
155 MaybeClearStringsStorage();
156 heap()->RemoveHeapObjectAllocationTracker(this);
157 }
158 }
159
GetSnapshotsCount() const160 int HeapProfiler::GetSnapshotsCount() const {
161 return static_cast<int>(snapshots_.size());
162 }
163
IsTakingSnapshot() const164 bool HeapProfiler::IsTakingSnapshot() const { return is_taking_snapshot_; }
165
GetSnapshot(int index)166 HeapSnapshot* HeapProfiler::GetSnapshot(int index) {
167 return snapshots_.at(index).get();
168 }
169
GetSnapshotObjectId(Handle<Object> obj)170 SnapshotObjectId HeapProfiler::GetSnapshotObjectId(Handle<Object> obj) {
171 if (!obj->IsHeapObject())
172 return v8::HeapProfiler::kUnknownObjectId;
173 return ids_->FindEntry(HeapObject::cast(*obj).address());
174 }
175
GetSnapshotObjectId(NativeObject obj)176 SnapshotObjectId HeapProfiler::GetSnapshotObjectId(NativeObject obj) {
177 // Try to find id of regular native node first.
178 SnapshotObjectId id = ids_->FindEntry(reinterpret_cast<Address>(obj));
179 // In case no id has been found, check whether there exists an entry where the
180 // native objects has been merged into a V8 entry.
181 if (id == v8::HeapProfiler::kUnknownObjectId) {
182 id = ids_->FindMergedNativeEntry(obj);
183 }
184 return id;
185 }
186
ObjectMoveEvent(Address from,Address to,int size)187 void HeapProfiler::ObjectMoveEvent(Address from, Address to, int size) {
188 base::MutexGuard guard(&profiler_mutex_);
189 bool known_object = ids_->MoveObject(from, to, size);
190 if (!known_object && allocation_tracker_) {
191 allocation_tracker_->address_to_trace()->MoveObject(from, to, size);
192 }
193 }
194
AllocationEvent(Address addr,int size)195 void HeapProfiler::AllocationEvent(Address addr, int size) {
196 DisallowGarbageCollection no_gc;
197 if (allocation_tracker_) {
198 allocation_tracker_->AllocationEvent(addr, size);
199 }
200 }
201
202
UpdateObjectSizeEvent(Address addr,int size)203 void HeapProfiler::UpdateObjectSizeEvent(Address addr, int size) {
204 ids_->UpdateObjectSize(addr, size);
205 }
206
FindHeapObjectById(SnapshotObjectId id)207 Handle<HeapObject> HeapProfiler::FindHeapObjectById(SnapshotObjectId id) {
208 HeapObject object;
209 CombinedHeapObjectIterator iterator(heap(),
210 HeapObjectIterator::kFilterUnreachable);
211 // Make sure that object with the given id is still reachable.
212 for (HeapObject obj = iterator.Next(); !obj.is_null();
213 obj = iterator.Next()) {
214 if (ids_->FindEntry(obj.address()) == id) {
215 DCHECK(object.is_null());
216 object = obj;
217 // Can't break -- kFilterUnreachable requires full heap traversal.
218 }
219 }
220
221 return !object.is_null() ? Handle<HeapObject>(object, isolate())
222 : Handle<HeapObject>();
223 }
224
225
ClearHeapObjectMap()226 void HeapProfiler::ClearHeapObjectMap() {
227 ids_.reset(new HeapObjectsMap(heap()));
228 if (!allocation_tracker_) is_tracking_object_moves_ = false;
229 }
230
231
heap() const232 Heap* HeapProfiler::heap() const { return ids_->heap(); }
233
isolate() const234 Isolate* HeapProfiler::isolate() const { return heap()->isolate(); }
235
QueryObjects(Handle<Context> context,debug::QueryObjectPredicate * predicate,PersistentValueVector<v8::Object> * objects)236 void HeapProfiler::QueryObjects(Handle<Context> context,
237 debug::QueryObjectPredicate* predicate,
238 PersistentValueVector<v8::Object>* objects) {
239 {
240 HandleScope handle_scope(isolate());
241 std::vector<Handle<JSTypedArray>> on_heap_typed_arrays;
242 CombinedHeapObjectIterator heap_iterator(
243 heap(), HeapObjectIterator::kFilterUnreachable);
244 for (HeapObject heap_obj = heap_iterator.Next(); !heap_obj.is_null();
245 heap_obj = heap_iterator.Next()) {
246 if (heap_obj.IsFeedbackVector()) {
247 FeedbackVector::cast(heap_obj).ClearSlots(isolate());
248 } else if (heap_obj.IsJSTypedArray() &&
249 JSTypedArray::cast(heap_obj).is_on_heap()) {
250 // Cannot call typed_array->GetBuffer() here directly because it may
251 // trigger GC. Defer that call by collecting the object in a vector.
252 on_heap_typed_arrays.push_back(
253 handle(JSTypedArray::cast(heap_obj), isolate()));
254 }
255 }
256 for (auto& typed_array : on_heap_typed_arrays) {
257 // Convert the on-heap typed array into off-heap typed array, so that
258 // its ArrayBuffer becomes valid and can be returned in the result.
259 typed_array->GetBuffer();
260 }
261 }
262 // We should return accurate information about live objects, so we need to
263 // collect all garbage first.
264 heap()->CollectAllAvailableGarbage(GarbageCollectionReason::kHeapProfiler);
265 CombinedHeapObjectIterator heap_iterator(
266 heap(), HeapObjectIterator::kFilterUnreachable);
267 PtrComprCageBase cage_base(isolate());
268 for (HeapObject heap_obj = heap_iterator.Next(); !heap_obj.is_null();
269 heap_obj = heap_iterator.Next()) {
270 if (!heap_obj.IsJSObject(cage_base) ||
271 heap_obj.IsJSExternalObject(cage_base))
272 continue;
273 v8::Local<v8::Object> v8_obj(
274 Utils::ToLocal(handle(JSObject::cast(heap_obj), isolate())));
275 if (!predicate->Filter(v8_obj)) continue;
276 objects->Append(v8_obj);
277 }
278 }
279
280 } // namespace internal
281 } // namespace v8
282