// 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. #include "src/heap/embedder-tracing.h" #include "include/v8-cppgc.h" #include "src/base/logging.h" #include "src/heap/gc-tracer.h" #include "src/objects/embedder-data-slot.h" #include "src/objects/js-objects-inl.h" namespace v8 { namespace internal { void LocalEmbedderHeapTracer::SetRemoteTracer(EmbedderHeapTracer* tracer) { if (remote_tracer_) remote_tracer_->isolate_ = nullptr; remote_tracer_ = tracer; default_embedder_roots_handler_.SetTracer(tracer); if (remote_tracer_) remote_tracer_->isolate_ = reinterpret_cast<v8::Isolate*>(isolate_); } void LocalEmbedderHeapTracer::TracePrologue( EmbedderHeapTracer::TraceFlags flags) { if (!InUse()) return; embedder_worklist_empty_ = false; remote_tracer_->TracePrologue(flags); } void LocalEmbedderHeapTracer::TraceEpilogue() { if (!InUse()) return; EmbedderHeapTracer::TraceSummary summary; remote_tracer_->TraceEpilogue(&summary); if (summary.allocated_size == SIZE_MAX) return; UpdateRemoteStats(summary.allocated_size, summary.time); } void LocalEmbedderHeapTracer::UpdateRemoteStats(size_t allocated_size, double time) { remote_stats_.used_size = allocated_size; // Force a check next time increased memory is reported. This allows for // setting limits close to actual heap sizes. remote_stats_.allocated_size_limit_for_check = 0; constexpr double kMinReportingTimeMs = 0.5; if (time > kMinReportingTimeMs) { isolate_->heap()->tracer()->RecordEmbedderSpeed(allocated_size, time); } } void LocalEmbedderHeapTracer::EnterFinalPause() { if (!InUse()) return; remote_tracer_->EnterFinalPause(embedder_stack_state_); // Resetting to state unknown as there may be follow up garbage collections // triggered from callbacks that have a different stack state. embedder_stack_state_ = EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers; } bool LocalEmbedderHeapTracer::Trace(double deadline) { if (!InUse()) return true; return remote_tracer_->AdvanceTracing(deadline); } bool LocalEmbedderHeapTracer::IsRemoteTracingDone() { return !InUse() || remote_tracer_->IsTracingDone(); } void LocalEmbedderHeapTracer::SetEmbedderStackStateForNextFinalization( EmbedderHeapTracer::EmbedderStackState stack_state) { if (!InUse()) return; embedder_stack_state_ = stack_state; if (EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers == stack_state) { remote_tracer()->NotifyEmptyEmbedderStack(); } } namespace { bool ExtractWrappableInfo(Isolate* isolate, JSObject js_object, const WrapperDescriptor& wrapper_descriptor, LocalEmbedderHeapTracer::WrapperInfo* info) { DCHECK(js_object.IsApiWrapper()); if (js_object.GetEmbedderFieldCount() < 2) return false; if (EmbedderDataSlot(js_object, wrapper_descriptor.wrappable_type_index) .ToAlignedPointerSafe(isolate, &info->first) && info->first && EmbedderDataSlot(js_object, wrapper_descriptor.wrappable_instance_index) .ToAlignedPointerSafe(isolate, &info->second) && info->second) { return (wrapper_descriptor.embedder_id_for_garbage_collected == WrapperDescriptor::kUnknownEmbedderId) || (*static_cast<uint16_t*>(info->first) == wrapper_descriptor.embedder_id_for_garbage_collected); } return false; } } // namespace LocalEmbedderHeapTracer::ProcessingScope::ProcessingScope( LocalEmbedderHeapTracer* tracer) : tracer_(tracer), wrapper_descriptor_(tracer->wrapper_descriptor_) { wrapper_cache_.reserve(kWrapperCacheSize); } LocalEmbedderHeapTracer::ProcessingScope::~ProcessingScope() { if (!wrapper_cache_.empty()) { tracer_->remote_tracer()->RegisterV8References(std::move(wrapper_cache_)); } } LocalEmbedderHeapTracer::WrapperInfo LocalEmbedderHeapTracer::ExtractWrapperInfo(Isolate* isolate, JSObject js_object) { WrapperInfo info; if (ExtractWrappableInfo(isolate, js_object, wrapper_descriptor_, &info)) { return info; } return {nullptr, nullptr}; } void LocalEmbedderHeapTracer::ProcessingScope::TracePossibleWrapper( JSObject js_object) { DCHECK(js_object.IsApiWrapper()); WrapperInfo info; if (ExtractWrappableInfo(tracer_->isolate_, js_object, wrapper_descriptor_, &info)) { wrapper_cache_.push_back(std::move(info)); FlushWrapperCacheIfFull(); } } void LocalEmbedderHeapTracer::ProcessingScope::FlushWrapperCacheIfFull() { if (wrapper_cache_.size() == wrapper_cache_.capacity()) { tracer_->remote_tracer()->RegisterV8References(std::move(wrapper_cache_)); wrapper_cache_.clear(); wrapper_cache_.reserve(kWrapperCacheSize); } } void LocalEmbedderHeapTracer::ProcessingScope::AddWrapperInfoForTesting( WrapperInfo info) { wrapper_cache_.push_back(info); FlushWrapperCacheIfFull(); } void LocalEmbedderHeapTracer::StartIncrementalMarkingIfNeeded() { if (!FLAG_global_gc_scheduling || !FLAG_incremental_marking) return; Heap* heap = isolate_->heap(); heap->StartIncrementalMarkingIfAllocationLimitIsReached( heap->GCFlagsForIncrementalMarking(), kGCCallbackScheduleIdleGarbageCollection); if (heap->AllocationLimitOvershotByLargeMargin()) { heap->FinalizeIncrementalMarkingAtomically( i::GarbageCollectionReason::kExternalFinalize); } } bool DefaultEmbedderRootsHandler::IsRoot( const v8::TracedReference<v8::Value>& handle) { return !tracer_ || tracer_->IsRootForNonTracingGC(handle); } bool DefaultEmbedderRootsHandler::IsRoot( const v8::TracedGlobal<v8::Value>& handle) { return !tracer_ || tracer_->IsRootForNonTracingGC(handle); } void DefaultEmbedderRootsHandler::ResetRoot( const v8::TracedReference<v8::Value>& handle) { // Resetting is only called when IsRoot() returns false which // can only happen the EmbedderHeapTracer is set on API level. DCHECK(tracer_); tracer_->ResetHandleInNonTracingGC(handle); } } // namespace internal } // namespace v8