[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:
parent
d43b93a7ac
commit
a8a1805e12
@ -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_
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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> {
|
||||
|
1
src/DEPS
1
src/DEPS
@ -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",
|
||||
]
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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_)
|
||||
cpp_heap()->InitializeTracing(type == CollectionType::kMajor
|
||||
? cppgc::internal::CollectionType::kMajor
|
||||
: cppgc::internal::CollectionType::kMinor,
|
||||
ConvertTraceFlags(flags));
|
||||
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,
|
||||
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);
|
||||
|
||||
cpp_heap()->StartTracing();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
cpp_heap()->TraceEpilogue();
|
||||
}
|
||||
|
||||
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_);
|
||||
cpp_heap()->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);
|
||||
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);
|
||||
DCHECK_NOT_NULL(heap->mark_compact_collector());
|
||||
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;
|
||||
}
|
||||
|
||||
END_ALLOW_USE_DEPRECATED()
|
||||
|
@ -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_;
|
||||
return cpp_heap()->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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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. ===============================================
|
||||
// ===========================================================================
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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
|
@ -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< //
|
||||
|
Loading…
Reference in New Issue
Block a user