[heap] Increase test coverage for embedder tracing
The tests illustrate the use of v8::EmbedderHeapTracer. Bug: v8:7176 Change-Id: Ic383c968691fddb0ec96d66cb33ee42b9c304a75 Reviewed-on: https://chromium-review.googlesource.com/811924 Commit-Queue: Michael Lippautz <mlippautz@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Cr-Commit-Position: refs/heads/master@{#49934}
This commit is contained in:
parent
12afb22458
commit
2abbc50c6e
@ -1976,6 +1976,18 @@ void Factory::NewJSArrayStorage(Handle<JSArray> array,
|
||||
array->set_length(Smi::FromInt(length));
|
||||
}
|
||||
|
||||
Handle<JSWeakMap> Factory::NewJSWeakMap() {
|
||||
Context* native_context = isolate()->raw_native_context();
|
||||
Handle<Map> map(native_context->js_weak_map_fun()->initial_map());
|
||||
Handle<JSWeakMap> weakmap(JSWeakMap::cast(*NewJSObjectFromMap(map)));
|
||||
{
|
||||
// Do not leak handles for the hash table, it would make entries strong.
|
||||
HandleScope scope(isolate());
|
||||
JSWeakCollection::Initialize(weakmap, isolate());
|
||||
}
|
||||
return weakmap;
|
||||
}
|
||||
|
||||
Handle<JSModuleNamespace> Factory::NewJSModuleNamespace() {
|
||||
Handle<Map> map = isolate()->js_module_namespace_map();
|
||||
Handle<JSModuleNamespace> module_namespace(
|
||||
|
@ -36,6 +36,7 @@ class JSMapIterator;
|
||||
class JSModuleNamespace;
|
||||
class JSSet;
|
||||
class JSSetIterator;
|
||||
class JSWeakMap;
|
||||
class NewFunctionArgs;
|
||||
struct SourceRange;
|
||||
class PreParsedScopeData;
|
||||
@ -555,6 +556,8 @@ class V8_EXPORT_PRIVATE Factory final {
|
||||
int capacity,
|
||||
ArrayStorageAllocationMode mode = DONT_INITIALIZE_ARRAY_ELEMENTS);
|
||||
|
||||
Handle<JSWeakMap> NewJSWeakMap();
|
||||
|
||||
Handle<JSGeneratorObject> NewJSGeneratorObject(Handle<JSFunction> function);
|
||||
|
||||
Handle<JSModuleNamespace> NewJSModuleNamespace();
|
||||
|
@ -103,6 +103,7 @@ v8_source_set("cctest_sources") {
|
||||
"heap/test-array-buffer-tracker.cc",
|
||||
"heap/test-compaction.cc",
|
||||
"heap/test-concurrent-marking.cc",
|
||||
"heap/test-embedder-tracing.cc",
|
||||
"heap/test-heap.cc",
|
||||
"heap/test-incremental-marking.cc",
|
||||
"heap/test-invalidated-slots.cc",
|
||||
|
@ -93,6 +93,7 @@
|
||||
'heap/test-array-buffer-tracker.cc',
|
||||
'heap/test-compaction.cc',
|
||||
'heap/test-concurrent-marking.cc',
|
||||
'heap/test-embedder-tracing.cc',
|
||||
'heap/test-heap.cc',
|
||||
'heap/test-incremental-marking.cc',
|
||||
'heap/test-invalidated-slots.cc',
|
||||
|
@ -88,6 +88,9 @@
|
||||
# BUG(2340). Preprocessing stack traces is disabled at the moment.
|
||||
'test-heap/PreprocessStackTrace': [FAIL],
|
||||
|
||||
# BUG(7176). Embedder heap tracer doesn't support finalizers.
|
||||
'test-embedder-tracing/TracingInRevivedSubgraph': [FAIL],
|
||||
|
||||
# BUG(4333). Function name inferrer does not work for ES6 clases.
|
||||
'test-func-name-inference/UpperCaseClass': [FAIL],
|
||||
'test-func-name-inference/LowerCaseClass': [FAIL],
|
||||
|
197
test/cctest/heap/test-embedder-tracing.cc
Normal file
197
test/cctest/heap/test-embedder-tracing.cc
Normal file
@ -0,0 +1,197 @@
|
||||
// Copyright 2017 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 "include/v8.h"
|
||||
#include "src/api.h"
|
||||
#include "src/objects-inl.h"
|
||||
#include "src/objects/module.h"
|
||||
#include "src/objects/script.h"
|
||||
#include "src/objects/shared-function-info.h"
|
||||
#include "test/cctest/cctest.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace heap {
|
||||
|
||||
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);
|
||||
CHECK(!instance.IsEmpty());
|
||||
i::Handle<i::JSReceiver> js_obj = v8::Utils::OpenHandle(*instance);
|
||||
CHECK_EQ(i::JS_API_OBJECT_TYPE, js_obj->map()->instance_type());
|
||||
return scope.Escape(instance);
|
||||
}
|
||||
|
||||
class TestEmbedderHeapTracer final : public v8::EmbedderHeapTracer {
|
||||
public:
|
||||
explicit TestEmbedderHeapTracer(v8::Isolate* isolate) : isolate_(isolate) {}
|
||||
|
||||
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::Persistent<v8::Object>* persistent) {
|
||||
to_register_with_v8_.push_back(persistent);
|
||||
}
|
||||
|
||||
bool AdvanceTracing(double deadline_in_ms,
|
||||
AdvanceTracingActions actions) final {
|
||||
for (auto persistent : to_register_with_v8_) {
|
||||
persistent->RegisterExternalReference(isolate_);
|
||||
}
|
||||
to_register_with_v8_.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
void TracePrologue() final {}
|
||||
void TraceEpilogue() final {}
|
||||
void AbortTracing() final {}
|
||||
void EnterFinalPause() final {}
|
||||
|
||||
bool IsRegisteredFromV8(void* first_field) const {
|
||||
for (auto pair : registered_from_v8_) {
|
||||
if (pair.first == first_field) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
v8::Isolate* const isolate_;
|
||||
std::vector<std::pair<void*, void*>> registered_from_v8_;
|
||||
std::vector<v8::Persistent<v8::Object>*> to_register_with_v8_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(V8RegisteringEmbedderReference) {
|
||||
// Tests that wrappers are properly registered with the embedder heap
|
||||
// tracer.
|
||||
ManualGCScope manual_gc;
|
||||
CcTest::InitializeVM();
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
TestEmbedderHeapTracer tracer(isolate);
|
||||
isolate->SetEmbedderHeapTracer(&tracer);
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
void* first_field = reinterpret_cast<void*>(0x2);
|
||||
v8::Local<v8::Object> api_object =
|
||||
ConstructTraceableJSApiObject(context, first_field, nullptr);
|
||||
CHECK(!api_object.IsEmpty());
|
||||
CcTest::CollectGarbage(i::OLD_SPACE);
|
||||
CHECK(tracer.IsRegisteredFromV8(first_field));
|
||||
}
|
||||
|
||||
TEST(EmbedderRegisteringV8Reference) {
|
||||
// Tests that references that are registered by the embedder heap tracer are
|
||||
// considered live by V8.
|
||||
ManualGCScope manual_gc;
|
||||
CcTest::InitializeVM();
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
TestEmbedderHeapTracer tracer(isolate);
|
||||
isolate->SetEmbedderHeapTracer(&tracer);
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
v8::Persistent<v8::Object> g;
|
||||
{
|
||||
v8::HandleScope inner_scope(isolate);
|
||||
v8::Local<v8::Object> o =
|
||||
v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
|
||||
g.Reset(isolate, o);
|
||||
g.SetWeak();
|
||||
}
|
||||
tracer.AddReferenceForTracing(&g);
|
||||
CcTest::CollectGarbage(i::OLD_SPACE);
|
||||
CHECK(!g.IsEmpty());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void ResurrectingFinalizer(
|
||||
const v8::WeakCallbackInfo<v8::Global<v8::Object>>& data) {
|
||||
data.GetParameter()->ClearWeak();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(TracingInRevivedSubgraph) {
|
||||
// Tests that wrappers are traced when they are contained with in a subgraph
|
||||
// that is revived by a finalizer.
|
||||
ManualGCScope manual_gc;
|
||||
CcTest::InitializeVM();
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
TestEmbedderHeapTracer tracer(isolate);
|
||||
isolate->SetEmbedderHeapTracer(&tracer);
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
v8::Global<v8::Object> g;
|
||||
void* first_field = reinterpret_cast<void*>(0x4);
|
||||
{
|
||||
v8::HandleScope inner_scope(isolate);
|
||||
v8::Local<v8::Object> api_object =
|
||||
ConstructTraceableJSApiObject(context, first_field, nullptr);
|
||||
CHECK(!api_object.IsEmpty());
|
||||
v8::Local<v8::Object> o =
|
||||
v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
|
||||
o->Set(context, v8_str("link"), api_object).FromJust();
|
||||
g.Reset(isolate, o);
|
||||
g.SetWeak(&g, ResurrectingFinalizer, v8::WeakCallbackType::kFinalizer);
|
||||
}
|
||||
CcTest::CollectGarbage(i::OLD_SPACE);
|
||||
CHECK(tracer.IsRegisteredFromV8(first_field));
|
||||
}
|
||||
|
||||
TEST(TracingInEphemerons) {
|
||||
// Tests that wrappers that are part of ephemerons are traced.
|
||||
ManualGCScope manual_gc;
|
||||
CcTest::InitializeVM();
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
TestEmbedderHeapTracer tracer(isolate);
|
||||
isolate->SetEmbedderHeapTracer(&tracer);
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
v8::Local<v8::Object> key =
|
||||
v8::Local<v8::Object>::New(isolate, v8::Object::New(isolate));
|
||||
void* first_field = reinterpret_cast<void*>(0x8);
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
Handle<JSWeakMap> weak_map = i_isolate->factory()->NewJSWeakMap();
|
||||
{
|
||||
v8::HandleScope inner_scope(isolate);
|
||||
v8::Local<v8::Object> api_object =
|
||||
ConstructTraceableJSApiObject(context, first_field, nullptr);
|
||||
CHECK(!api_object.IsEmpty());
|
||||
Handle<JSObject> js_key =
|
||||
handle(JSObject::cast(*v8::Utils::OpenHandle(*key)));
|
||||
Handle<JSReceiver> js_api_object = v8::Utils::OpenHandle(*api_object);
|
||||
int32_t hash = js_key->GetOrCreateHash(i_isolate)->value();
|
||||
JSWeakCollection::Set(weak_map, js_key, js_api_object, hash);
|
||||
}
|
||||
CcTest::CollectGarbage(i::OLD_SPACE);
|
||||
CHECK(tracer.IsRegisteredFromV8(first_field));
|
||||
}
|
||||
|
||||
} // namespace heap
|
||||
} // namespace internal
|
||||
} // namespace v8
|
@ -42,21 +42,6 @@ static Isolate* GetIsolateFrom(LocalContext* context) {
|
||||
return reinterpret_cast<Isolate*>((*context)->GetIsolate());
|
||||
}
|
||||
|
||||
|
||||
static Handle<JSWeakMap> AllocateJSWeakMap(Isolate* isolate) {
|
||||
Handle<Map> map =
|
||||
isolate->factory()->NewMap(JS_WEAK_MAP_TYPE, JSWeakMap::kSize);
|
||||
Handle<JSObject> weakmap_obj = isolate->factory()->NewJSObjectFromMap(map);
|
||||
Handle<JSWeakMap> weakmap(JSWeakMap::cast(*weakmap_obj));
|
||||
// Do not leak handles for the hash table, it would make entries strong.
|
||||
{
|
||||
HandleScope scope(isolate);
|
||||
Handle<ObjectHashTable> table = ObjectHashTable::New(isolate, 1);
|
||||
weakmap->set_table(*table);
|
||||
}
|
||||
return weakmap;
|
||||
}
|
||||
|
||||
static int NumberOfWeakCalls = 0;
|
||||
static void WeakPointerCallback(const v8::WeakCallbackInfo<void>& data) {
|
||||
std::pair<v8::Persistent<v8::Value>*, int>* p =
|
||||
@ -74,7 +59,7 @@ TEST(Weakness) {
|
||||
Isolate* isolate = GetIsolateFrom(&context);
|
||||
Factory* factory = isolate->factory();
|
||||
HandleScope scope(isolate);
|
||||
Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
|
||||
Handle<JSWeakMap> weakmap = isolate->factory()->NewJSWeakMap();
|
||||
GlobalHandles* global_handles = isolate->global_handles();
|
||||
|
||||
// Keep global reference to the key.
|
||||
@ -127,7 +112,7 @@ TEST(Shrinking) {
|
||||
Isolate* isolate = GetIsolateFrom(&context);
|
||||
Factory* factory = isolate->factory();
|
||||
HandleScope scope(isolate);
|
||||
Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
|
||||
Handle<JSWeakMap> weakmap = isolate->factory()->NewJSWeakMap();
|
||||
|
||||
// Check initial capacity.
|
||||
CHECK_EQ(32, ObjectHashTable::cast(weakmap->table())->Capacity());
|
||||
@ -174,7 +159,7 @@ TEST(Regress2060a) {
|
||||
Handle<JSFunction> function =
|
||||
factory->NewFunctionForTest(factory->function_string());
|
||||
Handle<JSObject> key = factory->NewJSObject(function);
|
||||
Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
|
||||
Handle<JSWeakMap> weakmap = isolate->factory()->NewJSWeakMap();
|
||||
|
||||
// Start second old-space page so that values land on evacuation candidate.
|
||||
Page* first_page = heap->old_space()->anchor()->next_page();
|
||||
@ -226,7 +211,7 @@ TEST(Regress2060b) {
|
||||
CHECK(!heap->InNewSpace(*keys[i]));
|
||||
CHECK(!first_page->Contains(keys[i]->address()));
|
||||
}
|
||||
Handle<JSWeakMap> weakmap = AllocateJSWeakMap(isolate);
|
||||
Handle<JSWeakMap> weakmap = isolate->factory()->NewJSWeakMap();
|
||||
for (int i = 0; i < 32; i++) {
|
||||
Handle<Smi> smi(Smi::FromInt(i), isolate);
|
||||
int32_t hash = keys[i]->GetOrCreateHash(isolate)->value();
|
||||
@ -250,7 +235,7 @@ TEST(Regress399527) {
|
||||
Heap* heap = isolate->heap();
|
||||
{
|
||||
HandleScope scope(isolate);
|
||||
AllocateJSWeakMap(isolate);
|
||||
isolate->factory()->NewJSWeakMap();
|
||||
heap::SimulateIncrementalMarking(heap);
|
||||
}
|
||||
// The weak map is marked black here but leaving the handle scope will make
|
||||
|
Loading…
Reference in New Issue
Block a user