[api, heap, handles] Remove deprecated EmbedderHeapTracer

This removes EmbedderHeapTracer from V8's API. Going forward
v8::TracedReference is only supported with using CppHeap (Oilpan).

Bug: v8:13207
Change-Id: I4e0efa94890ed147293b5df69fd7e0edad45abb5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4111546
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#85061}
This commit is contained in:
Michael Lippautz 2023-01-02 15:12:15 +01:00 committed by V8 LUCI CQ
parent d43b93a7ac
commit a8a1805e12
20 changed files with 68 additions and 1264 deletions

View File

@ -5,27 +5,14 @@
#ifndef INCLUDE_V8_EMBEDDER_HEAP_H_
#define INCLUDE_V8_EMBEDDER_HEAP_H_
#include <stddef.h>
#include <stdint.h>
#include <utility>
#include <vector>
#include "cppgc/common.h"
#include "v8-local-handle.h" // NOLINT(build/include_directory)
#include "v8-traced-handle.h" // NOLINT(build/include_directory)
#include "v8config.h" // NOLINT(build/include_directory)
namespace v8 {
class Data;
class Isolate;
class Value;
namespace internal {
class LocalEmbedderHeapTracer;
} // namespace internal
/**
* Handler for embedder roots on non-unified heap garbage collections.
*/
@ -62,162 +49,6 @@ class V8_EXPORT EmbedderRootsHandler {
virtual void ResetRoot(const v8::TracedReference<v8::Value>& handle) = 0;
};
/**
* Interface for tracing through the embedder heap. During a V8 garbage
* collection, V8 collects hidden fields of all potential wrappers, and at the
* end of its marking phase iterates the collection and asks the embedder to
* trace through its heap and use reporter to report each JavaScript object
* reachable from any of the given wrappers.
*/
class V8_EXPORT
// GCC doesn't like combining __attribute__(()) with [[deprecated]].
#ifdef __clang__
V8_DEPRECATED("Use CppHeap when working with v8::TracedReference.")
#endif // __clang__
EmbedderHeapTracer {
public:
using EmbedderStackState = cppgc::EmbedderStackState;
enum TraceFlags : uint64_t {
kNoFlags = 0,
kReduceMemory = 1 << 0,
kForced = 1 << 2,
};
/**
* Interface for iterating through |TracedReference| handles.
*/
class V8_EXPORT TracedGlobalHandleVisitor {
public:
virtual ~TracedGlobalHandleVisitor() = default;
virtual void VisitTracedReference(const TracedReference<Value>& handle) {}
};
/**
* Summary of a garbage collection cycle. See |TraceEpilogue| on how the
* summary is reported.
*/
struct TraceSummary {
/**
* Time spent managing the retained memory in milliseconds. This can e.g.
* include the time tracing through objects in the embedder.
*/
double time = 0.0;
/**
* Memory retained by the embedder through the |EmbedderHeapTracer|
* mechanism in bytes.
*/
size_t allocated_size = 0;
};
virtual ~EmbedderHeapTracer() = default;
/**
* Iterates all |TracedReference| handles created for the |v8::Isolate| the
* tracer is attached to.
*/
void IterateTracedGlobalHandles(TracedGlobalHandleVisitor* visitor);
/**
* Called by the embedder to set the start of the stack which is e.g. used by
* V8 to determine whether handles are used from stack or heap.
*/
void SetStackStart(void* stack_start);
/**
* Called by v8 to register internal fields of found wrappers.
*
* The embedder is expected to store them somewhere and trace reachable
* wrappers from them when called through |AdvanceTracing|.
*/
virtual void RegisterV8References(
const std::vector<std::pair<void*, void*>>& embedder_fields) = 0;
void RegisterEmbedderReference(const BasicTracedReference<v8::Data>& ref);
/**
* Called at the beginning of a GC cycle.
*/
virtual void TracePrologue(TraceFlags flags) {}
/**
* Called to advance tracing in the embedder.
*
* The embedder is expected to trace its heap starting from wrappers reported
* by RegisterV8References method, and report back all reachable wrappers.
* Furthermore, the embedder is expected to stop tracing by the given
* deadline. A deadline of infinity means that tracing should be finished.
*
* Returns |true| if tracing is done, and false otherwise.
*/
virtual bool AdvanceTracing(double deadline_in_ms) = 0;
/*
* Returns true if there no more tracing work to be done (see AdvanceTracing)
* and false otherwise.
*/
virtual bool IsTracingDone() = 0;
/**
* Called at the end of a GC cycle.
*
* Note that allocation is *not* allowed within |TraceEpilogue|. Can be
* overriden to fill a |TraceSummary| that is used by V8 to schedule future
* garbage collections.
*/
virtual void TraceEpilogue(TraceSummary* trace_summary) {}
/**
* Called upon entering the final marking pause. No more incremental marking
* steps will follow this call.
*/
virtual void EnterFinalPause(EmbedderStackState stack_state) = 0;
/*
* Called by the embedder to request immediate finalization of the currently
* running tracing phase that has been started with TracePrologue and not
* yet finished with TraceEpilogue.
*
* Will be a noop when currently not in tracing.
*
* This is an experimental feature.
*/
void FinalizeTracing();
/**
* See documentation on EmbedderRootsHandler.
*/
virtual bool IsRootForNonTracingGC(
const v8::TracedReference<v8::Value>& handle);
/**
* See documentation on EmbedderRootsHandler.
*/
virtual void ResetHandleInNonTracingGC(
const v8::TracedReference<v8::Value>& handle);
/*
* Called by the embedder to signal newly allocated or freed memory. Not bound
* to tracing phases. Embedders should trade off when increments are reported
* as V8 may consult global heuristics on whether to trigger garbage
* collection on this change.
*/
void IncreaseAllocatedSize(size_t bytes);
void DecreaseAllocatedSize(size_t bytes);
/*
* Returns the v8::Isolate this tracer is attached too and |nullptr| if it
* is not attached to any v8::Isolate.
*/
v8::Isolate* isolate() const { return v8_isolate_; }
protected:
v8::Isolate* v8_isolate_ = nullptr;
friend class internal::LocalEmbedderHeapTracer;
};
} // namespace v8
#endif // INCLUDE_V8_EMBEDDER_HEAP_H_

View File

