8360048: NMT crash in gtest/NMTGtests.java: fatal error: NMT corruption: Block at 0x0000017748307120: header canary broken

Reviewed-by: jsjolen, gziemski
This commit is contained in:
Afshin Zafari 2025-08-08 09:06:43 +00:00
parent a26a6f3152
commit 1b3e23110b
5 changed files with 104 additions and 103 deletions

View File

@ -215,7 +215,7 @@ bool MemBaseline::aggregate_virtual_memory_allocation_sites() {
site = node->data();
}
site->reserve_memory(rgn->size());
site->commit_memory(rgn->committed_size());
site->commit_memory(VirtualMemoryTracker::Instance::committed_size(rgn));
}
_virtual_memory_sites.move(&allocation_sites);

View File

@ -422,7 +422,7 @@ void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion*
outputStream* out = output();
const char* scale = current_scale();
const NativeCallStack* stack = reserved_rgn->call_stack();
bool all_committed = reserved_rgn->size() == reserved_rgn->committed_size();
bool all_committed = reserved_rgn->size() == VirtualMemoryTracker::Instance::committed_size(reserved_rgn);
const char* region_type = (all_committed ? "reserved and committed" : "reserved");
out->cr();
print_virtual_memory_region(region_type, reserved_rgn->base(), reserved_rgn->size());

View File

