Commit ce02d86b authored by Michael Lippautz's avatar Michael Lippautz Committed by Commit Bot

[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: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57795}
parent b7922406
......@@ -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::TracePrologue() {
void LocalEmbedderHeapTracer::TraceEpilogue() {
if (!InUse()) return;
CHECK(cached_wrappers_to_trace_.empty());
remote_tracer_->TraceEpilogue();
}
......@@ -37,37 +45,59 @@ void LocalEmbedderHeapTracer::EnterFinalPause() {
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;
return !InUse() || remote_tracer_->IsTracingDone();
}
void LocalEmbedderHeapTracer::RegisterWrappersWithRemoteTracer() {
void LocalEmbedderHeapTracer::SetEmbedderStackStateForNextFinalization(
EmbedderHeapTracer::EmbedderStackState stack_state) {
if (!InUse()) return;
if (cached_wrappers_to_trace_.empty()) {
return;
}
embedder_stack_state_ = stack_state;
}
remote_tracer_->RegisterV8References(cached_wrappers_to_trace_);
cached_wrappers_to_trace_.clear();
LocalEmbedderHeapTracer::ProcessingScope::ProcessingScope(
LocalEmbedderHeapTracer* tracer)
: tracer_(tracer) {
wrapper_cache_.reserve(kWrapperCacheSize);
}
bool LocalEmbedderHeapTracer::RequiresImmediateWrapperProcessing() {
const size_t kTooManyWrappers = 16000;
return cached_wrappers_to_trace_.size() > kTooManyWrappers;
LocalEmbedderHeapTracer::ProcessingScope::~ProcessingScope() {
if (!wrapper_cache_.empty()) {
tracer_->remote_tracer()->RegisterV8References(std::move(wrapper_cache_));
}
tracer_->embedder_worklist_empty_ = empty_worklist_;
}
void LocalEmbedderHeapTracer::SetEmbedderStackStateForNextFinalization(
EmbedderHeapTracer::EmbedderStackState stack_state) {
if (!InUse()) return;
void LocalEmbedderHeapTracer::ProcessingScope::TracePossibleWrapper(
JSObject* js_object) {
DCHECK(js_object->IsApiWrapper());
if (js_object->GetEmbedderFieldCount() < 2) return;
embedder_stack_state_ = stack_state;
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
......
......@@ -13,48 +13,49 @@ namespace v8 {
namespace internal {
class Heap;
class JSObject;
class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
public:
typedef std::pair<void*, void*> WrapperInfo;
typedef std::vector<WrapperInfo> WrapperCache;
explicit LocalEmbedderHeapTracer(Isolate* isolate) : isolate_(isolate) {}
class V8_EXPORT_PRIVATE ProcessingScope {
public:
explicit ProcessingScope(LocalEmbedderHeapTracer* tracer);
~ProcessingScope();
~LocalEmbedderHeapTracer() {
if (remote_tracer_) remote_tracer_->isolate_ = nullptr;
}
void TracePossibleWrapper(JSObject* js_object);
void set_not_done() { empty_worklist_ = false; }
EmbedderHeapTracer* remote_tracer() const { return remote_tracer_; }
void AddWrapperInfoForTesting(WrapperInfo info);
void SetRemoteTracer(EmbedderHeapTracer* tracer) {
if (remote_tracer_) remote_tracer_->isolate_ = nullptr;
private:
static constexpr size_t kWrapperCacheSize = 1000;
void FlushWrapperCacheIfFull();
LocalEmbedderHeapTracer* const tracer_;
WrapperCache wrapper_cache_;
bool empty_worklist_ = true;
};
remote_tracer_ = tracer;
if (remote_tracer_)
remote_tracer_->isolate_ = reinterpret_cast<v8::Isolate*>(isolate_);
explicit LocalEmbedderHeapTracer(Isolate* isolate) : isolate_(isolate) {}
~LocalEmbedderHeapTracer() {
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);
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 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
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.
......
......@@ -2965,9 +2965,6 @@ void Heap::RegisterDeserializedObjectsForBlackAllocation(
}
}
}
// 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 @@ EmbedderHeapTracer* Heap::GetEmbedderHeapTracer() const {
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.
......
......@@ -893,7 +893,6 @@ class Heap {
void SetEmbedderHeapTracer(EmbedderHeapTracer* tracer);
EmbedderHeapTracer* GetEmbedderHeapTracer() const;
void TracePossibleWrapper(JSObject* js_object);
void RegisterExternallyReferencedObject(Address* location);
void SetEmbedderStackStateForNextFinalizaton(
EmbedderHeapTracer::EmbedderStackState stack_state);
......
......@@ -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 @@ intptr_t IncrementalMarking::ProcessMarkingWorklist(
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 @@ void IncrementalMarking::EmbedderStep(double duration_ms) {
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 @@ double IncrementalMarking::AdvanceIncrementalMarking(
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 {
......
......@@ -74,7 +74,8 @@ V8_INLINE int
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);
......
......@@ -1487,6 +1487,7 @@ void MarkCompactCollector::ProcessEphemeronsUntilFixpoint() {
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::ProcessEphemeronsLinear() {
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 @@ void MarkCompactCollector::MarkLiveObjects() {
// 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());
}
......
......@@ -235,6 +235,8 @@ enum class RememberedSetUpdatingMode { ALL, OLD_TO_NEW_ONLY };
// 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 @@ class MarkCompactCollectorBase {
inline Isolate* isolate();
protected:
static const int kMainThread = 0;
explicit MarkCompactCollectorBase(Heap* heap)
: heap_(heap), old_to_new_slots_(0) {}
......
......@@ -279,10 +279,6 @@ void ScavengerCollector::CollectGarbage() {
// 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() {
......
......@@ -83,6 +83,14 @@ TEST(LocalEmbedderHeapTracer, EnterFinalPauseForwards) {
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 @@ TEST(LocalEmbedderHeapTracer, IsRemoteTracingDoneIncludesRemote) {
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());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment