551 lines
16 KiB
C++
551 lines
16 KiB
C++
|
/*
|
||
|
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||
|
*
|
||
|
* This code is free software; you can redistribute it and/or modify it
|
||
|
* under the terms of the GNU General Public License version 2 only, as
|
||
|
* published by the Free Software Foundation.
|
||
|
*
|
||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||
|
* accompanied this code).
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License version
|
||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
*
|
||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||
|
* or visit www.oracle.com if you need additional information or have any
|
||
|
* questions.
|
||
|
*/
|
||
|
|
||
|
#include "precompiled.hpp"
|
||
|
|
||
|
// Included early because the NMT flags don't include it.
|
||
|
#include "utilities/macros.hpp"
|
||
|
|
||
|
#if INCLUDE_NMT
|
||
|
|
||
|
#include "services/memTracker.hpp"
|
||
|
#include "services/virtualMemoryTracker.hpp"
|
||
|
#include "utilities/globalDefinitions.hpp"
|
||
|
#include "unittest.hpp"
|
||
|
|
||
|
namespace {
|
||
|
struct R {
|
||
|
address _addr;
|
||
|
size_t _size;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
#define check(rmr, regions) check_inner((rmr), (regions), ARRAY_SIZE(regions), __FILE__, __LINE__)
|
||
|
|
||
|
#define check_empty(rmr) \
|
||
|
do { \
|
||
|
check_inner((rmr), NULL, 0, __FILE__, __LINE__); \
|
||
|
} while (false)
|
||
|
|
||
|
static void check_inner(ReservedMemoryRegion* rmr, R* regions, size_t regions_size, const char* file, int line) {
|
||
|
CommittedRegionIterator iter = rmr->iterate_committed_regions();
|
||
|
size_t i = 0;
|
||
|
size_t size = 0;
|
||
|
|
||
|
#define WHERE " from " << file << ":" << line
|
||
|
|
||
|
for (const CommittedMemoryRegion* region = iter.next(); region != NULL; region = iter.next()) {
|
||
|
EXPECT_LT(i, regions_size) << WHERE;
|
||
|
EXPECT_EQ(region->base(), regions[i]._addr) << WHERE;
|
||
|
EXPECT_EQ(region->size(), regions[i]._size) << WHERE;
|
||
|
size += region->size();
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
EXPECT_EQ(i, regions_size) << WHERE;
|
||
|
EXPECT_EQ(size, rmr->committed_size()) << WHERE;
|
||
|
}
|
||
|
|
||
|
class VirtualMemoryTrackerTest {
|
||
|
public:
|
||
|
static void test_add_committed_region_adjacent() {
|
||
|
VirtualMemoryTracker::initialize(NMT_detail);
|
||
|
VirtualMemoryTracker::late_initialize(NMT_detail);
|
||
|
|
||
|
address addr = (address)0x10000000;
|
||
|
size_t size = 0x01000000;
|
||
|
|
||
|
address frame1 = (address)0x1234;
|
||
|
address frame2 = (address)0x1235;
|
||
|
|
||
|
NativeCallStack stack(&frame1, 1);
|
||
|
NativeCallStack stack2(&frame2, 1);
|
||
|
|
||
|
// Add the reserved memory
|
||
|
VirtualMemoryTracker::add_reserved_region(addr, size, stack, mtTest);
|
||
|
|
||
|
// Fetch the added RMR added above
|
||
|
ReservedMemoryRegion* rmr = VirtualMemoryTracker::_reserved_regions->find(ReservedMemoryRegion(addr, size));
|
||
|
|
||
|
ASSERT_EQ(rmr->size(), size);
|
||
|
ASSERT_EQ(rmr->base(), addr);
|
||
|
|
||
|
// Commit Size Granularity
|
||
|
const size_t cs = 0x1000;
|
||
|
|
||
|
// Commit adjacent regions with same stack
|
||
|
|
||
|
{ // Commit one region
|
||
|
rmr->add_committed_region(addr + cs, cs, stack);
|
||
|
R r[] = { {addr + cs, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit adjacent - lower address
|
||
|
rmr->add_committed_region(addr, cs, stack);
|
||
|
R r[] = { {addr, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit adjacent - higher address
|
||
|
rmr->add_committed_region(addr + 2 * cs, cs, stack);
|
||
|
R r[] = { {addr, 3 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
// Cleanup
|
||
|
rmr->remove_uncommitted_region(addr, 3 * cs);
|
||
|
ASSERT_EQ(rmr->committed_size(), 0u);
|
||
|
|
||
|
|
||
|
// Commit adjacent regions with different stacks
|
||
|
|
||
|
{ // Commit one region
|
||
|
rmr->add_committed_region(addr + cs, cs, stack);
|
||
|
R r[] = { {addr + cs, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit adjacent - lower address
|
||
|
rmr->add_committed_region(addr, cs, stack2);
|
||
|
R r[] = { {addr, cs},
|
||
|
{addr + cs, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit adjacent - higher address
|
||
|
rmr->add_committed_region(addr + 2 * cs, cs, stack2);
|
||
|
R r[] = { {addr, cs},
|
||
|
{addr + cs, cs},
|
||
|
{addr + 2 * cs, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
// Cleanup
|
||
|
rmr->remove_uncommitted_region(addr, 3 * cs);
|
||
|
ASSERT_EQ(rmr->committed_size(), 0u);
|
||
|
}
|
||
|
|
||
|
static void test_add_committed_region_adjacent_overlapping() {
|
||
|
VirtualMemoryTracker::initialize(NMT_detail);
|
||
|
VirtualMemoryTracker::late_initialize(NMT_detail);
|
||
|
|
||
|
address addr = (address)0x10000000;
|
||
|
size_t size = 0x01000000;
|
||
|
|
||
|
address frame1 = (address)0x1234;
|
||
|
address frame2 = (address)0x1235;
|
||
|
|
||
|
NativeCallStack stack(&frame1, 1);
|
||
|
NativeCallStack stack2(&frame2, 1);
|
||
|
|
||
|
// Add the reserved memory
|
||
|
VirtualMemoryTracker::add_reserved_region(addr, size, stack, mtTest);
|
||
|
|
||
|
// Fetch the added RMR added above
|
||
|
ReservedMemoryRegion* rmr = VirtualMemoryTracker::_reserved_regions->find(ReservedMemoryRegion(addr, size));
|
||
|
|
||
|
ASSERT_EQ(rmr->size(), size);
|
||
|
ASSERT_EQ(rmr->base(), addr);
|
||
|
|
||
|
// Commit Size Granularity
|
||
|
const size_t cs = 0x1000;
|
||
|
|
||
|
// Commit adjacent and overlapping regions with same stack
|
||
|
|
||
|
{ // Commit two non-adjacent regions
|
||
|
rmr->add_committed_region(addr, 2 * cs, stack);
|
||
|
rmr->add_committed_region(addr + 3 * cs, 2 * cs, stack);
|
||
|
R r[] = { {addr, 2 * cs},
|
||
|
{addr + 3 * cs, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit adjacent and overlapping
|
||
|
rmr->add_committed_region(addr + 2 * cs, 2 * cs, stack);
|
||
|
R r[] = { {addr, 5 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
// revert to two non-adjacent regions
|
||
|
rmr->remove_uncommitted_region(addr + 2 * cs, cs);
|
||
|
ASSERT_EQ(rmr->committed_size(), 4 * cs);
|
||
|
|
||
|
{ // Commit overlapping and adjacent
|
||
|
rmr->add_committed_region(addr + cs, 2 * cs, stack);
|
||
|
R r[] = { {addr, 5 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
// Cleanup
|
||
|
rmr->remove_uncommitted_region(addr, 5 * cs);
|
||
|
ASSERT_EQ(rmr->committed_size(), 0u);
|
||
|
|
||
|
|
||
|
// Commit adjacent and overlapping regions with different stacks
|
||
|
|
||
|
{ // Commit two non-adjacent regions
|
||
|
rmr->add_committed_region(addr, 2 * cs, stack);
|
||
|
rmr->add_committed_region(addr + 3 * cs, 2 * cs, stack);
|
||
|
R r[] = { {addr, 2 * cs},
|
||
|
{addr + 3 * cs, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit adjacent and overlapping
|
||
|
rmr->add_committed_region(addr + 2 * cs, 2 * cs, stack2);
|
||
|
R r[] = { {addr, 2 * cs},
|
||
|
{addr + 2 * cs, 2 * cs},
|
||
|
{addr + 4 * cs, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
// revert to two non-adjacent regions
|
||
|
rmr->add_committed_region(addr, 5 * cs, stack);
|
||
|
rmr->remove_uncommitted_region(addr + 2 * cs, cs);
|
||
|
ASSERT_EQ(rmr->committed_size(), 4 * cs);
|
||
|
|
||
|
{ // Commit overlapping and adjacent
|
||
|
rmr->add_committed_region(addr + cs, 2 * cs, stack2);
|
||
|
R r[] = { {addr, cs},
|
||
|
{addr + cs, 2 * cs},
|
||
|
{addr + 3 * cs, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void test_add_committed_region_overlapping() {
|
||
|
VirtualMemoryTracker::initialize(NMT_detail);
|
||
|
VirtualMemoryTracker::late_initialize(NMT_detail);
|
||
|
|
||
|
address addr = (address)0x10000000;
|
||
|
size_t size = 0x01000000;
|
||
|
|
||
|
address frame1 = (address)0x1234;
|
||
|
address frame2 = (address)0x1235;
|
||
|
|
||
|
NativeCallStack stack(&frame1, 1);
|
||
|
NativeCallStack stack2(&frame2, 1);
|
||
|
|
||
|
// Add the reserved memory
|
||
|
VirtualMemoryTracker::add_reserved_region(addr, size, stack, mtTest);
|
||
|
|
||
|
// Fetch the added RMR added above
|
||
|
ReservedMemoryRegion* rmr = VirtualMemoryTracker::_reserved_regions->find(ReservedMemoryRegion(addr, size));
|
||
|
|
||
|
ASSERT_EQ(rmr->size(), size);
|
||
|
ASSERT_EQ(rmr->base(), addr);
|
||
|
|
||
|
// Commit Size Granularity
|
||
|
const size_t cs = 0x1000;
|
||
|
|
||
|
// With same stack
|
||
|
|
||
|
{ // Commit one region
|
||
|
rmr->add_committed_region(addr, cs, stack);
|
||
|
R r[] = { {addr, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit the same region
|
||
|
rmr->add_committed_region(addr, cs, stack);
|
||
|
R r[] = { {addr, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit a succeeding region
|
||
|
rmr->add_committed_region(addr + cs, cs, stack);
|
||
|
R r[] = { {addr, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit over two regions
|
||
|
rmr->add_committed_region(addr, 2 * cs, stack);
|
||
|
R r[] = { {addr, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{// Commit first part of a region
|
||
|
rmr->add_committed_region(addr, cs, stack);
|
||
|
R r[] = { {addr, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit second part of a region
|
||
|
rmr->add_committed_region(addr + cs, cs, stack);
|
||
|
R r[] = { {addr, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit a third part
|
||
|
rmr->add_committed_region(addr + 2 * cs, cs, stack);
|
||
|
R r[] = { {addr, 3 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit in the middle of a region
|
||
|
rmr->add_committed_region(addr + 1 * cs, cs, stack);
|
||
|
R r[] = { {addr, 3 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
// Cleanup
|
||
|
rmr->remove_uncommitted_region(addr, 3 * cs);
|
||
|
ASSERT_EQ(rmr->committed_size(), 0u);
|
||
|
|
||
|
// With preceding region
|
||
|
|
||
|
rmr->add_committed_region(addr, cs, stack);
|
||
|
rmr->add_committed_region(addr + 2 * cs, 3 * cs, stack);
|
||
|
|
||
|
rmr->add_committed_region(addr + 2 * cs, cs, stack);
|
||
|
{
|
||
|
R r[] = { {addr, cs},
|
||
|
{addr + 2 * cs, 3 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
rmr->add_committed_region(addr + 3 * cs, cs, stack);
|
||
|
{
|
||
|
R r[] = { {addr, cs},
|
||
|
{addr + 2 * cs, 3 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
rmr->add_committed_region(addr + 4 * cs, cs, stack);
|
||
|
{
|
||
|
R r[] = { {addr, cs},
|
||
|
{addr + 2 * cs, 3 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
// Cleanup
|
||
|
rmr->remove_uncommitted_region(addr, 5 * cs);
|
||
|
ASSERT_EQ(rmr->committed_size(), 0u);
|
||
|
|
||
|
// With different stacks
|
||
|
|
||
|
{ // Commit one region
|
||
|
rmr->add_committed_region(addr, cs, stack);
|
||
|
R r[] = { {addr, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit the same region
|
||
|
rmr->add_committed_region(addr, cs, stack2);
|
||
|
R r[] = { {addr, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit a succeeding region
|
||
|
rmr->add_committed_region(addr + cs, cs, stack);
|
||
|
R r[] = { {addr, cs},
|
||
|
{addr + cs, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit over two regions
|
||
|
rmr->add_committed_region(addr, 2 * cs, stack);
|
||
|
R r[] = { {addr, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{// Commit first part of a region
|
||
|
rmr->add_committed_region(addr, cs, stack2);
|
||
|
R r[] = { {addr, cs},
|
||
|
{addr + cs, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit second part of a region
|
||
|
rmr->add_committed_region(addr + cs, cs, stack2);
|
||
|
R r[] = { {addr, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit a third part
|
||
|
rmr->add_committed_region(addr + 2 * cs, cs, stack2);
|
||
|
R r[] = { {addr, 3 * cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
{ // Commit in the middle of a region
|
||
|
rmr->add_committed_region(addr + 1 * cs, cs, stack);
|
||
|
R r[] = { {addr, cs},
|
||
|
{addr + cs, cs},
|
||
|
{addr + 2 * cs, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void test_add_committed_region() {
|
||
|
test_add_committed_region_adjacent();
|
||
|
test_add_committed_region_adjacent_overlapping();
|
||
|
test_add_committed_region_overlapping();
|
||
|
}
|
||
|
|
||
|
template <size_t S>
|
||
|
static void fix(R r[S]) {
|
||
|
|
||
|
}
|
||
|
|
||
|
static void test_remove_uncommitted_region() {
|
||
|
VirtualMemoryTracker::initialize(NMT_detail);
|
||
|
VirtualMemoryTracker::late_initialize(NMT_detail);
|
||
|
|
||
|
address addr = (address)0x10000000;
|
||
|
size_t size = 0x01000000;
|
||
|
|
||
|
address frame1 = (address)0x1234;
|
||
|
address frame2 = (address)0x1235;
|
||
|
|
||
|
NativeCallStack stack(&frame1, 1);
|
||
|
NativeCallStack stack2(&frame2, 1);
|
||
|
|
||
|
// Add the reserved memory
|
||
|
VirtualMemoryTracker::add_reserved_region(addr, size, stack, mtTest);
|
||
|
|
||
|
// Fetch the added RMR added above
|
||
|
ReservedMemoryRegion* rmr = VirtualMemoryTracker::_reserved_regions->find(ReservedMemoryRegion(addr, size));
|
||
|
|
||
|
ASSERT_EQ(rmr->size(), size);
|
||
|
ASSERT_EQ(rmr->base(), addr);
|
||
|
|
||
|
// Commit Size Granularity
|
||
|
const size_t cs = 0x1000;
|
||
|
|
||
|
{ // Commit regions
|
||
|
rmr->add_committed_region(addr, 3 * cs, stack);
|
||
|
R r[] = { {addr, 3 * cs} };
|
||
|
check(rmr, r);
|
||
|
|
||
|
// Remove only existing
|
||
|
rmr->remove_uncommitted_region(addr, 3 * cs);
|
||
|
check_empty(rmr);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
rmr->add_committed_region(addr + 0 * cs, cs, stack);
|
||
|
rmr->add_committed_region(addr + 2 * cs, cs, stack);
|
||
|
rmr->add_committed_region(addr + 4 * cs, cs, stack);
|
||
|
|
||
|
{ // Remove first
|
||
|
rmr->remove_uncommitted_region(addr, cs);
|
||
|
R r[] = { {addr + 2 * cs, cs},
|
||
|
{addr + 4 * cs, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
// add back
|
||
|
rmr->add_committed_region(addr, cs, stack);
|
||
|
|
||
|
{ // Remove middle
|
||
|
rmr->remove_uncommitted_region(addr + 2 * cs, cs);
|
||
|
R r[] = { {addr + 0 * cs, cs},
|
||
|
{addr + 4 * cs, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
// add back
|
||
|
rmr->add_committed_region(addr + 2 * cs, cs, stack);
|
||
|
|
||
|
{ // Remove end
|
||
|
rmr->remove_uncommitted_region(addr + 4 * cs, cs);
|
||
|
R r[] = { {addr + 0 * cs, cs},
|
||
|
{addr + 2 * cs, cs} };
|
||
|
check(rmr, r);
|
||
|
}
|
||
|
|
||
|
rmr->remove_uncommitted_region(addr, 5 * cs);
|
||
|
check_empty(rmr);
|
||
|
}
|
||
|
|
||
|
{ // Remove larger region
|
||
|
rmr->add_committed_region(addr + 1 * cs, cs, stack);
|
||
|
rmr->remove_uncommitted_region(addr, 3 * cs);
|
||
|
check_empty(rmr);
|
||
|
}
|
||
|
|
||
|
{ // Remove smaller region - in the middle
|
||
|
rmr->add_committed_region(addr, 3 * cs, stack);
|
||
|
rmr->remove_uncommitted_region(addr + 1 * cs, cs);
|
||
|
R r[] = { { addr + 0 * cs, cs},
|
||
|
{ addr + 2 * cs, cs} };
|
||
|
check(rmr, r);
|
||
|
|
||
|
rmr->remove_uncommitted_region(addr, 3 * cs);
|
||
|
check_empty(rmr);
|
||
|
}
|
||
|
|
||
|
{ // Remove smaller region - at the beginning
|
||
|
rmr->add_committed_region(addr, 3 * cs, stack);
|
||
|
rmr->remove_uncommitted_region(addr + 0 * cs, cs);
|
||
|
R r[] = { { addr + 1 * cs, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
|
||
|
rmr->remove_uncommitted_region(addr, 3 * cs);
|
||
|
check_empty(rmr);
|
||
|
}
|
||
|
|
||
|
{ // Remove smaller region - at the end
|
||
|
rmr->add_committed_region(addr, 3 * cs, stack);
|
||
|
rmr->remove_uncommitted_region(addr + 2 * cs, cs);
|
||
|
R r[] = { { addr, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
|
||
|
rmr->remove_uncommitted_region(addr, 3 * cs);
|
||
|
check_empty(rmr);
|
||
|
}
|
||
|
|
||
|
{ // Remove smaller, overlapping region - at the beginning
|
||
|
rmr->add_committed_region(addr + 1 * cs, 4 * cs, stack);
|
||
|
rmr->remove_uncommitted_region(addr, 2 * cs);
|
||
|
R r[] = { { addr + 2 * cs, 3 * cs} };
|
||
|
check(rmr, r);
|
||
|
|
||
|
rmr->remove_uncommitted_region(addr + 1 * cs, 4 * cs);
|
||
|
check_empty(rmr);
|
||
|
}
|
||
|
|
||
|
{ // Remove smaller, overlapping region - at the end
|
||
|
rmr->add_committed_region(addr, 3 * cs, stack);
|
||
|
rmr->remove_uncommitted_region(addr + 2 * cs, 2 * cs);
|
||
|
R r[] = { { addr, 2 * cs} };
|
||
|
check(rmr, r);
|
||
|
|
||
|
rmr->remove_uncommitted_region(addr, 3 * cs);
|
||
|
check_empty(rmr);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
TEST_VM(VirtualMemoryTracker, add_committed_region) {
|
||
|
VirtualMemoryTrackerTest::test_add_committed_region();
|
||
|
}
|
||
|
|
||
|
TEST_VM(VirtualMemoryTracker, remove_uncommitted_region) {
|
||
|
VirtualMemoryTrackerTest::test_remove_uncommitted_region();
|
||
|
}
|
||
|
|
||
|
#endif // INCLUDE_NMT
|