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