1 // Copyright 2016 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/heap/embedder-tracing.h"
6
7 #include "include/v8-cppgc.h"
8 #include "src/base/logging.h"
9 #include "src/handles/global-handles.h"
10 #include "src/heap/embedder-tracing-inl.h"
11 #include "src/heap/gc-tracer.h"
12 #include "src/heap/marking-worklist-inl.h"
13
14 namespace v8 {
15 namespace internal {
16
SetRemoteTracer(EmbedderHeapTracer * tracer)17 void LocalEmbedderHeapTracer::SetRemoteTracer(EmbedderHeapTracer* tracer) {
18 CHECK_NULL(cpp_heap_);
19 if (remote_tracer_) remote_tracer_->isolate_ = nullptr;
20
21 remote_tracer_ = tracer;
22 default_embedder_roots_handler_.SetTracer(tracer);
23 if (remote_tracer_)
24 remote_tracer_->isolate_ = reinterpret_cast<v8::Isolate*>(isolate_);
25 }
26
SetCppHeap(CppHeap * cpp_heap)27 void LocalEmbedderHeapTracer::SetCppHeap(CppHeap* cpp_heap) {
28 CHECK_NULL(remote_tracer_);
29 cpp_heap_ = cpp_heap;
30 }
31
32 namespace {
ConvertTraceFlags(EmbedderHeapTracer::TraceFlags flags)33 CppHeap::GarbageCollectionFlags ConvertTraceFlags(
34 EmbedderHeapTracer::TraceFlags flags) {
35 CppHeap::GarbageCollectionFlags result;
36 if (flags & EmbedderHeapTracer::TraceFlags::kForced)
37 result |= CppHeap::GarbageCollectionFlagValues::kForced;
38 if (flags & EmbedderHeapTracer::TraceFlags::kReduceMemory)
39 result |= CppHeap::GarbageCollectionFlagValues::kReduceMemory;
40 return result;
41 }
42 } // namespace
43
PrepareForTrace(EmbedderHeapTracer::TraceFlags flags)44 void LocalEmbedderHeapTracer::PrepareForTrace(
45 EmbedderHeapTracer::TraceFlags flags) {
46 if (cpp_heap_)
47 cpp_heap()->InitializeTracing(
48 cppgc::internal::GarbageCollector::Config::CollectionType::kMajor,
49 ConvertTraceFlags(flags));
50 }
51
TracePrologue(EmbedderHeapTracer::TraceFlags flags)52 void LocalEmbedderHeapTracer::TracePrologue(
53 EmbedderHeapTracer::TraceFlags flags) {
54 if (!InUse()) return;
55
56 embedder_worklist_empty_ = false;
57 if (cpp_heap_)
58 cpp_heap()->StartTracing();
59 else
60 remote_tracer_->TracePrologue(flags);
61 }
62
TraceEpilogue()63 void LocalEmbedderHeapTracer::TraceEpilogue() {
64 if (!InUse()) return;
65
66 // Resetting to state unknown as there may be follow up garbage collections
67 // triggered from callbacks that have a different stack state.
68 embedder_stack_state_ =
69 EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers;
70
71 if (cpp_heap_) {
72 cpp_heap()->TraceEpilogue();
73 } else {
74 EmbedderHeapTracer::TraceSummary summary;
75 remote_tracer_->TraceEpilogue(&summary);
76 UpdateRemoteStats(summary.allocated_size, summary.time);
77 }
78 }
79
UpdateRemoteStats(size_t allocated_size,double time)80 void LocalEmbedderHeapTracer::UpdateRemoteStats(size_t allocated_size,
81 double time) {
82 remote_stats_.used_size = allocated_size;
83 // Force a check next time increased memory is reported. This allows for
84 // setting limits close to actual heap sizes.
85 remote_stats_.allocated_size_limit_for_check = 0;
86 constexpr double kMinReportingTimeMs = 0.5;
87 if (time > kMinReportingTimeMs) {
88 isolate_->heap()->tracer()->RecordEmbedderSpeed(allocated_size, time);
89 }
90 }
91
EnterFinalPause()92 void LocalEmbedderHeapTracer::EnterFinalPause() {
93 if (!InUse()) return;
94
95 if (cpp_heap_)
96 cpp_heap()->EnterFinalPause(embedder_stack_state_);
97 else
98 remote_tracer_->EnterFinalPause(embedder_stack_state_);
99 }
100
Trace(double max_duration)101 bool LocalEmbedderHeapTracer::Trace(double max_duration) {
102 if (!InUse()) return true;
103
104 if (cpp_heap_)
105 return cpp_heap()->AdvanceTracing(max_duration);
106 else
107 return remote_tracer_->AdvanceTracing(max_duration);
108 }
109
IsRemoteTracingDone()110 bool LocalEmbedderHeapTracer::IsRemoteTracingDone() {
111 return !InUse() || (cpp_heap_ ? cpp_heap()->IsTracingDone()
112 : remote_tracer_->IsTracingDone());
113 }
114
ProcessingScope(LocalEmbedderHeapTracer * tracer)115 LocalEmbedderHeapTracer::ProcessingScope::ProcessingScope(
116 LocalEmbedderHeapTracer* tracer)
117 : tracer_(tracer), wrapper_descriptor_(tracer->wrapper_descriptor_) {
118 DCHECK(!tracer_->cpp_heap_);
119 wrapper_cache_.reserve(kWrapperCacheSize);
120 }
121
~ProcessingScope()122 LocalEmbedderHeapTracer::ProcessingScope::~ProcessingScope() {
123 DCHECK(!tracer_->cpp_heap_);
124 if (!wrapper_cache_.empty()) {
125 tracer_->remote_tracer_->RegisterV8References(std::move(wrapper_cache_));
126 }
127 }
128
129 LocalEmbedderHeapTracer::WrapperInfo
ExtractWrapperInfo(Isolate * isolate,JSObject js_object)130 LocalEmbedderHeapTracer::ExtractWrapperInfo(Isolate* isolate,
131 JSObject js_object) {
132 WrapperInfo info;
133 if (ExtractWrappableInfo(isolate, js_object, wrapper_descriptor(), &info)) {
134 return info;
135 }
136 return {nullptr, nullptr};
137 }
138
TracePossibleWrapper(JSObject js_object)139 void LocalEmbedderHeapTracer::ProcessingScope::TracePossibleWrapper(
140 JSObject js_object) {
141 DCHECK(js_object.MayHaveEmbedderFields());
142 WrapperInfo info;
143 if (ExtractWrappableInfo(tracer_->isolate_, js_object, wrapper_descriptor_,
144 &info)) {
145 wrapper_cache_.push_back(std::move(info));
146 FlushWrapperCacheIfFull();
147 }
148 }
149
FlushWrapperCacheIfFull()150 void LocalEmbedderHeapTracer::ProcessingScope::FlushWrapperCacheIfFull() {
151 DCHECK(!tracer_->cpp_heap_);
152 if (wrapper_cache_.size() == wrapper_cache_.capacity()) {
153 tracer_->remote_tracer_->RegisterV8References(std::move(wrapper_cache_));
154 wrapper_cache_.clear();
155 wrapper_cache_.reserve(kWrapperCacheSize);
156 }
157 }
158
AddWrapperInfoForTesting(WrapperInfo info)159 void LocalEmbedderHeapTracer::ProcessingScope::AddWrapperInfoForTesting(
160 WrapperInfo info) {
161 wrapper_cache_.push_back(info);
162 FlushWrapperCacheIfFull();
163 }
164
StartIncrementalMarkingIfNeeded()165 void LocalEmbedderHeapTracer::StartIncrementalMarkingIfNeeded() {
166 if (!FLAG_global_gc_scheduling || !FLAG_incremental_marking) return;
167
168 Heap* heap = isolate_->heap();
169 heap->StartIncrementalMarkingIfAllocationLimitIsReached(
170 heap->GCFlagsForIncrementalMarking(),
171 kGCCallbackScheduleIdleGarbageCollection);
172 if (heap->AllocationLimitOvershotByLargeMargin()) {
173 heap->FinalizeIncrementalMarkingAtomically(
174 i::GarbageCollectionReason::kExternalFinalize);
175 }
176 }
177
NotifyEmptyEmbedderStack()178 void LocalEmbedderHeapTracer::NotifyEmptyEmbedderStack() {
179 auto* overriden_stack_state = isolate_->heap()->overriden_stack_state();
180 if (overriden_stack_state &&
181 (*overriden_stack_state ==
182 cppgc::EmbedderStackState::kMayContainHeapPointers))
183 return;
184
185 isolate_->global_handles()->NotifyEmptyEmbedderStack();
186 }
187
EmbedderWriteBarrier(Heap * heap,JSObject js_object)188 void LocalEmbedderHeapTracer::EmbedderWriteBarrier(Heap* heap,
189 JSObject js_object) {
190 DCHECK(InUse());
191 DCHECK(js_object.MayHaveEmbedderFields());
192 if (cpp_heap_) {
193 DCHECK_NOT_NULL(heap->mark_compact_collector());
194 const EmbedderDataSlot type_slot(js_object,
195 wrapper_descriptor_.wrappable_type_index);
196 const EmbedderDataSlot instance_slot(
197 js_object, wrapper_descriptor_.wrappable_instance_index);
198 heap->mark_compact_collector()
199 ->local_marking_worklists()
200 ->cpp_marking_state()
201 ->MarkAndPush(type_slot, instance_slot);
202 return;
203 }
204 LocalEmbedderHeapTracer::ProcessingScope scope(this);
205 scope.TracePossibleWrapper(js_object);
206 }
207
IsRoot(const v8::TracedReference<v8::Value> & handle)208 bool DefaultEmbedderRootsHandler::IsRoot(
209 const v8::TracedReference<v8::Value>& handle) {
210 return !tracer_ || tracer_->IsRootForNonTracingGC(handle);
211 }
212
ResetRoot(const v8::TracedReference<v8::Value> & handle)213 void DefaultEmbedderRootsHandler::ResetRoot(
214 const v8::TracedReference<v8::Value>& handle) {
215 // Resetting is only called when IsRoot() returns false which
216 // can only happen the EmbedderHeapTracer is set on API level.
217 DCHECK(tracer_);
218 tracer_->ResetHandleInNonTracingGC(handle);
219 }
220
221 } // namespace internal
222 } // namespace v8
223