@ -924,27 +924,10 @@ class V8_EXPORT Isolate {
void RemoveGCPrologueCallback(GCCallbackWithData, void* data = nullptr);
void RemoveGCPrologueCallback(GCCallback callback);
START_ALLOW_USE_DEPRECATED()
/**
* Sets the embedder heap tracer for the isolate.
* SetEmbedderHeapTracer cannot be used simultaneously with AttachCppHeap.
*/
void SetEmbedderHeapTracer(EmbedderHeapTracer* tracer);
/*
* Gets the currently active heap tracer for the isolate that was set with
* SetEmbedderHeapTracer.
*/
EmbedderHeapTracer* GetEmbedderHeapTracer();
END_ALLOW_USE_DEPRECATED()
/**
* Sets an embedder roots handle that V8 should consider when performing
* non-unified heap garbage collections.
*
* Using only EmbedderHeapTracer automatically sets up a default handler.
* The intended use case is for setting a custom handler after invoking
* `AttachCppHeap()`.
* non-unified heap garbage collections. The intended use case is for setting
* a custom handler after invoking `AttachCppHeap()`.
*
* V8 does not take ownership of the handler.
*/
@ -955,8 +938,6 @@ class V8_EXPORT Isolate {
* embedder maintains ownership of the CppHeap. At most one C++ heap can be
* attached to V8.
*
* AttachCppHeap cannot be used simultaneously with SetEmbedderHeapTracer.
*
* Multi-threaded use requires the use of v8::Locker/v8::Unlocker, see
* CppHeap.
*/

View File

@ -117,11 +117,11 @@ class TracedReferenceBase {
/**
* A traced handle with copy and move semantics. The handle is to be used
* together with |v8::EmbedderHeapTracer| or as part of GarbageCollected objects
* (see v8-cppgc.h) and specifies edges from C++ objects to JavaScript.
* together as part of GarbageCollected objects (see v8-cppgc.h) or from stack
* and specifies edges from C++ objects to JavaScript.
*
* The exact semantics are:
* - Tracing garbage collections use |v8::EmbedderHeapTracer| or cppgc.
* - Tracing garbage collections using CppHeap.
* - Non-tracing garbage collections refer to
* |v8::EmbedderRootsHandler::IsRoot()| whether the handle should
* be treated as root or not.
@ -166,7 +166,6 @@ class BasicTracedReference : public TracedReferenceBase {
Isolate* isolate, T* that, void* slot,
internal::GlobalHandleStoreMode store_mode);
friend class EmbedderHeapTracer;
template <typename F>
friend class Local;
friend class Object;
@ -181,13 +180,7 @@ class BasicTracedReference : public TracedReferenceBase {
/**
* A traced handle without destructor that clears the handle. The embedder needs
* to ensure that the handle is not accessed once the V8 object has been
* reclaimed. This can happen when the handle is not passed through the
* EmbedderHeapTracer. For more details see BasicTracedReference.
*
* The reference assumes the embedder has precise knowledge about references at
* all times. In case V8 needs to separately handle on-stack references, the
* embedder is required to set the stack start through
* |EmbedderHeapTracer::SetStackStart|.
* reclaimed. For more details see BasicTracedReference.
*/
template <typename T>
class TracedReference : public BasicTracedReference<T> {

View File

@ -76,6 +76,7 @@ include_rules = [
"+starboard",
# Using cppgc inside v8 is not (yet) allowed.
"-include/cppgc",
"+include/cppgc/common.h",
"+include/cppgc/platform.h",
"+include/cppgc/source-location.h",
]

View File

@ -8874,21 +8874,6 @@ void Isolate::RemoveGCEpilogueCallback(GCCallback callback) {
RemoveGCEpilogueCallback(CallGCCallbackWithoutData, data);
}
START_ALLOW_USE_DEPRECATED()
void Isolate::SetEmbedderHeapTracer(EmbedderHeapTracer* tracer) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
CHECK_NULL(i_isolate->heap()->cpp_heap());
i_isolate->heap()->SetEmbedderHeapTracer(tracer);
}
EmbedderHeapTracer* Isolate::GetEmbedderHeapTracer() {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
return i_isolate->heap()->GetEmbedderHeapTracer();
}
END_ALLOW_USE_DEPRECATED()
void Isolate::SetEmbedderRootsHandler(EmbedderRootsHandler* handler) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
i_isolate->heap()->SetEmbedderRootsHandler(handler);
@ -8896,7 +8881,6 @@ void Isolate::SetEmbedderRootsHandler(EmbedderRootsHandler* handler) {
void Isolate::AttachCppHeap(CppHeap* cpp_heap) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
CHECK_NULL(GetEmbedderHeapTracer());
i_isolate->heap()->AttachCppHeap(cpp_heap);
}
@ -10705,71 +10689,6 @@ void HeapProfiler::SetGetDetachednessCallback(GetDetachednessCallback callback,
data);
}
void EmbedderHeapTracer::SetStackStart(void* stack_start) {
CHECK(v8_isolate_);
reinterpret_cast<i::Isolate*>(v8_isolate_)
->heap()
->SetStackStart(stack_start);
}
void EmbedderHeapTracer::FinalizeTracing() {
if (v8_isolate_) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate_);
if (i_isolate->heap()->incremental_marking()->IsMarking()) {
i_isolate->heap()->FinalizeIncrementalMarkingAtomically(
i::GarbageCollectionReason::kExternalFinalize);
}
}
}
void EmbedderHeapTracer::IncreaseAllocatedSize(size_t bytes) {
if (v8_isolate_) {
i::LocalEmbedderHeapTracer* const tracer =
reinterpret_cast<i::Isolate*>(v8_isolate_)
->heap()
->local_embedder_heap_tracer();
DCHECK_NOT_NULL(tracer);
tracer->IncreaseAllocatedSize(bytes);
}
}
void EmbedderHeapTracer::DecreaseAllocatedSize(size_t bytes) {
if (v8_isolate_) {
i::LocalEmbedderHeapTracer* const tracer =
reinterpret_cast<i::Isolate*>(v8_isolate_)
->heap()
->local_embedder_heap_tracer();
DCHECK_NOT_NULL(tracer);
tracer->DecreaseAllocatedSize(bytes);
}
}
void EmbedderHeapTracer::RegisterEmbedderReference(
const BasicTracedReference<v8::Data>& ref) {
if (ref.IsEmpty()) return;
i::Heap* const heap = reinterpret_cast<i::Isolate*>(v8_isolate_)->heap();
heap->RegisterExternallyReferencedObject(
reinterpret_cast<i::Address*>(ref.val_));
}
void EmbedderHeapTracer::IterateTracedGlobalHandles(
TracedGlobalHandleVisitor* visitor) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(v8_isolate_);
i::DisallowGarbageCollection no_gc;
i_isolate->traced_handles()->Iterate(visitor);
}
bool EmbedderHeapTracer::IsRootForNonTracingGC(
const v8::TracedReference<v8::Value>& handle) {
return true;
}
void EmbedderHeapTracer::ResetHandleInNonTracingGC(
const v8::TracedReference<v8::Value>& handle) {
UNREACHABLE();
}
EmbedderStateScope::EmbedderStateScope(Isolate* v8_isolate,
Local<v8::Context> context,
EmbedderStateTag tag)

View File

