node/deps/v8/test/unittests/heap/cppgc/age-table-unittest.cc
Michaël Zasso cfbf9e0d14 deps: update V8 to 12.4.254.14
PR-URL: https://github.com/nodejs/node/pull/52465
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Michael Dawson <midawson@redhat.com>
2024-04-22 21:25:15 +00:00

227 lines
9.2 KiB
C++

// Copyright 2022 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 <memory>
#include <vector>
#include "include/cppgc/internal/caged-heap-local-data.h"
#include "include/cppgc/internal/caged-heap.h"
#include "src/base/logging.h"
#include "src/heap/cppgc/heap-page.h"
#include "test/unittests/heap/cppgc/tests.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cppgc::internal {
namespace {
class AgeTableTest : public testing::TestSupportingAllocationOnly {
public:
using Age = AgeTable::Age;
using AdjacentCardsPolicy = AgeTable::AdjacentCardsPolicy;
static constexpr auto kCardSizeInBytes = AgeTable::kCardSizeInBytes;
AgeTableTest() : age_table_(CagedHeapLocalData::Get().age_table) {
CagedHeap::CommitAgeTable(*(GetPlatform().GetPageAllocator()));
}
~AgeTableTest() override { age_table_.ResetForTesting(); }
NormalPage* AllocateNormalPage() {
RawHeap& heap = Heap::From(GetHeap())->raw_heap();
auto* space = static_cast<NormalPageSpace*>(
heap.Space(RawHeap::RegularSpaceType::kNormal1));
auto* page =
NormalPage::TryCreate(*Heap::From(GetHeap())->page_backend(), *space);
CHECK_NOT_NULL(page);
allocated_pages_.push_back({page, DestroyPage});
return page;
}
LargePage* AllocateLargePage() {
constexpr size_t kObjectSize = 2 * kLargeObjectSizeThreshold;
RawHeap& heap = Heap::From(GetHeap())->raw_heap();
auto* space = static_cast<LargePageSpace*>(
heap.Space(RawHeap::RegularSpaceType::kLarge));
auto* page = LargePage::TryCreate(*Heap::From(GetHeap())->page_backend(),
*space, kObjectSize);
CHECK_NOT_NULL(page);
allocated_pages_.push_back({page, DestroyPage});
return page;
}
void SetAgeForAddressRange(void* begin, void* end, Age age,
AdjacentCardsPolicy adjacent_cards_policy) {
age_table_.SetAgeForRange(CagedHeap::OffsetFromAddress(begin),
CagedHeap::OffsetFromAddress(end), age,
adjacent_cards_policy);
}
Age GetAge(void* ptr) const {
return age_table_.GetAge(CagedHeap::OffsetFromAddress(ptr));
}
void SetAge(void* ptr, Age age) {
age_table_.SetAge(CagedHeap::OffsetFromAddress(ptr), age);
}
void AssertAgeForAddressRange(void* begin, void* end, Age age) {
const uintptr_t offset_begin = CagedHeap::OffsetFromAddress(begin);
const uintptr_t offset_end = CagedHeap::OffsetFromAddress(end);
for (auto offset = RoundDown(offset_begin, kCardSizeInBytes);
offset < RoundUp(offset_end, kCardSizeInBytes);
offset += kCardSizeInBytes)
EXPECT_EQ(age, age_table_.GetAge(offset));
}
private:
static void DestroyPage(BasePage* page) {
BasePage::Destroy(page, FreeMemoryHandling::kDoNotDiscard);
}
std::vector<std::unique_ptr<BasePage, void (*)(BasePage*)>> allocated_pages_;
AgeTable& age_table_;
};
} // namespace
TEST_F(AgeTableTest, SetAgeForNormalPage) {
auto* page = AllocateNormalPage();
// By default, everything is old.
AssertAgeForAddressRange(page->PayloadStart(), page->PayloadEnd(), Age::kOld);
// Set age for the entire page.
SetAgeForAddressRange(page->PayloadStart(), page->PayloadEnd(), Age::kYoung,
AdjacentCardsPolicy::kIgnore);
// Check that all cards have been set as young.
AssertAgeForAddressRange(page->PayloadStart(), page->PayloadEnd(),
Age::kYoung);
}
TEST_F(AgeTableTest, SetAgeForLargePage) {
auto* page = AllocateLargePage();
// By default, everything is old.
AssertAgeForAddressRange(page->PayloadStart(), page->PayloadEnd(), Age::kOld);
// Set age for the entire page.
SetAgeForAddressRange(page->PayloadStart(), page->PayloadEnd(), Age::kYoung,
AdjacentCardsPolicy::kIgnore);
// Check that all cards have been set as young.
AssertAgeForAddressRange(page->PayloadStart(), page->PayloadEnd(),
Age::kYoung);
}
TEST_F(AgeTableTest, SetAgeForSingleCardWithUnalignedAddresses) {
auto* page = AllocateNormalPage();
Address object_begin = reinterpret_cast<Address>(
RoundUp(reinterpret_cast<uintptr_t>(page->PayloadStart()),
kCardSizeInBytes) +
1);
Address object_end = object_begin + kCardSizeInBytes / 2;
EXPECT_EQ(Age::kOld, GetAge(object_begin));
// Try mark the card as young. This will mark the card as kMixed, since the
// card was previously marked as old.
SetAgeForAddressRange(object_begin, object_end, Age::kYoung,
AdjacentCardsPolicy::kConsider);
EXPECT_EQ(Age::kMixed, GetAge(object_begin));
SetAge(object_begin, Age::kOld);
// Try mark as old, but ignore ages of outer cards.
SetAgeForAddressRange(object_begin, object_end, Age::kYoung,
AdjacentCardsPolicy::kIgnore);
EXPECT_EQ(Age::kYoung, GetAge(object_begin));
}
TEST_F(AgeTableTest, SetAgeForSingleCardWithAlignedAddresses) {
auto* page = AllocateNormalPage();
Address object_begin = reinterpret_cast<Address>(RoundUp(
reinterpret_cast<uintptr_t>(page->PayloadStart()), kCardSizeInBytes));
Address object_end = object_begin + kCardSizeInBytes;
EXPECT_EQ(Age::kOld, GetAge(object_begin));
EXPECT_EQ(Age::kOld, GetAge(object_end));
// Try mark the card as young. This will mark the entire card as kYoung, since
// it's aligned.
SetAgeForAddressRange(object_begin, object_end, Age::kYoung,
AdjacentCardsPolicy::kConsider);
EXPECT_EQ(Age::kYoung, GetAge(object_begin));
// The end card should not be touched.
EXPECT_EQ(Age::kOld, GetAge(object_end));
}
TEST_F(AgeTableTest, SetAgeForSingleCardWithAlignedBeginButUnalignedEnd) {
auto* page = AllocateNormalPage();
Address object_begin = reinterpret_cast<Address>(RoundUp(
reinterpret_cast<uintptr_t>(page->PayloadStart()), kCardSizeInBytes));
Address object_end = object_begin + kCardSizeInBytes + 1;
EXPECT_EQ(Age::kOld, GetAge(object_begin));
EXPECT_EQ(Age::kOld, GetAge(object_end));
// Try mark the card as young. This will mark the entire card as kYoung, since
// it's aligned.
SetAgeForAddressRange(object_begin, object_end, Age::kYoung,
AdjacentCardsPolicy::kConsider);
EXPECT_EQ(Age::kYoung, GetAge(object_begin));
// The end card should be marked as mixed.
EXPECT_EQ(Age::kMixed, GetAge(object_end));
}
TEST_F(AgeTableTest, SetAgeForMultipleCardsWithUnalignedAddresses) {
static constexpr size_t kNumberOfCards = 4;
auto* page = AllocateNormalPage();
Address object_begin = reinterpret_cast<Address>(
RoundUp(reinterpret_cast<uintptr_t>(page->PayloadStart()),
kCardSizeInBytes) +
kCardSizeInBytes / 2);
Address object_end = object_begin + kNumberOfCards * kCardSizeInBytes;
AssertAgeForAddressRange(object_begin, object_end, Age::kOld);
// Try mark the cards as young. The inner 2 cards must be marked as young, the
// outer cards will be marked as mixed.
SetAgeForAddressRange(object_begin, object_end, Age::kYoung,
AdjacentCardsPolicy::kConsider);
EXPECT_EQ(Age::kMixed, GetAge(object_begin));
EXPECT_EQ(Age::kYoung, GetAge(object_begin + kCardSizeInBytes));
EXPECT_EQ(Age::kYoung, GetAge(object_begin + 2 * kCardSizeInBytes));
EXPECT_EQ(Age::kMixed, GetAge(object_end));
}
TEST_F(AgeTableTest, SetAgeForMultipleCardsConsiderAdjacentCards) {
static constexpr size_t kNumberOfCards = 4;
auto* page = AllocateNormalPage();
Address object_begin = reinterpret_cast<Address>(
RoundUp(reinterpret_cast<uintptr_t>(page->PayloadStart()),
kCardSizeInBytes) +
kCardSizeInBytes / 2);
Address object_end = object_begin + kNumberOfCards * kCardSizeInBytes;
// Mark the first and the last card as young.
SetAge(object_begin, Age::kYoung);
SetAge(object_end, Age::kYoung);
// Mark all the cards as young. The inner 2 cards must be marked as young, the
// outer cards will also be marked as young.
SetAgeForAddressRange(object_begin, object_end, Age::kYoung,
AdjacentCardsPolicy::kConsider);
EXPECT_EQ(Age::kYoung, GetAge(object_begin));
EXPECT_EQ(Age::kYoung, GetAge(object_begin + kCardSizeInBytes));
EXPECT_EQ(Age::kYoung, GetAge(object_begin + 2 * kCardSizeInBytes));
EXPECT_EQ(Age::kYoung, GetAge(object_end));
}
TEST_F(AgeTableTest, MarkAllCardsAsYoung) {
uint8_t* heap_start = reinterpret_cast<uint8_t*>(CagedHeapBase::GetBase());
void* heap_end =
heap_start + api_constants::kCagedHeapDefaultReservationSize - 1;
AssertAgeForAddressRange(heap_start, heap_end, Age::kOld);
SetAgeForAddressRange(heap_start, heap_end, Age::kYoung,
AdjacentCardsPolicy::kIgnore);
AssertAgeForAddressRange(heap_start, heap_end, Age::kYoung);
}
TEST_F(AgeTableTest, AgeTableSize) {
// The default cage size should yield a 1MB table.
EXPECT_EQ(1 * kMB, CagedHeapBase::GetAgeTableSize());
// Pretend there's a larger cage and verify that the age table reserves the
// correct amount of space for itself.
size_t age_table_size = AgeTable::CalculateAgeTableSizeForHeapSize(
api_constants::kCagedHeapDefaultReservationSize * 4);
EXPECT_EQ(4 * kMB, age_table_size);
}
} // namespace cppgc::internal