[heap] Cleanup embedder tracing APIs
Provide processing scope that makes it impossible to maintain locally
cached wrappers that could get invalidated in Blink and yield in
crashers.
Bug: chromium:843903, v8:8238
Change-Id: I7ba1905f6c77a97bcc61ac42f921dcac4772471f
Reviewed-on: https://chromium-review.googlesource.com/c/1349276
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57795}
diff --git a/src/heap/embedder-tracing.cc b/src/heap/embedder-tracing.cc
index 3b8e4ae..ff5ffd0 100644
--- a/src/heap/embedder-tracing.cc
+++ b/src/heap/embedder-tracing.cc
@@ -5,14 +5,23 @@
#include "src/heap/embedder-tracing.h"
#include "src/base/logging.h"
+#include "src/objects/embedder-data-slot.h"
+#include "src/objects/js-objects-inl.h"
namespace v8 {
namespace internal {
+void LocalEmbedderHeapTracer::SetRemoteTracer(EmbedderHeapTracer* tracer) {
+ if (remote_tracer_) remote_tracer_->isolate_ = nullptr;
+
+ remote_tracer_ = tracer;
+ if (remote_tracer_)
+ remote_tracer_->isolate_ = reinterpret_cast<v8::Isolate*>(isolate_);
+}
+
void LocalEmbedderHeapTracer::TracePrologue() {
if (!InUse()) return;
- CHECK(cached_wrappers_to_trace_.empty());
num_v8_marking_worklist_was_empty_ = 0;
embedder_worklist_empty_ = false;
remote_tracer_->TracePrologue();
@@ -21,7 +30,6 @@
void LocalEmbedderHeapTracer::TraceEpilogue() {
if (!InUse()) return;
- CHECK(cached_wrappers_to_trace_.empty());
remote_tracer_->TraceEpilogue();
}
@@ -37,30 +45,11 @@
bool LocalEmbedderHeapTracer::Trace(double deadline) {
if (!InUse()) return true;
- DCHECK_EQ(0, NumberOfCachedWrappersToTrace());
return remote_tracer_->AdvanceTracing(deadline);
}
bool LocalEmbedderHeapTracer::IsRemoteTracingDone() {
- return (InUse()) ? cached_wrappers_to_trace_.empty() &&
- remote_tracer_->IsTracingDone()
- : true;
-}
-
-void LocalEmbedderHeapTracer::RegisterWrappersWithRemoteTracer() {
- if (!InUse()) return;
-
- if (cached_wrappers_to_trace_.empty()) {
- return;
- }
-
- remote_tracer_->RegisterV8References(cached_wrappers_to_trace_);
- cached_wrappers_to_trace_.clear();
-}
-
-bool LocalEmbedderHeapTracer::RequiresImmediateWrapperProcessing() {
- const size_t kTooManyWrappers = 16000;
- return cached_wrappers_to_trace_.size() > kTooManyWrappers;
+ return !InUse() || remote_tracer_->IsTracingDone();
}
void LocalEmbedderHeapTracer::SetEmbedderStackStateForNextFinalization(
@@ -70,5 +59,46 @@
embedder_stack_state_ = stack_state;
}
+LocalEmbedderHeapTracer::ProcessingScope::ProcessingScope(
+ LocalEmbedderHeapTracer* tracer)
+ : tracer_(tracer) {
+ wrapper_cache_.reserve(kWrapperCacheSize);
+}
+
+LocalEmbedderHeapTracer::ProcessingScope::~ProcessingScope() {
+ if (!wrapper_cache_.empty()) {
+ tracer_->remote_tracer()->RegisterV8References(std::move(wrapper_cache_));
+ }
+ tracer_->embedder_worklist_empty_ = empty_worklist_;
+}
+
+void LocalEmbedderHeapTracer::ProcessingScope::TracePossibleWrapper(
+ JSObject* js_object) {
+ DCHECK(js_object->IsApiWrapper());
+ if (js_object->GetEmbedderFieldCount() < 2) return;
+
+ void* pointer0;
+ void* pointer1;
+ if (EmbedderDataSlot(js_object, 0).ToAlignedPointer(&pointer0) && pointer0 &&
+ EmbedderDataSlot(js_object, 1).ToAlignedPointer(&pointer1)) {
+ wrapper_cache_.push_back({pointer0, pointer1});
+ }
+ FlushWrapperCacheIfFull();
+}
+
+void LocalEmbedderHeapTracer::ProcessingScope::FlushWrapperCacheIfFull() {
+ if (wrapper_cache_.size() == wrapper_cache_.capacity()) {
+ tracer_->remote_tracer()->RegisterV8References(std::move(wrapper_cache_));
+ wrapper_cache_.clear();
+ wrapper_cache_.reserve(kWrapperCacheSize);
+ }
+}
+
+void LocalEmbedderHeapTracer::ProcessingScope::AddWrapperInfoForTesting(
+ WrapperInfo info) {
+ wrapper_cache_.push_back(info);
+ FlushWrapperCacheIfFull();
+}
+
} // namespace internal
} // namespace v8
diff --git a/src/heap/embedder-tracing.h b/src/heap/embedder-tracing.h
index 7b4d1ab..49103e5 100644
--- a/src/heap/embedder-tracing.h
+++ b/src/heap/embedder-tracing.h
@@ -13,10 +13,32 @@
namespace internal {
class Heap;
+class JSObject;
class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
public:
typedef std::pair<void*, void*> WrapperInfo;
+ typedef std::vector<WrapperInfo> WrapperCache;
+
+ class V8_EXPORT_PRIVATE ProcessingScope {
+ public:
+ explicit ProcessingScope(LocalEmbedderHeapTracer* tracer);
+ ~ProcessingScope();
+
+ void TracePossibleWrapper(JSObject* js_object);
+ void set_not_done() { empty_worklist_ = false; }
+
+ void AddWrapperInfoForTesting(WrapperInfo info);
+
+ private:
+ static constexpr size_t kWrapperCacheSize = 1000;
+
+ void FlushWrapperCacheIfFull();
+
+ LocalEmbedderHeapTracer* const tracer_;
+ WrapperCache wrapper_cache_;
+ bool empty_worklist_ = true;
+ };
explicit LocalEmbedderHeapTracer(Isolate* isolate) : isolate_(isolate) {}
@@ -24,37 +46,16 @@
if (remote_tracer_) remote_tracer_->isolate_ = nullptr;
}
+ bool InUse() const { return remote_tracer_ != nullptr; }
EmbedderHeapTracer* remote_tracer() const { return remote_tracer_; }
- void SetRemoteTracer(EmbedderHeapTracer* tracer) {
- if (remote_tracer_) remote_tracer_->isolate_ = nullptr;
-
- remote_tracer_ = tracer;
- if (remote_tracer_)
- remote_tracer_->isolate_ = reinterpret_cast<v8::Isolate*>(isolate_);
- }
-
- bool InUse() const { return remote_tracer_ != nullptr; }
-
+ void SetRemoteTracer(EmbedderHeapTracer* tracer);
void TracePrologue();
void TraceEpilogue();
void EnterFinalPause();
bool Trace(double deadline);
bool IsRemoteTracingDone();
- size_t NumberOfCachedWrappersToTrace() {
- return cached_wrappers_to_trace_.size();
- }
- void AddWrapperToTrace(WrapperInfo entry) {
- cached_wrappers_to_trace_.push_back(entry);
- }
- void ClearCachedWrappersToTrace() { cached_wrappers_to_trace_.clear(); }
- void RegisterWrappersWithRemoteTracer();
-
- // In order to avoid running out of memory we force tracing wrappers if there
- // are too many of them.
- bool RequiresImmediateWrapperProcessing();
-
void NotifyV8MarkingWorklistWasEmpty() {
num_v8_marking_worklist_was_empty_++;
}
@@ -66,23 +67,16 @@
num_v8_marking_worklist_was_empty_ > kMaxIncrementalFixpointRounds;
}
- void SetEmbedderWorklistEmpty(bool empty) {
- embedder_worklist_empty_ = empty;
- }
-
void SetEmbedderStackStateForNextFinalization(
EmbedderHeapTracer::EmbedderStackState stack_state);
private:
- typedef std::vector<WrapperInfo> WrapperCache;
-
Isolate* const isolate_;
- WrapperCache cached_wrappers_to_trace_;
EmbedderHeapTracer* remote_tracer_ = nullptr;
+
size_t num_v8_marking_worklist_was_empty_ = 0;
EmbedderHeapTracer::EmbedderStackState embedder_stack_state_ =
EmbedderHeapTracer::kUnknown;
-
// 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.
diff --git a/src/heap/heap.cc b/src/heap/heap.cc
index fbdaa1c..09b00f4 100644
--- a/src/heap/heap.cc
+++ b/src/heap/heap.cc
@@ -2965,9 +2965,6 @@
}
}
}
- // We potentially deserialized wrappers which require registering with the
- // embedder as the marker will not find them.
- local_embedder_heap_tracer()->RegisterWrappersWithRemoteTracer();
// Large object space doesn't use reservations, so it needs custom handling.
for (HeapObject* object : large_objects) {
@@ -4547,18 +4544,6 @@
return local_embedder_heap_tracer()->remote_tracer();
}
-void Heap::TracePossibleWrapper(JSObject* js_object) {
- DCHECK(js_object->IsApiWrapper());
- if (js_object->GetEmbedderFieldCount() < 2) return;
- void* pointer0;
- void* pointer1;
- if (EmbedderDataSlot(js_object, 0).ToAlignedPointer(&pointer0) && pointer0 &&
- EmbedderDataSlot(js_object, 1).ToAlignedPointer(&pointer1)) {
- local_embedder_heap_tracer()->AddWrapperToTrace(
- std::pair<void*, void*>(pointer0, pointer1));
- }
-}
-
void Heap::RegisterExternallyReferencedObject(Address* location) {
// The embedder is not aware of whether numbers are materialized as heap
// objects are just passed around as Smis.
diff --git a/src/heap/heap.h b/src/heap/heap.h
index 0c6c746..86eeb07 100644
--- a/src/heap/heap.h
+++ b/src/heap/heap.h
@@ -893,7 +893,6 @@
void SetEmbedderHeapTracer(EmbedderHeapTracer* tracer);
EmbedderHeapTracer* GetEmbedderHeapTracer() const;
- void TracePossibleWrapper(JSObject* js_object);
void RegisterExternallyReferencedObject(Address* location);
void SetEmbedderStackStateForNextFinalizaton(
EmbedderHeapTracer::EmbedderStackState stack_state);
diff --git a/src/heap/incremental-marking.cc b/src/heap/incremental-marking.cc
index 4fae57c..1a89aa3 100644
--- a/src/heap/incremental-marking.cc
+++ b/src/heap/incremental-marking.cc
@@ -8,6 +8,7 @@
#include "src/compilation-cache.h"
#include "src/conversions.h"
#include "src/heap/concurrent-marking.h"
+#include "src/heap/embedder-tracing.h"
#include "src/heap/gc-idle-time-handler.h"
#include "src/heap/gc-tracer.h"
#include "src/heap/heap-inl.h"
@@ -815,11 +816,6 @@
int size = VisitObject(obj->map(), obj);
bytes_processed += size - unscanned_bytes_of_large_object_;
}
- // Report all found wrappers to the embedder. This is necessary as the
- // embedder could potentially invalidate wrappers as soon as V8 is done
- // with its incremental marking processing. Any cached wrappers could
- // result in broken pointers at this point.
- heap_->local_embedder_heap_tracer()->RegisterWrappersWithRemoteTracer();
return bytes_processed;
}
@@ -829,20 +825,20 @@
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_INCREMENTAL_EMBEDDER_TRACING);
double deadline = heap_->MonotonicallyIncreasingTimeInMs() + duration_ms;
do {
- HeapObject* object;
- size_t cnt = 0;
- bool embedder_fields_empty = true;
- while (marking_worklist()->embedder()->Pop(0, &object)) {
- heap_->TracePossibleWrapper(JSObject::cast(object));
- if (++cnt == kObjectsToProcessBeforeInterrupt) {
- cnt = 0;
- embedder_fields_empty = false;
- break;
+ {
+ LocalEmbedderHeapTracer::ProcessingScope scope(
+ heap_->local_embedder_heap_tracer());
+ HeapObject* object;
+ size_t cnt = 0;
+ while (marking_worklist()->embedder()->Pop(0, &object)) {
+ scope.TracePossibleWrapper(JSObject::cast(object));
+ if (++cnt == kObjectsToProcessBeforeInterrupt) {
+ cnt = 0;
+ scope.set_not_done();
+ break;
+ }
}
}
- heap_->local_embedder_heap_tracer()->SetEmbedderWorklistEmpty(
- embedder_fields_empty);
- heap_->local_embedder_heap_tracer()->RegisterWrappersWithRemoteTracer();
heap_->local_embedder_heap_tracer()->Trace(deadline);
} while (heap_->MonotonicallyIncreasingTimeInMs() < deadline);
}
@@ -967,8 +963,6 @@
TRACE_EVENT0("v8", "V8.GCIncrementalMarking");
TRACE_GC(heap_->tracer(), GCTracer::Scope::MC_INCREMENTAL);
DCHECK(!IsStopped());
- DCHECK_EQ(
- 0, heap_->local_embedder_heap_tracer()->NumberOfCachedWrappersToTrace());
double remaining_time_in_ms = 0.0;
do {
diff --git a/src/heap/mark-compact-inl.h b/src/heap/mark-compact-inl.h
index 6bb3f2a..16c27ac 100644
--- a/src/heap/mark-compact-inl.h
+++ b/src/heap/mark-compact-inl.h
@@ -74,7 +74,8 @@
MarkingVisitor<fixed_array_mode, retaining_path_mode,
MarkingState>::VisitEmbedderTracingSubclass(Map map, T* object) {
if (heap_->local_embedder_heap_tracer()->InUse()) {
- heap_->TracePossibleWrapper(object);
+ marking_worklist()->embedder()->Push(MarkCompactCollectorBase::kMainThread,
+ object);
}
int size = T::BodyDescriptor::SizeOf(map, object);
T::BodyDescriptor::IterateBody(map, object, size, this);
diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc
index 2200aa6..3131583 100644
--- a/src/heap/mark-compact.cc
+++ b/src/heap/mark-compact.cc
@@ -1487,6 +1487,7 @@
work_to_do = work_to_do || !marking_worklist()->IsEmpty() ||
heap()->concurrent_marking()->ephemeron_marked() ||
+ !marking_worklist()->IsEmbedderEmpty() ||
!heap()->local_embedder_heap_tracer()->IsRemoteTracingDone();
++iterations;
}
@@ -1614,11 +1615,14 @@
void MarkCompactCollector::PerformWrapperTracing() {
if (heap_->local_embedder_heap_tracer()->InUse()) {
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_EMBEDDER_TRACING);
- HeapObject* object;
- while (marking_worklist()->embedder()->Pop(kMainThread, &object)) {
- heap_->TracePossibleWrapper(JSObject::cast(object));
+ {
+ LocalEmbedderHeapTracer::ProcessingScope scope(
+ heap_->local_embedder_heap_tracer());
+ HeapObject* object;
+ while (marking_worklist()->embedder()->Pop(kMainThread, &object)) {
+ scope.TracePossibleWrapper(JSObject::cast(object));
+ }
}
- heap_->local_embedder_heap_tracer()->RegisterWrappersWithRemoteTracer();
heap_->local_embedder_heap_tracer()->Trace(
std::numeric_limits<double>::infinity());
}
@@ -1779,7 +1783,8 @@
// once.
PerformWrapperTracing();
ProcessMarkingWorklist();
- } while (!heap_->local_embedder_heap_tracer()->IsRemoteTracingDone());
+ } while (!heap_->local_embedder_heap_tracer()->IsRemoteTracingDone() ||
+ !marking_worklist()->IsEmbedderEmpty());
DCHECK(marking_worklist()->IsEmbedderEmpty());
DCHECK(marking_worklist()->IsEmpty());
}
diff --git a/src/heap/mark-compact.h b/src/heap/mark-compact.h
index 0bef1f1..dbb2444 100644
--- a/src/heap/mark-compact.h
+++ b/src/heap/mark-compact.h
@@ -235,6 +235,8 @@
// Base class for minor and full MC collectors.
class MarkCompactCollectorBase {
public:
+ static const int kMainThread = 0;
+
virtual ~MarkCompactCollectorBase() = default;
virtual void SetUp() = 0;
@@ -245,7 +247,6 @@
inline Isolate* isolate();
protected:
- static const int kMainThread = 0;
explicit MarkCompactCollectorBase(Heap* heap)
: heap_(heap), old_to_new_slots_(0) {}
diff --git a/src/heap/scavenger.cc b/src/heap/scavenger.cc
index 56aeef7..6649d38 100644
--- a/src/heap/scavenger.cc
+++ b/src/heap/scavenger.cc
@@ -279,10 +279,6 @@
// Update how much has survived scavenge.
heap_->IncrementYoungSurvivorsCounter(heap_->SurvivedNewSpaceObjectSize());
-
- // Scavenger may find new wrappers by iterating objects promoted onto a black
- // page.
- heap_->local_embedder_heap_tracer()->RegisterWrappersWithRemoteTracer();
}
void ScavengerCollector::HandleSurvivingNewLargeObjects() {
diff --git a/test/unittests/heap/embedder-tracing-unittest.cc b/test/unittests/heap/embedder-tracing-unittest.cc
index 941c474..5bbbace 100644
--- a/test/unittests/heap/embedder-tracing-unittest.cc
+++ b/test/unittests/heap/embedder-tracing-unittest.cc
@@ -83,6 +83,14 @@
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);
@@ -152,52 +160,19 @@
local_tracer.IsRemoteTracingDone();
}
-TEST(LocalEmbedderHeapTracer, NumberOfCachedWrappersToTraceExcludesRemote) {
- LocalEmbedderHeapTracer local_tracer(nullptr);
- StrictMock<MockEmbedderHeapTracer> remote_tracer;
- local_tracer.SetRemoteTracer(&remote_tracer);
- local_tracer.NumberOfCachedWrappersToTrace();
-}
-
-TEST(LocalEmbedderHeapTracer, RegisterWrappersWithRemoteTracer) {
+TEST(LocalEmbedderHeapTracer, RegisterV8ReferencesWithRemoteTracer) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&remote_tracer);
- local_tracer.AddWrapperToTrace(CreateWrapperInfo());
- EXPECT_EQ(1u, local_tracer.NumberOfCachedWrappersToTrace());
- EXPECT_CALL(remote_tracer, RegisterV8References(_));
- local_tracer.RegisterWrappersWithRemoteTracer();
- EXPECT_EQ(0u, local_tracer.NumberOfCachedWrappersToTrace());
+ {
+ 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(LocalEmbedderHeapTracer, TraceFinishes) {
- StrictMock<MockEmbedderHeapTracer> remote_tracer;
- LocalEmbedderHeapTracer local_tracer(nullptr);
- local_tracer.SetRemoteTracer(&remote_tracer);
- local_tracer.AddWrapperToTrace(CreateWrapperInfo());
- EXPECT_EQ(1u, local_tracer.NumberOfCachedWrappersToTrace());
- EXPECT_CALL(remote_tracer, RegisterV8References(_));
- local_tracer.RegisterWrappersWithRemoteTracer();
- EXPECT_CALL(remote_tracer, AdvanceTracing(_)).WillOnce(Return(true));
- EXPECT_TRUE(local_tracer.Trace(std::numeric_limits<double>::infinity()));
- EXPECT_EQ(0u, local_tracer.NumberOfCachedWrappersToTrace());
-}
-
-TEST(LocalEmbedderHeapTracer, TraceDoesNotFinish) {
- StrictMock<MockEmbedderHeapTracer> remote_tracer;
- LocalEmbedderHeapTracer local_tracer(nullptr);
- local_tracer.SetRemoteTracer(&remote_tracer);
- local_tracer.AddWrapperToTrace(CreateWrapperInfo());
- EXPECT_EQ(1u, local_tracer.NumberOfCachedWrappersToTrace());
- EXPECT_CALL(remote_tracer, RegisterV8References(_));
- local_tracer.RegisterWrappersWithRemoteTracer();
- EXPECT_CALL(remote_tracer, AdvanceTracing(_)).WillOnce(Return(false));
- EXPECT_FALSE(local_tracer.Trace(1.0));
- EXPECT_EQ(0u, local_tracer.NumberOfCachedWrappersToTrace());
-}
-
TEST_F(LocalEmbedderHeapTracerWithIsolate, SetRemoteTracerSetsIsolate) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(isolate());