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 "include/v8.h" 9 #include "src/common/globals.h" 10 #include "src/flags/flags.h" 11 12 namespace v8 { 13 namespace internal { 14 15 class Heap; 16 class JSObject; 17 18 class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final { 19 public: 20 using WrapperInfo = std::pair<void*, void*>; 21 using WrapperCache = std::vector<WrapperInfo>; 22 23 // WrapperInfo is passed over the API. Use VerboseWrapperInfo to access pair 24 // internals in a named way. See ProcessingScope::TracePossibleJSWrapper() 25 // below on how a V8 object is parsed to gather the information. 26 struct VerboseWrapperInfo { VerboseWrapperInfoVerboseWrapperInfo27 explicit VerboseWrapperInfo(const WrapperInfo& raw_info) 28 : raw_info(raw_info) {} 29 30 // Information describing the type pointed to via instance(). type_infoVerboseWrapperInfo31 void* type_info() const { return raw_info.first; } 32 // Direct pointer to an instance described by type_info(). instanceVerboseWrapperInfo33 void* instance() const { return raw_info.second; } 34 is_validVerboseWrapperInfo35 bool is_valid() const { return type_info(); } 36 37 const WrapperInfo& raw_info; 38 }; 39 40 class V8_EXPORT_PRIVATE ProcessingScope { 41 public: 42 explicit ProcessingScope(LocalEmbedderHeapTracer* tracer); 43 ~ProcessingScope(); 44 45 void TracePossibleWrapper(JSObject js_object); 46 47 void AddWrapperInfoForTesting(WrapperInfo info); 48 49 private: 50 static constexpr size_t kWrapperCacheSize = 1000; 51 52 void FlushWrapperCacheIfFull(); 53 54 LocalEmbedderHeapTracer* const tracer_; 55 WrapperCache wrapper_cache_; 56 }; 57 58 static WrapperInfo ExtractWrapperInfo(Isolate* isolate, JSObject js_object); 59 LocalEmbedderHeapTracer(Isolate * isolate)60 explicit LocalEmbedderHeapTracer(Isolate* isolate) : isolate_(isolate) {} 61 ~LocalEmbedderHeapTracer()62 ~LocalEmbedderHeapTracer() { 63 if (remote_tracer_) remote_tracer_->isolate_ = nullptr; 64 } 65 InUse()66 bool InUse() const { return remote_tracer_ != nullptr; } remote_tracer()67 EmbedderHeapTracer* remote_tracer() const { return remote_tracer_; } 68 69 void SetRemoteTracer(EmbedderHeapTracer* tracer); 70 void TracePrologue(EmbedderHeapTracer::TraceFlags flags); 71 void TraceEpilogue(); 72 void EnterFinalPause(); 73 bool Trace(double deadline); 74 bool IsRemoteTracingDone(); 75 IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value> & handle)76 bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) { 77 return !InUse() || remote_tracer_->IsRootForNonTracingGC(handle); 78 } 79 IsRootForNonTracingGC(const v8::TracedReference<v8::Value> & handle)80 bool IsRootForNonTracingGC(const v8::TracedReference<v8::Value>& handle) { 81 return !InUse() || remote_tracer_->IsRootForNonTracingGC(handle); 82 } 83 ResetHandleInNonTracingGC(const v8::TracedReference<v8::Value> & handle)84 void ResetHandleInNonTracingGC(const v8::TracedReference<v8::Value>& handle) { 85 // Resetting is only called when IsRootForNonTracingGC returns false which 86 // can only happen the EmbedderHeapTracer is set on API level. 87 DCHECK(InUse()); 88 remote_tracer_->ResetHandleInNonTracingGC(handle); 89 } 90 ShouldFinalizeIncrementalMarking()91 bool ShouldFinalizeIncrementalMarking() { 92 return !FLAG_incremental_marking_wrappers || !InUse() || 93 (IsRemoteTracingDone() && embedder_worklist_empty_); 94 } 95 96 void SetEmbedderStackStateForNextFinalization( 97 EmbedderHeapTracer::EmbedderStackState stack_state); 98 SetEmbedderWorklistEmpty(bool is_empty)99 void SetEmbedderWorklistEmpty(bool is_empty) { 100 embedder_worklist_empty_ = is_empty; 101 } 102 IncreaseAllocatedSize(size_t bytes)103 void IncreaseAllocatedSize(size_t bytes) { 104 remote_stats_.used_size += bytes; 105 remote_stats_.allocated_size += bytes; 106 if (remote_stats_.allocated_size > 107 remote_stats_.allocated_size_limit_for_check) { 108 StartIncrementalMarkingIfNeeded(); 109 remote_stats_.allocated_size_limit_for_check = 110 remote_stats_.allocated_size + kEmbedderAllocatedThreshold; 111 } 112 } 113 DecreaseAllocatedSize(size_t bytes)114 void DecreaseAllocatedSize(size_t bytes) { 115 DCHECK_GE(remote_stats_.used_size, bytes); 116 remote_stats_.used_size -= bytes; 117 } 118 119 void StartIncrementalMarkingIfNeeded(); 120 used_size()121 size_t used_size() const { return remote_stats_.used_size; } allocated_size()122 size_t allocated_size() const { return remote_stats_.allocated_size; } 123 124 private: 125 static constexpr size_t kEmbedderAllocatedThreshold = 128 * KB; 126 127 Isolate* const isolate_; 128 EmbedderHeapTracer* remote_tracer_ = nullptr; 129 130 EmbedderHeapTracer::EmbedderStackState embedder_stack_state_ = 131 EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers; 132 // Indicates whether the embedder worklist was observed empty on the main 133 // thread. This is opportunistic as concurrent marking tasks may hold local 134 // segments of potential embedder fields to move to the main thread. 135 bool embedder_worklist_empty_ = false; 136 137 struct RemoteStatistics { 138 // Used size of objects in bytes reported by the embedder. Updated via 139 // TraceSummary at the end of tracing and incrementally when the GC is not 140 // in progress. 141 size_t used_size = 0; 142 // Totally bytes allocated by the embedder. Monotonically 143 // increasing value. Used to approximate allocation rate. 144 size_t allocated_size = 0; 145 // Limit for |allocated_size| in bytes to avoid checking for starting a GC 146 // on each increment. 147 size_t allocated_size_limit_for_check = 0; 148 } remote_stats_; 149 150 friend class EmbedderStackStateScope; 151 }; 152 153 class V8_EXPORT_PRIVATE EmbedderStackStateScope final { 154 public: EmbedderStackStateScope(LocalEmbedderHeapTracer * local_tracer,EmbedderHeapTracer::EmbedderStackState stack_state)155 EmbedderStackStateScope(LocalEmbedderHeapTracer* local_tracer, 156 EmbedderHeapTracer::EmbedderStackState stack_state) 157 : local_tracer_(local_tracer), 158 old_stack_state_(local_tracer_->embedder_stack_state_) { 159 local_tracer_->embedder_stack_state_ = stack_state; 160 if (EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers == 161 stack_state) { 162 if (local_tracer->remote_tracer()) 163 local_tracer->remote_tracer()->NotifyEmptyEmbedderStack(); 164 } 165 } 166 ~EmbedderStackStateScope()167 ~EmbedderStackStateScope() { 168 local_tracer_->embedder_stack_state_ = old_stack_state_; 169 } 170 171 private: 172 LocalEmbedderHeapTracer* const local_tracer_; 173 const EmbedderHeapTracer::EmbedderStackState old_stack_state_; 174 }; 175 176 } // namespace internal 177 } // namespace v8 178 179 #endif // V8_HEAP_EMBEDDER_TRACING_H_ 180