[embedder-tracing] Add GarbageCollectionForTesting call

This call can be used by embedder to request a GC for testing reasons.
The GC also takes the current embedder stack state as an argument that
is forwarded to the embedder when entering the atomic pause.

This way embedders can request garbage collections for testing and set
how the embedder should treat the stack.

Bug: chromium:843903
Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Change-Id: Id10604565b4457dd0fca402afeb5f8e592fa0bae
Reviewed-on: https://chromium-review.googlesource.com/1183431
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55285}
This commit is contained in:
Michael Lippautz 2018-08-21 19:14:19 +02:00 committed by Commit Bot
parent 3d35921eb3
commit a6938128f4
10 changed files with 188 additions and 61 deletions

View File

@ -7126,18 +7126,21 @@ class V8_EXPORT PersistentHandleVisitor { // NOLINT
enum class MemoryPressureLevel { kNone, kModerate, kCritical };
/**
* Interface for tracing through the embedder heap. During a v8 garbage
* collection, v8 collects hidden fields of all potential wrappers, and at the
* 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.
*
* Before the first call to the TraceWrappersFrom function TracePrologue will be
* called. When the garbage collection cycle is finished, TraceEpilogue will be
* called.
*/
class V8_EXPORT EmbedderHeapTracer {
public:
// Indicator for the stack state of the embedder.
enum EmbedderStackState {
kUnknown,
kNonEmpty,
kEmpty,
};
enum ForceCompletionAction { FORCE_COMPLETION, DO_NOT_FORCE_COMPLETION };
struct AdvanceTracingActions {
@ -7164,7 +7167,7 @@ class V8_EXPORT EmbedderHeapTracer {
virtual void TracePrologue() = 0;
/**
* Called to to make a tracing step in the embedder.
* Called to make a tracing step in the embedder.
*
* The embedder is expected to trace its heap starting from wrappers reported
* by RegisterV8References method, and report back all reachable wrappers.
@ -7172,26 +7175,36 @@ class V8_EXPORT EmbedderHeapTracer {
* deadline.
*
* Returns true if there is still work to do.
*
* Note: Only one of the AdvanceTracing methods needs to be overriden by the
* embedder.
*/
virtual bool AdvanceTracing(double deadline_in_ms,
AdvanceTracingActions actions) = 0;
V8_DEPRECATE_SOON("Use void AdvanceTracing(deadline_in_ms)",
virtual bool AdvanceTracing(
double deadline_in_ms, AdvanceTracingActions actions)) {
return false;
}
/**
* 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.
*
* Note: Only one of the AdvanceTracing methods needs to be overriden by the
* embedder.
*/
virtual bool AdvanceTracing(double deadline_in_ms);
/*
* Returns true if there no more tracing work to be done (see AdvanceTracing)
* and false otherwise.
*/
virtual bool IsTracingDone() {
// TODO(delphick): When NumberOfWrappersToTrace is removed, this should be
// replaced with: return true;
#if __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
#endif
return NumberOfWrappersToTrace() == 0;
#if __clang__
#pragma clang diagnostic pop
#endif
}
virtual bool IsTracingDone();
/**
* Called at the end of a GC cycle.
@ -7203,8 +7216,13 @@ class V8_EXPORT EmbedderHeapTracer {
/**
* Called upon entering the final marking pause. No more incremental marking
* steps will follow this call.
*
* Note: Only one of the EnterFinalPause methods needs to be overriden by the
* embedder.
*/
virtual void EnterFinalPause() = 0;
V8_DEPRECATE_SOON("Use void EnterFinalPause(EmbedderStackState)",
virtual void EnterFinalPause()) {}
virtual void EnterFinalPause(EmbedderStackState stack_state);
/**
* Called when tracing is aborted.
@ -7215,7 +7233,7 @@ class V8_EXPORT EmbedderHeapTracer {
virtual void AbortTracing() = 0;
/*
* Called by the embedder to request immediaet finalization of the currently
* 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.
*
@ -7225,6 +7243,13 @@ class V8_EXPORT EmbedderHeapTracer {
*/
void FinalizeTracing();
/*
* Called by the embedder to immediately perform a full garbage collection.
*
* Should only be used in testing code.
*/
void GarbageCollectionForTesting(EmbedderStackState stack_state);
/*
* Returns the v8::Isolate this tracer is attached too and |nullptr| if it
* is not attached to any v8::Isolate.
@ -7237,7 +7262,7 @@ class V8_EXPORT EmbedderHeapTracer {
V8_DEPRECATE_SOON("Use IsTracingDone",
virtual size_t NumberOfWrappersToTrace()) {
return 0;
};
}
protected:
v8::Isolate* isolate_ = nullptr;

View File

@ -10789,6 +10789,54 @@ void EmbedderHeapTracer::FinalizeTracing() {
}
}
void EmbedderHeapTracer::GarbageCollectionForTesting(
EmbedderStackState stack_state) {
DCHECK(isolate_);
i::Heap* const heap = reinterpret_cast<i::Isolate*>(isolate_)->heap();
heap->SetEmbedderStackStateForNextFinalizaton(stack_state);
heap->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask,
i::GarbageCollectionReason::kTesting,
kGCCallbackFlagForced);
}
bool EmbedderHeapTracer::AdvanceTracing(double deadline_in_ms) {
#if __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
#endif
return !this->AdvanceTracing(
deadline_in_ms,
AdvanceTracingActions(isinf(deadline_in_ms) ? FORCE_COMPLETION
: DO_NOT_FORCE_COMPLETION));
#if __clang__
#pragma clang diagnostic pop
#endif
}
void EmbedderHeapTracer::EnterFinalPause(EmbedderStackState stack_state) {
#if __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
#endif
this->EnterFinalPause();
#if __clang__
#pragma clang diagnostic pop
#endif
}
bool EmbedderHeapTracer::IsTracingDone() {
// TODO(mlippautz): Implement using "return true" after removing the deprecated
// call.
#if __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
#endif
return NumberOfWrappersToTrace() == 0;
#if __clang__
#pragma clang diagnostic pop
#endif
}
namespace internal {
void HandleScopeImplementer::FreeThreadResources() {

View File

@ -34,15 +34,17 @@ void LocalEmbedderHeapTracer::AbortTracing() {
void LocalEmbedderHeapTracer::EnterFinalPause() {
if (!InUse()) return;
remote_tracer_->EnterFinalPause();
remote_tracer_->EnterFinalPause(embedder_stack_state_);
// Resetting to state unknown as there may be follow up garbage collections
// triggered from callbacks that have a different stack state.
embedder_stack_state_ = EmbedderHeapTracer::kUnknown;
}
bool LocalEmbedderHeapTracer::Trace(
double deadline, EmbedderHeapTracer::AdvanceTracingActions actions) {
if (!InUse()) return false;
bool LocalEmbedderHeapTracer::Trace(double deadline) {
if (!InUse()) return true;
DCHECK_EQ(0, NumberOfCachedWrappersToTrace());
return remote_tracer_->AdvanceTracing(deadline, actions);
return remote_tracer_->AdvanceTracing(deadline);
}
bool LocalEmbedderHeapTracer::IsRemoteTracingDone() {
@ -67,5 +69,12 @@ bool LocalEmbedderHeapTracer::RequiresImmediateWrapperProcessing() {
return cached_wrappers_to_trace_.size() > kTooManyWrappers;
}
void LocalEmbedderHeapTracer::SetEmbedderStackStateForNextFinalization(
EmbedderHeapTracer::EmbedderStackState stack_state) {
if (!InUse()) return;
embedder_stack_state_ = stack_state;
}
} // namespace internal
} // namespace v8

View File

@ -18,10 +18,7 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
public:
typedef std::pair<void*, void*> WrapperInfo;
explicit LocalEmbedderHeapTracer(Isolate* isolate)
: isolate_(isolate),
remote_tracer_(nullptr),
num_v8_marking_worklist_was_empty_(0) {}
explicit LocalEmbedderHeapTracer(Isolate* isolate) : isolate_(isolate) {}
~LocalEmbedderHeapTracer() {
if (remote_tracer_) remote_tracer_->isolate_ = nullptr;
@ -35,14 +32,13 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
remote_tracer_->isolate_ = reinterpret_cast<v8::Isolate*>(isolate_);
}
bool InUse() { return remote_tracer_ != nullptr; }
bool InUse() const { return remote_tracer_ != nullptr; }
void TracePrologue();
void TraceEpilogue();
void AbortTracing();
void EnterFinalPause();
bool Trace(double deadline,
EmbedderHeapTracer::AdvanceTracingActions actions);
bool Trace(double deadline);
bool IsRemoteTracingDone();
size_t NumberOfCachedWrappersToTrace() {
@ -68,13 +64,20 @@ class V8_EXPORT_PRIVATE LocalEmbedderHeapTracer final {
num_v8_marking_worklist_was_empty_ > kMaxIncrementalFixpointRounds;
}
void SetEmbedderStackStateForNextFinalization(
EmbedderHeapTracer::EmbedderStackState stack_state);
private:
typedef std::vector<WrapperInfo> WrapperCache;
Isolate* const isolate_;
EmbedderHeapTracer* remote_tracer_;
WrapperCache cached_wrappers_to_trace_;
size_t num_v8_marking_worklist_was_empty_;
EmbedderHeapTracer* remote_tracer_ = nullptr;
size_t num_v8_marking_worklist_was_empty_ = 0;
EmbedderHeapTracer::EmbedderStackState embedder_stack_state_ =
EmbedderHeapTracer::kUnknown;
friend class EmbedderStackStateScope;
};
} // namespace internal

View File

@ -5968,5 +5968,11 @@ static_assert(MemoryChunk::kFlagsOffset ==
heap_internals::MemoryChunk::kFlagsOffset,
"Flag offset inconsistent");
void Heap::SetEmbedderStackStateForNextFinalizaton(
EmbedderHeapTracer::EmbedderStackState stack_state) {
local_embedder_heap_tracer()->SetEmbedderStackStateForNextFinalization(
stack_state);
}
} // namespace internal
} // namespace v8

View File

@ -1090,6 +1090,8 @@ class Heap {
void SetEmbedderHeapTracer(EmbedderHeapTracer* tracer);
void TracePossibleWrapper(JSObject* js_object);
void RegisterExternallyReferencedObject(Object** object);
void SetEmbedderStackStateForNextFinalizaton(
EmbedderHeapTracer::EmbedderStackState stack_state);
// ===========================================================================
// External string table API. ================================================

View File

@ -936,10 +936,7 @@ double IncrementalMarking::AdvanceIncrementalMarking(
heap_->MonotonicallyIncreasingTimeInMs() + kStepSizeInMs;
if (!heap_->local_embedder_heap_tracer()
->ShouldFinalizeIncrementalMarking()) {
heap_->local_embedder_heap_tracer()->Trace(
wrapper_deadline, EmbedderHeapTracer::AdvanceTracingActions(
EmbedderHeapTracer::ForceCompletionAction::
DO_NOT_FORCE_COMPLETION));
heap_->local_embedder_heap_tracer()->Trace(wrapper_deadline);
}
} else {
Step(step_size_in_bytes, completion_action, step_origin);

View File

@ -1635,8 +1635,7 @@ void MarkCompactCollector::PerformWrapperTracing() {
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_WRAPPER_TRACING);
heap_->local_embedder_heap_tracer()->RegisterWrappersWithRemoteTracer();
heap_->local_embedder_heap_tracer()->Trace(
0, EmbedderHeapTracer::AdvanceTracingActions(
EmbedderHeapTracer::ForceCompletionAction::FORCE_COMPLETION));
std::numeric_limits<double>::infinity());
}
}

View File

@ -61,7 +61,7 @@ class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer {
void TracePrologue() final {}
void TraceEpilogue() final {}
void AbortTracing() final {}
void EnterFinalPause() final {}
void EnterFinalPause(EmbedderStackState) final {}
bool IsRegisteredFromV8(void* first_field) const {
for (auto pair : registered_from_v8_) {
@ -251,6 +251,19 @@ TEST(FinalizeTracingWhenMarking) {
CHECK(marking->IsStopped());
}
TEST(GarbageCollectionForTesting) {
ManualGCScope manual_gc;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
Isolate* i_isolate = CcTest::i_isolate();
TestEmbedderHeapTracer tracer(isolate);
TemporaryEmbedderHeapTracerScope tracer_scope(isolate, &tracer);
int saved_gc_counter = i_isolate->heap()->gc_count();
tracer.GarbageCollectionForTesting(EmbedderHeapTracer::kUnknown);
CHECK_GT(i_isolate->heap()->gc_count(), saved_gc_counter);
}
} // namespace heap
} // namespace internal
} // namespace v8

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "src/heap/embedder-tracing.h"
#include "src/heap/heap.h"
#include "test/unittests/test-utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@ -33,12 +34,11 @@ class MockEmbedderHeapTracer : public EmbedderHeapTracer {
MOCK_METHOD0(TracePrologue, void());
MOCK_METHOD0(TraceEpilogue, void());
MOCK_METHOD0(AbortTracing, void());
MOCK_METHOD0(EnterFinalPause, void());
MOCK_METHOD1(EnterFinalPause, void(EmbedderHeapTracer::EmbedderStackState));
MOCK_METHOD0(IsTracingDone, bool());
MOCK_METHOD1(RegisterV8References,
void(const std::vector<std::pair<void*, void*> >&));
MOCK_METHOD2(AdvanceTracing,
bool(double deadline_in_ms, AdvanceTracingActions actions));
MOCK_METHOD1(AdvanceTracing, bool(double deadline_in_ms));
};
TEST(LocalEmbedderHeapTracer, InUse) {
@ -55,10 +55,8 @@ TEST(LocalEmbedderHeapTracer, NoRemoteTracer) {
EXPECT_FALSE(local_tracer.InUse());
local_tracer.TracePrologue();
local_tracer.EnterFinalPause();
bool more_work = local_tracer.Trace(
0, EmbedderHeapTracer::AdvanceTracingActions(
EmbedderHeapTracer::ForceCompletionAction::FORCE_COMPLETION));
EXPECT_FALSE(more_work);
bool done = local_tracer.Trace(std::numeric_limits<double>::infinity());
EXPECT_TRUE(done);
local_tracer.TraceEpilogue();
}
@ -100,7 +98,38 @@ TEST(LocalEmbedderHeapTracer, EnterFinalPauseForwards) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&remote_tracer);
EXPECT_CALL(remote_tracer, EnterFinalPause());
EXPECT_CALL(remote_tracer, EnterFinalPause(_));
local_tracer.EnterFinalPause();
}
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::kUnknown));
local_tracer.EnterFinalPause();
}
TEST(LocalEmbedderHeapTracer, EnterFinalPauseStackStateIsForwarded) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&remote_tracer);
local_tracer.SetEmbedderStackStateForNextFinalization(
EmbedderHeapTracer::kEmpty);
EXPECT_CALL(remote_tracer, EnterFinalPause(EmbedderHeapTracer::kEmpty));
local_tracer.EnterFinalPause();
}
TEST(LocalEmbedderHeapTracer, EnterFinalPauseStackStateResets) {
StrictMock<MockEmbedderHeapTracer> remote_tracer;
LocalEmbedderHeapTracer local_tracer(nullptr);
local_tracer.SetRemoteTracer(&remote_tracer);
local_tracer.SetEmbedderStackStateForNextFinalization(
EmbedderHeapTracer::kEmpty);
EXPECT_CALL(remote_tracer, EnterFinalPause(EmbedderHeapTracer::kEmpty));
local_tracer.EnterFinalPause();
EXPECT_CALL(remote_tracer, EnterFinalPause(EmbedderHeapTracer::kUnknown));
local_tracer.EnterFinalPause();
}
@ -140,10 +169,8 @@ TEST(LocalEmbedderHeapTracer, TraceFinishes) {
EXPECT_EQ(1u, local_tracer.NumberOfCachedWrappersToTrace());
EXPECT_CALL(remote_tracer, RegisterV8References(_));
local_tracer.RegisterWrappersWithRemoteTracer();
EXPECT_CALL(remote_tracer, AdvanceTracing(0, _)).WillOnce(Return(false));
EXPECT_FALSE(local_tracer.Trace(
0, EmbedderHeapTracer::AdvanceTracingActions(
EmbedderHeapTracer::ForceCompletionAction::FORCE_COMPLETION)));
EXPECT_CALL(remote_tracer, AdvanceTracing(_)).WillOnce(Return(true));
EXPECT_TRUE(local_tracer.Trace(std::numeric_limits<double>::infinity()));
EXPECT_EQ(0u, local_tracer.NumberOfCachedWrappersToTrace());
}
@ -155,10 +182,8 @@ TEST(LocalEmbedderHeapTracer, TraceDoesNotFinish) {
EXPECT_EQ(1u, local_tracer.NumberOfCachedWrappersToTrace());
EXPECT_CALL(remote_tracer, RegisterV8References(_));
local_tracer.RegisterWrappersWithRemoteTracer();
EXPECT_CALL(remote_tracer, AdvanceTracing(0, _)).WillOnce(Return(true));
EXPECT_TRUE(local_tracer.Trace(
0, EmbedderHeapTracer::AdvanceTracingActions(
EmbedderHeapTracer::ForceCompletionAction::FORCE_COMPLETION)));
EXPECT_CALL(remote_tracer, AdvanceTracing(_)).WillOnce(Return(false));
EXPECT_FALSE(local_tracer.Trace(1.0));
EXPECT_EQ(0u, local_tracer.NumberOfCachedWrappersToTrace());
}