8371341: ZGC: Improve gtest interoperability with instrumented builds (ASAN)

Reviewed-by: stefank, eosterlund
This commit is contained in:
Axel Boldt-Christmas 2025-11-10 05:55:34 +00:00
parent a8b35bf5a6
commit 4e4cced710
5 changed files with 101 additions and 66 deletions

View File

@ -33,6 +33,7 @@
using ZVirtualMemoryRegistry = ZRangeRegistry<ZVirtualMemory>;
class ZVirtualMemoryReserver {
friend class ZTest;
friend class ZMapperTest;
friend class ZVirtualMemoryManagerTest;

View File

@ -28,9 +28,10 @@
#include "gc/z/zGlobals.hpp"
#include "gc/z/zHeap.hpp"
#include "gc/z/zPage.inline.hpp"
#include "gc/z/zRangeRegistry.inline.hpp"
#include "gc/z/zVirtualMemory.inline.hpp"
#include "runtime/os.hpp"
#include "unittest.hpp"
#include "zunittest.hpp"
using namespace testing;
@ -40,35 +41,21 @@ using namespace testing;
#define CAPTURE(expression) CAPTURE1(expression)
class ZForwardingTest : public Test {
class ZForwardingTest : public ZTest {
public:
// Setup and tear down
ZHeap* _old_heap;
ZGenerationOld* _old_old;
ZGenerationYoung* _old_young;
char* _reserved;
static size_t _page_offset;
char* reserve_page_memory() {
// Probe for a free 2MB region inside the usable address range.
// Inspired by ZVirtualMemoryManager::reserve_contiguous.
const size_t unused = ZAddressOffsetMax - ZGranuleSize;
const size_t increment = MAX2(align_up(unused / 100, ZGranuleSize), ZGranuleSize);
for (uintptr_t start = 0; start + ZGranuleSize <= ZAddressOffsetMax; start += increment) {
char* const reserved = os::attempt_reserve_memory_at((char*)ZAddressHeapBase + start, ZGranuleSize, mtTest);
if (reserved != nullptr) {
// Success
return reserved;
}
}
// Failed
return nullptr;
}
ZAddressReserver _zaddress_reserver;
zoffset _page_offset;
virtual void SetUp() {
ZGlobalsPointers::initialize();
// Only run test on supported Windows versions
if (!is_os_supported()) {
GTEST_SKIP() << "OS not supported";
}
_old_heap = ZHeap::_heap;
ZHeap::_heap = (ZHeap*)os::malloc(sizeof(ZHeap), mtTest);
@ -84,36 +71,34 @@ public:
ZGeneration::_old->_seqnum = 1;
ZGeneration::_young->_seqnum = 2;
// Preconditions for reserve_free_granule()
ASSERT_NE(ZAddressHeapBase, 0u);
ASSERT_NE(ZAddressOffsetMax, 0u);
ASSERT_NE(ZGranuleSize, 0u);
_zaddress_reserver.SetUp(ZGranuleSize);
_page_offset = _zaddress_reserver.registry()->peek_low_address();
_reserved = nullptr;
if (_page_offset == zoffset::invalid) {
GTEST_SKIP() << "Unable to reserve memory";
}
// Find a suitable address for the testing page
char* reserved = reserve_page_memory();
ASSERT_NE(reserved, nullptr) << "Failed to reserve the page granule. Test needs tweaking";
ASSERT_GE(reserved, (char*)ZAddressHeapBase);
ASSERT_LT(reserved, (char*)ZAddressHeapBase + ZAddressOffsetMax);
_reserved = reserved;
os::commit_memory((char*)_reserved, ZGranuleSize, false /* executable */);
_page_offset = uintptr_t(_reserved) - ZAddressHeapBase;
char* const addr = (char*)untype(ZOffset::address_unsafe(_page_offset));
os::commit_memory(addr, ZGranuleSize, /* executable */ false);
}
virtual void TearDown() {
if (!is_os_supported()) {
// Test skipped, nothing to cleanup
return;
}
os::free(ZHeap::_heap);
ZHeap::_heap = _old_heap;
ZGeneration::_old = _old_old;
ZGeneration::_young = _old_young;
if (_reserved != nullptr) {
os::uncommit_memory((char*)_reserved, ZGranuleSize, false /* executable */);
os::release_memory((char*)_reserved, ZGranuleSize);
if (_page_offset != zoffset::invalid) {
char* const addr = (char*)untype(ZOffset::address_unsafe(_page_offset));
os::uncommit_memory(addr, ZGranuleSize, false /* executable */);
}
_zaddress_reserver.TearDown();
}
// Helper functions
@ -219,7 +204,7 @@ public:
}
}
static void test(void (*function)(ZForwarding*), uint32_t size) {
void test(void (*function)(ZForwarding*), uint32_t size) {
// Create page
const ZVirtualMemory vmem(zoffset(_page_offset), ZPageSizeSmall);
ZPage page(ZPageType::small, ZPageAge::eden, vmem, 0u);
@ -257,7 +242,7 @@ public:
}
// Run the given function with a few different input values.
static void test(void (*function)(ZForwarding*)) {
void test(void (*function)(ZForwarding*)) {
test(function, 1);
test(function, 2);
test(function, 3);
@ -285,5 +270,3 @@ TEST_VM_F(ZForwardingTest, find_full) {
TEST_VM_F(ZForwardingTest, find_every_other) {
test(&ZForwardingTest::find_every_other);
}
size_t ZForwardingTest::_page_offset;

View File

@ -34,8 +34,9 @@ using namespace testing;
class ZMapperTest : public ZTest {
private:
static constexpr size_t ReservationSize = 32 * M;
static constexpr size_t ReservationSize = 3 * ZGranuleSize;
ZAddressReserver _zaddress_reserver;
ZVirtualMemoryReserver* _reserver;
ZVirtualMemoryRegistry* _registry;
@ -46,9 +47,14 @@ public:
GTEST_SKIP() << "OS not supported";
}
_reserver = (ZVirtualMemoryReserver*)os::malloc(sizeof(ZVirtualMemoryManager), mtTest);
_reserver = ::new (_reserver) ZVirtualMemoryReserver(ReservationSize);
_registry = &_reserver->_registry;
_zaddress_reserver.SetUp(ReservationSize);
_reserver = _zaddress_reserver.reserver();
_registry = _zaddress_reserver.registry();
if (_reserver->reserved() < ReservationSize || !_registry->is_contiguous()) {
GTEST_SKIP() << "Fixture failed to reserve adequate memory, reserved "
<< (_reserver->reserved() >> ZGranuleSizeShift) << " * ZGranuleSize";
}
}
virtual void TearDown() {
@ -58,9 +64,9 @@ public:
}
// Best-effort cleanup
_reserver->unreserve_all();
_reserver->~ZVirtualMemoryReserver();
os::free(_reserver);
_registry = nullptr;
_reserver = nullptr;
_zaddress_reserver.TearDown();
}
void test_unreserve() {

View File

@ -56,6 +56,7 @@ class ZVirtualMemoryManagerTest : public ZTest {
private:
static constexpr size_t ReservationSize = 32 * M;
ZAddressReserver _zaddress_reserver;
ZVirtualMemoryReserver* _reserver;
ZVirtualMemoryRegistry* _registry;
@ -66,9 +67,14 @@ public:
GTEST_SKIP() << "OS not supported";
}
_reserver = (ZVirtualMemoryReserver*)os::malloc(sizeof(ZVirtualMemoryManager), mtTest);
_reserver = ::new (_reserver) ZVirtualMemoryReserver(ReservationSize);
_registry = &_reserver->_registry;
_zaddress_reserver.SetUp(ReservationSize);
_reserver = _zaddress_reserver.reserver();
_registry = _zaddress_reserver.registry();
if (_reserver->reserved() < ReservationSize || !_registry->is_contiguous()) {
GTEST_SKIP() << "Fixture failed to reserve adequate memory, reserved "
<< (_reserver->reserved() >> ZGranuleSizeShift) << " * ZGranuleSize";
}
}
virtual void TearDown() {
@ -77,10 +83,9 @@ public:
return;
}
// Best-effort cleanup
_reserver->unreserve_all();
_reserver->~ZVirtualMemoryReserver();
os::free(_reserver);
_registry = nullptr;
_reserver = nullptr;
_zaddress_reserver.TearDown();
}
void test_reserve_discontiguous_and_coalesce() {
@ -112,11 +117,6 @@ public:
// to separate the fetched memory from the memory left in the manager. This
// used to fail because the memory was already split into two placeholders.
if (_reserver->reserved() < 4 * ZGranuleSize || !_registry->is_contiguous()) {
GTEST_SKIP() << "Fixture failed to reserve adequate memory, reserved "
<< (_reserver->reserved() >> ZGranuleSizeShift) << " * ZGranuleSize";
}
// Start at the offset we reserved.
const zoffset base_offset = _registry->peek_low_address();

View File

@ -30,6 +30,7 @@
#include "gc/z/zNUMA.hpp"
#include "gc/z/zRangeRegistry.hpp"
#include "gc/z/zVirtualMemory.inline.hpp"
#include "gc/z/zVirtualMemoryManager.hpp"
#include "runtime/os.hpp"
#include "unittest.hpp"
@ -60,6 +61,50 @@ public:
};
class ZTest : public testing::Test {
public:
class ZAddressReserver {
ZVirtualMemoryReserver* _reserver;
bool _active;
public:
ZAddressReserver()
: _reserver(nullptr),
_active(false) {}
~ZAddressReserver() {
GTEST_EXPECT_FALSE(_active) << "ZAddressReserver deconstructed without calling TearDown";
}
void SetUp(size_t reservation_size) {
GTEST_EXPECT_TRUE(ZArguments::is_os_supported()) << "Should not use SetUp on unsupported systems";
GTEST_EXPECT_FALSE(_active) << "SetUp called twice without a TearDown";
_active = true;
_reserver = (ZVirtualMemoryReserver*)os::malloc(sizeof(ZVirtualMemoryManager), mtTest);
_reserver = ::new (_reserver) ZVirtualMemoryReserver(reservation_size);
}
void TearDown() {
GTEST_EXPECT_TRUE(_active) << "TearDown called without a preceding SetUp";
_active = false;
// Best-effort cleanup
_reserver->unreserve_all();
_reserver->~ZVirtualMemoryReserver();
os::free(_reserver);
}
ZVirtualMemoryReserver* reserver() {
GTEST_EXPECT_TRUE(_active) << "Should only use HeapReserver while active";
return _reserver;
}
ZVirtualMemoryRegistry* registry() {
GTEST_EXPECT_TRUE(_active) << "Should only use HeapReserver while active";
return &_reserver->_registry;
}
};
private:
ZAddressOffsetMaxSetter _zaddress_offset_max_setter;
unsigned int _rand_seed;