[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_
|
#ifndef INCLUDE_V8_EMBEDDER_HEAP_H_
|
||||||
#define 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 "v8-traced-handle.h" // NOLINT(build/include_directory)
|
||||||
#include "v8config.h" // NOLINT(build/include_directory)
|
#include "v8config.h" // NOLINT(build/include_directory)
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
|
|
||||||
class Data;
|
|
||||||
class Isolate;
|
class Isolate;
|
||||||
class Value;
|
class Value;
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
class LocalEmbedderHeapTracer;
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for embedder roots on non-unified heap garbage collections.
|
* 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;
|
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
|
} // namespace v8
|
||||||
|
|
||||||
#endif // INCLUDE_V8_EMBEDDER_HEAP_H_
|
#endif // INCLUDE_V8_EMBEDDER_HEAP_H_
|
||||||
|
@ -924,27 +924,10 @@ class V8_EXPORT Isolate {
|
|||||||
void RemoveGCPrologueCallback(GCCallbackWithData, void* data = nullptr);
|
void RemoveGCPrologueCallback(GCCallbackWithData, void* data = nullptr);
|
||||||
void RemoveGCPrologueCallback(GCCallback callback);
|
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
|
* Sets an embedder roots handle that V8 should consider when performing
|
||||||
* non-unified heap garbage collections.
|
* non-unified heap garbage collections. The intended use case is for setting
|
||||||
*
|
* a custom handler after invoking `AttachCppHeap()`.
|
||||||
* Using only EmbedderHeapTracer automatically sets up a default handler.
|
|
||||||
* The intended use case is for setting a custom handler after invoking
|
|
||||||
* `AttachCppHeap()`.
|
|
||||||
*
|
*
|
||||||
* V8 does not take ownership of the handler.
|
* 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
|
* embedder maintains ownership of the CppHeap. At most one C++ heap can be
|
||||||
* attached to V8.
|
* attached to V8.
|
||||||
*
|
*
|
||||||
* AttachCppHeap cannot be used simultaneously with SetEmbedderHeapTracer.
|
|
||||||
*
|
|
||||||
* Multi-threaded use requires the use of v8::Locker/v8::Unlocker, see
|
* Multi-threaded use requires the use of v8::Locker/v8::Unlocker, see
|
||||||
* CppHeap.
|
* CppHeap.
|
||||||
*/
|
*/
|
||||||
|
@ -117,11 +117,11 @@ class TracedReferenceBase {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A traced handle with copy and move semantics. The handle is to be used
|
* A traced handle with copy and move semantics. The handle is to be used
|
||||||
* together with |v8::EmbedderHeapTracer| or as part of GarbageCollected objects
|
* together as part of GarbageCollected objects (see v8-cppgc.h) or from stack
|
||||||
* (see v8-cppgc.h) and specifies edges from C++ objects to JavaScript.
|
* and specifies edges from C++ objects to JavaScript.
|
||||||
*
|
*
|
||||||
* The exact semantics are:
|
* The exact semantics are:
|
||||||
* - Tracing garbage collections use |v8::EmbedderHeapTracer| or cppgc.
|
* - Tracing garbage collections using CppHeap.
|
||||||
* - Non-tracing garbage collections refer to
|
* - Non-tracing garbage collections refer to
|
||||||
* |v8::EmbedderRootsHandler::IsRoot()| whether the handle should
|
* |v8::EmbedderRootsHandler::IsRoot()| whether the handle should
|
||||||
* be treated as root or not.
|
* be treated as root or not.
|
||||||
@ -166,7 +166,6 @@ class BasicTracedReference : public TracedReferenceBase {
|
|||||||
Isolate* isolate, T* that, void* slot,
|
Isolate* isolate, T* that, void* slot,
|
||||||
internal::GlobalHandleStoreMode store_mode);
|
internal::GlobalHandleStoreMode store_mode);
|
||||||
|
|
||||||
friend class EmbedderHeapTracer;
|
|
||||||
template <typename F>
|
template <typename F>
|
||||||
friend class Local;
|
friend class Local;
|
||||||
friend class Object;
|
friend class Object;
|
||||||
@ -181,13 +180,7 @@ class BasicTracedReference : public TracedReferenceBase {
|
|||||||
/**
|
/**
|
||||||
* A traced handle without destructor that clears the handle. The embedder needs
|
* 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
|
* 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
|
* reclaimed. For more details see BasicTracedReference.
|
||||||
* 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|.
|
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class TracedReference : public BasicTracedReference<T> {
|
class TracedReference : public BasicTracedReference<T> {
|
||||||
|
1
src/DEPS
1
src/DEPS
@ -76,6 +76,7 @@ include_rules = [
|
|||||||
"+starboard",
|
"+starboard",
|
||||||
# Using cppgc inside v8 is not (yet) allowed.
|
# Using cppgc inside v8 is not (yet) allowed.
|
||||||
"-include/cppgc",
|
"-include/cppgc",
|
||||||
|
"+include/cppgc/common.h",
|
||||||
"+include/cppgc/platform.h",
|
"+include/cppgc/platform.h",
|
||||||
"+include/cppgc/source-location.h",
|
"+include/cppgc/source-location.h",
|
||||||
]
|
]
|
||||||
|
@ -8874,21 +8874,6 @@ void Isolate::RemoveGCEpilogueCallback(GCCallback callback) {
|
|||||||
RemoveGCEpilogueCallback(CallGCCallbackWithoutData, data);
|
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) {
|
void Isolate::SetEmbedderRootsHandler(EmbedderRootsHandler* handler) {
|
||||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
|
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
|
||||||
i_isolate->heap()->SetEmbedderRootsHandler(handler);
|
i_isolate->heap()->SetEmbedderRootsHandler(handler);
|
||||||
@ -8896,7 +8881,6 @@ void Isolate::SetEmbedderRootsHandler(EmbedderRootsHandler* handler) {
|
|||||||
|
|
||||||
void Isolate::AttachCppHeap(CppHeap* cpp_heap) {
|
void Isolate::AttachCppHeap(CppHeap* cpp_heap) {
|
||||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
|
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(this);
|
||||||
CHECK_NULL(GetEmbedderHeapTracer());
|
|
||||||
i_isolate->heap()->AttachCppHeap(cpp_heap);
|
i_isolate->heap()->AttachCppHeap(cpp_heap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10705,71 +10689,6 @@ void HeapProfiler::SetGetDetachednessCallback(GetDetachednessCallback callback,
|
|||||||
data);
|
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,
|
EmbedderStateScope::EmbedderStateScope(Isolate* v8_isolate,
|
||||||
Local<v8::Context> context,
|
Local<v8::Context> context,
|
||||||
EmbedderStateTag tag)
|
EmbedderStateTag tag)
|
||||||
|
@ -539,12 +539,6 @@ class TracedHandlesImpl final {
|
|||||||
size_t used_size_bytes() const { return sizeof(TracedNode) * used_nodes_; }
|
size_t used_size_bytes() const { return sizeof(TracedNode) * used_nodes_; }
|
||||||
size_t total_size_bytes() const { return block_size_bytes_; }
|
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:
|
private:
|
||||||
TracedNode* AllocateNode();
|
TracedNode* AllocateNode();
|
||||||
void FreeNode(TracedNode*);
|
void FreeNode(TracedNode*);
|
||||||
@ -894,6 +888,8 @@ void TracedHandlesImpl::ComputeWeaknessForYoungObjects(
|
|||||||
if (is_marking_) return;
|
if (is_marking_) return;
|
||||||
|
|
||||||
auto* const handler = isolate_->heap()->GetEmbedderRootsHandler();
|
auto* const handler = isolate_->heap()->GetEmbedderRootsHandler();
|
||||||
|
if (!handler) return;
|
||||||
|
|
||||||
for (TracedNode* node : young_nodes_) {
|
for (TracedNode* node : young_nodes_) {
|
||||||
if (node->is_in_use()) {
|
if (node->is_in_use()) {
|
||||||
DCHECK(node->is_root());
|
DCHECK(node->is_root());
|
||||||
@ -912,6 +908,8 @@ void TracedHandlesImpl::ProcessYoungObjects(
|
|||||||
if (!v8_flags.reclaim_unmodified_wrappers) return;
|
if (!v8_flags.reclaim_unmodified_wrappers) return;
|
||||||
|
|
||||||
auto* const handler = isolate_->heap()->GetEmbedderRootsHandler();
|
auto* const handler = isolate_->heap()->GetEmbedderRootsHandler();
|
||||||
|
if (!handler) return;
|
||||||
|
|
||||||
for (TracedNode* node : young_nodes_) {
|
for (TracedNode* node : young_nodes_) {
|
||||||
if (!node->is_in_use()) continue;
|
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)
|
TracedHandles::TracedHandles(Isolate* isolate)
|
||||||
: impl_(std::make_unique<TracedHandlesImpl>(isolate)) {}
|
: impl_(std::make_unique<TracedHandlesImpl>(isolate)) {}
|
||||||
|
|
||||||
@ -1092,15 +1073,6 @@ size_t TracedHandles::used_size_bytes() const {
|
|||||||
return impl_->used_size_bytes();
|
return impl_->used_size_bytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
START_ALLOW_USE_DEPRECATED()
|
|
||||||
|
|
||||||
void TracedHandles::Iterate(
|
|
||||||
v8::EmbedderHeapTracer::TracedGlobalHandleVisitor* visitor) {
|
|
||||||
impl_->Iterate(visitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
END_ALLOW_USE_DEPRECATED()
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void TracedHandles::Destroy(Address* location) {
|
void TracedHandles::Destroy(Address* location) {
|
||||||
if (!location) return;
|
if (!location) return;
|
||||||
|
@ -76,14 +76,6 @@ class V8_EXPORT_PRIVATE TracedHandles final {
|
|||||||
void IterateAndMarkYoungRootsWithOldHosts(RootVisitor*);
|
void IterateAndMarkYoungRootsWithOldHosts(RootVisitor*);
|
||||||
void IterateYoungRootsWithOldHostsForTesting(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 used_node_count() const;
|
||||||
size_t total_size_bytes() const;
|
size_t total_size_bytes() const;
|
||||||
size_t used_size_bytes() const;
|
size_t used_size_bytes() const;
|
||||||
|
@ -548,8 +548,7 @@ void CppHeap::DetachIsolate() {
|
|||||||
// CHECK across all relevant embedders and setups.
|
// CHECK across all relevant embedders and setups.
|
||||||
if (!isolate_) return;
|
if (!isolate_) return;
|
||||||
|
|
||||||
// Delegate to existing EmbedderHeapTracer API to finish any ongoing garbage
|
// Finish any ongoing garbage collection.
|
||||||
// collection.
|
|
||||||
if (isolate_->heap()->incremental_marking()->IsMarking()) {
|
if (isolate_->heap()->incremental_marking()->IsMarking()) {
|
||||||
isolate_->heap()->FinalizeIncrementalMarkingAtomically(
|
isolate_->heap()->FinalizeIncrementalMarkingAtomically(
|
||||||
i::GarbageCollectionReason::kExternalFinalize);
|
i::GarbageCollectionReason::kExternalFinalize);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "src/heap/embedder-tracing.h"
|
#include "src/heap/embedder-tracing.h"
|
||||||
|
|
||||||
|
#include "include/cppgc/common.h"
|
||||||
#include "include/v8-cppgc.h"
|
#include "include/v8-cppgc.h"
|
||||||
#include "src/base/logging.h"
|
#include "src/base/logging.h"
|
||||||
#include "src/handles/global-handles.h"
|
#include "src/handles/global-handles.h"
|
||||||
@ -15,51 +16,34 @@ namespace v8::internal {
|
|||||||
|
|
||||||
START_ALLOW_USE_DEPRECATED()
|
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) {
|
void LocalEmbedderHeapTracer::SetCppHeap(CppHeap* cpp_heap) {
|
||||||
CHECK_NULL(remote_tracer_);
|
|
||||||
cpp_heap_ = cpp_heap;
|
cpp_heap_ = cpp_heap;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
void LocalEmbedderHeapTracer::PrepareForTrace(CollectionType type) {
|
||||||
CppHeap::GarbageCollectionFlags ConvertTraceFlags(
|
if (!InUse()) return;
|
||||||
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(
|
CppHeap::GarbageCollectionFlags flags =
|
||||||
EmbedderHeapTracer::TraceFlags flags, CollectionType type) {
|
CppHeap::GarbageCollectionFlagValues::kNoFlags;
|
||||||
if (cpp_heap_)
|
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
|
cpp_heap()->InitializeTracing(type == CollectionType::kMajor
|
||||||
? cppgc::internal::CollectionType::kMajor
|
? cppgc::internal::CollectionType::kMajor
|
||||||
: cppgc::internal::CollectionType::kMinor,
|
: cppgc::internal::CollectionType::kMinor,
|
||||||
ConvertTraceFlags(flags));
|
flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalEmbedderHeapTracer::TracePrologue(
|
void LocalEmbedderHeapTracer::TracePrologue() {
|
||||||
EmbedderHeapTracer::TraceFlags flags) {
|
|
||||||
if (!InUse()) return;
|
if (!InUse()) return;
|
||||||
|
|
||||||
embedder_worklist_empty_ = false;
|
embedder_worklist_empty_ = false;
|
||||||
if (cpp_heap_)
|
|
||||||
cpp_heap()->StartTracing();
|
cpp_heap()->StartTracing();
|
||||||
else
|
|
||||||
remote_tracer_->TracePrologue(flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalEmbedderHeapTracer::TraceEpilogue() {
|
void LocalEmbedderHeapTracer::TraceEpilogue() {
|
||||||
@ -67,16 +51,9 @@ void LocalEmbedderHeapTracer::TraceEpilogue() {
|
|||||||
|
|
||||||
// Resetting to state unknown as there may be follow up garbage collections
|
// Resetting to state unknown as there may be follow up garbage collections
|
||||||
// triggered from callbacks that have a different stack state.
|
// triggered from callbacks that have a different stack state.
|
||||||
embedder_stack_state_ =
|
embedder_stack_state_ = cppgc::EmbedderStackState::kMayContainHeapPointers;
|
||||||
EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers;
|
|
||||||
|
|
||||||
if (cpp_heap_) {
|
|
||||||
cpp_heap()->TraceEpilogue();
|
cpp_heap()->TraceEpilogue();
|
||||||
} else {
|
|
||||||
EmbedderHeapTracer::TraceSummary summary;
|
|
||||||
remote_tracer_->TraceEpilogue(&summary);
|
|
||||||
UpdateRemoteStats(summary.allocated_size, summary.time);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalEmbedderHeapTracer::UpdateRemoteStats(size_t allocated_size,
|
void LocalEmbedderHeapTracer::UpdateRemoteStats(size_t allocated_size,
|
||||||
@ -94,41 +71,21 @@ void LocalEmbedderHeapTracer::UpdateRemoteStats(size_t allocated_size,
|
|||||||
void LocalEmbedderHeapTracer::EnterFinalPause() {
|
void LocalEmbedderHeapTracer::EnterFinalPause() {
|
||||||
if (!InUse()) return;
|
if (!InUse()) return;
|
||||||
|
|
||||||
if (cpp_heap_)
|
|
||||||
cpp_heap()->EnterFinalPause(embedder_stack_state_);
|
cpp_heap()->EnterFinalPause(embedder_stack_state_);
|
||||||
else
|
|
||||||
remote_tracer_->EnterFinalPause(embedder_stack_state_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LocalEmbedderHeapTracer::Trace(double max_duration) {
|
bool LocalEmbedderHeapTracer::Trace(double max_duration) {
|
||||||
if (!InUse()) return true;
|
return !InUse() || cpp_heap()->AdvanceTracing(max_duration);
|
||||||
|
|
||||||
return cpp_heap_ ? cpp_heap_->AdvanceTracing(max_duration)
|
|
||||||
: remote_tracer_->AdvanceTracing(max_duration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LocalEmbedderHeapTracer::IsRemoteTracingDone() {
|
bool LocalEmbedderHeapTracer::IsRemoteTracingDone() {
|
||||||
return !InUse() || (cpp_heap_ ? cpp_heap()->IsTracingDone()
|
return !InUse() || 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_));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalEmbedderHeapTracer::WrapperInfo
|
LocalEmbedderHeapTracer::WrapperInfo
|
||||||
LocalEmbedderHeapTracer::ExtractWrapperInfo(Isolate* isolate,
|
LocalEmbedderHeapTracer::ExtractWrapperInfo(Isolate* isolate,
|
||||||
JSObject js_object) {
|
JSObject js_object) {
|
||||||
|
DCHECK(InUse());
|
||||||
WrapperInfo info;
|
WrapperInfo info;
|
||||||
if (ExtractWrappableInfo(isolate, js_object, wrapper_descriptor(), &info)) {
|
if (ExtractWrappableInfo(isolate, js_object, wrapper_descriptor(), &info)) {
|
||||||
return info;
|
return info;
|
||||||
@ -136,32 +93,6 @@ LocalEmbedderHeapTracer::ExtractWrapperInfo(Isolate* isolate,
|
|||||||
return {nullptr, nullptr};
|
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() {
|
void LocalEmbedderHeapTracer::StartIncrementalMarkingIfNeeded() {
|
||||||
if (!v8_flags.global_gc_scheduling || !v8_flags.incremental_marking) return;
|
if (!v8_flags.global_gc_scheduling || !v8_flags.incremental_marking) return;
|
||||||
|
|
||||||
@ -179,33 +110,16 @@ void LocalEmbedderHeapTracer::EmbedderWriteBarrier(Heap* heap,
|
|||||||
JSObject js_object) {
|
JSObject js_object) {
|
||||||
DCHECK(InUse());
|
DCHECK(InUse());
|
||||||
DCHECK(js_object.MayHaveEmbedderFields());
|
DCHECK(js_object.MayHaveEmbedderFields());
|
||||||
if (cpp_heap_) {
|
|
||||||
DCHECK_NOT_NULL(heap->mark_compact_collector());
|
DCHECK_NOT_NULL(heap->mark_compact_collector());
|
||||||
const EmbedderDataSlot type_slot(js_object,
|
auto descriptor = wrapper_descriptor();
|
||||||
wrapper_descriptor_.wrappable_type_index);
|
const EmbedderDataSlot type_slot(js_object, descriptor.wrappable_type_index);
|
||||||
const EmbedderDataSlot instance_slot(
|
const EmbedderDataSlot instance_slot(js_object,
|
||||||
js_object, wrapper_descriptor_.wrappable_instance_index);
|
descriptor.wrappable_instance_index);
|
||||||
heap->mark_compact_collector()
|
heap->mark_compact_collector()
|
||||||
->local_marking_worklists()
|
->local_marking_worklists()
|
||||||
->cpp_marking_state()
|
->cpp_marking_state()
|
||||||
->MarkAndPush(type_slot, instance_slot);
|
->MarkAndPush(type_slot, instance_slot);
|
||||||
return;
|
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()
|
END_ALLOW_USE_DEPRECATED()
|
||||||
|
@ -8,8 +8,6 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#include "include/v8-cppgc.h"
|
#include "include/v8-cppgc.h"
|
||||||
#include "include/v8-embedder-heap.h"
|
|
||||||
#include "include/v8-traced-handle.h"
|
|
||||||
#include "src/common/globals.h"
|
#include "src/common/globals.h"
|
||||||
#include "src/execution/isolate.h"
|
#include "src/execution/isolate.h"
|
||||||
#include "src/flags/flags.h"
|
#include "src/flags/flags.h"
|
||||||
@ -21,21 +19,6 @@ namespace internal {
|
|||||||
class Heap;
|
class Heap;
|
||||||
class JSObject;
|
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 {
|
class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
|
||||||
public:
|
public:
|
||||||
enum class CollectionType : uint8_t {
|
enum class CollectionType : uint8_t {
|
||||||
@ -43,7 +26,6 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
|
|||||||
kMajor,
|
kMajor,
|
||||||
};
|
};
|
||||||
using WrapperInfo = std::pair<void*, void*>;
|
using WrapperInfo = std::pair<void*, void*>;
|
||||||
using WrapperCache = std::vector<WrapperInfo>;
|
|
||||||
|
|
||||||
// WrapperInfo is passed over the API. Use VerboseWrapperInfo to access pair
|
// WrapperInfo is passed over the API. Use VerboseWrapperInfo to access pair
|
||||||
// internals in a named way. See ProcessingScope::TracePossibleJSWrapper()
|
// internals in a named way. See ProcessingScope::TracePossibleJSWrapper()
|
||||||
@ -63,25 +45,6 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
|
|||||||
const WrapperInfo& raw_info;
|
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,
|
static V8_INLINE bool ExtractWrappableInfo(Isolate*, JSObject,
|
||||||
const WrapperDescriptor&,
|
const WrapperDescriptor&,
|
||||||
WrapperInfo*);
|
WrapperInfo*);
|
||||||
@ -92,23 +55,15 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
|
|||||||
explicit LocalEmbedderHeapTracer(Isolate* isolate) : isolate_(isolate) {}
|
explicit LocalEmbedderHeapTracer(Isolate* isolate) : isolate_(isolate) {}
|
||||||
|
|
||||||
~LocalEmbedderHeapTracer() {
|
~LocalEmbedderHeapTracer() {
|
||||||
if (remote_tracer_) remote_tracer_->v8_isolate_ = nullptr;
|
|
||||||
// CppHeap is not detached from Isolate here. Detaching is done explicitly
|
// CppHeap is not detached from Isolate here. Detaching is done explicitly
|
||||||
// on Isolate/Heap/CppHeap destruction.
|
// on Isolate/Heap/CppHeap destruction.
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InUse() const { return cpp_heap_ || (remote_tracer_ != nullptr); }
|
bool InUse() const { return cpp_heap_; }
|
||||||
// This method doesn't take CppHeap into account.
|
|
||||||
EmbedderHeapTracer* remote_tracer() const {
|
|
||||||
DCHECK_NULL(cpp_heap_);
|
|
||||||
return remote_tracer_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetRemoteTracer(EmbedderHeapTracer* tracer);
|
|
||||||
void SetCppHeap(CppHeap* cpp_heap);
|
void SetCppHeap(CppHeap* cpp_heap);
|
||||||
void PrepareForTrace(EmbedderHeapTracer::TraceFlags flags,
|
void PrepareForTrace(CollectionType type);
|
||||||
CollectionType type);
|
void TracePrologue();
|
||||||
void TracePrologue(EmbedderHeapTracer::TraceFlags flags);
|
|
||||||
void TraceEpilogue();
|
void TraceEpilogue();
|
||||||
void EnterFinalPause();
|
void EnterFinalPause();
|
||||||
bool Trace(double deadline);
|
bool Trace(double deadline);
|
||||||
@ -125,8 +80,7 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
|
|||||||
bool SupportsIncrementalEmbedderSteps() const {
|
bool SupportsIncrementalEmbedderSteps() const {
|
||||||
if (!InUse()) return false;
|
if (!InUse()) return false;
|
||||||
|
|
||||||
return cpp_heap_ ? v8_flags.cppheap_incremental_marking
|
return v8_flags.cppheap_incremental_marking;
|
||||||
: v8_flags.incremental_marking_wrappers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetEmbedderWorklistEmpty(bool is_empty) {
|
void SetEmbedderWorklistEmpty(bool is_empty) {
|
||||||
@ -158,18 +112,9 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
|
|||||||
|
|
||||||
WrapperInfo ExtractWrapperInfo(Isolate* isolate, JSObject js_object);
|
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);
|
void UpdateRemoteStats(size_t, double);
|
||||||
|
|
||||||
DefaultEmbedderRootsHandler& default_embedder_roots_handler() {
|
cppgc::EmbedderStackState embedder_stack_state() const {
|
||||||
return default_embedder_roots_handler_;
|
|
||||||
}
|
|
||||||
|
|
||||||
EmbedderHeapTracer::EmbedderStackState embedder_stack_state() const {
|
|
||||||
return embedder_stack_state_;
|
return embedder_stack_state_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,39 +123,21 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
|
|||||||
private:
|
private:
|
||||||
static constexpr size_t kEmbedderAllocatedThreshold = 128 * KB;
|
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() {
|
CppHeap* cpp_heap() {
|
||||||
DCHECK_NOT_NULL(cpp_heap_);
|
DCHECK_NOT_NULL(cpp_heap_);
|
||||||
DCHECK_NULL(remote_tracer_);
|
|
||||||
DCHECK_IMPLIES(isolate_, cpp_heap_ == isolate_->heap()->cpp_heap());
|
DCHECK_IMPLIES(isolate_, cpp_heap_ == isolate_->heap()->cpp_heap());
|
||||||
return cpp_heap_;
|
return cpp_heap_;
|
||||||
}
|
}
|
||||||
|
|
||||||
WrapperDescriptor wrapper_descriptor() {
|
WrapperDescriptor wrapper_descriptor() {
|
||||||
if (cpp_heap_)
|
|
||||||
return cpp_heap()->wrapper_descriptor();
|
return cpp_heap()->wrapper_descriptor();
|
||||||
else
|
|
||||||
return wrapper_descriptor_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Isolate* const isolate_;
|
Isolate* const isolate_;
|
||||||
EmbedderHeapTracer* remote_tracer_ = nullptr;
|
|
||||||
CppHeap* cpp_heap_ = nullptr;
|
CppHeap* cpp_heap_ = nullptr;
|
||||||
DefaultEmbedderRootsHandler default_embedder_roots_handler_;
|
|
||||||
|
|
||||||
EmbedderHeapTracer::EmbedderStackState embedder_stack_state_ =
|
cppgc::EmbedderStackState embedder_stack_state_ =
|
||||||
EmbedderHeapTracer::EmbedderStackState::kMayContainHeapPointers;
|
cppgc::EmbedderStackState::kMayContainHeapPointers;
|
||||||
// Indicates whether the embedder worklist was observed empty on the main
|
// Indicates whether the embedder worklist was observed empty on the main
|
||||||
// thread. This is opportunistic as concurrent marking tasks may hold local
|
// thread. This is opportunistic as concurrent marking tasks may hold local
|
||||||
// segments of potential embedder fields to move to the main thread.
|
// 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;
|
size_t allocated_size_limit_for_check = 0;
|
||||||
} remote_stats_;
|
} 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;
|
friend class EmbedderStackStateScope;
|
||||||
};
|
};
|
||||||
|
|
||||||
END_ALLOW_USE_DEPRECATED()
|
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
|
||||||
|
@ -347,7 +347,7 @@ class V8_EXPORT_PRIVATE GCTracer {
|
|||||||
double time_ms = 0) const;
|
double time_ms = 0) const;
|
||||||
|
|
||||||
// Allocation throughput in the embedder in bytes/millisecond in the
|
// 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.
|
// Returns 0 if no allocation events have been recorded.
|
||||||
double EmbedderAllocationThroughputInBytesPerMillisecond(
|
double EmbedderAllocationThroughputInBytesPerMillisecond(
|
||||||
double time_ms = 0) const;
|
double time_ms = 0) const;
|
||||||
@ -368,7 +368,7 @@ class V8_EXPORT_PRIVATE GCTracer {
|
|||||||
double CurrentOldGenerationAllocationThroughputInBytesPerMillisecond() const;
|
double CurrentOldGenerationAllocationThroughputInBytesPerMillisecond() const;
|
||||||
|
|
||||||
// Allocation throughput in the embedder in bytes/milliseconds in the last
|
// 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.
|
// Returns 0 if no allocation events have been recorded.
|
||||||
double CurrentEmbedderAllocationThroughputInBytesPerMillisecond() const;
|
double CurrentEmbedderAllocationThroughputInBytesPerMillisecond() const;
|
||||||
|
|
||||||
|
@ -5612,8 +5612,6 @@ void Heap::SetUpSpaces(LinearAllocationArea& new_allocation_info,
|
|||||||
dead_object_stats_.reset(new ObjectStats(this));
|
dead_object_stats_.reset(new ObjectStats(this));
|
||||||
}
|
}
|
||||||
local_embedder_heap_tracer_.reset(new LocalEmbedderHeapTracer(isolate()));
|
local_embedder_heap_tracer_.reset(new LocalEmbedderHeapTracer(isolate()));
|
||||||
embedder_roots_handler_ =
|
|
||||||
&local_embedder_heap_tracer()->default_embedder_roots_handler();
|
|
||||||
if (Heap::AllocationTrackerForDebugging::IsNeeded()) {
|
if (Heap::AllocationTrackerForDebugging::IsNeeded()) {
|
||||||
allocation_tracker_for_debugging_ =
|
allocation_tracker_for_debugging_ =
|
||||||
std::make_unique<Heap::AllocationTrackerForDebugging>(this);
|
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) {
|
void Heap::SetEmbedderRootsHandler(EmbedderRootsHandler* handler) {
|
||||||
embedder_roots_handler_ = handler;
|
embedder_roots_handler_ = handler;
|
||||||
}
|
}
|
||||||
@ -5847,20 +5821,6 @@ void Heap::SetStackStart(void* stack_start) {
|
|||||||
return isolate_->thread_local_top()->stack_;
|
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() {
|
void Heap::StartTearDown() {
|
||||||
// Finish any ongoing sweeping to avoid stray background tasks still accessing
|
// Finish any ongoing sweeping to avoid stray background tasks still accessing
|
||||||
// the heap during teardown.
|
// the heap during teardown.
|
||||||
|
@ -1163,16 +1163,6 @@ class Heap {
|
|||||||
return local_embedder_heap_tracer_.get();
|
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. ===============================================
|
// Unified heap (C++) support. ===============================================
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
@ -315,14 +315,13 @@ void IncrementalMarking::StartMarkingMajor() {
|
|||||||
isolate()->external_pointer_table().StartCompactingIfNeeded();
|
isolate()->external_pointer_table().StartCompactingIfNeeded();
|
||||||
#endif // V8_COMPRESS_POINTERS
|
#endif // V8_COMPRESS_POINTERS
|
||||||
|
|
||||||
auto embedder_flags = heap_->flags_for_embedder_tracer();
|
|
||||||
{
|
{
|
||||||
TRACE_GC(heap()->tracer(),
|
TRACE_GC(heap()->tracer(),
|
||||||
GCTracer::Scope::MC_INCREMENTAL_EMBEDDER_PROLOGUE);
|
GCTracer::Scope::MC_INCREMENTAL_EMBEDDER_PROLOGUE);
|
||||||
// PrepareForTrace should be called before visitor initialization in
|
// PrepareForTrace should be called before visitor initialization in
|
||||||
// StartMarking. It is only used with CppHeap.
|
// StartMarking. It is only used with CppHeap.
|
||||||
heap_->local_embedder_heap_tracer()->PrepareForTrace(
|
heap_->local_embedder_heap_tracer()->PrepareForTrace(
|
||||||
embedder_flags, LocalEmbedderHeapTracer::CollectionType::kMajor);
|
LocalEmbedderHeapTracer::CollectionType::kMajor);
|
||||||
}
|
}
|
||||||
|
|
||||||
major_collector_->StartMarking();
|
major_collector_->StartMarking();
|
||||||
@ -358,7 +357,7 @@ void IncrementalMarking::StartMarkingMajor() {
|
|||||||
// marking (including write barriers) is fully set up.
|
// marking (including write barriers) is fully set up.
|
||||||
TRACE_GC(heap()->tracer(),
|
TRACE_GC(heap()->tracer(),
|
||||||
GCTracer::Scope::MC_INCREMENTAL_EMBEDDER_PROLOGUE);
|
GCTracer::Scope::MC_INCREMENTAL_EMBEDDER_PROLOGUE);
|
||||||
heap_->local_embedder_heap_tracer()->TracePrologue(embedder_flags);
|
heap_->local_embedder_heap_tracer()->TracePrologue();
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_->InvokeIncrementalMarkingEpilogueCallbacks();
|
heap_->InvokeIncrementalMarkingEpilogueCallbacks();
|
||||||
@ -555,8 +554,6 @@ void IncrementalMarking::EmbedderStep(double expected_duration_ms,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t kObjectsToProcessBeforeDeadlineCheck = 500;
|
|
||||||
|
|
||||||
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_INCREMENTAL_EMBEDDER_TRACING);
|
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_INCREMENTAL_EMBEDDER_TRACING);
|
||||||
LocalEmbedderHeapTracer* local_tracer = heap_->local_embedder_heap_tracer();
|
LocalEmbedderHeapTracer* local_tracer = heap_->local_embedder_heap_tracer();
|
||||||
const double start = heap_->MonotonicallyIncreasingTimeInMs();
|
const double start = heap_->MonotonicallyIncreasingTimeInMs();
|
||||||
@ -564,21 +561,6 @@ void IncrementalMarking::EmbedderStep(double expected_duration_ms,
|
|||||||
bool empty_worklist = true;
|
bool empty_worklist = true;
|
||||||
if (local_marking_worklists()->PublishWrapper()) {
|
if (local_marking_worklists()->PublishWrapper()) {
|
||||||
DCHECK(local_marking_worklists()->IsWrapperEmpty());
|
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,
|
// |deadline - heap_->MonotonicallyIncreasingTimeInMs()| could be negative,
|
||||||
// which means |local_tracer| won't do any actual tracing, so there is no
|
// 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
|
// static
|
||||||
void MarkCompactCollector::RecordSlot(HeapObject object, ObjectSlot slot,
|
void MarkCompactCollector::RecordSlot(HeapObject object, ObjectSlot slot,
|
||||||
HeapObject target) {
|
HeapObject target) {
|
||||||
|
@ -910,13 +910,12 @@ void MarkCompactCollector::Prepare() {
|
|||||||
DCHECK(!heap_->memory_allocator()->unmapper()->IsRunning());
|
DCHECK(!heap_->memory_allocator()->unmapper()->IsRunning());
|
||||||
|
|
||||||
if (!heap()->incremental_marking()->IsMarking()) {
|
if (!heap()->incremental_marking()->IsMarking()) {
|
||||||
const auto embedder_flags = heap_->flags_for_embedder_tracer();
|
|
||||||
{
|
{
|
||||||
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_PROLOGUE);
|
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_PROLOGUE);
|
||||||
// PrepareForTrace should be called before visitor initialization in
|
// PrepareForTrace should be called before visitor initialization in
|
||||||
// StartMarking.
|
// StartMarking.
|
||||||
heap_->local_embedder_heap_tracer()->PrepareForTrace(
|
heap_->local_embedder_heap_tracer()->PrepareForTrace(
|
||||||
embedder_flags, LocalEmbedderHeapTracer::CollectionType::kMajor);
|
LocalEmbedderHeapTracer::CollectionType::kMajor);
|
||||||
}
|
}
|
||||||
StartCompaction(StartCompactionMode::kAtomic);
|
StartCompaction(StartCompactionMode::kAtomic);
|
||||||
StartMarking();
|
StartMarking();
|
||||||
@ -924,7 +923,7 @@ void MarkCompactCollector::Prepare() {
|
|||||||
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_PROLOGUE);
|
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_PROLOGUE);
|
||||||
// TracePrologue immediately starts marking which requires V8 worklists to
|
// TracePrologue immediately starts marking which requires V8 worklists to
|
||||||
// be set up.
|
// be set up.
|
||||||
heap_->local_embedder_heap_tracer()->TracePrologue(embedder_flags);
|
heap_->local_embedder_heap_tracer()->TracePrologue();
|
||||||
}
|
}
|
||||||
#ifdef V8_COMPRESS_POINTERS
|
#ifdef V8_COMPRESS_POINTERS
|
||||||
heap_->isolate()->external_pointer_table().StartCompactingIfNeeded();
|
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()) {
|
if (!heap_->cpp_heap() && heap_->local_embedder_heap_tracer()->InUse()) {
|
||||||
// Conservative global handle scanning is necessary for keeping
|
// Conservative global handle scanning is necessary for keeping
|
||||||
// v8::TracedReference alive from the stack. This is only needed when using
|
// v8::TracedReference alive from the stack.
|
||||||
// `EmbedderHeapTracer` and not using `CppHeap`.
|
//
|
||||||
|
// TODO(v8:v8:13207): Remove as this is not required when using `CppHeap`.
|
||||||
auto& stack = heap()->stack();
|
auto& stack = heap()->stack();
|
||||||
if (heap_->local_embedder_heap_tracer()->embedder_stack_state() ==
|
if (heap_->local_embedder_heap_tracer()->embedder_stack_state() ==
|
||||||
cppgc::EmbedderStackState::kMayContainHeapPointers) {
|
cppgc::EmbedderStackState::kMayContainHeapPointers) {
|
||||||
@ -2551,14 +2551,6 @@ void MarkCompactCollector::PerformWrapperTracing() {
|
|||||||
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_TRACING);
|
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_TRACING);
|
||||||
if (local_marking_worklists()->PublishWrapper()) {
|
if (local_marking_worklists()->PublishWrapper()) {
|
||||||
DCHECK(local_marking_worklists()->IsWrapperEmpty());
|
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(
|
heap_->local_embedder_heap_tracer()->Trace(
|
||||||
std::numeric_limits<double>::infinity());
|
std::numeric_limits<double>::infinity());
|
||||||
@ -5790,8 +5782,8 @@ void MinorMarkCompactCollector::SweepArrayBufferExtensions() {
|
|||||||
|
|
||||||
void MinorMarkCompactCollector::PerformWrapperTracing() {
|
void MinorMarkCompactCollector::PerformWrapperTracing() {
|
||||||
if (!heap_->local_embedder_heap_tracer()->InUse()) return;
|
if (!heap_->local_embedder_heap_tracer()->InUse()) return;
|
||||||
// TODO(v8:13475): DCHECK instead of bailing out once EmbedderHeapTracer is
|
// TODO(v8:v8:13207): DCHECK instead of bailing out as only CppHeap is
|
||||||
// removed.
|
// supported.
|
||||||
if (!local_marking_worklists()->PublishWrapper()) return;
|
if (!local_marking_worklists()->PublishWrapper()) return;
|
||||||
DCHECK_NOT_NULL(CppHeap::From(heap_->cpp_heap()));
|
DCHECK_NOT_NULL(CppHeap::From(heap_->cpp_heap()));
|
||||||
DCHECK(CppHeap::From(heap_->cpp_heap())->generational_gc_supported());
|
DCHECK(CppHeap::From(heap_->cpp_heap())->generational_gc_supported());
|
||||||
@ -5986,21 +5978,20 @@ void MinorMarkCompactCollector::Prepare() {
|
|||||||
|
|
||||||
// Probably requires more.
|
// Probably requires more.
|
||||||
if (!heap()->incremental_marking()->IsMarking()) {
|
if (!heap()->incremental_marking()->IsMarking()) {
|
||||||
const auto embedder_flags = heap_->flags_for_embedder_tracer();
|
|
||||||
{
|
{
|
||||||
TRACE_GC(heap()->tracer(),
|
TRACE_GC(heap()->tracer(),
|
||||||
GCTracer::Scope::MINOR_MC_MARK_EMBEDDER_PROLOGUE);
|
GCTracer::Scope::MINOR_MC_MARK_EMBEDDER_PROLOGUE);
|
||||||
// PrepareForTrace should be called before visitor initialization in
|
// PrepareForTrace should be called before visitor initialization in
|
||||||
// StartMarking.
|
// StartMarking.
|
||||||
heap_->local_embedder_heap_tracer()->PrepareForTrace(
|
heap_->local_embedder_heap_tracer()->PrepareForTrace(
|
||||||
embedder_flags, LocalEmbedderHeapTracer::CollectionType::kMinor);
|
LocalEmbedderHeapTracer::CollectionType::kMinor);
|
||||||
}
|
}
|
||||||
StartMarking();
|
StartMarking();
|
||||||
{
|
{
|
||||||
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_PROLOGUE);
|
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_PROLOGUE);
|
||||||
// TracePrologue immediately starts marking which requires V8 worklists to
|
// TracePrologue immediately starts marking which requires V8 worklists to
|
||||||
// be set up.
|
// 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);
|
explicit MarkCompactCollector(Heap* heap);
|
||||||
~MarkCompactCollector() final;
|
~MarkCompactCollector() final;
|
||||||
|
|
||||||
// Used by wrapper tracing.
|
|
||||||
V8_INLINE void MarkExternallyReferencedObject(HeapObject obj);
|
|
||||||
|
|
||||||
std::unique_ptr<UpdatingItem> CreateRememberedSetUpdatingItem(
|
std::unique_ptr<UpdatingItem> CreateRememberedSetUpdatingItem(
|
||||||
MemoryChunk* chunk);
|
MemoryChunk* chunk);
|
||||||
|
|
||||||
|
@ -412,7 +412,6 @@ v8_source_set("unittests_sources") {
|
|||||||
"heap/cppgc-js/unified-heap-utils.cc",
|
"heap/cppgc-js/unified-heap-utils.cc",
|
||||||
"heap/cppgc-js/unified-heap-utils.h",
|
"heap/cppgc-js/unified-heap-utils.h",
|
||||||
"heap/cppgc-js/young-unified-heap-unittest.cc",
|
"heap/cppgc-js/young-unified-heap-unittest.cc",
|
||||||
"heap/embedder-tracing-unittest.cc",
|
|
||||||
"heap/gc-idle-time-handler-unittest.cc",
|
"heap/gc-idle-time-handler-unittest.cc",
|
||||||
"heap/gc-tracer-unittest.cc",
|
"heap/gc-tracer-unittest.cc",
|
||||||
"heap/global-handles-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 = //
|
using TestWithHeapInternals = //
|
||||||
WithHeapInternals< //
|
WithHeapInternals< //
|
||||||
WithInternalIsolateMixin< //
|
WithInternalIsolateMixin< //
|
||||||
|
Loading…
Reference in New Issue
Block a user