/* * Copyright (c) 2024, 2026, 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 "memory/allocation.hpp" #include "nmt/memTag.hpp" #include "nmt/memTracker.hpp" #include "nmt/nmtNativeCallStackStorage.hpp" #include "nmt/vmatree.hpp" #include "runtime/os.hpp" #include "unittest.hpp" using Tree = VMATree; using TNode = Tree::TNode; using NCS = NativeCallStackStorage; class NMTVMATreeTest : public testing::Test { public: NCS ncs; constexpr static const int si_len = 4; NCS::StackIndex si[si_len]; NativeCallStack stacks[si_len]; NMTVMATreeTest() : ncs(true) { stacks[0] = make_stack(0xA); stacks[1] = make_stack(0xB); stacks[2] = make_stack(0xC); stacks[3] = make_stack(0xD); si[0] = ncs.push(stacks[0]); si[1] = ncs.push(stacks[1]); si[2] = ncs.push(stacks[2]); si[3] = ncs.push(stacks[3]); } // Utilities VMATree::TNode* rbtree_root(VMATree& tree) { return static_cast(tree._tree._root); } VMATree::VMARBTree& rbtree(VMATree& tree) { return tree._tree; } VMATree::TNode* find(VMATree::VMARBTree& rbtree, const VMATree::position key) { return rbtree.find_node(key); } NativeCallStack make_stack(size_t a) { NativeCallStack stack((address*)&a, 1); return stack; } VMATree::StateType in_type_of(VMATree::TNode* x) { return x->val().in.type(); } VMATree::StateType out_type_of(VMATree::TNode* x) { return x->val().out.type(); } int count_nodes(Tree& tree) { int count = 0; rbtree(tree).visit_in_order([&](TNode* x) { ++count; return true; }); return count; } // Tests // Adjacent reservations are merged if the properties match. void adjacent_2_nodes(const VMATree::RegionData& rd) { Tree tree; VMATree::SummaryDiff not_used; for (int i = 0; i < 10; i++) { tree.reserve_mapping(i * 100, 100, rd, not_used); } EXPECT_EQ(2, count_nodes(tree)); // Reserving the exact same space again should result in still having only 2 nodes for (int i = 0; i < 10; i++) { tree.reserve_mapping(i * 100, 100, rd, not_used); } EXPECT_EQ(2, count_nodes(tree)); // Do it backwards instead. Tree tree2; // 900---1000 // 800--900 // 700--800 // ... // 0--100 for (int i = 9; i >= 0; i--) { tree2.reserve_mapping(i * 100, 100, rd, not_used); } EXPECT_EQ(2, count_nodes(tree2)); } // After removing all ranges we should be left with an entirely empty tree void remove_all_leaves_empty_tree(const VMATree::RegionData& rd) { Tree tree; VMATree::SummaryDiff not_used; tree.reserve_mapping(0, 100 * 10, rd, not_used); for (int i = 0; i < 10; i++) { tree.release_mapping(i * 100, 100, not_used); } EXPECT_EQ(nullptr, rbtree_root(tree)); // Other way around tree.reserve_mapping(0, 100 * 10, rd, not_used); for (int i = 9; i >= 0; i--) { tree.release_mapping(i * 100, 100, not_used); } EXPECT_EQ(nullptr, rbtree_root(tree)); } // Committing in a whole reserved range results in 2 nodes void commit_whole(const VMATree::RegionData& rd) { Tree tree; VMATree::SummaryDiff not_used; tree.reserve_mapping(0, 100 * 10, rd, not_used); for (int i = 0; i < 10; i++) { tree.commit_mapping(i * 100, 100, rd, not_used); } rbtree(tree).visit_in_order([&](TNode* x) { VMATree::StateType in = in_type_of(x); VMATree::StateType out = out_type_of(x); EXPECT_TRUE((in == VMATree::StateType::Released && out == VMATree::StateType::Committed) || (in == VMATree::StateType::Committed && out == VMATree::StateType::Released)); return true; }); EXPECT_EQ(2, count_nodes(tree)); } // Committing in middle of reservation ends with a sequence of 4 nodes void commit_middle(const VMATree::RegionData& rd) { Tree tree; VMATree::SummaryDiff not_used; tree.reserve_mapping(0, 100, rd, not_used); tree.commit_mapping(50, 25, rd, not_used); size_t found[16]; size_t wanted[4] = {0, 50, 75, 100}; auto exists = [&](size_t x) { for (int i = 0; i < 4; i++) { if (wanted[i] == x) return true; } return false; }; int i = 0; rbtree(tree).visit_in_order([&](TNode* x) { if (i < 16) { found[i] = x->key(); } i++; return true; }); ASSERT_EQ(4, i) << "0 - 50 - 75 - 100 nodes expected"; EXPECT_TRUE(exists(found[0])); EXPECT_TRUE(exists(found[1])); EXPECT_TRUE(exists(found[2])); EXPECT_TRUE(exists(found[3])); }; template struct ExpectedTree { int nodes[NodeCount]; MemTag tags[NodeCount + 1]; VMATree::StateType states[NodeCount + 1]; NativeCallStackStorage::StackIndex res_si[NodeCount + 1]; NativeCallStackStorage::StackIndex com_si[NodeCount + 1]; }; using State = VMATree::StateType; using SIndex = VMATree::SIndex; struct UpdateCallInfo { VMATree::IntervalState ex_st; VMATree::RequestInfo req; VMATree::IntervalState new_st; int reserve[2], commit[2]; }; void call_update_region(const UpdateCallInfo upd) { VMATree::TNode n1{upd.req.A, {}}, n2{upd.req.B, {}}; n1.val().out= upd.ex_st; n2.val().in = n1.val().out; Tree tree; VMATree::SummaryDiff diff; tree.update_region(&n1, &n2, upd.req, diff); int from = NMTUtil::tag_to_index(upd.ex_st.mem_tag()); int to = NMTUtil::tag_to_index(upd.new_st.mem_tag()); stringStream ss; ss.print("Ex. State: %d, op: %d, use-tag:%d, from==to: %d", (int)upd.ex_st.type(), (int)upd.req.op_to_index(), upd.req.use_tag_inplace, from == to); const char* failed_case = ss.base(); EXPECT_EQ(n1.val().out.type(), upd.new_st.type()) << failed_case; EXPECT_EQ(n1.val().out.mem_tag(), upd.new_st.mem_tag()) << failed_case; EXPECT_EQ(n1.val().out.reserved_stack(), upd.new_st.reserved_stack()) << failed_case; EXPECT_EQ(n1.val().out.committed_stack(), upd.new_st.committed_stack()) << failed_case; if (from == to) { EXPECT_EQ(diff.tag(from).reserve, upd.reserve[0] + upd.reserve[1]) << failed_case; EXPECT_EQ(diff.tag(from).commit, upd.commit[0] + upd.commit[1]) << failed_case; } else { EXPECT_EQ(diff.tag(from).reserve, upd.reserve[0]) << failed_case; EXPECT_EQ(diff.tag(from).commit, upd.commit[0]) << failed_case; EXPECT_EQ(diff.tag(to).reserve, upd.reserve[1]) << failed_case; EXPECT_EQ(diff.tag(to).commit, upd.commit[1]) << failed_case; } } template void create_tree(Tree& tree, ExpectedTree& et, int line_no) { using SIndex = NativeCallStackStorage::StackIndex; const SIndex ES = NativeCallStackStorage::invalid; // Empty Stack VMATree::SummaryDiff not_used; VMATree::IntervalChange st; for (int i = 0; i < N; i++) { st.in.set_type(et.states[i]); st.in.set_tag(et.tags[i]); if (et.res_si[i] >= 0) { st.in.set_reserve_stack(et.res_si[i]); } else { st.in.set_reserve_stack(ES); } if (et.com_si[i] >= 0) { st.in.set_commit_stack(et.com_si[i]); } else { st.in.set_commit_stack(ES); } st.out.set_type(et.states[i+1]); st.out.set_tag(et.tags[i+1]); if (et.res_si[i+1] >= 0) { st.out.set_reserve_stack(et.res_si[i+1]); } else { st.out.set_reserve_stack(ES); } if (et.com_si[i+1] >= 0) { st.out.set_commit_stack(et.com_si[i+1]); } else { st.out.set_commit_stack(ES); } tree.tree().upsert((VMATree::position)et.nodes[i], st); } } template void check_tree(Tree& tree, const ExpectedTree& et, int line_no) { using Node = VMATree::TNode; auto left_released = [&](Node n) -> bool { return n.val().in.type() == VMATree::StateType::Released and n.val().in.mem_tag() == mtNone; }; auto right_released = [&](Node n) -> bool { return n.val().out.type() == VMATree::StateType::Released and n.val().out.mem_tag() == mtNone; }; for (int i = 0; i < N; i++) { VMATree::VMARBTree::Range r = tree.tree().find_enclosing_range(et.nodes[i]); ASSERT_TRUE(r.start != nullptr); Node node = *r.start; ASSERT_EQ(node.key(), (VMATree::position)et.nodes[i]) << "at line " << line_no; if (i == (N -1)) { // last node EXPECT_TRUE(right_released(node)) << "right-of last node is not Released"; break; } if (i == 0) { // first node EXPECT_TRUE(left_released(node)) << "left-of first node is not Released"; } stringStream ss(50); ss.print("test at line: %d, for node: %d", line_no, et.nodes[i]); const char* for_this_node = ss.base(); EXPECT_EQ(node.val().out.type(), et.states[i+1]) << for_this_node; EXPECT_EQ(node.val().out.mem_tag(), et.tags[i+1]) << for_this_node; if (et.res_si[i+1] >= 0) { EXPECT_EQ(node.val().out.reserved_stack(), et.res_si[i+1]) << for_this_node; EXPECT_EQ(r.end->val().in.reserved_stack(), et.res_si[i+1]) << for_this_node; } else { EXPECT_FALSE(node.val().out.has_reserved_stack()) << for_this_node; EXPECT_FALSE(r.end->val().in.has_reserved_stack()) << for_this_node; } if (et.com_si[i+1] >= 0) { EXPECT_EQ(node.val().out.committed_stack(), et.com_si[i+1]) << for_this_node; EXPECT_EQ(r.end->val().in.committed_stack(), et.com_si[i+1]) << for_this_node; } else { EXPECT_FALSE(node.val().out.has_committed_stack()) << for_this_node; EXPECT_FALSE(r.end->val().in.has_committed_stack()) << for_this_node; } } } template void print_tree(const ExpectedTree& et, int line_no) { const State Rs = State::Reserved; const State Rl = State::Released; const State C = State::Committed; stringStream ss; ss.print_cr("Tree nodes for line %d", line_no); ss.print_cr(" // 1 2 3 4 5"); ss.print_cr(" // 012345678901234567890123456789012345678901234567890"); ss.print (" // "); int j = 0; for (int i = 0; i < N; i++) { char state_char = et.states[i+1] == Rl ? '.' : et.states[i+1] == Rs ? 'r' : et.states[i+1] == C ? 'C' : ' '; if (i == 0 && et.nodes[i] != 0) { for (j = 0; j < et.nodes[i]; j++) { ss.put('.'); } } for (j = et.nodes[i]; i < (N - 1) && j < et.nodes[i + 1]; j++) { ss.put(state_char); } } for (; j <= 50; j++) { ss.put('.'); } tty->print_cr("%s", ss.base()); } }; TEST_VM_F(NMTVMATreeTest, OverlappingReservationsResultInTwoNodes) { VMATree::RegionData rd{si[0], mtTest}; Tree tree; VMATree::SummaryDiff not_used; for (int i = 99; i >= 0; i--) { tree.reserve_mapping(i * 100, 101, rd, not_used); } EXPECT_EQ(2, count_nodes(tree)); } TEST_VM_F(NMTVMATreeTest, DuplicateReserve) { VMATree::RegionData rd{si[0], mtTest}; Tree tree; VMATree::SummaryDiff not_used; tree.reserve_mapping(100, 100, rd, not_used); tree.reserve_mapping(100, 100, rd, not_used); EXPECT_EQ(2, count_nodes(tree)); VMATree::VMARBTree::Range r = tree.tree().find_enclosing_range(110); EXPECT_EQ(100, (int)(r.end->key() - r.start->key())); } TEST_VM_F(NMTVMATreeTest, UseTagInplace) { Tree tree; VMATree::SummaryDiff not_used; VMATree::RegionData rd_Test_cs0(si[0], mtTest); VMATree::RegionData rd_None_cs1(si[1], mtNone); tree.reserve_mapping(0, 100, rd_Test_cs0, not_used); // reserve: 0---------------------100 // commit: 20**********70 // uncommit: 30--40 // post-cond: 0---20**30--40**70----100 tree.commit_mapping(20, 50, rd_None_cs1, not_used, true); tree.uncommit_mapping(30, 10, rd_None_cs1, not_used); tree.visit_in_order([&](const TNode* node) { if (node->key() != 100) { EXPECT_EQ(mtTest, node->val().out.mem_tag()) << "failed at: " << node->key(); if (node->key() != 20 && node->key() != 40) { EXPECT_EQ(VMATree::StateType::Reserved, node->val().out.type()); } } return true; }); } // Low-level tests inspecting the state of the tree. TEST_VM_F(NMTVMATreeTest, LowLevel) { adjacent_2_nodes(VMATree::empty_regiondata); remove_all_leaves_empty_tree(VMATree::empty_regiondata); commit_middle(VMATree::empty_regiondata); commit_whole(VMATree::empty_regiondata); VMATree::RegionData rd{si[0], mtTest }; adjacent_2_nodes(rd); remove_all_leaves_empty_tree(rd); commit_middle(rd); commit_whole(rd); { // Identical operation but different metadata should not merge Tree tree; VMATree::SummaryDiff not_used; VMATree::RegionData rd_Test_cs0{si[0], mtTest}; VMATree::RegionData rd_NMT_cs1{si[1], mtNMT}; tree.reserve_mapping(0, 100, rd_Test_cs0, not_used); tree.reserve_mapping(100, 100, rd_NMT_cs1, not_used); EXPECT_EQ(3, count_nodes(tree)); int found_nodes = 0; } { // Reserving after commit should overwrite commit Tree tree; VMATree::SummaryDiff not_used; VMATree::RegionData rd_Test_cs0{si[0], mtTest}; VMATree::RegionData rd_NMT_cs1{si[1], mtNMT}; tree.commit_mapping(50, 50, rd_NMT_cs1, not_used); tree.reserve_mapping(0, 100, rd_Test_cs0, not_used); rbtree(tree).visit_in_order([&](const TNode* x) { EXPECT_TRUE(x->key() == 0 || x->key() == 100); if (x->key() == 0UL) { EXPECT_EQ(x->val().out.reserved_regiondata().mem_tag, mtTest); } return true; }); EXPECT_EQ(2, count_nodes(tree)); } { // Split a reserved region into two different reserved regions Tree tree; VMATree::SummaryDiff not_used; VMATree::RegionData rd_Test_cs0{si[0], mtTest}; VMATree::RegionData rd_NMT_cs1{si[1], mtNMT}; VMATree::RegionData rd_None_cs0{si[0], mtNone}; tree.reserve_mapping(0, 100, rd_Test_cs0, not_used); tree.reserve_mapping(0, 50, rd_NMT_cs1, not_used); tree.reserve_mapping(50, 50, rd_None_cs0, not_used); EXPECT_EQ(3, count_nodes(tree)); } { // One big reserve + release leaves an empty tree VMATree::RegionData rd_NMT_cs0{si[0], mtNMT}; Tree tree; VMATree::SummaryDiff not_used; tree.reserve_mapping(0, 500000, rd_NMT_cs0, not_used); tree.release_mapping(0, 500000, not_used); EXPECT_EQ(nullptr, rbtree_root(tree)); } { // A committed region inside of/replacing a reserved region // should replace the reserved region's metadata. VMATree::RegionData rd_NMT_cs0{si[0], mtNMT}; VMATree::RegionData rd_Test_cs1{si[1], mtTest}; Tree tree; VMATree::SummaryDiff not_used; tree.reserve_mapping(0, 100, rd_NMT_cs0, not_used); tree.commit_mapping(0, 100, rd_Test_cs1, not_used); rbtree(tree).visit_range_in_order(0, 99999, [&](TNode* x) { if (x->key() == 0) { EXPECT_EQ(mtTest, x->val().out.reserved_regiondata().mem_tag); } if (x->key() == 100) { EXPECT_EQ(mtTest, x->val().in.reserved_regiondata().mem_tag); } return true; }); } { // Attempting to reserve or commit an empty region should not change the tree. Tree tree; VMATree::SummaryDiff not_used; VMATree::RegionData rd_NMT_cs0{si[0], mtNMT}; tree.reserve_mapping(0, 0, rd_NMT_cs0, not_used); EXPECT_EQ(nullptr, rbtree_root(tree)); tree.commit_mapping(0, 0, rd_NMT_cs0, not_used); EXPECT_EQ(nullptr, rbtree_root(tree)); } } TEST_VM_F(NMTVMATreeTest, SetTag) { using State = VMATree::StateType; struct testrange { VMATree::position from; VMATree::position to; MemTag tag; NCS::StackIndex reserve_stack; State state; }; // Take a sorted list of testranges and check that those and only those are found in the tree. auto expect_equivalent_form = [&](auto& expected, VMATree& tree, int line_no) { // With auto& our arrays do not deteriorate to pointers but are kept as testrange[N] // so this actually works! size_t len = sizeof(expected) / sizeof(testrange); VMATree::position previous_to = 0; for (size_t i = 0; i < len; i++) { testrange expect = expected[i]; assert(previous_to == 0 || previous_to <= expect.from, "the expected list must be sorted"); previous_to = expect.to; VMATree::VMARBTree::Range found = tree.tree().find_enclosing_range(expect.from); ASSERT_NE(nullptr, found.start); ASSERT_NE(nullptr, found.end); // Same region EXPECT_EQ(expect.from, found.start->key()); EXPECT_EQ(expect.to, found.end->key()); // Same tag EXPECT_EQ(expect.tag, found.start->val().out.mem_tag()) << " and at test-line: " << line_no; EXPECT_EQ(expect.tag, found.end->val().in.mem_tag()) << " and at test-line: " << line_no; // Same stack EXPECT_EQ(expect.reserve_stack, found.start->val().out.reserved_stack()) << "Unexpected stack at region: " << i << " and at test-line: " << line_no; EXPECT_EQ(expect.reserve_stack, found.end->val().in.reserved_stack()) << "Unexpected stack at region: " << i << " and at test-line: " << line_no; // Same state EXPECT_EQ(expect.state, found.start->val().out.type()); EXPECT_EQ(expect.state, found.end->val().in.type()); } // expected must cover all nodes EXPECT_EQ(len+1, tree.tree().size()); }; NCS::StackIndex si = NCS::StackIndex(); NCS::StackIndex es = NCS::invalid; // empty or no stack is stored Tree::RegionData rd(si, mtNone); { // The gc/cds case with only reserved data testrange expected[2]{ { 0, 500, mtGC, si, State::Reserved}, {500, 600, mtClassShared, si, State::Reserved} }; VMATree tree; VMATree::SummaryDiff not_used; tree.reserve_mapping(0, 600, rd, not_used); tree.set_tag(0, 500, mtGC, not_used); tree.set_tag(500, 100, mtClassShared, not_used); expect_equivalent_form(expected, tree, __LINE__); } { // Now let's add in some committed data testrange expected[]{ { 0, 100, mtGC, si, State::Reserved}, {100, 225, mtGC, si, State::Committed}, {225, 500, mtGC, si, State::Reserved}, {500, 550, mtClassShared, si, State::Reserved}, {550, 560, mtClassShared, si, State::Committed}, {560, 565, mtClassShared, si, State::Reserved}, {565, 575, mtClassShared, si, State::Committed}, {575, 600, mtClassShared, si, State::Reserved} }; VMATree tree; VMATree::SummaryDiff not_used; // 0---------------------------------------------------600 // 100****225 // 550***560 // 565***575 // 0------100****225---------550***560---565***575-----600 // 0------100****225---500---550***560---565***575-----600 // <-------mtGC---------><-----------mtClassShared-------> tree.reserve_mapping(0, 600, rd, not_used); // The committed areas tree.commit_mapping(100, 125, rd, not_used); tree.commit_mapping(550, 10, rd, not_used); tree.commit_mapping(565, 10, rd, not_used); // OK, set tag tree.set_tag(0, 500, mtGC, not_used); tree.set_tag(500, 100, mtClassShared, not_used); expect_equivalent_form(expected, tree, __LINE__); } { // Setting the tag for adjacent regions with same stacks should merge the regions testrange expected[]{ {0, 200, mtGC, si, State::Reserved} }; VMATree tree; VMATree::SummaryDiff not_used; Tree::RegionData gc(si, mtGC); Tree::RegionData compiler(si, mtCompiler); tree.reserve_mapping(0, 100, gc, not_used); tree.reserve_mapping(100, 100, compiler, not_used); tree.set_tag(0, 200, mtGC, not_used); expect_equivalent_form(expected, tree, __LINE__); } { // Setting the tag for adjacent regions with different stacks should NOT merge the regions NCS::StackIndex si1 = 1; NCS::StackIndex si2 = 2; testrange expected[]{ { 0, 100, mtGC, si1, State::Reserved}, {100, 200, mtGC, si2, State::Reserved} }; VMATree tree; VMATree::SummaryDiff not_used; Tree::RegionData gc(si1, mtGC); Tree::RegionData compiler(si2, mtCompiler); tree.reserve_mapping(0, 100, gc, not_used); tree.reserve_mapping(100, 100, compiler, not_used); tree.set_tag(0, 200, mtGC, not_used); expect_equivalent_form(expected, tree, __LINE__); } { // Setting the tag in the middle of a range causes a split testrange expected[]{ { 0, 100, mtCompiler, si, State::Reserved}, {100, 150, mtGC, si, State::Reserved}, {150, 200, mtCompiler, si, State::Reserved} }; VMATree tree; VMATree::SummaryDiff not_used; Tree::RegionData compiler(si, mtCompiler); tree.reserve_mapping(0, 200, compiler, not_used); tree.set_tag(100, 50, mtGC, not_used); expect_equivalent_form(expected, tree, __LINE__); } { // Setting the tag in between two ranges causes a split testrange expected[]{ { 0, 75, mtGC, si, State::Reserved}, { 75, 125, mtClass, si, State::Reserved}, {125, 200, mtCompiler, si, State::Reserved}, }; VMATree tree; VMATree::SummaryDiff not_used; Tree::RegionData gc(si, mtGC); Tree::RegionData compiler(si, mtCompiler); tree.reserve_mapping(0, 100, gc, not_used); tree.reserve_mapping(100, 100, compiler, not_used); tree.set_tag(75, 50, mtClass, not_used); expect_equivalent_form(expected, tree, __LINE__); } { // Holes in the address range are acceptable and untouched testrange expected[]{ { 0, 50, mtGC, si, State::Reserved}, {50, 75, mtNone, es, State::Released}, {75, 80, mtGC, si, State::Reserved}, {80, 100, mtClassShared, si, State::Reserved} }; VMATree tree; VMATree::SummaryDiff not_used; Tree::RegionData class_shared(si, mtClassShared); tree.reserve_mapping(0, 50, class_shared, not_used); tree.reserve_mapping(75, 25, class_shared, not_used); tree.set_tag(0, 80, mtGC, not_used); expect_equivalent_form(expected, tree, __LINE__); } { // Check that setting tag with 'hole' not consisting of any regions work testrange expected[]{ {10, 20, mtCompiler, si, State::Reserved} }; VMATree tree; VMATree::SummaryDiff not_used; Tree::RegionData class_shared(si, mtClassShared); tree.reserve_mapping(10, 10, class_shared, not_used); tree.set_tag(0, 100, mtCompiler, not_used); expect_equivalent_form(expected, tree, __LINE__); } { // Check that multiple holes still work testrange expected[]{ { 0, 1, mtGC, si, State::Reserved}, { 1, 50, mtNone, es, State::Released}, {50, 75, mtGC, si, State::Reserved}, {75, 99, mtNone, es, State::Released}, {99, 100, mtGC, si, State::Reserved} }; VMATree tree; VMATree::SummaryDiff not_used; Tree::RegionData class_shared(si, mtClassShared); tree.reserve_mapping(0, 100, class_shared, not_used); tree.release_mapping(1, 49, not_used); tree.release_mapping(75, 24, not_used); tree.set_tag(0, 100, mtGC, not_used); expect_equivalent_form(expected, tree, __LINE__); } } // Tests for summary accounting TEST_VM_F(NMTVMATreeTest, SummaryAccounting) { { // Fully enclosed re-reserving works correctly. Tree::RegionData rd_Test_cs0(NCS::StackIndex(), mtTest); Tree::RegionData rd_NMT_cs0(NCS::StackIndex(), mtNMT); Tree tree; VMATree::SummaryDiff all_diff; tree.reserve_mapping(0, 100, rd_Test_cs0, all_diff); // 1 2 3 4 5 6 7 8 9 10 11 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.......... // Legend: // A - Test (reserved) // . - free VMATree::SingleDiff diff = all_diff.tag(mtTest); EXPECT_EQ(100, diff.reserve); tree.reserve_mapping(50, 25, rd_NMT_cs0, all_diff); // 1 2 3 4 5 6 7 8 9 10 11 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCC.......... // Legend: // A - Test (reserved) // B - Native Memory Tracking (reserved) // C - Test (reserved) // . - free diff = all_diff.tag(mtTest); VMATree::SingleDiff diff2 = all_diff.tag(mtNMT); EXPECT_EQ(-25, diff.reserve); EXPECT_EQ(25, diff2.reserve); } { // Fully release reserved mapping Tree::RegionData rd_Test_cs0(NCS::StackIndex(), mtTest); Tree tree; VMATree::SummaryDiff all_diff; tree.reserve_mapping(0, 100, rd_Test_cs0, all_diff); // 1 2 3 4 5 6 7 8 9 10 11 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.......... // Legend: // A - Test (reserved) // . - free VMATree::SingleDiff diff = all_diff.tag(mtTest); EXPECT_EQ(100, diff.reserve); tree.release_mapping(0, 100, all_diff); // 1 2 3 4 5 6 7 8 9 10 11 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 // .............................................................................................................. // Legend: diff = all_diff.tag(mtTest); EXPECT_EQ(-100, diff.reserve); } { // Convert some of a released mapping to a committed one Tree::RegionData rd_Test_cs0(NCS::StackIndex(), mtTest); Tree tree; VMATree::SummaryDiff all_diff; tree.reserve_mapping(0, 100, rd_Test_cs0, all_diff); // 1 2 3 4 5 6 7 8 9 10 11 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.......... // Legend: // A - Test (reserved) // . - free VMATree::SingleDiff diff = all_diff.tag(mtTest); EXPECT_EQ(diff.reserve, 100); tree.commit_mapping(0, 100, rd_Test_cs0, all_diff); // 1 2 3 4 5 6 7 8 9 10 11 // 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.......... // Legend: // a - Test (committed) // . - free diff = all_diff.tag(mtTest); EXPECT_EQ(0, diff.reserve); EXPECT_EQ(100, diff.commit); } { // Adjacent reserved mappings with same type Tree::RegionData rd_Test_cs0(NCS::StackIndex(), mtTest); Tree tree; VMATree::SummaryDiff all_diff; tree.reserve_mapping(0, 10, rd_Test_cs0, all_diff); // 1 2 // 01234567890123456789 // AAAAAAAAAA.......... // Legend: // A - Test (reserved) // . - free VMATree::SingleDiff diff = all_diff.tag(mtTest); EXPECT_EQ(diff.reserve, 10); tree.reserve_mapping(10, 10, rd_Test_cs0, all_diff); // 1 2 3 // 012345678901234567890123456789 // AAAAAAAAAAAAAAAAAAAA.......... // Legend: // A - Test (reserved) // . - free diff = all_diff.tag(mtTest); EXPECT_EQ(10, diff.reserve); } { // Adjacent reserved mappings with different tags Tree::RegionData rd_Test_cs0(NCS::StackIndex(), mtTest); Tree::RegionData rd_NMT_cs0(NCS::StackIndex(), mtNMT); Tree tree; VMATree::SummaryDiff all_diff; tree.reserve_mapping(0, 10, rd_Test_cs0, all_diff); // 1 2 // 01234567890123456789 // AAAAAAAAAA.......... // Legend: // A - Test (reserved) // . - free VMATree::SingleDiff diff = all_diff.tag(mtTest); EXPECT_EQ(diff.reserve, 10); tree.reserve_mapping(10, 10, rd_NMT_cs0, all_diff); // 1 2 3 // 012345678901234567890123456789 // AAAAAAAAAABBBBBBBBBB.......... // Legend: // A - Test (reserved) // B - Native Memory Tracking (reserved) // . - free diff = all_diff.tag(mtTest); EXPECT_EQ(0, diff.reserve); diff = all_diff.tag(mtNMT); EXPECT_EQ(10, diff.reserve); } { // A commit with two previous commits inside of it should only register // the new memory in the commit diff. Tree tree; VMATree::SummaryDiff diff; Tree::RegionData rd_Test_cs0(NCS::StackIndex(), mtTest); tree.commit_mapping(16, 16, rd_Test_cs0, diff); // 1 2 3 4 // 0123456789012345678901234567890123456789 // ................aaaaaaaaaaaaaaaa.......... // Legend: // a - Test (committed) // . - free tree.commit_mapping(32, 32, rd_Test_cs0, diff); // 1 2 3 4 5 6 7 // 0123456789012345678901234567890123456789012345678901234567890123456789 // ................aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.......... // Legend: // a - Test (committed) // . - free tree.commit_mapping(0, 64, rd_Test_cs0, diff); // 1 2 3 4 5 6 7 // 0123456789012345678901234567890123456789012345678901234567890123456789 // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.......... // Legend: // a - Test (committed) // . - free EXPECT_EQ(16, diff.tag(mtTest).commit); EXPECT_EQ(16, diff.tag(mtTest).reserve); } } TEST_VM_F(NMTVMATreeTest, SummaryAccountingReserveAsUncommit) { Tree tree; Tree::RegionData rd(NCS::StackIndex(), mtTest); VMATree::SummaryDiff diff1, diff2, diff3; tree.reserve_mapping(1200, 100, rd, diff1); tree.commit_mapping(1210, 50, rd, diff2); EXPECT_EQ(100, diff1.tag(mtTest).reserve); EXPECT_EQ(50, diff2.tag(mtTest).commit); tree.reserve_mapping(1220, 20, rd, diff3); EXPECT_EQ(-20, diff3.tag(mtTest).commit); EXPECT_EQ(0, diff3.tag(mtTest).reserve); } // Exceedingly simple tracker for page-granular allocations // Use it for testing consistency with VMATree. struct SimpleVMATracker : public CHeapObj { const size_t page_size = 4096; enum Kind { Reserved, Committed, Free }; struct Info { Kind kind; MemTag mem_tag; NativeCallStack stack; Info() : kind(Free), mem_tag(mtNone), stack() {} Info(Kind kind, NativeCallStack stack, MemTag mem_tag) : kind(kind), mem_tag(mem_tag), stack(stack) {} bool eq(Info other) { return kind == other.kind && stack.equals(other.stack); } }; // Page (4KiB) granular array static constexpr const size_t num_pages = 1024 * 4; Info pages[num_pages]; SimpleVMATracker() : pages() { for (size_t i = 0; i < num_pages; i++) { pages[i] = Info(); } } void do_it(Kind kind, size_t start, size_t size, NativeCallStack stack, MemTag mem_tag, VMATree::SummaryDiff& diff) { assert(is_aligned(size, page_size) && is_aligned(start, page_size), "page alignment"); const size_t page_count = size / page_size; const size_t start_idx = start / page_size; const size_t end_idx = start_idx + page_count; assert(end_idx < SimpleVMATracker::num_pages, ""); Info new_info(kind, stack, mem_tag); for (size_t i = start_idx; i < end_idx; i++) { Info& old_info = pages[i]; // Register diff if (old_info.kind == Reserved) { diff.tag(old_info.mem_tag).reserve -= page_size; } else if (old_info.kind == Committed) { diff.tag(old_info.mem_tag).reserve -= page_size; diff.tag(old_info.mem_tag).commit -= page_size; } if (kind == Reserved) { diff.tag(new_info.mem_tag).reserve += page_size; } else if (kind == Committed) { diff.tag(new_info.mem_tag).reserve += page_size; diff.tag(new_info.mem_tag).commit += page_size; } // Overwrite old one with new pages[i] = new_info; } } void reserve(size_t start, size_t size, NativeCallStack stack, MemTag mem_tag, VMATree::SummaryDiff& diff) { return do_it(Reserved, start, size, stack, mem_tag, diff); } void commit(size_t start, size_t size, NativeCallStack stack, MemTag mem_tag, VMATree::SummaryDiff& diff) { return do_it(Committed, start, size, stack, mem_tag, diff); } void release(size_t start, size_t size, VMATree::SummaryDiff& diff) { return do_it(Free, start, size, NativeCallStack(), mtNone, diff); } }; constexpr const size_t SimpleVMATracker::num_pages; TEST_VM_F(NMTVMATreeTest, TestConsistencyWithSimpleTracker) { // In this test we use ASSERT macros from gtest instead of EXPECT // as any error will propagate and become larger as the test progresses. SimpleVMATracker* tr = new SimpleVMATracker(); const size_t page_size = tr->page_size; VMATree tree; NCS ncss(true); constexpr const int candidates_len_tags = 4; constexpr const int candidates_len_stacks = 2; NativeCallStack candidate_stacks[candidates_len_stacks] = { make_stack(0xA), make_stack(0xB), }; const MemTag candidate_tags[candidates_len_tags] = { mtNMT, mtTest, }; const int operation_count = 100000; // One hundred thousand for (int i = 0; i < operation_count; i++) { size_t page_start = (size_t)(os::random() % SimpleVMATracker::num_pages); size_t page_end = (size_t)(os::random() % (SimpleVMATracker::num_pages)); if (page_end < page_start) { const size_t temp = page_start; page_start = page_end; page_end = page_start; } const size_t num_pages = page_end - page_start; if (num_pages == 0) { i--; continue; } const size_t start = page_start * page_size; const size_t size = num_pages * page_size; const MemTag mem_tag = candidate_tags[os::random() % candidates_len_tags]; const NativeCallStack stack = candidate_stacks[os::random() % candidates_len_stacks]; const NCS::StackIndex si = ncss.push(stack); VMATree::RegionData data(si, mem_tag); const SimpleVMATracker::Kind kind = (SimpleVMATracker::Kind)(os::random() % 3); VMATree::SummaryDiff tree_diff; VMATree::SummaryDiff simple_diff; if (kind == SimpleVMATracker::Reserved) { tr->reserve(start, size, stack, mem_tag, simple_diff); tree.reserve_mapping(start, size, data, tree_diff); } else if (kind == SimpleVMATracker::Committed) { tr->commit(start, size, stack, mem_tag, simple_diff); tree.commit_mapping(start, size, data, tree_diff); } else { tr->release(start, size, simple_diff); tree.release_mapping(start, size, tree_diff); } for (int j = 0; j < mt_number_of_tags; j++) { VMATree::SingleDiff td = tree_diff.tag(j); VMATree::SingleDiff sd = simple_diff.tag(j); ASSERT_EQ(td.reserve, sd.reserve); ASSERT_EQ(td.commit, sd.commit); } // Do an in-depth check every 25 000 iterations. if (i % 25000 == 0) { size_t j = 0; while (j < SimpleVMATracker::num_pages) { while (j < SimpleVMATracker::num_pages && tr->pages[j].kind == SimpleVMATracker::Free) { j++; } if (j == SimpleVMATracker::num_pages) { break; } size_t start = j; SimpleVMATracker::Info starti = tr->pages[start]; while (j < SimpleVMATracker::num_pages && tr->pages[j].eq(starti)) { j++; } size_t end = j-1; ASSERT_LE(end, SimpleVMATracker::num_pages); SimpleVMATracker::Info endi = tr->pages[end]; VMATree::VMARBTree& rbtree = this->rbtree(tree); VMATree::TNode* startn = find(rbtree, start * page_size); ASSERT_NE(nullptr, startn); VMATree::TNode* endn = find(rbtree, (end * page_size) + page_size); ASSERT_NE(nullptr, endn); const NativeCallStack& start_stack = ncss.get(startn->val().out.reserved_stack()); const NativeCallStack& end_stack = ncss.get(endn->val().in.reserved_stack()); // If start-node of a reserved region is committed, the stack is stored in the second_stack of the node. if (startn->val().out.has_committed_stack()) { const NativeCallStack& start_second_stack = ncss.get(startn->val().out.committed_stack()); ASSERT_TRUE(starti.stack.equals(start_stack) || starti.stack.equals(start_second_stack)); } else { ASSERT_TRUE(starti.stack.equals(start_stack)); } if (endn->val().in.has_committed_stack()) { const NativeCallStack& end_second_stack = ncss.get(endn->val().in.committed_stack()); ASSERT_TRUE(endi.stack.equals(end_stack) || endi.stack.equals(end_second_stack)); } else { ASSERT_TRUE(endi.stack.equals(end_stack)); } ASSERT_EQ(starti.mem_tag, startn->val().out.mem_tag()); ASSERT_EQ(endi.mem_tag, endn->val().in.mem_tag()); } } } } TEST_VM_F(NMTVMATreeTest, SummaryAccountingWhenUseTagInplace) { Tree tree; VMATree::SummaryDiff diff; VMATree::RegionData rd_Test_cs0(si[0], mtTest); VMATree::RegionData rd_None_cs1(si[1], mtNone); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .................................................. tree.reserve_mapping(0, 50, rd_Test_cs0, diff); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr tree.commit_mapping(0, 25, rd_None_cs1, diff, true); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // CCCCCCCCCCCCCCCCCCCCCCCCCrrrrrrrrrrrrrrrrrrrrrrrrr EXPECT_EQ(0, diff.tag(mtTest).reserve); EXPECT_EQ(25, diff.tag(mtTest).commit); tree.commit_mapping(30, 5, rd_None_cs1, diff, true); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // CCCCCCCCCCCCCCCCCCCCCCCCCrrrrrCCCCCrrrrrrrrrrrrrrr EXPECT_EQ(0, diff.tag(mtTest).reserve); EXPECT_EQ(5, diff.tag(mtTest).commit); tree.uncommit_mapping(0, 25, rd_None_cs1, diff); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrCCCCCrrrrrrrrrrrrrrr EXPECT_EQ(0, diff.tag(mtTest).reserve); EXPECT_EQ(-25, diff.tag(mtTest).commit); } // How the memory regions are visualized: // 1 2 3 4 5 6 7 | // 0123456789012345678901234567890123456789012345678901234567890123456789 |_> memory address // aaaaaaBBBBBBBcccccccDDDDDDDeeeeeeeFFFFFFFF........................... |->some letters showing the state of the memory // Legend: // . - None (free/released) // r - MemTag (reserved) // C - MemTag (committed) // MemTag is Test if omitted. TEST_VM_F(NMTVMATreeTest, SeparateStacksForCommitAndReserve) { using SIndex = NativeCallStackStorage::StackIndex; using State = VMATree::StateType; SIndex si_1 = si[0]; SIndex si_2 = si[1]; const State Rs = State::Reserved; const State Rl = State::Released; const State C = State::Committed; VMATree::RegionData rd_Test_cs1(si_1, mtTest); VMATree::RegionData rd_None_cs2(si_2, mtNone); {// Check committing into a reserved region inherits the call stacks Tree tree; VMATree::SummaryDiff diff; tree.reserve_mapping(0, 50, rd_Test_cs1, diff); // reserve in an empty tree // Pre: empty tree. // Post: // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr. ExpectedTree<2> et1 = {{ 0, 50 }, {mtNone, mtTest, mtNone}, {Rl , Rs , Rl }, {-1 , si_1 , -1 }, {-1 , -1 , -1 }}; check_tree(tree, et1, __LINE__); tree.commit_mapping(25, 10, rd_None_cs2, diff, true); // commit at the middle of the region // Post: // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrrrrrrrrrrrrrrrrCCCCCCCCCCrrrrrrrrrrrrrrr. ExpectedTree<4> et2 = {{ 0, 25, 35, 50 }, {mtNone, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , C , Rs , Rl }, {-1 , si_1 , si_1 , si_1 , -1 }, {-1 , -1 , si_2 , -1 , -1 }}; check_tree(tree, et2, __LINE__); tree.commit_mapping(0, 20, rd_None_cs2, diff, true); // commit at the beginning of the region // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // CCCCCCCCCCCCCCCCCCCCrrrrrCCCCCCCCCCrrrrrrrrrrrrrrr. ExpectedTree<5> et3 = {{ 0, 20, 25, 35, 50 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , C , Rs , C , Rs , Rl }, {-1 , si_1 , si_1 , si_1 , si_1 , -1 }, {-1 , si_2 , -1 , si_2 , -1 , -1 }}; check_tree(tree, et3, __LINE__); tree.commit_mapping(40, 10, rd_None_cs2, diff, true); // commit at the end of the region // Post: // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // CCCCCCCCCCCCCCCCCCCCrrrrrCCCCCCCCCCrrrrrCCCCCCCCCC. ExpectedTree<6> et4 = {{ 0, 20, 25, 35, 40, 50 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , C , Rs , C , Rs , C , Rl }, {-1 , si_1 , si_1 , si_1 , si_1 , si_1 , -1 }, {-1 , si_2 , -1 , si_2 , -1 , si_2 , -1 }}; check_tree(tree, et4, __LINE__); } {// committing overlapped regions does not destroy the old call-stacks Tree tree; VMATree::SummaryDiff diff; tree.reserve_mapping(0, 50, rd_Test_cs1, diff); // reserving in an empty tree // Pre: empty tree. // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr ExpectedTree<2> et1 = {{ 0 , 50 }, {mtNone, mtTest, mtNone}, {Rl , Rs , Rl }, {-1 , si_1 , -1 }, {-1 , -1 , -1 }}; check_tree(tree, et1, __LINE__); tree.commit_mapping(10, 10, rd_None_cs2, diff, true); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrCCCCCCCCCCrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr ExpectedTree<4> et2 = {{ 0, 10, 20, 50 }, {mtNone, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , C , Rs , Rl }, {-1 , si_1 , si_1 , si_1 , -1 }, {-1 , -1 , si_2 , -1 , -1 }}; check_tree(tree, et2, __LINE__); SIndex si_3 = si[2]; VMATree::RegionData rd_Test_cs3(si_3, mtTest); // commit with overlap at the region's start tree.commit_mapping(5, 10, rd_Test_cs3, diff); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrCCCCCCCCCCCCCCCrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr ExpectedTree<5> et3 = {{ 0, 5, 15, 20, 50 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , C , C , Rs , Rl }, {-1 , si_1 , si_1 , si_1 , si_1 , -1 }, {-1 , -1 , si_3 , si_2 , -1 , -1 }}; check_tree(tree, et3, __LINE__); SIndex si_4 = si[3]; VMATree::RegionData call_stack_4(si_4, mtTest); // commit with overlap at the region's end tree.commit_mapping(15, 10, call_stack_4, diff); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrCCCCCCCCCCCCCCCCCCCCrrrrrrrrrrrrrrrrrrrrrrrrr ExpectedTree<5> et4 = {{ 0, 5, 15, 25, 50 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , C , C , Rs , Rl }, {-1 , si_1 , si_1 , si_1 , si_1 , -1 }, {-1 , -1 , si_3 , si_4 , -1 , -1 }}; check_tree(tree, et4, __LINE__); } {// uncommit should not store any call-stack Tree tree; VMATree::SummaryDiff diff; tree.reserve_mapping(0, 50, rd_Test_cs1, diff); tree.commit_mapping(10, 10, rd_None_cs2, diff, true); tree.commit_mapping(0, 5, rd_None_cs2, diff, true); tree.uncommit_mapping(0, 3, rd_None_cs2, diff); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrCCrrrrrCCCCCCCCCCrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr ExpectedTree<6> et1 = {{ 0, 3, 5, 10, 20, 50 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , C , Rs , C , Rs , Rl }, {-1 , si_1 , si_1 , si_1 , si_1 , si_1 , -1 }, {-1 , -1 , si_2 , -1 , si_2 , -1 , -1 }}; check_tree(tree, et1, __LINE__); tree.uncommit_mapping(5, 10, rd_None_cs2, diff); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrCCrrrrrrrrrrCCCCCrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr. ExpectedTree<6> et2 = {{ 0, 3, 5, 15, 20, 50 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , C , Rs , C , Rs , Rl }, {-1 , si_1 , si_1 , si_1 , si_1 , si_1 , -1 }, {-1 , -1 , si_2 , -1 , si_2 , -1 , -1 }}; check_tree(tree, et2, __LINE__); } {// reserve after reserve, but only different call-stacks SIndex si_4 = si[3]; VMATree::RegionData call_stack_4(si_4, mtTest); Tree tree; VMATree::SummaryDiff diff; tree.reserve_mapping(0, 50, rd_Test_cs1, diff); tree.reserve_mapping(10, 10, call_stack_4, diff); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr ExpectedTree<4> et1 = {{ 0, 10, 20, 50 }, {mtNone, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rs , Rs , Rl }, {-1 , si_1 , si_4 , si_1 , -1 }, {-1 , -1 , -1 , -1 , -1 }}; check_tree(tree, et1, __LINE__); } {// commit without reserve Tree tree; VMATree::SummaryDiff diff; tree.commit_mapping(0, 50, rd_Test_cs1, diff); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC ExpectedTree<2> et = {{ 0, 50 }, {mtNone, mtTest, mtNone}, {Rl , C , Rl }, {-1 , si_1 , -1 }, {-1 , si_1 , -1 }}; check_tree(tree, et, __LINE__); } {// reserve after commit Tree tree; VMATree::SummaryDiff diff; tree.commit_mapping(0, 50, rd_None_cs2, diff); tree.reserve_mapping(0, 50, rd_Test_cs1, diff); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr ExpectedTree<2> et = {{ 0, 50 }, {mtNone, mtTest, mtNone}, {Rl , Rs , Rl }, {-1 , si_1 , -1 }, {-1 , -1 , -1 }}; check_tree(tree, et, __LINE__); } } TEST_VM_F(NMTVMATreeTest, OverlapTableRows0To3) { using SIndex = NativeCallStackStorage::StackIndex; using State = VMATree::StateType; SIndex si_1 = si[0]; SIndex si_2 = si[1]; SIndex si_3 = si[2]; const State Rs = State::Reserved; const State Rl = State::Released; const State C = State::Committed; VMATree::RegionData rd_Test_cs1(si_1, mtTest); VMATree::RegionData rd_None_cs1(si_1, mtNone); VMATree::RegionData rd_Test_cs2(si_2, mtTest); VMATree::RegionData rd_None_cs2(si_2, mtNone); VMATree::RegionData rd_None_cs3(si_3, mtNone); // row 0: .........A..................B..... // case of empty tree is already covered in other tests. // row 1 is impossible. See the implementation. { // row 2: .........A...Y.......................W.....B.......... // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr......................................... Tree tree; ExpectedTree<5> pre = {{ 10, 12, 14, 16, 20 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rs , Rs , Rs , Rl }, {-1 , si_1 , si_2 , si_1 , si_2 , -1 }, {-1 , -1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(5, 20, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCCCCCCC.......................... EXPECT_EQ(diff.tag(mtTest).commit, 20); EXPECT_EQ(diff.tag(mtTest).reserve, 10); ExpectedTree<6> et = {{ 5, 10, 12, 14, 16, 25 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , C , C , C , C , C , Rl }, {-1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 }, {-1 , si_2 , si_2 , si_2 , si_2 , si_2 , -1 } }; check_tree(tree, et, __LINE__); } { // row 3: .........A...Y.......................WB..... // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // ..........rrrrrrrrrr............................... Tree tree; ExpectedTree<5> pre = {{ 10, 12, 14, 16, 20 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rs , Rs , Rs , Rl }, {-1 , si_1 , si_2 , si_1 , si_2 , -1 }, {-1 , -1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(5, 15, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCC............................... EXPECT_EQ(diff.tag(mtTest).commit, 15); EXPECT_EQ(diff.tag(mtTest).reserve, 5); ExpectedTree<6> et = {{ 5, 10, 12, 14, 16, 20 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , C , C , C , C , C , Rl }, {-1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 }, {-1 , si_2 , si_2 , si_2 , si_2 , si_2 , -1 } }; check_tree(tree, et, __LINE__); } } TEST_VM_F(NMTVMATreeTest, OverlapTableRows4to7) { using SIndex = NativeCallStackStorage::StackIndex; using State = VMATree::StateType; SIndex si_1 = si[0]; SIndex si_2 = si[1]; SIndex si_3 = si[2]; const State Rs = State::Reserved; const State Rl = State::Released; const State C = State::Committed; VMATree::RegionData rd_Test_cs1(si_1, mtTest); VMATree::RegionData rd_None_cs1(si_1, mtNone); VMATree::RegionData rd_Test_cs2(si_2, mtTest); VMATree::RegionData rd_None_cs2(si_2, mtNone); VMATree::RegionData rd_None_cs3(si_3, mtNone); { // row 4: .....X...A..................B..... // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr......................................... Tree tree; ExpectedTree<2> pre = {{ 0, 10, }, {mtNone, mtTest, mtNone}, {Rl , Rs , Rl }, {-1 , si_1 , -1 }, {-1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(20, 20, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr..........CCCCCCCCCCCCCCCCCCCC........... EXPECT_EQ(diff.tag(mtTest).commit, 20); EXPECT_EQ(diff.tag(mtTest).reserve, 20); ExpectedTree<4> et = {{ 0, 10, 20, 40 }, {mtNone, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , C , Rl }, {-1 , si_1 , -1 , si_2 , -1 }, {-1 , -1 , -1 , si_2 , -1 } }; check_tree(tree, et, __LINE__); } { // row 5: .....X...A...YW.............B..... // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....rrrrrrrrrr.................................... Tree tree; ExpectedTree<2> pre = {{ 5, 15, }, {mtNone, mtTest, mtNone}, {Rl , Rs , Rl }, {-1 , si_1 , -1 }, {-1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(10, 10, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....rrrrrCCCCCCCCCC............................... EXPECT_EQ(diff.tag(mtTest).commit, 10); EXPECT_EQ(diff.tag(mtTest).reserve, 20 - 15); ExpectedTree<4> et = {{ 5, 10, 15, 20 }, {mtNone, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , C , C , Rl }, {-1 , si_1 , si_1 , si_2 , -1 }, {-1 , -1 , si_2 , si_2 , -1 } }; check_tree(tree, et, __LINE__); } { // row 6: .....X...A.....Y.......................W.....B... // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr.....rrrrrrrrrr............................... Tree tree; ExpectedTree<7> pre = {{ 0, 5, 10, 12, 14, 16, 20 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rl , Rs , Rs , Rs , Rs , Rl }, {-1 , si_1 , -1 , si_1 , si_2 , si_1 , si_2 , -1 }, {-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(7, 20, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr..CCCCCCCCCCCCCCCCCCCC........................ EXPECT_EQ(diff.tag(mtTest).commit, 20); EXPECT_EQ(diff.tag(mtTest).reserve, 10); ExpectedTree<8> et = {{ 0, 5, 7, 10, 12, 14, 16, 27 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rl , C , C , C , C , C , Rl }, {-1 , si_1 , -1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 }, {-1 , -1 , -1 , si_2 , si_2 , si_2 , si_2 , si_2 , -1 } }; check_tree(tree, et, __LINE__); } { // row 7: .....X...A...Y.......................WB..... // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr.....rrrrrrrrrr............................... Tree tree; ExpectedTree<7> pre = {{ 0, 5, 10, 12, 14, 16, 20 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rl , Rs , Rs , Rs , Rs , Rl }, {-1 , si_1 , -1 , si_1 , si_2 , si_1 , si_2 , -1 }, {-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(7, 13, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr..CCCCCCCCCCCCC............................... EXPECT_EQ(diff.tag(mtTest).commit, 13); EXPECT_EQ(diff.tag(mtTest).reserve, 3); ExpectedTree<8> et = {{ 0, 5, 7, 10, 12, 14, 16, 20 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rl , C , C , C , C , C , Rl }, {-1 , si_1 , -1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 }, {-1 , -1 , -1 , si_2 , si_2 , si_2 , si_2 , si_2 , -1 } }; check_tree(tree, et, __LINE__); } } TEST_VM_F(NMTVMATreeTest, OverlapTableRows8to11) { using SIndex = NativeCallStackStorage::StackIndex; using State = VMATree::StateType; SIndex si_1 = si[0]; SIndex si_2 = si[1]; SIndex si_3 = si[2]; const State Rs = State::Reserved; const State Rl = State::Released; const State C = State::Committed; VMATree::RegionData rd_Test_cs1(si_1, mtTest); VMATree::RegionData rd_None_cs1(si_1, mtNone); VMATree::RegionData rd_Test_cs2(si_2, mtTest); VMATree::RegionData rd_None_cs2(si_2, mtNone); VMATree::RegionData rd_None_cs3(si_3, mtNone); { // row 8: ........XA..................B..... // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr......................................... // nodes: 0--------50........................... // si1 // - // request: 50*****************250 // post: 0--------50*****************250 // si1 si2 // - si2 Tree tree; ExpectedTree<2> pre = {{ 0, 10, }, {mtNone, mtTest, mtNone}, {Rl , Rs , Rl }, {-1 , si_1 , -1 }, {-1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(10, 20, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrCCCCCCCCCCCCCCCCCCCC..................... EXPECT_EQ(diff.tag(mtTest).commit, 20); EXPECT_EQ(diff.tag(mtTest).reserve, 20); ExpectedTree<3> et = {{ 0, 10, 30 }, {mtNone, mtTest, mtTest, mtNone}, {Rl , Rs , C , Rl }, {-1 , si_1 , si_2 , -1 }, {-1 , -1 , si_2 , -1 } }; check_tree(tree, et, __LINE__); } { // row 9: ........XA....YW.............B..... // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr......................................... Tree tree; ExpectedTree<2> pre = {{ 0, 10, }, {mtNone, mtTest, mtNone}, {Rl , Rs , Rl }, {-1 , si_1 , -1 }, {-1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(0, 20, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // CCCCCCCCCCCCCCCCCCCC............................... EXPECT_EQ(diff.tag(mtTest).commit, 20); EXPECT_EQ(diff.tag(mtTest).reserve, 10); ExpectedTree<3> et = {{ 0, 10, 20 }, {mtNone, mtTest, mtTest, mtNone}, {Rl , C , C , Rl }, {-1 , si_1 , si_2 , -1 }, {-1 , si_2 , si_2 , -1 } }; check_tree(tree, et, __LINE__); } { // row 10: ........XA...Y.......................W.....B... // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....rrrrrrrrrrrrrrr............................... Tree tree; ExpectedTree<6> pre = {{ 5, 10, 12, 14, 16, 20 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rs , Rs , Rs , Rs , Rl }, {-1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 }, {-1 , -1 , -1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(5, 20, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCCCCCCC.......................... EXPECT_EQ(diff.tag(mtTest).commit, 20); EXPECT_EQ(diff.tag(mtTest).reserve, 25 - 20); ExpectedTree<6> et = {{ 5, 10, 12, 14, 16, 25 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , C , C , C , C , C , Rl }, {-1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 }, {-1 , si_2 , si_2 , si_2 , si_2 , si_2 , -1 } }; check_tree(tree, et, __LINE__); } { // row 11: ........XA...Y.......................WB..... // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....rrrrrrrrrrrrrrr............................... Tree tree; ExpectedTree<6> pre = {{ 5, 10, 12, 14, 16, 20 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rs , Rs , Rs , Rs , Rl }, {-1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 }, {-1 , -1 , -1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(5, 15, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCC............................... EXPECT_EQ(diff.tag(mtTest).commit, 15); EXPECT_EQ(diff.tag(mtTest).reserve, 0); ExpectedTree<6> et = {{ 5, 10, 12, 14, 16, 20 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , C , C , C , C , C , Rl }, {-1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 }, {-1 , si_2 , si_2 , si_2 , si_2 , si_2 , -1 } }; check_tree(tree, et, __LINE__); } } TEST_VM_F(NMTVMATreeTest, OverlapTableRows12to15) { using SIndex = NativeCallStackStorage::StackIndex; using State = VMATree::StateType; SIndex si_1 = si[0]; SIndex si_2 = si[1]; SIndex si_3 = si[2]; const State Rs = State::Reserved; const State Rl = State::Released; const State C = State::Committed; VMATree::RegionData rd_Test_cs1(si_1, mtTest); VMATree::RegionData rd_None_cs1(si_1, mtNone); VMATree::RegionData rd_Test_cs2(si_2, mtTest); VMATree::RegionData rd_None_cs2(si_2, mtNone); VMATree::RegionData rd_None_cs3(si_3, mtNone); { // row 12: .........A..................B.....U // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // ..............................rrrrrrrrrr........... Tree tree; ExpectedTree<2> pre = {{ 30, 40 }, {mtNone, mtTest, mtNone}, {Rl , Rs , Rl }, {-1 , si_1 , -1 }, {-1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(5, 20, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCCCCCCC.....rrrrrrrrrr........... EXPECT_EQ(diff.tag(mtTest).commit, 20); EXPECT_EQ(diff.tag(mtTest).reserve, 20); ExpectedTree<4> et = {{ 5, 25, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtNone}, {Rl , C , Rl , Rs , Rl }, {-1 , si_2 , -1 , si_1 , -1 }, {-1 , si_2 , -1 , -1 , -1 } }; check_tree(tree, et, __LINE__); } { // row 13: .........A...YW.............B....U // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // ..........rrrrrrrrrrrrrrrrrrrr..................... Tree tree; ExpectedTree<2> pre = {{ 10, 30 }, {mtNone, mtTest, mtNone}, {Rl , Rs , Rl }, {-1 , si_1 , -1 }, {-1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(5, 20, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCCCCCCCrrrrr..................... EXPECT_EQ(diff.tag(mtTest).commit, 20); EXPECT_EQ(diff.tag(mtTest).reserve, 30 - 25); ExpectedTree<4> et = {{ 5, 10, 25, 30 }, {mtNone, mtTest, mtTest, mtTest, mtNone}, {Rl , C , C , Rs , Rl }, {-1 , si_2 , si_1 , si_1 , -1 }, {-1 , si_2 , si_2 , -1 , -1 } }; check_tree(tree, et, __LINE__); } { // row 14: .........A...Y.......................W....B....U // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // ..........rrrrrrrrrr..........rrrrrrrrrr........... Tree tree; ExpectedTree<7> pre = {{ 10, 12, 14, 16, 20, 30, 40 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rs , Rs , Rs , Rl , Rs , Rl }, {-1 , si_1 , si_2 , si_1 , si_2 , -1 , si_1 , -1 }, {-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(5, 20, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCCCCCCC.....rrrrrrrrrr........... EXPECT_EQ(diff.tag(mtTest).commit, 20); EXPECT_EQ(diff.tag(mtTest).reserve, (10 - 5) + ( 25 - 20)); ExpectedTree<8> et = {{ 5, 10, 12, 14, 16, 25, 30, 40 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , C , C , C , C , C , Rl , Rs , Rl }, {-1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 , si_1 , -1 }, {-1 , si_2 , si_2 , si_2 , si_2 , si_2 , -1 , -1 , -1 } }; check_tree(tree, et, __LINE__); } { // row 15: .........A...Y.......................WB....U // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // ..........rrrrrrrrrr..........rrrrrrrrrr........... Tree tree; ExpectedTree<7> pre = {{ 10, 12, 14, 16, 20, 30, 40 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rs , Rs , Rs , Rl , Rs , Rl }, {-1 , si_1 , si_2 , si_1 , si_2 , -1 , si_1 , -1 }, {-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff ; tree.commit_mapping(5, 15, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // .....CCCCCCCCCCCCCCC..........rrrrrrrrrr........... EXPECT_EQ(diff.tag(mtTest).commit, 15); EXPECT_EQ(diff.tag(mtTest).reserve, 10 - 5); ExpectedTree<8> et = {{ 5, 10, 12, 14, 16, 20, 30, 40 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , C , C , C , C , C , Rl , Rs , Rl }, {-1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 , si_1 , -1 }, {-1 , si_2 , si_2 , si_2 , si_2 , si_2 , -1 , -1 , -1 } }; check_tree(tree, et, __LINE__); } } TEST_VM_F(NMTVMATreeTest, OverlapTableRows16to19) { using SIndex = NativeCallStackStorage::StackIndex; using State = VMATree::StateType; SIndex si_1 = si[0]; SIndex si_2 = si[1]; SIndex si_3 = si[2]; const State Rs = State::Reserved; const State Rl = State::Released; const State C = State::Committed; VMATree::RegionData rd_Test_cs1(si_1, mtTest); VMATree::RegionData rd_None_cs1(si_1, mtNone); VMATree::RegionData rd_Test_cs2(si_2, mtTest); VMATree::RegionData rd_None_cs2(si_2, mtNone); VMATree::RegionData rd_None_cs3(si_3, mtNone); { // row 16: .....X...A..................B....U // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr....................rrrrrrrrrr........... Tree tree; ExpectedTree<4> pre = {{ 0, 10, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , Rs , Rl }, {-1 , si_1 , -1 , si_1 , -1 }, {-1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(15, 10, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr.....CCCCCCCCCC.....rrrrrrrrrr........... EXPECT_EQ(diff.tag(mtTest).commit, 10); EXPECT_EQ(diff.tag(mtTest).reserve, 10); ExpectedTree<6> et = {{ 0, 10, 15, 25, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , C , Rl , Rs , Rl }, {-1 , si_1 , -1 , si_2 , -1 , si_1 , -1 }, {-1 , -1 , -1 , si_2 , -1 , -1 , -1 } }; check_tree(tree, et, __LINE__); } { // row 17: .....X...A...YW.............B....U // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr..........rrrrrrrrrr..................... Tree tree; ExpectedTree<4> pre = {{ 0, 10, 20, 30 }, {mtNone, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , Rs , Rl }, {-1 , si_1 , -1 , si_1 , -1 }, {-1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(15, 10, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr.....CCCCCCCCCCrrrrr..................... EXPECT_EQ(diff.tag(mtTest).commit, 10); EXPECT_EQ(diff.tag(mtTest).reserve, 20 - 15); ExpectedTree<6> et = {{ 0, 10, 15, 20, 25, 30 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , Rl , C , C , Rs , Rl }, {-1 , si_1 , -1 , si_2 , si_1 , si_1 , -1 }, {-1 , -1 , -1 , si_2 , si_2 , -1 , -1 } }; check_tree(tree, et, __LINE__); } { // row 18: ....X....A...Y.......................W....B....U // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr.....rrrrrrrrrr..........rrrrrrrrrr........... Tree tree; ExpectedTree<9> pre = {{ 0, 5, 10, 12, 14, 16, 20, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , Rs , Rs , Rs , Rs , Rl , Rs , Rl }, {-1 , si_1 , -1 , si_2 , si_1 , si_2 , si_1 , -1 , si_1 , -1 }, {-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(7, 20, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr..CCCCCCCCCCCCCCCCCCCC...rrrrrrrrrr........... EXPECT_EQ(diff.tag(mtTest).commit, 20); EXPECT_EQ(diff.tag(mtTest).reserve, (10 - 7) + (27 - 20)); ExpectedTree<10> et = {{ 0, 5, 7, 12, 14, 16, 20, 27, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , C , C , C , C , C , Rl , Rs , Rl }, {-1 , si_1 , -1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 , si_1 , -1 }, {-1 , -1 , -1 , si_2 , si_2 , si_2 , si_2 , si_2 , -1 , -1 , -1 } }; check_tree(tree, et, __LINE__); } { // row 19: .....X...A...Y.......................WB....U // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr.....rrrrrrrrrr..........rrrrrrrrrr........... Tree tree; ExpectedTree<9> pre = {{ 0, 5, 10, 12, 14, 16, 20, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , Rs , Rs , Rs , Rs , Rl , Rs , Rl }, {-1 , si_1 , -1 , si_1 , si_2 , si_1 , si_2 , -1 , si_1 , -1 }, {-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(7, 13, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr..CCCCCCCCCCCCC..........rrrrrrrrrr........... EXPECT_EQ(diff.tag(mtTest).commit, 13); EXPECT_EQ(diff.tag(mtTest).reserve, 10 - 7); ExpectedTree<10> et = {{ 0, 5, 7, 10, 12, 14, 16, 20, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , C , C , C , C , C , Rl , Rs , Rl }, {-1 , si_1 , -1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 , si_1 , -1 }, {-1 , -1 , -1 , si_2 , si_2 , si_2 , si_2 , si_2 , -1 , -1 , -1 } }; check_tree(tree, et, __LINE__); } } TEST_VM_F(NMTVMATreeTest, OverlapTableRows20to23) { using SIndex = NativeCallStackStorage::StackIndex; using State = VMATree::StateType; SIndex si_1 = si[0]; SIndex si_2 = si[1]; SIndex si_3 = si[2]; const State Rs = State::Reserved; const State Rl = State::Released; const State C = State::Committed; VMATree::RegionData rd_Test_cs1(si_1, mtTest); VMATree::RegionData rd_None_cs1(si_1, mtNone); VMATree::RegionData rd_Test_cs2(si_2, mtTest); VMATree::RegionData rd_None_cs2(si_2, mtNone); VMATree::RegionData rd_None_cs3(si_3, mtNone); { // row 20: ........XA..................B....U // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr....................rrrrrrrrrr........... Tree tree; ExpectedTree<4> pre = {{ 0, 10, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , Rs , Rl }, {-1 , si_1 , -1 , si_1 , -1 }, {-1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(10, 15, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrCCCCCCCCCCCCCCC.....rrrrrrrrrr........... EXPECT_EQ(diff.tag(mtTest).commit, 15); EXPECT_EQ(diff.tag(mtTest).reserve, 15); ExpectedTree<5> et = {{ 0, 10, 25, 30, 40 }, {mtNone, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , C , Rl , Rs , Rl }, {-1 , si_1 , si_2 , -1 , si_1 , -1 }, {-1 , -1 , si_2 , -1 , -1 , -1 } }; check_tree(tree, et, __LINE__); } { // row 21: ........XA...YW.............B....U // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrr..........rrrrrrrrrr..................... Tree tree; ExpectedTree<4> pre = {{ 0, 10, 20, 30 }, {mtNone, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , Rs , Rl }, {-1 , si_1 , -1 , si_1 , -1 }, {-1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(10, 15, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrrrrrrCCCCCCCCCCCCCCCrrrrr..................... EXPECT_EQ(diff.tag(mtTest).commit, 15); EXPECT_EQ(diff.tag(mtTest).reserve, 20 - 10); ExpectedTree<5> et = {{ 0, 10, 20, 25, 30 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtNone}, {Rl , Rs , C , C , Rs , Rl }, {-1 , si_1 , si_2 , si_1 , si_1 , -1 }, {-1 , -1 , si_2 , si_2 , -1 , -1 } }; check_tree(tree, et, __LINE__); } { // row 22: ........XA...Y.......................W....B....U // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr.....rrrrrrrrrr..........rrrrrrrrrr........... Tree tree; ExpectedTree<9> pre = {{ 0, 5, 10, 12, 14, 16, 20, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , Rs , Rs , Rs , Rs , Rl , Rs , Rl }, {-1 , si_1 , -1 , si_2 , si_1 , si_2 , si_1 , -1 , si_1 , -1 }, {-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(5, 20, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrCCCCCCCCCCCCCCCCCCCC.....rrrrrrrrrr........... EXPECT_EQ(diff.tag(mtTest).commit, 20); EXPECT_EQ(diff.tag(mtTest).reserve, (10 - 5) + (25 - 20)); ExpectedTree<9> et = {{ 0, 5, 12, 14, 16, 20, 25, 30, 40 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , C , C , C , C , C , Rl , Rs , Rl }, {-1 , si_1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 , si_1 , -1 }, {-1 , -1 , si_2 , si_2 , si_2 , si_2 , si_2 , -1 , -1 , -1 } }; check_tree(tree, et, __LINE__); } { // row 23: ........XA...Y.......................WB....U // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrr.....rrrrrrrrrr..........rrrrrrrrrr........... Tree tree; ExpectedTree<9> pre = {{ 0, 5, 10, 12, 14, 16, 20, 30, 40 }, {mtNone, mtTest, mtNone, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , Rl , Rs , Rs , Rs , Rs , Rl , Rs , Rl }, {-1 , si_1 , -1 , si_1 , si_2 , si_1 , si_2 , -1 , si_1 , -1 }, {-1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 } }; create_tree(tree, pre, __LINE__); VMATree::SummaryDiff diff; tree.commit_mapping(5, 15, rd_Test_cs2, diff, false); // 1 2 3 4 5 // 012345678901234567890123456789012345678901234567890 // rrrrrCCCCCCCCCCCCCCC..........rrrrrrrrrr........... EXPECT_EQ(diff.tag(mtTest).commit, 15); EXPECT_EQ(diff.tag(mtTest).reserve, 10 - 5); ExpectedTree<9> et = {{ 0, 5, 10, 12, 14, 16, 20, 30, 40 }, {mtNone, mtTest, mtTest, mtTest, mtTest, mtTest, mtTest, mtNone, mtTest, mtNone}, {Rl , Rs , C , C , C , C , C , Rl , Rs , Rl }, {-1 , si_1 , si_2 , si_1 , si_2 , si_1 , si_2 , -1 , si_1 , -1 }, {-1 , -1 , si_2 , si_2 , si_2 , si_2 , si_2 , -1 , -1 , -1 } }; check_tree(tree, et, __LINE__); } } TEST_VM_F(NMTVMATreeTest, UpdateRegionTest) { using State = VMATree::StateType; using SIndex = VMATree::SIndex; SIndex ES = NativeCallStackStorage::invalid; SIndex s0 = si[0]; SIndex s1 = si[1]; SIndex s2 = si[2]; const State Rs = State::Reserved; const State Rl = State::Released; const State C = State::Committed; const int a = 100; const MemTag ReqTag = mtTest; const VMATree::RequestInfo ReleaseRequest{0, a, Rl, mtNone, ES, false}; const VMATree::RequestInfo ReserveRequest{0, a, Rs, ReqTag, s2, false}; const VMATree::RequestInfo CommitRequest{0, a, C, ReqTag, s2, false}; const VMATree::RequestInfo UncommitRequest{0, a, Rs, mtNone, ES, true}; const VMATree::RequestInfo CopyTagCommitRequest{0, a, C, ReqTag, s2, true}; // existing state request expected state expected diff // st tag stacks st tag stacks reserve commit // -- ------ ------ ---------------------- -- ------ ------ ------- ------- UpdateCallInfo call_info[]={{{Rl, mtNone, ES, ES}, ReleaseRequest, {Rl, mtNone, ES, ES}, {0, 0}, {0, 0}}, {{Rl, mtNone, ES, ES}, ReserveRequest, {Rs, ReqTag, s2, ES}, {0, a}, {0, 0}}, {{Rl, mtNone, ES, ES}, CommitRequest, { C, ReqTag, s2, s2}, {0, a}, {0, a}}, {{Rl, mtNone, ES, ES}, CopyTagCommitRequest, { C, mtNone, s2, s2}, {0, a}, {0, a}}, {{Rl, mtNone, ES, ES}, UncommitRequest, {Rl, mtNone, ES, ES}, {0, 0}, {0, 0}}, {{Rs, mtGC, s0, ES}, ReleaseRequest, {Rl, mtNone, ES, ES}, {-a, 0}, {0, 0}}, {{Rs, mtGC, s0, ES}, ReserveRequest, {Rs, ReqTag, s2, ES}, {-a, a}, {0, 0}}, // diff tag {{Rs, mtTest, s0, ES}, ReserveRequest, {Rs, ReqTag, s2, ES}, {0, 0}, {0, 0}}, // same tag {{Rs, mtGC, s0, ES}, CommitRequest, { C, ReqTag, s0, s2}, {-a, a}, {0, a}}, {{Rs, mtGC, s0, ES}, CopyTagCommitRequest, { C, mtGC, s0, s2}, {0, 0}, {0, a}}, {{Rs, mtGC, s0, ES}, UncommitRequest, {Rs, mtGC, s0, ES}, {0, 0}, {0, 0}}, {{ C, mtGC, s0, s1}, ReleaseRequest, {Rl, mtNone, ES, ES}, {-a, 0}, {-a, 0}}, {{ C, mtGC, s0, s1}, ReserveRequest, {Rs, ReqTag, s2, ES}, {-a, a}, {-a, 0}}, // diff tag {{ C, mtTest, s0, s1}, ReserveRequest, {Rs, ReqTag, s2, ES}, {0, 0}, {-a, 0}}, // same tag {{ C, mtGC, s0, s1}, CommitRequest, { C, ReqTag, s0, s2}, {-a, a}, {-a, a}}, {{ C, mtGC, s0, s1}, CopyTagCommitRequest, { C, mtGC, s0, s2}, {0, 0}, {-a, a}}, {{ C, mtGC, s0, s1}, UncommitRequest, {Rs, mtGC, s0, ES}, {0, 0}, {-a, 0}} }; for (auto ci : call_info) { call_update_region(ci); } }