/* * 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 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