@ -539,12 +539,6 @@ class TracedHandlesImpl final {
size_t used_size_bytes() const { return sizeof(TracedNode) * used_nodes_; }
size_t total_size_bytes() const { return block_size_bytes_; }
START_ALLOW_USE_DEPRECATED()
void Iterate(v8::EmbedderHeapTracer::TracedGlobalHandleVisitor* visitor);
END_ALLOW_USE_DEPRECATED()
private:
TracedNode* AllocateNode();
void FreeNode(TracedNode*);
@ -894,6 +888,8 @@ void TracedHandlesImpl::ComputeWeaknessForYoungObjects(
if (is_marking_) return;
auto* const handler = isolate_->heap()->GetEmbedderRootsHandler();
if (!handler) return;
for (TracedNode* node : young_nodes_) {
if (node->is_in_use()) {
DCHECK(node->is_root());
@ -912,6 +908,8 @@ void TracedHandlesImpl::ProcessYoungObjects(
if (!v8_flags.reclaim_unmodified_wrappers) return;
auto* const handler = isolate_->heap()->GetEmbedderRootsHandler();
if (!handler) return;
for (TracedNode* node : young_nodes_) {
if (!node->is_in_use()) continue;
@ -996,23 +994,6 @@ void TracedHandlesImpl::IterateYoungRootsWithOldHostsForTesting(
}
}
START_ALLOW_USE_DEPRECATED()
void TracedHandlesImpl::Iterate(
v8::EmbedderHeapTracer::TracedGlobalHandleVisitor* visitor) {
for (auto* block : blocks_) {
for (auto* node : *block) {
if (node->is_in_use()) {
v8::Value* value = ToApi<v8::Value>(node->handle());
visitor->VisitTracedReference(
*reinterpret_cast<v8::TracedReference<v8::Value>*>(&value));
}
}
}
}
END_ALLOW_USE_DEPRECATED()
TracedHandles::TracedHandles(Isolate* isolate)
: impl_(std::make_unique<TracedHandlesImpl>(isolate)) {}
@ -1092,15 +1073,6 @@ size_t TracedHandles::used_size_bytes() const {
return impl_->used_size_bytes();
}
START_ALLOW_USE_DEPRECATED()
void TracedHandles::Iterate(
v8::EmbedderHeapTracer::TracedGlobalHandleVisitor* visitor) {
impl_->Iterate(visitor);
}
END_ALLOW_USE_DEPRECATED()
// static
void TracedHandles::Destroy(Address* location) {
if (!location) return;

View File

@ -76,14 +76,6 @@ class V8_EXPORT_PRIVATE TracedHandles final {
void IterateAndMarkYoungRootsWithOldHosts(RootVisitor*);
void IterateYoungRootsWithOldHostsForTesting(RootVisitor*);
START_ALLOW_USE_DEPRECATED()
// Iterates over all traces handles represented by
// `v8::TracedReferenceBase`.
void Iterate(v8::EmbedderHeapTracer::TracedGlobalHandleVisitor* visitor);
END_ALLOW_USE_DEPRECATED()
size_t used_node_count() const;
size_t total_size_bytes() const;
size_t used_size_bytes() const;

View File

@ -548,8 +548,7 @@ void CppHeap::DetachIsolate() {
// CHECK across all relevant embedders and setups.
if (!isolate_) return;
// Delegate to existing EmbedderHeapTracer API to finish any ongoing garbage
// collection.
// Finish any ongoing garbage collection.
if (isolate_->heap()->incremental_marking()->IsMarking()) {
isolate_->heap()->FinalizeIncrementalMarkingAtomically(
i::GarbageCollectionReason::kExternalFinalize);

View File

@ -4,6 +4,7 @@
#include "src/heap/embedder-tracing.h"
#include "include/cppgc/common.h"
#include "include/v8-cppgc.h"
#include "src/base/logging.h"
#include "src/handles/global-handles.h"
@ -15,51 +16,34 @@ namespace v8::internal {
START_ALLOW_USE_DEPRECATED()
void LocalEmbedderHeapTracer::SetRemoteTracer(EmbedderHeapTracer* tracer) {
CHECK_NULL(cpp_heap_);
if (remote_tracer_) remote_tracer_->v8_isolate_ = nullptr;
remote_tracer_ = tracer;
default_embedder_roots_handler_.SetTracer(tracer);
if (remote_tracer_)
remote_tracer_->v8_isolate_ = reinterpret_cast<v8::Isolate*>(isolate_);
}
void LocalEmbedderHeapTracer::SetCppHeap(CppHeap* cpp_heap) {
CHECK_NULL(remote_tracer_);
cpp_heap_ = cpp_heap;
}
namespace {
CppHeap::GarbageCollectionFlags ConvertTraceFlags(
EmbedderHeapTracer::TraceFlags flags) {
CppHeap::GarbageCollectionFlags result;
if (flags & EmbedderHeapTracer::TraceFlags::kForced)
result |= CppHeap::GarbageCollectionFlagValues::kForced;
if (flags & EmbedderHeapTracer::TraceFlags::kReduceMemory)
result |= CppHeap::GarbageCollectionFlagValues::kReduceMemory;
return result;
}
} // namespace
void LocalEmbedderHeapTracer::PrepareForTrace(CollectionType type) {
if (!InUse()) return;
void LocalEmbedderHeapTracer::PrepareForTrace(
EmbedderHeapTracer::TraceFlags flags, CollectionType type) {
if (cpp_heap_)
CppHeap::GarbageCollectionFlags flags =
CppHeap::GarbageCollectionFlagValues::kNoFlags;
auto* heap = isolate_->heap();
if (heap->is_current_gc_forced()) {
flags |= CppHeap::GarbageCollectionFlagValues::kForced;
}
if (heap->ShouldReduceMemory()) {
flags |= CppHeap::GarbageCollectionFlagValues::kReduceMemory;
}
cpp_heap()->InitializeTracing(type == CollectionType::kMajor
? cppgc::internal::CollectionType::kMajor
: cppgc::internal::CollectionType::kMinor,
ConvertTraceFlags(flags));
flags);
}
void LocalEmbedderHeapTracer::TracePrologue(
EmbedderHeapTracer::TraceFlags flags) {
void LocalEmbedderHeapTracer::TracePrologue() {
if (!InUse()) return;
embedder_worklist_empty_ = false;
if (cpp_heap_)
cpp_heap()->StartTracing();
else
remote_tracer_->TracePrologue(flags);
}
void LocalEmbedderHeapTracer::TraceEpilogue() {
@ -67,16 +51,9 @@ void LocalEmbedderHeapTracer::TraceEpilogue() {
// 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;
embedder_stack_state_ = cppgc::EmbedderStackState::kMayContainHeapPointers;
if (cpp_heap_) {
cpp_heap()->TraceEpilogue();
} else {
EmbedderHeapTracer::TraceSummary summary;
remote_tracer_->TraceEpilogue(&summary);
UpdateRemoteStats(summary.allocated_size, summary.time);
}
}
void LocalEmbedderHeapTracer::UpdateRemoteStats(size_t allocated_size,
@ -94,41 +71,21 @@ void LocalEmbedderHeapTracer::UpdateRemoteStats(size_t allocated_size,
void LocalEmbedderHeapTracer::EnterFinalPause() {
if (!InUse()) return;
if (cpp_heap_)
cpp_heap()->EnterFinalPause(embedder_stack_state_);
else
remote_tracer_->EnterFinalPause(embedder_stack_state_);
}
bool LocalEmbedderHeapTracer::Trace(double max_duration) {
if (!InUse()) return true;
return cpp_heap_ ? cpp_heap_->AdvanceTracing(max_duration)
: remote_tracer_->AdvanceTracing(max_duration);
return !InUse() || cpp_heap()->AdvanceTracing(max_duration);
}
bool LocalEmbedderHeapTracer::IsRemoteTracingDone() {
return !InUse() || (cpp_heap_ ? cpp_heap()->IsTracingDone()
: remote_tracer_->IsTracingDone());
}
LocalEmbedderHeapTracer::ProcessingScope::ProcessingScope(
LocalEmbedderHeapTracer* tracer)
: tracer_(tracer), wrapper_descriptor_(tracer->wrapper_descriptor_) {
DCHECK(!tracer_->cpp_heap_);
wrapper_cache_.reserve(kWrapperCacheSize);
}
LocalEmbedderHeapTracer::ProcessingScope::~ProcessingScope() {
DCHECK(!tracer_->cpp_heap_);
if (!wrapper_cache_.empty()) {
tracer_->remote_tracer_->RegisterV8References(std::move(wrapper_cache_));
}
return !InUse() || cpp_heap()->IsTracingDone();
}
LocalEmbedderHeapTracer::WrapperInfo
LocalEmbedderHeapTracer::ExtractWrapperInfo(Isolate* isolate,
JSObject js_object) {
DCHECK(InUse());
WrapperInfo info;
if (ExtractWrappableInfo(isolate, js_object, wrapper_descriptor(), &info)) {
return info;
@ -136,32 +93,6 @@ LocalEmbedderHeapTracer::ExtractWrapperInfo(Isolate* isolate,
return {nullptr, nullptr};
}
void LocalEmbedderHeapTracer::ProcessingScope::TracePossibleWrapper(
JSObject js_object) {
DCHECK(js_object.MayHaveEmbedderFields());
WrapperInfo info;
if (ExtractWrappableInfo(tracer_->isolate_, js_object, wrapper_descriptor_,
&info)) {
wrapper_cache_.push_back(std::move(info));
FlushWrapperCacheIfFull();
}
}
void LocalEmbedderHeapTracer::ProcessingScope::FlushWrapperCacheIfFull() {
DCHECK(!tracer_->cpp_heap_);
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 (!v8_flags.global_gc_scheduling || !v8_flags.incremental_marking) return;
@ -179,33 +110,16 @@ void LocalEmbedderHeapTracer::EmbedderWriteBarrier(Heap* heap,
JSObject js_object) {
DCHECK(InUse());
DCHECK(js_object.MayHaveEmbedderFields());
if (cpp_heap_) {
DCHECK_NOT_NULL(heap->mark_compact_collector());
const EmbedderDataSlot type_slot(js_object,
wrapper_descriptor_.wrappable_type_index);
const EmbedderDataSlot instance_slot(
js_object, wrapper_descriptor_.wrappable_instance_index);
auto descriptor = wrapper_descriptor();
const EmbedderDataSlot type_slot(js_object, descriptor.wrappable_type_index);
const EmbedderDataSlot instance_slot(js_object,
descriptor.wrappable_instance_index);
heap->mark_compact_collector()
->local_marking_worklists()
->cpp_marking_state()
->MarkAndPush(type_slot, instance_slot);
return;
}
LocalEmbedderHeapTracer::ProcessingScope scope(this);
scope.TracePossibleWrapper(js_object);
}
bool DefaultEmbedderRootsHandler::IsRoot(
const v8::TracedReference<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);
}
END_ALLOW_USE_DEPRECATED()

View File

@ -8,8 +8,6 @@
#include <atomic>
#include "include/v8-cppgc.h"
#include "include/v8-embedder-heap.h"
#include "include/v8-traced-handle.h"
#include "src/common/globals.h"
#include "src/execution/isolate.h"
#include "src/flags/flags.h"
@ -21,21 +19,6 @@ namespace internal {
class Heap;
class JSObject;
START_ALLOW_USE_DEPRECATED()
class V8_EXPORT_PRIVATE DefaultEmbedderRootsHandler final
: public EmbedderRootsHandler {
public:
bool IsRoot(const v8::TracedReference<v8::Value>& handle) final;
void ResetRoot(const v8::TracedReference<v8::Value>& handle) final;
void SetTracer(EmbedderHeapTracer* tracer) { tracer_ = tracer; }
private:
EmbedderHeapTracer* tracer_ = nullptr;
};
class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
public:
enum class CollectionType : uint8_t {
@ -43,7 +26,6 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
kMajor,
};
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()
@ -63,25 +45,6 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
const WrapperInfo& raw_info;
};
class V8_EXPORT_PRIVATE V8_NODISCARD 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_;
const WrapperDescriptor wrapper_descriptor_;
WrapperCache wrapper_cache_;
};
static V8_INLINE bool ExtractWrappableInfo(Isolate*, JSObject,
const WrapperDescriptor&,
WrapperInfo*);
@ -92,23 +55,15 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
explicit LocalEmbedderHeapTracer(Isolate* isolate) : isolate_(isolate) {}
~LocalEmbedderHeapTracer() {
if (remote_tracer_) remote_tracer_->v8_isolate_ = nullptr;
// CppHeap is not detached from Isolate here. Detaching is done explicitly
// on Isolate/Heap/CppHeap destruction.
}
bool InUse() const { return cpp_heap_ || (remote_tracer_ != nullptr); }
// This method doesn't take CppHeap into account.
EmbedderHeapTracer* remote_tracer() const {
DCHECK_NULL(cpp_heap_);
return remote_tracer_;
}
bool InUse() const { return cpp_heap_; }
void SetRemoteTracer(EmbedderHeapTracer* tracer);
void SetCppHeap(CppHeap* cpp_heap);
void PrepareForTrace(EmbedderHeapTracer::TraceFlags flags,
CollectionType type);
void TracePrologue(EmbedderHeapTracer::TraceFlags flags);
void PrepareForTrace(CollectionType type);
void TracePrologue();
void TraceEpilogue();
void EnterFinalPause();
bool Trace(double deadline);
@ -125,8 +80,7 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
bool SupportsIncrementalEmbedderSteps() const {
if (!InUse()) return false;
return cpp_heap_ ? v8_flags.cppheap_incremental_marking
: v8_flags.incremental_marking_wrappers;
return v8_flags.cppheap_incremental_marking;
}
void SetEmbedderWorklistEmpty(bool is_empty) {
@ -158,18 +112,9 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
WrapperInfo ExtractWrapperInfo(Isolate* isolate, JSObject js_object);
void SetWrapperDescriptor(const WrapperDescriptor& wrapper_descriptor) {
DCHECK_NULL(cpp_heap_);
wrapper_descriptor_ = wrapper_descriptor;
}
void UpdateRemoteStats(size_t, double);
DefaultEmbedderRootsHandler& default_embedder_roots_handler() {
return default_embedder_roots_handler_;
}
EmbedderHeapTracer::EmbedderStackState embedder_stack_state() const {
cppgc::EmbedderStackState embedder_stack_state() const {
return embedder_stack_state_;
}
@ -178,39 +123,21 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
private:
static constexpr size_t kEmbedderAllocatedThreshold = 128 * KB;
static constexpr WrapperDescriptor::InternalFieldIndex
kDefaultWrapperTypeEmbedderIndex = 0;
static constexpr WrapperDescriptor::InternalFieldIndex
kDefaultWrapperInstanceEmbedderIndex = 1;
static constexpr WrapperDescriptor GetDefaultWrapperDescriptor() {
// The default descriptor assumes the indices that known embedders use.
return WrapperDescriptor(kDefaultWrapperTypeEmbedderIndex,
kDefaultWrapperInstanceEmbedderIndex,
WrapperDescriptor::kUnknownEmbedderId);
}
CppHeap* cpp_heap() {
DCHECK_NOT_NULL(cpp_heap_);
DCHECK_NULL(remote_tracer_);
DCHECK_IMPLIES(isolate_, cpp_heap_ == isolate_->heap()->cpp_heap());
return cpp_heap_;
}
WrapperDescriptor wrapper_descriptor() {
if (cpp_heap_)
return cpp_heap()->wrapper_descriptor();
else
return wrapper_descriptor_;
}
Isolate* const isolate_;
EmbedderHeapTracer* remote_tracer_ = nullptr;
CppHeap* cpp_heap_ = nullptr;
DefaultEmbedderRootsHandler default_embedder_roots_handler_;
EmbedderHeapTracer::EmbedderStackState embedder_stack_state_ =
EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers;
cppgc::EmbedderStackState embedder_stack_state_ =
cppgc::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.
@ -229,16 +156,9 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
size_t allocated_size_limit_for_check = 0;
} remote_stats_;
// Default descriptor only used when the embedder is using EmbedderHeapTracer.
// The value is overriden by CppHeap with values that the embedder provided
// upon initialization.
WrapperDescriptor wrapper_descriptor_ = GetDefaultWrapperDescriptor();
friend class EmbedderStackStateScope;
};
END_ALLOW_USE_DEPRECATED()
} // namespace internal
} // namespace v8

View File

@ -347,7 +347,7 @@ class V8_EXPORT_PRIVATE GCTracer {
double time_ms = 0) const;
// Allocation throughput in the embedder in bytes/millisecond in the
// last time_ms milliseconds. Reported through v8::EmbedderHeapTracer.
// last time_ms milliseconds.
// Returns 0 if no allocation events have been recorded.
double EmbedderAllocationThroughputInBytesPerMillisecond(
double time_ms = 0) const;
@ -368,7 +368,7 @@ class V8_EXPORT_PRIVATE GCTracer {
double CurrentOldGenerationAllocationThroughputInBytesPerMillisecond() const;
// Allocation throughput in the embedder in bytes/milliseconds in the last
// kThroughputTimeFrameMs seconds. Reported through v8::EmbedderHeapTracer.
// kThroughputTimeFrameMs seconds.
// Returns 0 if no allocation events have been recorded.
double CurrentEmbedderAllocationThroughputInBytesPerMillisecond() const;

View File

@ -5612,8 +5612,6 @@ void Heap::SetUpSpaces(LinearAllocationArea& new_allocation_info,
dead_object_stats_.reset(new ObjectStats(this));
}
local_embedder_heap_tracer_.reset(new LocalEmbedderHeapTracer(isolate()));
embedder_roots_handler_ =
&local_embedder_heap_tracer()->default_embedder_roots_handler();
if (Heap::AllocationTrackerForDebugging::IsNeeded()) {
allocation_tracker_for_debugging_ =
std::make_unique<Heap::AllocationTrackerForDebugging>(this);
@ -5789,30 +5787,6 @@ void Heap::NotifyOldGenerationExpansion(AllocationSpace space,
}
}
START_ALLOW_USE_DEPRECATED()
void Heap::SetEmbedderHeapTracer(EmbedderHeapTracer* tracer) {
DCHECK_EQ(gc_state(), HeapState::NOT_IN_GC);
// Setting a tracer is only supported when CppHeap is not used.
DCHECK_IMPLIES(tracer, !cpp_heap_);
local_embedder_heap_tracer()->SetRemoteTracer(tracer);
}
EmbedderHeapTracer* Heap::GetEmbedderHeapTracer() const {
return local_embedder_heap_tracer()->remote_tracer();
}
EmbedderHeapTracer::TraceFlags Heap::flags_for_embedder_tracer() const {
if (is_current_gc_forced()) {
return EmbedderHeapTracer::TraceFlags::kForced;
} else if (ShouldReduceMemory()) {
return EmbedderHeapTracer::TraceFlags::kReduceMemory;
}
return EmbedderHeapTracer::TraceFlags::kNoFlags;
}
END_ALLOW_USE_DEPRECATED()
void Heap::SetEmbedderRootsHandler(EmbedderRootsHandler* handler) {
embedder_roots_handler_ = handler;
}
@ -5847,20 +5821,6 @@ void Heap::SetStackStart(void* stack_start) {
return isolate_->thread_local_top()->stack_;
}
void Heap::RegisterExternallyReferencedObject(Address* location) {
Object object = TracedHandles::Mark(location, TracedHandles::MarkMode::kAll);
if (!object.IsHeapObject()) {
// The embedder is not aware of whether numbers are materialized as heap
// objects are just passed around as Smis.
return;
}
HeapObject heap_object = HeapObject::cast(object);
DCHECK(IsValidHeapObject(this, heap_object));
DCHECK(incremental_marking()->IsMarking() ||
mark_compact_collector()->in_use());
mark_compact_collector()->MarkExternallyReferencedObject(heap_object);
}
void Heap::StartTearDown() {
// Finish any ongoing sweeping to avoid stray background tasks still accessing
// the heap during teardown.

View File

@ -1163,16 +1163,6 @@ class Heap {
return local_embedder_heap_tracer_.get();
}
START_ALLOW_USE_DEPRECATED()
V8_EXPORT_PRIVATE void SetEmbedderHeapTracer(EmbedderHeapTracer* tracer);
EmbedderHeapTracer* GetEmbedderHeapTracer() const;
EmbedderHeapTracer::TraceFlags flags_for_embedder_tracer() const;
END_ALLOW_USE_DEPRECATED()
void RegisterExternallyReferencedObject(Address* location);
// ===========================================================================
// Unified heap (C++) support. ===============================================
// ===========================================================================

View File

@ -315,14 +315,13 @@ void IncrementalMarking::StartMarkingMajor() {
isolate()->external_pointer_table().StartCompactingIfNeeded();
#endif // V8_COMPRESS_POINTERS
auto embedder_flags = heap_->flags_for_embedder_tracer();
{
TRACE_GC(heap()->tracer(),
GCTracer::Scope::MC_INCREMENTAL_EMBEDDER_PROLOGUE);
// PrepareForTrace should be called before visitor initialization in
// StartMarking. It is only used with CppHeap.
heap_->local_embedder_heap_tracer()->PrepareForTrace(
embedder_flags, LocalEmbedderHeapTracer::CollectionType::kMajor);
LocalEmbedderHeapTracer::CollectionType::kMajor);
}
major_collector_->StartMarking();
@ -358,7 +357,7 @@ void IncrementalMarking::StartMarkingMajor() {
// marking (including write barriers) is fully set up.
TRACE_GC(heap()->tracer(),
GCTracer::Scope::MC_INCREMENTAL_EMBEDDER_PROLOGUE);
heap_->local_embedder_heap_tracer()->TracePrologue(embedder_flags);
heap_->local_embedder_heap_tracer()->TracePrologue();
}
heap_->InvokeIncrementalMarkingEpilogueCallbacks();
@ -555,8 +554,6 @@ void IncrementalMarking::EmbedderStep(double expected_duration_ms,
return;
}
constexpr size_t kObjectsToProcessBeforeDeadlineCheck = 500;
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_INCREMENTAL_EMBEDDER_TRACING);
LocalEmbedderHeapTracer* local_tracer = heap_->local_embedder_heap_tracer();
const double start = heap_->MonotonicallyIncreasingTimeInMs();
@ -564,21 +561,6 @@ void IncrementalMarking::EmbedderStep(double expected_duration_ms,
bool empty_worklist = true;
if (local_marking_worklists()->PublishWrapper()) {
DCHECK(local_marking_worklists()->IsWrapperEmpty());
} else {
// Cannot directly publish wrapper objects.
LocalEmbedderHeapTracer::ProcessingScope scope(local_tracer);
HeapObject object;
size_t cnt = 0;
while (local_marking_worklists()->PopWrapper(&object)) {
scope.TracePossibleWrapper(JSObject::cast(object));
if (++cnt == kObjectsToProcessBeforeDeadlineCheck) {
if (deadline <= heap_->MonotonicallyIncreasingTimeInMs()) {
empty_worklist = false;
break;
}
cnt = 0;
}
}
}
// |deadline - heap_->MonotonicallyIncreasingTimeInMs()| could be negative,
// which means |local_tracer| won't do any actual tracing, so there is no

View File

@ -51,16 +51,6 @@ void MinorMarkCompactCollector::MarkRootObject(HeapObject obj) {
}
}
void MarkCompactCollector::MarkExternallyReferencedObject(HeapObject obj) {
DCHECK(ReadOnlyHeap::Contains(obj) || heap()->Contains(obj));
if (marking_state()->WhiteToGrey(obj)) {
local_marking_worklists()->Push(obj);
if (V8_UNLIKELY(v8_flags.track_retaining_path)) {
heap_->AddRetainingRoot(Root::kWrapperTracing, obj);
}
}
}
// static
void MarkCompactCollector::RecordSlot(HeapObject object, ObjectSlot slot,
HeapObject target) {

View File

@ -910,13 +910,12 @@ void MarkCompactCollector::Prepare() {
DCHECK(!heap_->memory_allocator()->unmapper()->IsRunning());
if (!heap()->incremental_marking()->IsMarking()) {
const auto embedder_flags = heap_->flags_for_embedder_tracer();
{
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_PROLOGUE);
// PrepareForTrace should be called before visitor initialization in
// StartMarking.
heap_->local_embedder_heap_tracer()->PrepareForTrace(
embedder_flags, LocalEmbedderHeapTracer::CollectionType::kMajor);
LocalEmbedderHeapTracer::CollectionType::kMajor);
}
StartCompaction(StartCompactionMode::kAtomic);
StartMarking();
@ -924,7 +923,7 @@ void MarkCompactCollector::Prepare() {
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_PROLOGUE);
// TracePrologue immediately starts marking which requires V8 worklists to
// be set up.
heap_->local_embedder_heap_tracer()->TracePrologue(embedder_flags);
heap_->local_embedder_heap_tracer()->TracePrologue();
}
#ifdef V8_COMPRESS_POINTERS
heap_->isolate()->external_pointer_table().StartCompactingIfNeeded();
@ -2112,8 +2111,9 @@ void MarkCompactCollector::MarkRoots(RootVisitor* root_visitor) {
if (!heap_->cpp_heap() && heap_->local_embedder_heap_tracer()->InUse()) {
// Conservative global handle scanning is necessary for keeping
// v8::TracedReference alive from the stack. This is only needed when using
// `EmbedderHeapTracer` and not using `CppHeap`.
// v8::TracedReference alive from the stack.
//
// TODO(v8:v8:13207): Remove as this is not required when using `CppHeap`.
auto& stack = heap()->stack();
if (heap_->local_embedder_heap_tracer()->embedder_stack_state() ==
cppgc::EmbedderStackState::kMayContainHeapPointers) {
@ -2551,14 +2551,6 @@ void MarkCompactCollector::PerformWrapperTracing() {
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_TRACING);
if (local_marking_worklists()->PublishWrapper()) {
DCHECK(local_marking_worklists()->IsWrapperEmpty());
} else {
// Cannot directly publish wrapper objects.
LocalEmbedderHeapTracer::ProcessingScope scope(
heap_->local_embedder_heap_tracer());
HeapObject object;
while (local_marking_worklists()->PopWrapper(&object)) {
scope.TracePossibleWrapper(JSObject::cast(object));
}
}
heap_->local_embedder_heap_tracer()->Trace(
std::numeric_limits<double>::infinity());
@ -5790,8 +5782,8 @@ void MinorMarkCompactCollector::SweepArrayBufferExtensions() {
void MinorMarkCompactCollector::PerformWrapperTracing() {
if (!heap_->local_embedder_heap_tracer()->InUse()) return;
// TODO(v8:13475): DCHECK instead of bailing out once EmbedderHeapTracer is
// removed.
// TODO(v8:v8:13207): DCHECK instead of bailing out as only CppHeap is
// supported.
if (!local_marking_worklists()->PublishWrapper()) return;
DCHECK_NOT_NULL(CppHeap::From(heap_->cpp_heap()));
DCHECK(CppHeap::From(heap_->cpp_heap())->generational_gc_supported());
@ -5986,21 +5978,20 @@ void MinorMarkCompactCollector::Prepare() {
// Probably requires more.
if (!heap()->incremental_marking()->IsMarking()) {
const auto embedder_flags = heap_->flags_for_embedder_tracer();
{
TRACE_GC(heap()->tracer(),
GCTracer::Scope::MINOR_MC_MARK_EMBEDDER_PROLOGUE);
// PrepareForTrace should be called before visitor initialization in
// StartMarking.
heap_->local_embedder_heap_tracer()->PrepareForTrace(
embedder_flags, LocalEmbedderHeapTracer::CollectionType::kMinor);
LocalEmbedderHeapTracer::CollectionType::kMinor);
}
StartMarking();
{
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_PROLOGUE);
// TracePrologue immediately starts marking which requires V8 worklists to
// be set up.
heap_->local_embedder_heap_tracer()->TracePrologue(embedder_flags);
heap_->local_embedder_heap_tracer()->TracePrologue();
}
}

View File

@ -452,9 +452,6 @@ class MarkCompactCollector final : public CollectorBase {
explicit MarkCompactCollector(Heap* heap);
~MarkCompactCollector() final;
// Used by wrapper tracing.
V8_INLINE void MarkExternallyReferencedObject(HeapObject obj);
std::unique_ptr<UpdatingItem> CreateRememberedSetUpdatingItem(
MemoryChunk* chunk);

View File

@ -412,7 +412,6 @@ v8_source_set("unittests_sources") {
"heap/cppgc-js/unified-heap-utils.cc",
"heap/cppgc-js/unified-heap-utils.h",
"heap/cppgc-js/young-unified-heap-unittest.cc",
"heap/embedder-tracing-unittest.cc",
"heap/gc-idle-time-handler-unittest.cc",
"heap/gc-tracer-unittest.cc",
"heap/global-handles-unittest.cc",

View File

@ -1,607 +0,0 @@
// 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-embedder-heap.h"
#include "include/v8-function.h"
#include "include/v8-template.h"
#include "src/handles/global-handles.h"
#include "src/heap/gc-tracer.h"
#include "src/heap/heap.h"
#include "test/unittests/heap/heap-utils.h"
#include "test/unittests/test-utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
using LocalEmbedderHeapTracerWithIsolate = TestWithHeapInternals;
namespace heap {
using testing::StrictMock;
using testing::_;
using testing::Return;
using v8::EmbedderHeapTracer;
using v8::internal::LocalEmbedderHeapTracer;
namespace {
LocalEmbedderHeapTracer::WrapperInfo CreateWrapperInfo() {
return LocalEmbedderHeapTracer::WrapperInfo(nullptr, nullptr);
}
} // namespace
START_ALLOW_USE_DEPRECATED()
class MockEmbedderHeapTracer : public EmbedderHeapTracer {
public:
MOCK_METHOD(void, TracePrologue, (EmbedderHeapTracer::TraceFlags),
(override));
MOCK_METHOD(void, TraceEpilogue, (EmbedderHeapTracer::TraceSummary*),
(override));
MOCK_METHOD(void, EnterFinalPause, (EmbedderHeapTracer::EmbedderStackState),
(override));
MOCK_METHOD(bool, IsTracingDone, (), (override));
MOCK_METHOD(void, RegisterV8References,
((const std::vector<std::pair<void*, void*> >&)), (override));
MOCK_METHOD(bool, AdvanceTracing, (double deadline_in_ms), (override));
};
END_ALLOW_USE_DEPRECATED()
TEST(LocalEmbedderHeapTracer, InUse) {
MockEmbedderHeapTracer mock_remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&mock_remote_tracer);
EXPECT_TRUE(local_tracer.InUse());
}
TEST(LocalEmbedderHeapTracer, NoRemoteTracer) {
LocalEmbedderHeapTracer local_tracer(nullptr);
// We should be able to call all functions without a remote tracer being
// attached.
EXPECT_FALSE(local_tracer.InUse());
local_tracer.TracePrologue(EmbedderHeapTracer::TraceFlags::kNoFlags);
local_tracer.EnterFinalPause();
bool done = local_tracer.Trace(std::numeric_limits<double>::infinity());
EXPECT_TRUE(done);
local_tracer.TraceEpilogue();
}
TEST(LocalEmbedderHeapTracer, TracePrologueForwards) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&remote_tracer);
EXPECT_CALL(remote_tracer, TracePrologue(_));
local_tracer.TracePrologue(EmbedderHeapTracer::TraceFlags::kNoFlags);
}
TEST(LocalEmbedderHeapTracer, TracePrologueForwardsMemoryReducingFlag) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&remote_tracer);
EXPECT_CALL(remote_tracer,
TracePrologue(EmbedderHeapTracer::TraceFlags::kReduceMemory));
local_tracer.TracePrologue(EmbedderHeapTracer::TraceFlags::kReduceMemory);
}
TEST(LocalEmbedderHeapTracer, TraceEpilogueForwards) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&remote_tracer);
EXPECT_CALL(remote_tracer, TraceEpilogue(_));
local_tracer.TraceEpilogue();
}
TEST(LocalEmbedderHeapTracer, EnterFinalPauseForwards) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&remote_tracer);
EXPECT_CALL(remote_tracer, EnterFinalPause(_));
local_tracer.EnterFinalPause();
}
TEST(LocalEmbedderHeapTracer, IsRemoteTracingDoneForwards) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&remote_tracer);
EXPECT_CALL(remote_tracer, IsTracingDone());
local_tracer.IsRemoteTracingDone();
}
TEST(LocalEmbedderHeapTracer, EnterFinalPauseDefaultStackStateUnkown) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&remote_tracer);
// The default stack state is expected to be unkown.
EXPECT_CALL(
remote_tracer,
EnterFinalPause(
EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers));
local_tracer.EnterFinalPause();
}
TEST_F(LocalEmbedderHeapTracerWithIsolate,
EnterFinalPauseStackStateIsForwarded) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(isolate());
local_tracer.SetRemoteTracer(&remote_tracer);
EmbedderStackStateScope scope =
EmbedderStackStateScope::ExplicitScopeForTesting(
&local_tracer,
EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers);
EXPECT_CALL(
remote_tracer,
EnterFinalPause(EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers));
local_tracer.EnterFinalPause();
}
TEST_F(LocalEmbedderHeapTracerWithIsolate, TemporaryEmbedderStackState) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(isolate());
local_tracer.SetRemoteTracer(&remote_tracer);
// Default is unknown, see above.
{
EmbedderStackStateScope scope =
EmbedderStackStateScope::ExplicitScopeForTesting(
&local_tracer,
EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers);
EXPECT_CALL(remote_tracer,
EnterFinalPause(
EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers));
local_tracer.EnterFinalPause();
}
}
TEST_F(LocalEmbedderHeapTracerWithIsolate,
TemporaryEmbedderStackStateRestores) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(isolate());
local_tracer.SetRemoteTracer(&remote_tracer);
// Default is unknown, see above.
{
EmbedderStackStateScope scope =
EmbedderStackStateScope::ExplicitScopeForTesting(
&local_tracer,
EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers);
{
EmbedderStackStateScope nested_scope =
EmbedderStackStateScope::ExplicitScopeForTesting(
&local_tracer,
EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers);
EXPECT_CALL(
remote_tracer,
EnterFinalPause(
EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers));
local_tracer.EnterFinalPause();
}
EXPECT_CALL(remote_tracer,
EnterFinalPause(
EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers));
local_tracer.EnterFinalPause();
}
}
TEST_F(LocalEmbedderHeapTracerWithIsolate, TraceEpilogueStackStateResets) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(isolate());
local_tracer.SetRemoteTracer(&remote_tracer);
EmbedderStackStateScope scope =
EmbedderStackStateScope::ExplicitScopeForTesting(
&local_tracer,
EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers);
EXPECT_CALL(
remote_tracer,
EnterFinalPause(EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers));
local_tracer.EnterFinalPause();
EXPECT_CALL(remote_tracer, TraceEpilogue(_));
local_tracer.TraceEpilogue();
EXPECT_CALL(
remote_tracer,
EnterFinalPause(
EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers));
local_tracer.EnterFinalPause();
}
TEST(LocalEmbedderHeapTracer, IsRemoteTracingDoneIncludesRemote) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&remote_tracer);
EXPECT_CALL(remote_tracer, IsTracingDone());
local_tracer.IsRemoteTracingDone();
}
TEST(LocalEmbedderHeapTracer, RegisterV8ReferencesWithRemoteTracer) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&remote_tracer);
{
LocalEmbedderHeapTracer::ProcessingScope scope(&local_tracer);
scope.AddWrapperInfoForTesting(CreateWrapperInfo());
EXPECT_CALL(remote_tracer, RegisterV8References(_));
}
EXPECT_CALL(remote_tracer, IsTracingDone()).WillOnce(Return(false));
EXPECT_FALSE(local_tracer.IsRemoteTracingDone());
}
TEST_F(LocalEmbedderHeapTracerWithIsolate, SetRemoteTracerSetsIsolate) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(isolate());
local_tracer.SetRemoteTracer(&remote_tracer);
EXPECT_EQ(isolate(), reinterpret_cast<Isolate*>(remote_tracer.isolate()));
}
TEST_F(LocalEmbedderHeapTracerWithIsolate, DestructorClearsIsolate) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
{
LocalEmbedderHeapTracer local_tracer(isolate());
local_tracer.SetRemoteTracer(&remote_tracer);
EXPECT_EQ(isolate(), reinterpret_cast<Isolate*>(remote_tracer.isolate()));
}
EXPECT_EQ(nullptr, remote_tracer.isolate());
}
namespace {
v8::Local<v8::Object> ConstructTraceableJSApiObject(
v8::Local<v8::Context> context, void* first_field, void* second_field) {
v8::EscapableHandleScope scope(context->GetIsolate());
v8::Local<v8::FunctionTemplate> function_t =
v8::FunctionTemplate::New(context->GetIsolate());
v8::Local<v8::ObjectTemplate> instance_t = function_t->InstanceTemplate();
instance_t->SetInternalFieldCount(2);
v8::Local<v8::Function> function =
function_t->GetFunction(context).ToLocalChecked();
v8::Local<v8::Object> instance =
function->NewInstance(context).ToLocalChecked();
instance->SetAlignedPointerInInternalField(0, first_field);
instance->SetAlignedPointerInInternalField(1, second_field);
EXPECT_FALSE(instance.IsEmpty());
i::Handle<i::JSReceiver> js_obj = v8::Utils::OpenHandle(*instance);
EXPECT_EQ(i::JS_API_OBJECT_TYPE, js_obj->map().instance_type());
return scope.Escape(instance);
}
enum class TracePrologueBehavior { kNoop, kCallV8WriteBarrier };
START_ALLOW_USE_DEPRECATED()
class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer {
public:
TestEmbedderHeapTracer() = default;
TestEmbedderHeapTracer(TracePrologueBehavior prologue_behavior,
v8::Global<v8::Array> array)
: prologue_behavior_(prologue_behavior), array_(std::move(array)) {}
void RegisterV8References(
const std::vector<std::pair<void*, void*>>& embedder_fields) final {
registered_from_v8_.insert(registered_from_v8_.end(),
embedder_fields.begin(), embedder_fields.end());
}
void AddReferenceForTracing(v8::TracedReference<v8::Value>* ref) {
to_register_with_v8_references_.push_back(ref);
}
bool AdvanceTracing(double deadline_in_ms) final {
for (auto ref : to_register_with_v8_references_) {
RegisterEmbedderReference(ref->As<v8::Data>());
}
to_register_with_v8_references_.clear();
return true;
}
bool IsTracingDone() final { return to_register_with_v8_references_.empty(); }
void TracePrologue(EmbedderHeapTracer::TraceFlags) final {
if (prologue_behavior_ == TracePrologueBehavior::kCallV8WriteBarrier) {
auto local = array_.Get(isolate());
local
->Set(local->GetCreationContext().ToLocalChecked(), 0,
v8::Object::New(isolate()))
.Check();
}
}
void TraceEpilogue(TraceSummary*) final {}
void EnterFinalPause(EmbedderStackState) final {}
bool IsRegisteredFromV8(void* first_field) const {
for (auto pair : registered_from_v8_) {
if (pair.first == first_field) return true;
}
return false;
}
void DoNotConsiderAsRootForScavenge(v8::TracedReference<v8::Value>* handle) {
handle->SetWrapperClassId(17);
non_root_handles_.push_back(handle);
}
bool IsRootForNonTracingGC(
const v8::TracedReference<v8::Value>& handle) final {
return handle.WrapperClassId() != 17;
}
void ResetHandleInNonTracingGC(
const v8::TracedReference<v8::Value>& handle) final {
for (auto* non_root_handle : non_root_handles_) {
if (*non_root_handle == handle) {
non_root_handle->Reset();
}
}
}
private:
std::vector<std::pair<void*, void*>> registered_from_v8_;
std::vector<v8::TracedReference<v8::Value>*> to_register_with_v8_references_;
TracePrologueBehavior prologue_behavior_ = TracePrologueBehavior::kNoop;
v8::Global<v8::Array> array_;
std::vector<v8::TracedReference<v8::Value>*> non_root_handles_;
};
class V8_NODISCARD TemporaryEmbedderHeapTracerScope final {
public:
TemporaryEmbedderHeapTracerScope(v8::Isolate* isolate,
v8::EmbedderHeapTracer* tracer)
: isolate_(isolate) {
isolate_->SetEmbedderHeapTracer(tracer);
}
~TemporaryEmbedderHeapTracerScope() {
isolate_->SetEmbedderHeapTracer(nullptr);
}
private:
v8::Isolate* const isolate_;
};
END_ALLOW_USE_DEPRECATED()
} // namespace
using EmbedderTracingTest = TestWithHeapInternalsAndContext;
TEST_F(EmbedderTracingTest, V8RegisterEmbedderReference) {
// Tests that wrappers are properly registered with the embedder heap
// tracer.
ManualGCScope manual_gc(i_isolate());
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer);
v8::HandleScope scope(v8_isolate());
v8::Local<v8::Context> context = v8::Context::New(v8_isolate());
v8::Context::Scope context_scope(context);
void* first_and_second_field = reinterpret_cast<void*>(0x2);
v8::Local<v8::Object> api_object = ConstructTraceableJSApiObject(
context, first_and_second_field, first_and_second_field);
ASSERT_FALSE(api_object.IsEmpty());
CollectGarbage(i::OLD_SPACE);
EXPECT_TRUE(tracer.IsRegisteredFromV8(first_and_second_field));
}
TEST_F(EmbedderTracingTest, EmbedderRegisteringV8Reference) {
// Tests that references that are registered by the embedder heap tracer are
// considered live by V8.
ManualGCScope manual_gc(i_isolate());
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer);
v8::HandleScope scope(v8_isolate());
v8::Local<v8::Context> context = v8::Context::New(v8_isolate());
v8::Context::Scope context_scope(context);
auto handle = std::make_unique<v8::TracedReference<v8::Value>>();
{
v8::HandleScope inner_scope(v8_isolate());
v8::Local<v8::Value> o =
v8::Local<v8::Object>::New(v8_isolate(), v8::Object::New(v8_isolate()));
handle->Reset(v8_isolate(), o);
}
tracer.AddReferenceForTracing(handle.get());
CollectGarbage(i::OLD_SPACE);
EXPECT_FALSE(handle->IsEmpty());
}
TEST_F(EmbedderTracingTest, FinalizeTracingIsNoopWhenNotMarking) {
ManualGCScope manual_gc(i_isolate());
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer);
// Finalize a potentially running garbage collection.
CollectGarbage(OLD_SPACE);
EXPECT_TRUE(i_isolate()->heap()->incremental_marking()->IsStopped());
int gc_counter = i_isolate()->heap()->gc_count();
tracer.FinalizeTracing();
EXPECT_TRUE(i_isolate()->heap()->incremental_marking()->IsStopped());
EXPECT_EQ(gc_counter, i_isolate()->heap()->gc_count());
}
TEST_F(EmbedderTracingTest, FinalizeTracingWhenMarking) {
if (!v8_flags.incremental_marking) return;
ManualGCScope manual_gc(i_isolate());
Heap* heap = i_isolate()->heap();
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer);
// Finalize a potentially running garbage collection.
CollectGarbage(OLD_SPACE);
if (heap->sweeping_in_progress()) {
heap->EnsureSweepingCompleted(
Heap::SweepingForcedFinalizationMode::kV8Only);
}
heap->tracer()->StopFullCycleIfNeeded();
EXPECT_TRUE(heap->incremental_marking()->IsStopped());
i::IncrementalMarking* marking = heap->incremental_marking();
{
IsolateSafepointScope scope(heap);
heap->tracer()->StartCycle(
GarbageCollector::MARK_COMPACTOR, GarbageCollectionReason::kTesting,
"collector cctest", GCTracer::MarkingType::kIncremental);
marking->Start(GarbageCollector::MARK_COMPACTOR,
GarbageCollectionReason::kTesting);
}
// Sweeping is not runing so we should immediately start marking.
EXPECT_TRUE(marking->IsMarking());
tracer.FinalizeTracing();
EXPECT_TRUE(marking->IsStopped());
}
namespace {
void ConstructJSObject(v8::Isolate* isolate, v8::Local<v8::Context> context,
v8::TracedReference<v8::Object>* handle) {
v8::HandleScope scope(isolate);
v8::Local<v8::Object> object(v8::Object::New(isolate));
EXPECT_FALSE(object.IsEmpty());
*handle = v8::TracedReference<v8::Object>(isolate, object);
EXPECT_FALSE(handle->IsEmpty());
}
} // namespace
TEST_F(EmbedderTracingTest, TracedReferenceHandlesMarking) {
ManualGCScope manual_gc(i_isolate());
v8::HandleScope scope(v8_isolate());
auto live = std::make_unique<v8::TracedReference<v8::Value>>();
auto dead = std::make_unique<v8::TracedReference<v8::Value>>();
live->Reset(v8_isolate(), v8::Undefined(v8_isolate()));
dead->Reset(v8_isolate(), v8::Undefined(v8_isolate()));
auto* traced_handles = i_isolate()->traced_handles();
{
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer);
tracer.AddReferenceForTracing(live.get());
const size_t initial_count = traced_handles->used_node_count();
{
// Conservative scanning may find stale pointers to on-stack handles.
// Disable scanning, assuming the slots are overwritten.
DisableConservativeStackScanningScopeForTesting no_stack_scanning(
i_isolate()->heap());
EmbedderStackStateScope scope =
EmbedderStackStateScope::ExplicitScopeForTesting(
reinterpret_cast<i::Isolate*>(v8_isolate())
->heap()
->local_embedder_heap_tracer(),
EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers);
FullGC();
}
const size_t final_count = traced_handles->used_node_count();
// Handles are not black allocated, so `dead` is immediately reclaimed.
EXPECT_EQ(initial_count, final_count + 1);
}
}
namespace {
START_ALLOW_USE_DEPRECATED()
class TracedReferenceVisitor final
: public v8::EmbedderHeapTracer::TracedGlobalHandleVisitor {
public:
~TracedReferenceVisitor() override = default;
void VisitTracedReference(const TracedReference<Value>& value) final {
if (value.WrapperClassId() == 57) {
count_++;
}
}
size_t count() const { return count_; }
private:
size_t count_ = 0;
};
END_ALLOW_USE_DEPRECATED()
} // namespace
TEST_F(EmbedderTracingTest, TracedReferenceIteration) {
ManualGCScope manual_gc(i_isolate());
v8::HandleScope scope(v8_isolate());
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer);
auto handle = std::make_unique<v8::TracedReference<v8::Object>>();
ConstructJSObject(v8_isolate(), v8_isolate()->GetCurrentContext(),
handle.get());
EXPECT_FALSE(handle->IsEmpty());
handle->SetWrapperClassId(57);
TracedReferenceVisitor visitor;
{
v8::HandleScope new_scope(v8_isolate());
tracer.IterateTracedGlobalHandles(&visitor);
}
EXPECT_EQ(1u, visitor.count());
}
TEST_F(EmbedderTracingTest, TracePrologueCallingIntoV8WriteBarrier) {
// Regression test: https://crbug.com/940003
if (!v8_flags.incremental_marking) return;
ManualGCScope manual_gc(isolate());
v8::HandleScope scope(v8_isolate());
v8::Global<v8::Array> global;
{
v8::HandleScope new_scope(v8_isolate());
auto local = v8::Array::New(v8_isolate(), 10);
global.Reset(v8_isolate(), local);
}
TestEmbedderHeapTracer tracer(TracePrologueBehavior::kCallV8WriteBarrier,
std::move(global));
TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer);
SimulateIncrementalMarking();
// Finish GC to avoid removing the tracer while GC is running which may end up
// in an infinite loop because of unprocessed objects.
FullGC();
}
TEST_F(EmbedderTracingTest, BasicTracedReference) {
ManualGCScope manual_gc(i_isolate());
v8::HandleScope scope(v8_isolate());
TestEmbedderHeapTracer tracer;
heap::TemporaryEmbedderHeapTracerScope tracer_scope(v8_isolate(), &tracer);
tracer.SetStackStart(
static_cast<void*>(base::Stack::GetCurrentFrameAddress()));
auto* traced_handles = i_isolate()->traced_handles();
const size_t initial_count = traced_handles->used_node_count();
char* memory = new char[sizeof(v8::TracedReference<v8::Value>)];
auto* traced = new (memory) v8::TracedReference<v8::Value>();
{
v8::HandleScope new_scope(v8_isolate());
v8::Local<v8::Value> object(ConstructTraceableJSApiObject(
v8_isolate()->GetCurrentContext(), nullptr, nullptr));
EXPECT_TRUE(traced->IsEmpty());
*traced = v8::TracedReference<v8::Value>(v8_isolate(), object);
EXPECT_FALSE(traced->IsEmpty());
EXPECT_EQ(initial_count + 1, traced_handles->used_node_count());
}
traced->~TracedReference<v8::Value>();
EXPECT_EQ(initial_count + 1, traced_handles->used_node_count());
{
// Conservative scanning may find stale pointers to on-stack handles.
// Disable scanning, assuming the slots are overwritten.
DisableConservativeStackScanningScopeForTesting no_stack_scanning(
i_isolate()->heap());
EmbedderStackStateScope scope =
EmbedderStackStateScope::ExplicitScopeForTesting(
reinterpret_cast<i::Isolate*>(v8_isolate())
->heap()
->local_embedder_heap_tracer(),
EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers);
FullGC();
}
EXPECT_EQ(initial_count, traced_handles->used_node_count());
delete[] memory;
}
} // namespace heap
} // namespace internal
} // namespace v8

View File

@ -97,26 +97,6 @@ class WithHeapInternals : public TMixin, HeapInternalsBase {
}
};
START_ALLOW_USE_DEPRECATED()
class V8_NODISCARD TemporaryEmbedderHeapTracerScope {
public:
TemporaryEmbedderHeapTracerScope(v8::Isolate* isolate,
v8::EmbedderHeapTracer* tracer)
: isolate_(isolate) {
isolate_->SetEmbedderHeapTracer(tracer);
}
~TemporaryEmbedderHeapTracerScope() {
isolate_->SetEmbedderHeapTracer(nullptr);
}
private:
v8::Isolate* const isolate_;
};
END_ALLOW_USE_DEPRECATED()
using TestWithHeapInternals = //
WithHeapInternals< //
WithInternalIsolateMixin< //