f50c64bdfe
Adds NameProvider to allow specifying names of objects. The corresponding internal NameTrait is registered with the GCInfo object. Use name infrastructure to provide a hint on encountering an unmarked object in the marking verifier. Bug: chromium:1056170 Change-Id: I95bb290660f5905500f861bd5cc85148a1b47184 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2454087 Commit-Queue: Michael Lippautz <mlippautz@chromium.org> Reviewed-by: Anton Bikineev <bikineev@chromium.org> Reviewed-by: Omer Katz <omerkatz@chromium.org> Cr-Commit-Position: refs/heads/master@{#70400}
166 lines
4.9 KiB
C++
166 lines
4.9 KiB
C++
// Copyright 2020 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/cppgc/internal/gc-info.h"
|
|
|
|
#include "include/cppgc/platform.h"
|
|
#include "src/base/page-allocator.h"
|
|
#include "src/base/platform/platform.h"
|
|
#include "src/heap/cppgc/gc-info-table.h"
|
|
#include "test/unittests/heap/cppgc/tests.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace cppgc {
|
|
namespace internal {
|
|
|
|
namespace {
|
|
|
|
constexpr GCInfo GetEmptyGCInfo() { return {nullptr, nullptr, nullptr, false}; }
|
|
|
|
} // namespace
|
|
|
|
TEST(GCInfoTableTest, InitialEmpty) {
|
|
v8::base::PageAllocator page_allocator;
|
|
GCInfoTable table(&page_allocator);
|
|
EXPECT_EQ(GCInfoTable::kMinIndex, table.NumberOfGCInfosForTesting());
|
|
}
|
|
|
|
TEST(GCInfoTableTest, ResizeToMaxIndex) {
|
|
v8::base::PageAllocator page_allocator;
|
|
GCInfoTable table(&page_allocator);
|
|
GCInfo info = GetEmptyGCInfo();
|
|
for (GCInfoIndex i = GCInfoTable::kMinIndex; i < GCInfoTable::kMaxIndex;
|
|
i++) {
|
|
GCInfoIndex index = table.RegisterNewGCInfo(info);
|
|
EXPECT_EQ(i, index);
|
|
}
|
|
}
|
|
|
|
TEST(GCInfoTableDeathTest, MoreThanMaxIndexInfos) {
|
|
v8::base::PageAllocator page_allocator;
|
|
GCInfoTable table(&page_allocator);
|
|
GCInfo info = GetEmptyGCInfo();
|
|
// Create GCInfoTable::kMaxIndex entries.
|
|
for (GCInfoIndex i = GCInfoTable::kMinIndex; i < GCInfoTable::kMaxIndex;
|
|
i++) {
|
|
table.RegisterNewGCInfo(info);
|
|
}
|
|
EXPECT_DEATH_IF_SUPPORTED(table.RegisterNewGCInfo(info), "");
|
|
}
|
|
|
|
TEST(GCInfoTableDeathTest, OldTableAreaIsReadOnly) {
|
|
v8::base::PageAllocator page_allocator;
|
|
GCInfoTable table(&page_allocator);
|
|
GCInfo info = GetEmptyGCInfo();
|
|
// Use up all slots until limit.
|
|
GCInfoIndex limit = table.LimitForTesting();
|
|
// Bail out if initial limit is already the maximum because of large committed
|
|
// pages. In this case, nothing can be comitted as read-only.
|
|
if (limit == GCInfoTable::kMaxIndex) {
|
|
return;
|
|
}
|
|
for (GCInfoIndex i = GCInfoTable::kMinIndex; i < limit; i++) {
|
|
table.RegisterNewGCInfo(info);
|
|
}
|
|
EXPECT_EQ(limit, table.LimitForTesting());
|
|
table.RegisterNewGCInfo(info);
|
|
EXPECT_NE(limit, table.LimitForTesting());
|
|
// Old area is now read-only.
|
|
auto& first_slot = table.TableSlotForTesting(GCInfoTable::kMinIndex);
|
|
EXPECT_DEATH_IF_SUPPORTED(first_slot.finalize = nullptr, "");
|
|
}
|
|
|
|
namespace {
|
|
|
|
class ThreadRegisteringGCInfoObjects final : public v8::base::Thread {
|
|
public:
|
|
ThreadRegisteringGCInfoObjects(GCInfoTable* table,
|
|
GCInfoIndex num_registrations)
|
|
: v8::base::Thread(Options("Thread registering GCInfo objects.")),
|
|
table_(table),
|
|
num_registrations_(num_registrations) {}
|
|
|
|
void Run() final {
|
|
GCInfo info = GetEmptyGCInfo();
|
|
for (GCInfoIndex i = 0; i < num_registrations_; i++) {
|
|
table_->RegisterNewGCInfo(info);
|
|
}
|
|
}
|
|
|
|
private:
|
|
GCInfoTable* table_;
|
|
GCInfoIndex num_registrations_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST(GCInfoTableTest, MultiThreadedResizeToMaxIndex) {
|
|
constexpr size_t num_threads = 4;
|
|
constexpr size_t main_thread_initialized = 2;
|
|
constexpr size_t gc_infos_to_register =
|
|
(GCInfoTable::kMaxIndex - 1) -
|
|
(GCInfoTable::kMinIndex + main_thread_initialized);
|
|
static_assert(gc_infos_to_register % num_threads == 0,
|
|
"must sum up to kMaxIndex");
|
|
constexpr size_t gc_infos_per_thread = gc_infos_to_register / num_threads;
|
|
|
|
v8::base::PageAllocator page_allocator;
|
|
GCInfoTable table(&page_allocator);
|
|
GCInfo info = GetEmptyGCInfo();
|
|
for (size_t i = 0; i < main_thread_initialized; i++) {
|
|
table.RegisterNewGCInfo(info);
|
|
}
|
|
|
|
v8::base::Thread* threads[num_threads];
|
|
for (size_t i = 0; i < num_threads; i++) {
|
|
threads[i] =
|
|
new ThreadRegisteringGCInfoObjects(&table, gc_infos_per_thread);
|
|
}
|
|
for (size_t i = 0; i < num_threads; i++) {
|
|
CHECK(threads[i]->Start());
|
|
}
|
|
for (size_t i = 0; i < num_threads; i++) {
|
|
threads[i]->Join();
|
|
delete threads[i];
|
|
}
|
|
}
|
|
|
|
// Tests using the global table and GCInfoTrait.
|
|
|
|
namespace {
|
|
|
|
class GCInfoTraitTest : public testing::TestWithPlatform {};
|
|
|
|
class BasicType final {
|
|
public:
|
|
void Trace(Visitor*) const {}
|
|
};
|
|
class OtherBasicType final {
|
|
public:
|
|
void Trace(Visitor*) const {}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
TEST_F(GCInfoTraitTest, IndexInBounds) {
|
|
const GCInfoIndex index = GCInfoTrait<BasicType>::Index();
|
|
EXPECT_GT(GCInfoTable::kMaxIndex, index);
|
|
EXPECT_LE(GCInfoTable::kMinIndex, index);
|
|
}
|
|
|
|
TEST_F(GCInfoTraitTest, TraitReturnsSameIndexForSameType) {
|
|
const GCInfoIndex index1 = GCInfoTrait<BasicType>::Index();
|
|
const GCInfoIndex index2 = GCInfoTrait<BasicType>::Index();
|
|
EXPECT_EQ(index1, index2);
|
|
}
|
|
|
|
TEST_F(GCInfoTraitTest, TraitReturnsDifferentIndexForDifferentTypes) {
|
|
const GCInfoIndex index1 = GCInfoTrait<BasicType>::Index();
|
|
const GCInfoIndex index2 = GCInfoTrait<OtherBasicType>::Index();
|
|
EXPECT_NE(index1, index2);
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace cppgc
|