| // Copyright 2016 the V8 project authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #ifndef V8_HEAP_EMBEDDER_TRACING_H_ | 
 | #define V8_HEAP_EMBEDDER_TRACING_H_ | 
 |  | 
 | #include "include/v8.h" | 
 | #include "src/common/globals.h" | 
 | #include "src/flags/flags.h" | 
 |  | 
 | namespace v8 { | 
 | namespace internal { | 
 |  | 
 | class Heap; | 
 | class JSObject; | 
 |  | 
 | class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final { | 
 |  public: | 
 |   using WrapperInfo = std::pair<void*, void*>; | 
 |   using WrapperCache = std::vector<WrapperInfo>; | 
 |  | 
 |   // WrapperInfo is passed over the API. Use VerboseWrapperInfo to access pair | 
 |   // internals in a named way. See ProcessingScope::TracePossibleJSWrapper() | 
 |   // below on how a V8 object is parsed to gather the information. | 
 |   struct VerboseWrapperInfo { | 
 |     explicit VerboseWrapperInfo(const WrapperInfo& raw_info) | 
 |         : raw_info(raw_info) {} | 
 |  | 
 |     // Information describing the type pointed to via instance(). | 
 |     void* type_info() const { return raw_info.first; } | 
 |     // Direct pointer to an instance described by type_info(). | 
 |     void* instance() const { return raw_info.second; } | 
 |  | 
 |     bool is_valid() const { return type_info(); } | 
 |  | 
 |     const WrapperInfo& raw_info; | 
 |   }; | 
 |  | 
 |   class V8_EXPORT_PRIVATE ProcessingScope { | 
 |    public: | 
 |     explicit ProcessingScope(LocalEmbedderHeapTracer* tracer); | 
 |     ~ProcessingScope(); | 
 |  | 
 |     void TracePossibleWrapper(JSObject js_object); | 
 |  | 
 |     void AddWrapperInfoForTesting(WrapperInfo info); | 
 |  | 
 |    private: | 
 |     static constexpr size_t kWrapperCacheSize = 1000; | 
 |  | 
 |     void FlushWrapperCacheIfFull(); | 
 |  | 
 |     LocalEmbedderHeapTracer* const tracer_; | 
 |     WrapperCache wrapper_cache_; | 
 |   }; | 
 |  | 
 |   static WrapperInfo ExtractWrapperInfo(Isolate* isolate, JSObject js_object); | 
 |  | 
 |   explicit LocalEmbedderHeapTracer(Isolate* isolate) : isolate_(isolate) {} | 
 |  | 
 |   ~LocalEmbedderHeapTracer() { | 
 |     if (remote_tracer_) remote_tracer_->isolate_ = nullptr; | 
 |   } | 
 |  | 
 |   bool InUse() const { return remote_tracer_ != nullptr; } | 
 |   EmbedderHeapTracer* remote_tracer() const { return remote_tracer_; } | 
 |  | 
 |   void SetRemoteTracer(EmbedderHeapTracer* tracer); | 
 |   void TracePrologue(EmbedderHeapTracer::TraceFlags flags); | 
 |   void TraceEpilogue(); | 
 |   void EnterFinalPause(); | 
 |   bool Trace(double deadline); | 
 |   bool IsRemoteTracingDone(); | 
 |  | 
 |   bool IsRootForNonTracingGC(const v8::TracedGlobal<v8::Value>& handle) { | 
 |     return !InUse() || remote_tracer_->IsRootForNonTracingGC(handle); | 
 |   } | 
 |  | 
 |   bool IsRootForNonTracingGC(const v8::TracedReference<v8::Value>& handle) { | 
 |     return !InUse() || remote_tracer_->IsRootForNonTracingGC(handle); | 
 |   } | 
 |  | 
 |   void ResetHandleInNonTracingGC(const v8::TracedReference<v8::Value>& handle) { | 
 |     // Resetting is only called when IsRootForNonTracingGC returns false which | 
 |     // can only happen the EmbedderHeapTracer is set on API level. | 
 |     DCHECK(InUse()); | 
 |     remote_tracer_->ResetHandleInNonTracingGC(handle); | 
 |   } | 
 |  | 
 |   bool ShouldFinalizeIncrementalMarking() { | 
 |     return !FLAG_incremental_marking_wrappers || !InUse() || | 
 |            (IsRemoteTracingDone() && embedder_worklist_empty_); | 
 |   } | 
 |  | 
 |   void SetEmbedderStackStateForNextFinalization( | 
 |       EmbedderHeapTracer::EmbedderStackState stack_state); | 
 |  | 
 |   void SetEmbedderWorklistEmpty(bool is_empty) { | 
 |     embedder_worklist_empty_ = is_empty; | 
 |   } | 
 |  | 
 |   void IncreaseAllocatedSize(size_t bytes) { | 
 |     remote_stats_.used_size += bytes; | 
 |     remote_stats_.allocated_size += bytes; | 
 |     if (remote_stats_.allocated_size > | 
 |         remote_stats_.allocated_size_limit_for_check) { | 
 |       StartIncrementalMarkingIfNeeded(); | 
 |       remote_stats_.allocated_size_limit_for_check = | 
 |           remote_stats_.allocated_size + kEmbedderAllocatedThreshold; | 
 |     } | 
 |   } | 
 |  | 
 |   void DecreaseAllocatedSize(size_t bytes) { | 
 |     DCHECK_GE(remote_stats_.used_size, bytes); | 
 |     remote_stats_.used_size -= bytes; | 
 |   } | 
 |  | 
 |   void StartIncrementalMarkingIfNeeded(); | 
 |  | 
 |   size_t used_size() const { return remote_stats_.used_size; } | 
 |   size_t allocated_size() const { return remote_stats_.allocated_size; } | 
 |  | 
 |  private: | 
 |   static constexpr size_t kEmbedderAllocatedThreshold = 128 * KB; | 
 |  | 
 |   Isolate* const isolate_; | 
 |   EmbedderHeapTracer* remote_tracer_ = nullptr; | 
 |  | 
 |   EmbedderHeapTracer::EmbedderStackState embedder_stack_state_ = | 
 |       EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers; | 
 |   // Indicates whether the embedder worklist was observed empty on the main | 
 |   // thread. This is opportunistic as concurrent marking tasks may hold local | 
 |   // segments of potential embedder fields to move to the main thread. | 
 |   bool embedder_worklist_empty_ = false; | 
 |  | 
 |   struct RemoteStatistics { | 
 |     // Used size of objects in bytes reported by the embedder. Updated via | 
 |     // TraceSummary at the end of tracing and incrementally when the GC is not | 
 |     // in progress. | 
 |     size_t used_size = 0; | 
 |     // Totally bytes allocated by the embedder. Monotonically | 
 |     // increasing value. Used to approximate allocation rate. | 
 |     size_t allocated_size = 0; | 
 |     // Limit for |allocated_size| in bytes to avoid checking for starting a GC | 
 |     // on each increment. | 
 |     size_t allocated_size_limit_for_check = 0; | 
 |   } remote_stats_; | 
 |  | 
 |   friend class EmbedderStackStateScope; | 
 | }; | 
 |  | 
 | class V8_EXPORT_PRIVATE EmbedderStackStateScope final { | 
 |  public: | 
 |   EmbedderStackStateScope(LocalEmbedderHeapTracer* local_tracer, | 
 |                           EmbedderHeapTracer::EmbedderStackState stack_state) | 
 |       : local_tracer_(local_tracer), | 
 |         old_stack_state_(local_tracer_->embedder_stack_state_) { | 
 |     local_tracer_->embedder_stack_state_ = stack_state; | 
 |     if (EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers == | 
 |         stack_state) { | 
 |       if (local_tracer->remote_tracer()) | 
 |         local_tracer->remote_tracer()->NotifyEmptyEmbedderStack(); | 
 |     } | 
 |   } | 
 |  | 
 |   ~EmbedderStackStateScope() { | 
 |     local_tracer_->embedder_stack_state_ = old_stack_state_; | 
 |   } | 
 |  | 
 |  private: | 
 |   LocalEmbedderHeapTracer* const local_tracer_; | 
 |   const EmbedderHeapTracer::EmbedderStackState old_stack_state_; | 
 | }; | 
 |  | 
 | }  // namespace internal | 
 | }  // namespace v8 | 
 |  | 
 | #endif  // V8_HEAP_EMBEDDER_TRACING_H_ |