v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
heap-profiler.cc
Go to the documentation of this file.
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
6
7#include <fstream>
8#include <optional>
9
10#include "include/v8-profiler.h"
11#include "src/api/api-inl.h"
12#include "src/debug/debug.h"
14#include "src/heap/heap-inl.h"
16#include "src/heap/heap.h"
21
22namespace v8::internal {
23
25 : ids_(new HeapObjectsMap(heap)),
27 is_tracking_object_moves_(false),
28 is_taking_snapshot_(false) {}
29
31
36
43
45 snapshots_.erase(
46 std::find_if(snapshots_.begin(), snapshots_.end(),
47 [&](const std::unique_ptr<HeapSnapshot>& entry) {
48 return entry.get() == snapshot;
49 }));
50}
51
52std::vector<v8::Local<v8::Value>> HeapProfiler::GetDetachedJSWrapperObjects() {
54
55 std::vector<v8::Local<v8::Value>> js_objects_found;
56 HeapObjectIterator iterator(heap());
57 for (Tagged<HeapObject> obj = iterator.Next(); !obj.is_null();
58 obj = iterator.Next()) {
59 if (HeapLayout::InCodeSpace(obj)) continue;
60 if (!IsJSApiWrapperObject(obj)) continue;
61 // Ensure object is wrappable, otherwise GetDetachedness() can crash
63 if (!wrapper.GetCppHeapWrappable(isolate(), kAnyCppHeapPointer)) continue;
64
66 Utils::ToLocal(direct_handle(Cast<JSObject>(obj), isolate())));
68 GetDetachedness(data, 0);
69
71 continue;
72
73 js_objects_found.push_back(data);
74 }
75
76 return js_objects_found;
77}
78
83
92
94 v8::EmbedderGraph* graph) {
95 for (const auto& cb : build_embedder_graph_callbacks_) {
96 cb.first(reinterpret_cast<v8::Isolate*>(isolate), graph, cb.second);
97 }
98}
99
104
106 const v8::Local<v8::Value> v8_value, uint16_t class_id) {
108 return get_detachedness_callback_.first(
109 reinterpret_cast<v8::Isolate*>(heap()->isolate()), v8_value, class_id,
111}
112
113const char* HeapProfiler::CopyNameForHeapSnapshot(const char* name) {
115 return names_->GetCopy(name);
116}
117
120 is_taking_snapshot_ = true;
122 new HeapSnapshot(this, options.snapshot_mode, options.numerics_mode);
123
124 // We need a stack marker here to allow deterministic passes over the stack.
125 // The garbage collection and the filling of references in GenerateSnapshot
126 // should scan the same part of the stack.
127 heap()->stack().SetMarkerIfNeededAndCallback([this, &options, &result]() {
128 std::optional<CppClassNamesAsHeapObjectNameScope> use_cpp_class_name;
129 if (result->expose_internals() && heap()->cpp_heap()) {
130 use_cpp_class_name.emplace(heap()->cpp_heap());
131 }
132
133 HeapSnapshotGenerator generator(result, options.control,
134 options.global_object_name_resolver, heap(),
135 options.stack_state);
136 if (!generator.GenerateSnapshot()) {
137 delete result;
138 result = nullptr;
139 } else {
140 snapshots_.emplace_back(result);
141 }
142 });
143 ids_->RemoveDeadEntries();
145 native_move_listener_->StartListening();
146 }
149 is_taking_snapshot_ = false;
150
151 return result;
152}
153
155 public:
156 explicit FileOutputStream(const char* filename) : os_(filename) {}
157 ~FileOutputStream() override { os_.close(); }
158
159 WriteResult WriteAsciiChunk(char* data, int size) override {
160 os_.write(data, size);
161 return kContinue;
162 }
163
164 void EndOfStream() override { os_.close(); }
165
166 private:
167 std::ofstream os_;
168};
169
170// Precondition: only call this if you have just completed a full GC cycle.
172 // We need to set a stack marker for the stack walk performed by the
173 // snapshot generator to work.
174 heap()->stack().SetMarkerIfNeededAndCallback([this, snapshot_mode]() {
176 std::string filename = "v8-heap-" + std::to_string(time) + ".heapsnapshot";
178 std::unique_ptr<HeapSnapshot> result(
179 new HeapSnapshot(this, snapshot_mode, options.numerics_mode));
180 HeapSnapshotGenerator generator(result.get(), options.control,
181 options.global_object_name_resolver, heap(),
182 options.stack_state);
183 if (!generator.GenerateSnapshotAfterGC()) return;
184 FileOutputStream stream(filename.c_str());
185 HeapSnapshotJSONSerializer serializer(result.get());
186 serializer.Serialize(&stream);
187 PrintF("Wrote heap snapshot to %s.\n", filename.c_str());
188 });
189}
190
192 const v8::HeapProfiler::HeapSnapshotOptions options, std::string filename) {
193 HeapSnapshot* snapshot = TakeSnapshot(options);
194 FileOutputStream stream(filename.c_str());
195 HeapSnapshotJSONSerializer serializer(snapshot);
196 serializer.Serialize(&stream);
197}
198
200 uint64_t sample_interval, int stack_depth,
202 if (sampling_heap_profiler_) return false;
204 heap(), names_.get(), sample_interval, stack_depth, flags));
205 return true;
206}
207
212
215 return sampling_heap_profiler_->GetAllocationProfile();
216 } else {
217 return nullptr;
218 }
219}
220
221void HeapProfiler::StartHeapObjectsTracking(bool track_allocations) {
222 ids_->UpdateHeapObjectsMap();
224 native_move_listener_->StartListening();
225 }
229 if (track_allocations) {
230 allocation_tracker_.reset(new AllocationTracker(ids_.get(), names_.get()));
232 }
233}
234
236 int64_t* timestamp_us) {
237 return ids_->PushHeapObjectsStats(stream, timestamp_us);
238}
239
241 ids_->StopHeapObjectsTracking();
243 allocation_tracker_.reset();
246 }
247}
248
250 return static_cast<int>(snapshots_.size());
251}
252
254
256 return snapshots_.at(index).get();
257}
258
263
265 // Try to find id of regular native node first.
266 SnapshotObjectId id = ids_->FindEntry(reinterpret_cast<Address>(obj));
267 // In case no id has been found, check whether there exists an entry where the
268 // native objects has been merged into a V8 entry.
270 id = ids_->FindMergedNativeEntry(obj);
271 }
272 return id;
273}
274
276 int size) {
277 profiler_->ObjectMoveEvent(from, to, size, /*is_native_object=*/true);
278}
279
281 bool is_native_object) {
283 bool known_object = ids_->MoveObject(from, to, size);
284 if (!known_object && allocation_tracker_ && !is_native_object) {
285 allocation_tracker_->address_to_trace()->MoveObject(from, to, size);
286 }
287}
288
292 allocation_tracker_->AllocationEvent(addr, size);
293 }
294}
295
297 ids_->UpdateObjectSize(addr, size);
298}
299
303 // Make sure that the object with the given id is still reachable.
304 for (Tagged<HeapObject> obj = iterator.Next(); !obj.is_null();
305 obj = iterator.Next()) {
306 if (ids_->FindEntry(obj.address()) == id)
307 return DirectHandle<HeapObject>(obj, isolate());
308 }
310}
311
313 ids_.reset(new HeapObjectsMap(heap()));
314 if (!allocation_tracker_) {
316 native_move_listener_->StopListening();
317 }
320 }
321}
322
323Heap* HeapProfiler::heap() const { return ids_->heap(); }
324
325Isolate* HeapProfiler::isolate() const { return heap()->isolate(); }
326
328 v8::QueryObjectPredicate* predicate,
329 std::vector<v8::Global<v8::Object>>* objects) {
330 // We need a stack marker here to allow deterministic passes over the stack.
331 // The garbage collection and the two object heap iterators should scan the
332 // same part of the stack.
333 heap()->stack().SetMarkerIfNeededAndCallback([this, predicate, objects]() {
334 {
335 HandleScope handle_scope(isolate());
336 std::vector<Handle<JSTypedArray>> on_heap_typed_arrays;
337 CombinedHeapObjectIterator heap_iterator(
339 for (Tagged<HeapObject> heap_obj = heap_iterator.Next();
340 !heap_obj.is_null(); heap_obj = heap_iterator.Next()) {
341 if (IsFeedbackVector(heap_obj)) {
342 Cast<FeedbackVector>(heap_obj)->ClearSlots(isolate());
343 } else if (IsJSTypedArray(heap_obj) &&
344 Cast<JSTypedArray>(heap_obj)->is_on_heap()) {
345 // Cannot call typed_array->GetBuffer() here directly because it may
346 // trigger GC. Defer that call by collecting the object in a vector.
347 on_heap_typed_arrays.push_back(
348 handle(Cast<JSTypedArray>(heap_obj), isolate()));
349 }
350 }
351 for (auto& typed_array : on_heap_typed_arrays) {
352 // Convert the on-heap typed array into off-heap typed array, so that
353 // its ArrayBuffer becomes valid and can be returned in the result.
354 typed_array->GetBuffer();
355 }
356 }
357 // We should return accurate information about live objects, so we need to
358 // collect all garbage first.
360 CombinedHeapObjectIterator heap_iterator(
362 PtrComprCageBase cage_base(isolate());
363 for (Tagged<HeapObject> heap_obj = heap_iterator.Next();
364 !heap_obj.is_null(); heap_obj = heap_iterator.Next()) {
365 if (!IsJSObject(heap_obj, cage_base) ||
366 IsJSExternalObject(heap_obj, cage_base))
367 continue;
369 Utils::ToLocal(direct_handle(Cast<JSObject>(heap_obj), isolate())));
370 if (!predicate->Filter(v8_obj)) continue;
371 objects->emplace_back(reinterpret_cast<v8::Isolate*>(isolate()), v8_obj);
372 }
373 });
374}
375
376} // namespace v8::internal
union v8::internal::@341::BuiltinMetadata::KindSpecificData data
EmbedderGraph::Node::Detachedness(*)( v8::Isolate *isolate, const v8::Local< v8::Value > &v8_value, uint16_t class_id, void *data) GetDetachednessCallback
static const SnapshotObjectId kUnknownObjectId
void(*) BuildEmbedderGraphCallback(v8::Isolate *isolate, v8::EmbedderGraph *graph, void *data)
virtual int64_t CurrentClockTimeMilliseconds()
virtual bool Filter(v8::Local< v8::Object > object)=0
FileOutputStream(const char *filename)
WriteResult WriteAsciiChunk(char *data, int size) override
static V8_INLINE bool InCodeSpace(Tagged< HeapObject > object)
Tagged< HeapObject > Next()
Definition heap.cc:6658
void ObjectMoveEvent(Address from, Address to, int size)
std::vector< std::pair< v8::HeapProfiler::BuildEmbedderGraphCallback, void * > > build_embedder_graph_callbacks_
const char * CopyNameForHeapSnapshot(const char *name)
std::vector< std::unique_ptr< HeapSnapshot > > snapshots_
std::unique_ptr< StringsStorage > names_
DirectHandle< HeapObject > FindHeapObjectById(SnapshotObjectId id)
std::vector< v8::Local< v8::Value > > GetDetachedJSWrapperObjects()
v8::EmbedderGraph::Node::Detachedness GetDetachedness(const v8::Local< v8::Value > v8_value, uint16_t class_id)
SnapshotObjectId GetSnapshotObjectId(DirectHandle< Object > obj)
SnapshotObjectId PushHeapObjectsStats(OutputStream *stream, int64_t *timestamp_us)
std::unique_ptr< SamplingHeapProfiler > sampling_heap_profiler_
void TakeSnapshotToFile(const v8::HeapProfiler::HeapSnapshotOptions options, std::string filename)
void SetGetDetachednessCallback(v8::HeapProfiler::GetDetachednessCallback callback, void *data)
void AllocationEvent(Address addr, int size) override
HeapSnapshot * GetSnapshot(int index)
void RemoveBuildEmbedderGraphCallback(v8::HeapProfiler::BuildEmbedderGraphCallback callback, void *data)
bool HasGetDetachednessCallback() const
void StartHeapObjectsTracking(bool track_allocations)
void WriteSnapshotToDiskAfterGC(HeapSnapshotMode snapshot_mode=HeapSnapshotMode::kRegular)
std::unique_ptr< HeapObjectsMap > ids_
void ObjectMoveEvent(Address from, Address to, int size, bool is_native_object)
std::unique_ptr< HeapProfilerNativeMoveListener > native_move_listener_
void AddBuildEmbedderGraphCallback(v8::HeapProfiler::BuildEmbedderGraphCallback callback, void *data)
AllocationProfile * GetAllocationProfile()
void UpdateObjectSizeEvent(Address addr, int size) override
bool StartSamplingHeapProfiler(uint64_t sample_interval, int stack_depth, v8::HeapProfiler::SamplingFlags)
void RemoveSnapshot(HeapSnapshot *snapshot)
std::unique_ptr< AllocationTracker > allocation_tracker_
void QueryObjects(DirectHandle< Context > context, QueryObjectPredicate *predicate, std::vector< v8::Global< v8::Object > > *objects)
HeapSnapshot * TakeSnapshot(const v8::HeapProfiler::HeapSnapshotOptions options)
std::pair< v8::HeapProfiler::GetDetachednessCallback, void * > get_detachedness_callback_
void BuildEmbedderGraph(Isolate *isolate, v8::EmbedderGraph *graph)
V8_EXPORT_PRIVATE void RemoveHeapObjectAllocationTracker(HeapObjectAllocationTracker *tracker)
Definition heap.cc:926
V8_EXPORT_PRIVATE void CollectAllAvailableGarbage(GarbageCollectionReason gc_reason)
Definition heap.cc:1327
V8_EXPORT_PRIVATE void AddHeapObjectAllocationTracker(HeapObjectAllocationTracker *tracker)
Definition heap.cc:915
V8_EXPORT_PRIVATE::heap::base::Stack & stack()
Definition heap.cc:6057
Isolate * isolate() const
Definition heap-inl.h:61
void UpdateLogObjectRelocation()
Definition isolate.cc:4383
V8_INLINE void * GetCppHeapWrappable(IsolateForPointerCompression isolate) const
V8_INLINE constexpr bool is_null() const
Definition tagged.h:502
static V8_EXPORT_PRIVATE v8::Platform * GetCurrentPlatform()
Definition v8.cc:282
std::string filename
StringsStorage * names_
TNode< Object > callback
DirectHandle< JSReceiver > options
ZoneVector< RpoNumber > & result
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
Definition handles-inl.h:72
void PrintF(const char *format,...)
Definition utils.cc:39
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
V8_INLINE constexpr bool IsHeapObject(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:669
bool IsJSApiWrapperObject(Tagged< Map > map)
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
void * NativeObject
Definition v8-profiler.h:30
constexpr CppHeapPointerTagRange kAnyCppHeapPointer(CppHeapPointerTag::kFirstTag, CppHeapPointerTag::kLastTag)
uint32_t SnapshotObjectId
Definition v8-profiler.h:31
#define CHECK(condition)
Definition logging.h:124
#define DCHECK(condition)
Definition logging.h:482