@ -219,20 +219,29 @@ bool VirtualMemoryTracker::walk_virtual_memory(VirtualMemoryWalker* walker) {
return true;
}
size_t ReservedMemoryRegion::committed_size() const {
size_t committed = 0;
size_t VirtualMemoryTracker::committed_size(const ReservedMemoryRegion* rmr) {
size_t result = 0;
VirtualMemoryTracker::Instance::tree()->visit_committed_regions(*this, [&](CommittedMemoryRegion& crgn) {
tree()->visit_committed_regions(*rmr, [&](CommittedMemoryRegion& crgn) {
result += crgn.size();
return true;
});
return result;
}
address ReservedMemoryRegion::thread_stack_uncommitted_bottom() const {
address bottom = base();
address top = base() + size();
VirtualMemoryTracker::Instance::tree()->visit_committed_regions(*this, [&](CommittedMemoryRegion& crgn) {
size_t VirtualMemoryTracker::Instance::committed_size(const ReservedMemoryRegion* rmr) {
assert(_tracker != nullptr, "Sanity check");
return _tracker->committed_size(rmr);
}
address VirtualMemoryTracker::Instance::thread_stack_uncommitted_bottom(const ReservedMemoryRegion* rmr) {
assert(_tracker != nullptr, "Sanity check");
return _tracker->thread_stack_uncommitted_bottom(rmr);
}
address VirtualMemoryTracker::thread_stack_uncommitted_bottom(const ReservedMemoryRegion* rmr) {
address bottom = rmr->base();
address top = rmr->end();
tree()->visit_committed_regions(*rmr, [&](CommittedMemoryRegion& crgn) {
address committed_top = crgn.base() + crgn.size();
if (committed_top < top) {
// committed stack guard pages, skip them
@ -291,7 +300,7 @@ public:
assert_lock_strong(NmtVirtualMemory_lock);
}
if (rgn->mem_tag() == mtThreadStack) {
address stack_bottom = rgn->thread_stack_uncommitted_bottom();
address stack_bottom = VirtualMemoryTracker::Instance::thread_stack_uncommitted_bottom(rgn);
address committed_start;
size_t committed_size;
size_t stack_size = rgn->base() + rgn->size() - stack_bottom;

View File

@ -330,12 +330,6 @@ class ReservedMemoryRegion : public VirtualMemoryRegion {
inline MemTag mem_tag() const { return _mem_tag; }
// uncommitted thread stack bottom, above guard pages if there is any.
address thread_stack_uncommitted_bottom() const;
size_t committed_size() const;
ReservedMemoryRegion& operator= (const ReservedMemoryRegion& other) {
set_base(other.base());
set_size(other.size());
@ -382,6 +376,9 @@ class VirtualMemoryTracker {
// Snapshot current thread stacks
void snapshot_thread_stacks();
void apply_summary_diff(VMATree::SummaryDiff diff);
size_t committed_size(const ReservedMemoryRegion* rmr);
address thread_stack_uncommitted_bottom(const ReservedMemoryRegion* rmr);
RegionsTree* tree() { return &_tree; }
class Instance : public AllStatic {
@ -404,6 +401,9 @@ class VirtualMemoryTracker {
static bool print_containing_region(const void* p, outputStream* st);
static void snapshot_thread_stacks();
static void apply_summary_diff(VMATree::SummaryDiff diff);
static size_t committed_size(const ReservedMemoryRegion* rmr);
// uncommitted thread stack bottom, above guard pages if there is any.
static address thread_stack_uncommitted_bottom(const ReservedMemoryRegion* rmr);
static RegionsTree* tree() { return _tracker->tree(); }
};

View File

@ -50,14 +50,14 @@ namespace {
};
}
#define check(rmr, regions) check_inner((rmr), (regions), ARRAY_SIZE(regions), __FILE__, __LINE__)
#define check(vmt, rmr, regions) check_inner((vmt), (rmr), (regions), ARRAY_SIZE(regions), __FILE__, __LINE__)
#define check_empty(rmr) \
#define check_empty(vmt, rmr) \
do { \
check_inner((rmr), nullptr, 0, __FILE__, __LINE__); \
check_inner((vmt), (rmr), nullptr, 0, __FILE__, __LINE__); \
} while (false)
static void diagnostic_print(const ReservedMemoryRegion& rmr) {
static void diagnostic_print(VirtualMemoryTracker& vmt, const ReservedMemoryRegion& rmr) {
LOG("In reserved region " PTR_FORMAT ", size %X:", p2i(rmr.base()), rmr.size());
VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rmr, [&](CommittedMemoryRegion& region) {
LOG(" committed region: " PTR_FORMAT ", size %X", p2i(region.base()), region.size());
@ -65,16 +65,16 @@ static void diagnostic_print(const ReservedMemoryRegion& rmr) {
});
}
static void check_inner(const ReservedMemoryRegion& rmr, R* regions, size_t regions_size, const char* file, int line) {
static void check_inner(VirtualMemoryTracker& vmt, const ReservedMemoryRegion& rmr, R* regions, size_t regions_size, const char* file, int line) {
size_t i = 0;
size_t size = 0;
// Helpful log
diagnostic_print(rmr);
diagnostic_print(vmt, rmr);
#define WHERE " from " << file << ":" << line
VirtualMemoryTracker::Instance::tree()->visit_committed_regions(rmr, [&](CommittedMemoryRegion& region) {
vmt.tree()->visit_committed_regions(rmr, [&](CommittedMemoryRegion& region) {
EXPECT_LT(i, regions_size) << WHERE;
EXPECT_EQ(region.base(), regions[i]._addr) << WHERE;
EXPECT_EQ(region.size(), regions[i]._size) << WHERE;
@ -84,16 +84,18 @@ static void check_inner(const ReservedMemoryRegion& rmr, R* regions, size_t regi
});
EXPECT_EQ(i, regions_size) << WHERE;
EXPECT_EQ(size, rmr.committed_size()) << WHERE;
EXPECT_EQ(size, vmt.committed_size(&rmr)) << WHERE;
}
class VirtualMemoryTrackerTest {
public:
static void test_add_committed_region_adjacent() {
VirtualMemoryTracker vmt(true);
RegionsTree* rtree = vmt.tree();
size_t size = 0x01000000;
ReservedSpace rs = MemoryReserver::reserve(size, mtTest);
const address addr = (address)0x0000A000;
address addr = (address)rs.base();
vmt.add_reserved_region(addr, size, CALLER_PC, mtTest);
address frame1 = (address)0x1234;
address frame2 = (address)0x1235;
@ -102,9 +104,6 @@ public:
NativeCallStack stack2(&frame2, 1);
// Fetch the added RMR for the space
RegionsTree* rtree = VirtualMemoryTracker::Instance::tree();
MemTracker::NmtVirtualMemoryLocker nvml;
ReservedMemoryRegion rmr = rtree->find_reserved_region(addr);
ASSERT_EQ(rmr.size(), size);
@ -118,24 +117,24 @@ public:
{ // Commit one region
rtree->commit_region(addr + cs, cs, stack);
R r[] = { {addr + cs, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit adjacent - lower address
rtree->commit_region(addr, cs, stack);
R r[] = { {addr, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit adjacent - higher address
rtree->commit_region(addr + 2 * cs, cs, stack);
R r[] = { {addr, 3 * cs} };
check(rmr, r);
check(vmt,rmr, r);
}
// Cleanup
rtree->uncommit_region(addr, 3 * cs);
ASSERT_EQ(rmr.committed_size(), 0u);
ASSERT_EQ(vmt.committed_size(&rmr), 0u);
// Commit adjacent regions with different stacks
@ -143,14 +142,14 @@ public:
{ // Commit one region
rtree->commit_region(addr + cs, cs, stack);
R r[] = { {addr + cs, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit adjacent - lower address
rtree->commit_region(addr, cs, stack2);
R r[] = { {addr, cs},
{addr + cs, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit adjacent - higher address
@ -158,25 +157,21 @@ public:
R r[] = { {addr, cs},
{addr + cs, cs},
{addr + 2 * cs, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
// Cleanup
rtree->uncommit_region(addr, 3 * cs);
ASSERT_EQ(rmr.committed_size(), 0u);
rtree->tree().remove_all();
ASSERT_EQ(vmt.committed_size(&rmr), 0u);
}
static void test_add_committed_region_adjacent_overlapping() {
VirtualMemoryTracker vmt(true);
RegionsTree* rtree = vmt.tree();
size_t size = 0x01000000;
ReservedSpace rs = MemoryReserver::reserve(size, mtTest);
RegionsTree* rtree = VirtualMemoryTracker::Instance::tree();
MemTracker::NmtVirtualMemoryLocker nvml;
address addr = (address)rs.base();
const address addr = (address)0x0000A000;
vmt.add_reserved_region(addr, size, CALLER_PC, mtTest);
address frame1 = (address)0x1234;
address frame2 = (address)0x1235;
@ -199,28 +194,28 @@ public:
rtree->commit_region(addr + 3 * cs, 2 * cs, stack);
R r[] = { {addr, 2 * cs},
{addr + 3 * cs, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit adjacent and overlapping
rtree->commit_region(addr + 2 * cs, 2 * cs, stack);
R r[] = { {addr, 5 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
// revert to two non-adjacent regions
rtree->uncommit_region(addr + 2 * cs, cs);
ASSERT_EQ(rmr.committed_size(), 4 * cs);
ASSERT_EQ(vmt.committed_size(&rmr), 4 * cs);
{ // Commit overlapping and adjacent
rtree->commit_region(addr + cs, 2 * cs, stack);
R r[] = { {addr, 5 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
// Cleanup
rtree->uncommit_region(addr, 5 * cs);
ASSERT_EQ(rmr.committed_size(), 0u);
ASSERT_EQ(vmt.committed_size(&rmr), 0u);
// Commit adjacent and overlapping regions with different stacks
@ -230,7 +225,7 @@ public:
rtree->commit_region(addr + 3 * cs, 2 * cs, stack);
R r[] = { {addr, 2 * cs},
{addr + 3 * cs, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit adjacent and overlapping
@ -238,33 +233,32 @@ public:
R r[] = { {addr, 2 * cs},
{addr + 2 * cs, 2 * cs},
{addr + 4 * cs, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
// revert to two non-adjacent regions
rtree->commit_region(addr, 5 * cs, stack);
rtree->uncommit_region(addr + 2 * cs, cs);
ASSERT_EQ(rmr.committed_size(), 4 * cs);
ASSERT_EQ(vmt.committed_size(&rmr), 4 * cs);
{ // Commit overlapping and adjacent
rtree->commit_region(addr + cs, 2 * cs, stack2);
R r[] = { {addr, cs},
{addr + cs, 2 * cs},
{addr + 3 * cs, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
rtree->tree().remove_all();
}
static void test_add_committed_region_overlapping() {
VirtualMemoryTracker vmt(true);
RegionsTree* rtree = vmt.tree();
size_t size = 0x01000000;
ReservedSpace rs = MemoryReserver::reserve(size, mtTest);
const address addr = (address)0x0000A000;
RegionsTree* rtree = VirtualMemoryTracker::Instance::tree();
MemTracker::NmtVirtualMemoryLocker nvml;
address addr = (address)rs.base();
vmt.add_reserved_region(addr, size, CALLER_PC, mtTest);
address frame1 = (address)0x1234;
address frame2 = (address)0x1235;
@ -287,54 +281,54 @@ public:
{ // Commit one region
rtree->commit_region(addr, cs, stack);
R r[] = { {addr, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit the same region
rtree->commit_region(addr, cs, stack);
R r[] = { {addr, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit a succeeding region
rtree->commit_region(addr + cs, cs, stack);
R r[] = { {addr, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit over two regions
rtree->commit_region(addr, 2 * cs, stack);
R r[] = { {addr, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{// Commit first part of a region
rtree->commit_region(addr, cs, stack);
R r[] = { {addr, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit second part of a region
rtree->commit_region(addr + cs, cs, stack);
R r[] = { {addr, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit a third part
rtree->commit_region(addr + 2 * cs, cs, stack);
R r[] = { {addr, 3 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit in the middle of a region
rtree->commit_region(addr + 1 * cs, cs, stack);
R r[] = { {addr, 3 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
// Cleanup
rtree->uncommit_region(addr, 3 * cs);
ASSERT_EQ(rmr.committed_size(), 0u);
ASSERT_EQ(vmt.committed_size(&rmr), 0u);
// With preceding region
@ -345,71 +339,71 @@ public:
{
R r[] = { {addr, cs},
{addr + 2 * cs, 3 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
rtree->commit_region(addr + 3 * cs, cs, stack);
{
R r[] = { {addr, cs},
{addr + 2 * cs, 3 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
rtree->commit_region(addr + 4 * cs, cs, stack);
{
R r[] = { {addr, cs},
{addr + 2 * cs, 3 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
// Cleanup
rtree->uncommit_region(addr, 5 * cs);
ASSERT_EQ(rmr.committed_size(), 0u);
ASSERT_EQ(vmt.committed_size(&rmr), 0u);
// With different stacks
{ // Commit one region
rtree->commit_region(addr, cs, stack);
R r[] = { {addr, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit the same region
rtree->commit_region(addr, cs, stack2);
R r[] = { {addr, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit a succeeding region
rtree->commit_region(addr + cs, cs, stack);
R r[] = { {addr, cs},
{addr + cs, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit over two regions
rtree->commit_region(addr, 2 * cs, stack);
R r[] = { {addr, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{// Commit first part of a region
rtree->commit_region(addr, cs, stack2);
R r[] = { {addr, cs},
{addr + cs, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit second part of a region
rtree->commit_region(addr + cs, cs, stack2);
R r[] = { {addr, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit a third part
rtree->commit_region(addr + 2 * cs, cs, stack2);
R r[] = { {addr, 3 * cs} };
check(rmr, r);
check(vmt, rmr, r);
}
{ // Commit in the middle of a region
@ -417,7 +411,7 @@ public:
R r[] = { {addr, cs},
{addr + cs, cs},
{addr + 2 * cs, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
rtree->tree().remove_all();
@ -435,14 +429,12 @@ public:
}
static void test_remove_uncommitted_region() {
VirtualMemoryTracker vmt(true);
RegionsTree* rtree = vmt.tree();
size_t size = 0x01000000;
ReservedSpace rs = MemoryReserver::reserve(size, mtTest);
RegionsTree* rtree = VirtualMemoryTracker::Instance::tree();
MemTracker::NmtVirtualMemoryLocker nvml;
address addr = (address)rs.base();
const address addr = (address)0x0000A000;
vmt.add_reserved_region(addr, size, CALLER_PC, mtTest);
address frame1 = (address)0x1234;
address frame2 = (address)0x1235;
@ -461,11 +453,11 @@ public:
{ // Commit regions
rtree->commit_region(addr, 3 * cs, stack);
R r[] = { {addr, 3 * cs} };
check(rmr, r);
check(vmt, rmr, r);
// Remove only existing
rtree->uncommit_region(addr, 3 * cs);
check_empty(rmr);
check_empty(vmt, rmr);
}
{
@ -477,7 +469,7 @@ public:
rtree->uncommit_region(addr, cs);
R r[] = { {addr + 2 * cs, cs},
{addr + 4 * cs, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
// add back
@ -487,7 +479,7 @@ public:
rtree->uncommit_region(addr + 2 * cs, cs);
R r[] = { {addr + 0 * cs, cs},
{addr + 4 * cs, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
// add back
@ -497,17 +489,17 @@ public:
rtree->uncommit_region(addr + 4 * cs, cs);
R r[] = { {addr + 0 * cs, cs},
{addr + 2 * cs, cs} };
check(rmr, r);
check(vmt, rmr, r);
}
rtree->uncommit_region(addr, 5 * cs);
check_empty(rmr);
check_empty(vmt, rmr);
}
{ // Remove larger region
rtree->commit_region(addr + 1 * cs, cs, stack);
rtree->uncommit_region(addr, 3 * cs);
check_empty(rmr);
check_empty(vmt, rmr);
}
{ // Remove smaller region - in the middle
@ -515,50 +507,50 @@ public:
rtree->uncommit_region(addr + 1 * cs, cs);
R r[] = { { addr + 0 * cs, cs},
{ addr + 2 * cs, cs} };
check(rmr, r);
check(vmt, rmr, r);
rtree->uncommit_region(addr, 3 * cs);
check_empty(rmr);
check_empty(vmt, rmr);
}
{ // Remove smaller region - at the beginning
rtree->commit_region(addr, 3 * cs, stack);
rtree->uncommit_region(addr + 0 * cs, cs);
R r[] = { { addr + 1 * cs, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
rtree->uncommit_region(addr, 3 * cs);
check_empty(rmr);
check_empty(vmt, rmr);
}
{ // Remove smaller region - at the end
rtree->commit_region(addr, 3 * cs, stack);
rtree->uncommit_region(addr + 2 * cs, cs);
R r[] = { { addr, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
rtree->uncommit_region(addr, 3 * cs);
check_empty(rmr);
check_empty(vmt, rmr);
}
{ // Remove smaller, overlapping region - at the beginning
rtree->commit_region(addr + 1 * cs, 4 * cs, stack);
rtree->uncommit_region(addr, 2 * cs);
R r[] = { { addr + 2 * cs, 3 * cs} };
check(rmr, r);
check(vmt, rmr, r);
rtree->uncommit_region(addr + 1 * cs, 4 * cs);
check_empty(rmr);
check_empty(vmt, rmr);
}
{ // Remove smaller, overlapping region - at the end
rtree->commit_region(addr, 3 * cs, stack);
rtree->uncommit_region(addr + 2 * cs, 2 * cs);
R r[] = { { addr, 2 * cs} };
check(rmr, r);
check(vmt, rmr, r);
rtree->uncommit_region(addr, 3 * cs);
check_empty(rmr);
check_empty(vmt, rmr);
}
rtree->tree().remove_all();