• 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 #ifndef V8_HEAP_EMBEDDER_TRACING_H_
6 #define V8_HEAP_EMBEDDER_TRACING_H_
7 
8 #include <atomic>
9 
10 #include "include/v8-cppgc.h"
11 #include "include/v8-embedder-heap.h"
12 #include "include/v8-traced-handle.h"
13 #include "src/common/allow-deprecated.h"
14 #include "src/common/globals.h"
15 #include "src/execution/isolate.h"
16 #include "src/flags/flags.h"
17 #include "src/heap/cppgc-js/cpp-heap.h"
18 
19 namespace v8 {
20 namespace internal {
21 
22 class Heap;
23 class JSObject;
24 
25 class V8_EXPORT_PRIVATE DefaultEmbedderRootsHandler final
26     : public EmbedderRootsHandler {
27  public:
28   bool IsRoot(const v8::TracedReference<v8::Value>& handle) final;
29 
30   void ResetRoot(const v8::TracedReference<v8::Value>& handle) final;
31 
SetTracer(EmbedderHeapTracer * tracer)32   void SetTracer(EmbedderHeapTracer* tracer) { tracer_ = tracer; }
33 
34  private:
35   EmbedderHeapTracer* tracer_ = nullptr;
36 };
37 
38 class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
39  public:
40   using WrapperInfo = std::pair<void*, void*>;
41   using WrapperCache = std::vector<WrapperInfo>;
42 
43   // WrapperInfo is passed over the API. Use VerboseWrapperInfo to access pair
44   // internals in a named way. See ProcessingScope::TracePossibleJSWrapper()
45   // below on how a V8 object is parsed to gather the information.
46   struct VerboseWrapperInfo {
VerboseWrapperInfoVerboseWrapperInfo47     constexpr explicit VerboseWrapperInfo(const WrapperInfo& raw_info)
48         : raw_info(raw_info) {}
49 
50     // Information describing the type pointed to via instance().
type_infoVerboseWrapperInfo51     void* type_info() const { return raw_info.first; }
52     // Direct pointer to an instance described by type_info().
instanceVerboseWrapperInfo53     void* instance() const { return raw_info.second; }
54     // Returns whether the info is empty and thus does not keep a C++ object
55     // alive.
is_emptyVerboseWrapperInfo56     bool is_empty() const { return !type_info() || !instance(); }
57 
58     const WrapperInfo& raw_info;
59   };
60 
61   class V8_EXPORT_PRIVATE V8_NODISCARD ProcessingScope {
62    public:
63     explicit ProcessingScope(LocalEmbedderHeapTracer* tracer);
64     ~ProcessingScope();
65 
66     void TracePossibleWrapper(JSObject js_object);
67 
68     void AddWrapperInfoForTesting(WrapperInfo info);
69 
70    private:
71     static constexpr size_t kWrapperCacheSize = 1000;
72 
73     void FlushWrapperCacheIfFull();
74 
75     LocalEmbedderHeapTracer* const tracer_;
76     const WrapperDescriptor wrapper_descriptor_;
77     WrapperCache wrapper_cache_;
78   };
79 
80   static V8_INLINE bool ExtractWrappableInfo(Isolate*, JSObject,
81                                              const WrapperDescriptor&,
82                                              WrapperInfo*);
83   static V8_INLINE bool ExtractWrappableInfo(
84       Isolate*, const WrapperDescriptor&, const EmbedderDataSlot& type_slot,
85       const EmbedderDataSlot& instance_slot, WrapperInfo*);
86 
LocalEmbedderHeapTracer(Isolate * isolate)87   explicit LocalEmbedderHeapTracer(Isolate* isolate) : isolate_(isolate) {}
88 
~LocalEmbedderHeapTracer()89   ~LocalEmbedderHeapTracer() {
90     if (remote_tracer_) remote_tracer_->isolate_ = nullptr;
91     // CppHeap is not detached from Isolate here. Detaching is done explciitly
92     // on Isolate/Heap/CppHeap destruction.
93   }
94 
InUse()95   bool InUse() const { return cpp_heap_ || (remote_tracer_ != nullptr); }
96   // This method doesn't take CppHeap into account.
remote_tracer()97   EmbedderHeapTracer* remote_tracer() const {
98     DCHECK_NULL(cpp_heap_);
99     return remote_tracer_;
100   }
101 
102   void SetRemoteTracer(EmbedderHeapTracer* tracer);
103   void SetCppHeap(CppHeap* cpp_heap);
104   void PrepareForTrace(EmbedderHeapTracer::TraceFlags flags);
105   void TracePrologue(EmbedderHeapTracer::TraceFlags flags);
106   void TraceEpilogue();
107   void EnterFinalPause();
108   bool Trace(double deadline);
109   bool IsRemoteTracingDone();
110 
ShouldFinalizeIncrementalMarking()111   bool ShouldFinalizeIncrementalMarking() {
112     return !FLAG_incremental_marking_wrappers || !InUse() ||
113            (IsRemoteTracingDone() && embedder_worklist_empty_);
114   }
115 
SetEmbedderWorklistEmpty(bool is_empty)116   void SetEmbedderWorklistEmpty(bool is_empty) {
117     embedder_worklist_empty_ = is_empty;
118   }
119 
IncreaseAllocatedSize(size_t bytes)120   void IncreaseAllocatedSize(size_t bytes) {
121     remote_stats_.used_size.fetch_add(bytes, std::memory_order_relaxed);
122     remote_stats_.allocated_size += bytes;
123     if (remote_stats_.allocated_size >
124         remote_stats_.allocated_size_limit_for_check) {
125       StartIncrementalMarkingIfNeeded();
126       remote_stats_.allocated_size_limit_for_check =
127           remote_stats_.allocated_size + kEmbedderAllocatedThreshold;
128     }
129   }
130 
DecreaseAllocatedSize(size_t bytes)131   void DecreaseAllocatedSize(size_t bytes) {
132     DCHECK_GE(remote_stats_.used_size.load(std::memory_order_relaxed), bytes);
133     remote_stats_.used_size.fetch_sub(bytes, std::memory_order_relaxed);
134   }
135 
136   void StartIncrementalMarkingIfNeeded();
137 
used_size()138   size_t used_size() const {
139     return remote_stats_.used_size.load(std::memory_order_relaxed);
140   }
allocated_size()141   size_t allocated_size() const { return remote_stats_.allocated_size; }
142 
143   WrapperInfo ExtractWrapperInfo(Isolate* isolate, JSObject js_object);
144 
SetWrapperDescriptor(const WrapperDescriptor & wrapper_descriptor)145   void SetWrapperDescriptor(const WrapperDescriptor& wrapper_descriptor) {
146     DCHECK_NULL(cpp_heap_);
147     wrapper_descriptor_ = wrapper_descriptor;
148   }
149 
150   void UpdateRemoteStats(size_t, double);
151 
default_embedder_roots_handler()152   DefaultEmbedderRootsHandler& default_embedder_roots_handler() {
153     return default_embedder_roots_handler_;
154   }
155 
156   void NotifyEmptyEmbedderStack();
157 
embedder_stack_state()158   EmbedderHeapTracer::EmbedderStackState embedder_stack_state() const {
159     return embedder_stack_state_;
160   }
161 
162   void EmbedderWriteBarrier(Heap*, JSObject);
163 
164  private:
165   static constexpr size_t kEmbedderAllocatedThreshold = 128 * KB;
166 
167   static constexpr WrapperDescriptor::InternalFieldIndex
168       kDefaultWrapperTypeEmbedderIndex = 0;
169   static constexpr WrapperDescriptor::InternalFieldIndex
170       kDefaultWrapperInstanceEmbedderIndex = 1;
171 
GetDefaultWrapperDescriptor()172   static constexpr WrapperDescriptor GetDefaultWrapperDescriptor() {
173     // The default descriptor assumes the indices that known embedders use.
174     return WrapperDescriptor(kDefaultWrapperTypeEmbedderIndex,
175                              kDefaultWrapperInstanceEmbedderIndex,
176                              WrapperDescriptor::kUnknownEmbedderId);
177   }
178 
cpp_heap()179   CppHeap* cpp_heap() {
180     DCHECK_NOT_NULL(cpp_heap_);
181     DCHECK_NULL(remote_tracer_);
182     DCHECK_IMPLIES(isolate_, cpp_heap_ == isolate_->heap()->cpp_heap());
183     return cpp_heap_;
184   }
185 
wrapper_descriptor()186   WrapperDescriptor wrapper_descriptor() {
187     if (cpp_heap_)
188       return cpp_heap()->wrapper_descriptor();
189     else
190       return wrapper_descriptor_;
191   }
192 
193   Isolate* const isolate_;
194   EmbedderHeapTracer* remote_tracer_ = nullptr;
195   CppHeap* cpp_heap_ = nullptr;
196   DefaultEmbedderRootsHandler default_embedder_roots_handler_;
197 
198   EmbedderHeapTracer::EmbedderStackState embedder_stack_state_ =
199       EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers;
200   // Indicates whether the embedder worklist was observed empty on the main
201   // thread. This is opportunistic as concurrent marking tasks may hold local
202   // segments of potential embedder fields to move to the main thread.
203   bool embedder_worklist_empty_ = false;
204 
205   struct RemoteStatistics {
206     // Used size of objects in bytes reported by the embedder. Updated via
207     // TraceSummary at the end of tracing and incrementally when the GC is not
208     // in progress.
209     std::atomic<size_t> used_size{0};
210     // Totally bytes allocated by the embedder. Monotonically
211     // increasing value. Used to approximate allocation rate.
212     size_t allocated_size = 0;
213     // Limit for |allocated_size| in bytes to avoid checking for starting a GC
214     // on each increment.
215     size_t allocated_size_limit_for_check = 0;
216   } remote_stats_;
217 
218   // Default descriptor only used when the embedder is using EmbedderHeapTracer.
219   // The value is overriden by CppHeap with values that the embedder provided
220   // upon initialization.
221   WrapperDescriptor wrapper_descriptor_ = GetDefaultWrapperDescriptor();
222 
223   friend class EmbedderStackStateScope;
224 };
225 
226 }  // namespace internal
227 }  // namespace v8
228 
229 #endif  // V8_HEAP_EMBEDDER_TRACING_H_
230