• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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