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