openjdk/test/hotspot/gtest/nmt/test_vmatree.cpp
2025-06-12 14:46:39 +00:00

1988 lines
89 KiB
C++

/*
* Copyright (c) 2024, 2025, 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/nmtNativeCallStackStorage.hpp"
#include "nmt/vmatree.hpp"
#include "runtime/os.hpp"
#include "unittest.hpp"
using Tree = VMATree;
using TNode = Tree::TreapNode;
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::TreapNode* treap_root(VMATree& tree) {
return tree._tree._root;
}
VMATree::VMATreap& treap(VMATree& tree) {
return tree._tree;
}
VMATree::TreapNode* find(VMATree::VMATreap& treap, const VMATree::position key) {
return treap.find(treap._root, key);
}
NativeCallStack make_stack(size_t a) {
NativeCallStack stack((address*)&a, 1);
return stack;
}
VMATree::StateType in_type_of(VMATree::TreapNode* x) {
return x->val().in.type();
}
VMATree::StateType out_type_of(VMATree::TreapNode* x) {
return x->val().out.type();
}
int count_nodes(Tree& tree) {
int count = 0;
treap(tree).visit_in_order([&](TNode* x) {
++count;
});
return count;
}
// Tests
// Adjacent reservations are merged if the properties match.
void adjacent_2_nodes(const VMATree::RegionData& rd) {
Tree tree;
for (int i = 0; i < 10; i++) {
tree.reserve_mapping(i * 100, 100, rd);
}
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);
}
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);
}
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;
tree.reserve_mapping(0, 100 * 10, rd);
for (int i = 0; i < 10; i++) {
tree.release_mapping(i * 100, 100);
}
EXPECT_EQ(nullptr, treap_root(tree));
// Other way around
tree.reserve_mapping(0, 100 * 10, rd);
for (int i = 9; i >= 0; i--) {
tree.release_mapping(i * 100, 100);
}
EXPECT_EQ(nullptr, treap_root(tree));
}
// Committing in a whole reserved range results in 2 nodes
void commit_whole(const VMATree::RegionData& rd) {
Tree tree;
tree.reserve_mapping(0, 100 * 10, rd);
for (int i = 0; i < 10; i++) {
tree.commit_mapping(i * 100, 100, rd);
}
treap(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));
});
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;
tree.reserve_mapping(0, 100, rd);
tree.commit_mapping(50, 25, rd);
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;
treap(tree).visit_in_order([&](TNode* x) {
if (i < 16) {
found[i] = x->key();
}
i++;
});
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<int NodeCount> 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::TreapNode n1{upd.req.A, {}, 0}, n2{upd.req.B, {}, 0};
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<int N>
void create_tree(Tree& tree, ExpectedTree<N>& et, int line_no) {
using SIndex = NativeCallStackStorage::StackIndex;
const SIndex ES = NativeCallStackStorage::invalid; // Empty Stack
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);
}
print_tree(et, line_no);
}
template <int N>
void check_tree(Tree& tree, const ExpectedTree<N>& et, int line_no) {
using Node = VMATree::TreapNode;
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::VMATreap::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;
}
}
print_tree(et, line_no);
}
template<int N>
void print_tree(const ExpectedTree<N>& 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;
for (int i = 99; i >= 0; i--) {
tree.reserve_mapping(i * 100, 101, rd);
}
EXPECT_EQ(2, count_nodes(tree));
}
TEST_VM_F(NMTVMATreeTest, UseTagInplace) {
Tree tree;
VMATree::RegionData rd_Test_cs0(si[0], mtTest);
VMATree::RegionData rd_None_cs1(si[1], mtNone);
tree.reserve_mapping(0, 100, rd_Test_cs0);
// 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, true);
tree.uncommit_mapping(30, 10, rd_None_cs1);
tree.visit_in_order([&](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());
}
}
});
}
// 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::RegionData rd_Test_cs0{si[0], mtTest};
VMATree::RegionData rd_NMT_cs1{si[1], mtNMT};
tree.reserve_mapping(0, 100, rd_Test_cs0);
tree.reserve_mapping(100, 100, rd_NMT_cs1);
EXPECT_EQ(3, count_nodes(tree));
int found_nodes = 0;
}
{ // Reserving after commit should overwrite commit
Tree tree;
VMATree::RegionData rd_Test_cs0{si[0], mtTest};
VMATree::RegionData rd_NMT_cs1{si[1], mtNMT};
tree.commit_mapping(50, 50, rd_NMT_cs1);
tree.reserve_mapping(0, 100, rd_Test_cs0);
treap(tree).visit_in_order([&](TNode* x) {
EXPECT_TRUE(x->key() == 0 || x->key() == 100);
if (x->key() == 0) {
EXPECT_EQ(x->val().out.reserved_regiondata().mem_tag, mtTest);
}
});
EXPECT_EQ(2, count_nodes(tree));
}
{ // Split a reserved region into two different reserved regions
Tree tree;
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);
tree.reserve_mapping(0, 50, rd_NMT_cs1);
tree.reserve_mapping(50, 50, rd_None_cs0);
EXPECT_EQ(3, count_nodes(tree));
}
{ // One big reserve + release leaves an empty tree
VMATree::RegionData rd_NMT_cs0{si[0], mtNMT};
Tree tree;
tree.reserve_mapping(0, 500000, rd_NMT_cs0);
tree.release_mapping(0, 500000);
EXPECT_EQ(nullptr, treap_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;
tree.reserve_mapping(0, 100, rd_NMT_cs0);
tree.commit_mapping(0, 100, rd_Test_cs1);
treap(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);
}
});
}
{ // Attempting to reserve or commit an empty region should not change the tree.
Tree tree;
VMATree::RegionData rd_NMT_cs0{si[0], mtNMT};
tree.reserve_mapping(0, 0, rd_NMT_cs0);
EXPECT_EQ(nullptr, treap_root(tree));
tree.commit_mapping(0, 0, rd_NMT_cs0);
EXPECT_EQ(nullptr, treap_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!
int len = sizeof(expected) / sizeof(testrange);
VMATree::position previous_to = 0;
for (int 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::VMATreap::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;
tree.reserve_mapping(0, 600, rd);
tree.set_tag(0, 500, mtGC);
tree.set_tag(500, 100, mtClassShared);
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;
// 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);
// The committed areas
tree.commit_mapping(100, 125, rd);
tree.commit_mapping(550, 10, rd);
tree.commit_mapping(565, 10, rd);
// OK, set tag
tree.set_tag(0, 500, mtGC);
tree.set_tag(500, 100, mtClassShared);
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;
Tree::RegionData gc(si, mtGC);
Tree::RegionData compiler(si, mtCompiler);
tree.reserve_mapping(0, 100, gc);
tree.reserve_mapping(100, 100, compiler);
tree.set_tag(0, 200, mtGC);
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;
Tree::RegionData gc(si1, mtGC);
Tree::RegionData compiler(si2, mtCompiler);
tree.reserve_mapping(0, 100, gc);
tree.reserve_mapping(100, 100, compiler);
tree.set_tag(0, 200, mtGC);
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;
Tree::RegionData compiler(si, mtCompiler);
tree.reserve_mapping(0, 200, compiler);
tree.set_tag(100, 50, mtGC);
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;
Tree::RegionData gc(si, mtGC);
Tree::RegionData compiler(si, mtCompiler);
tree.reserve_mapping(0, 100, gc);
tree.reserve_mapping(100, 100, compiler);
tree.set_tag(75, 50, mtClass);
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;
Tree::RegionData class_shared(si, mtClassShared);
tree.reserve_mapping(0, 50, class_shared);
tree.reserve_mapping(75, 25, class_shared);
tree.set_tag(0, 80, mtGC);
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;
Tree::RegionData class_shared(si, mtClassShared);
tree.reserve_mapping(10, 10, class_shared);
tree.set_tag(0, 100, mtCompiler);
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;
Tree::RegionData class_shared(si, mtClassShared);
tree.reserve_mapping(0, 100, class_shared);
tree.release_mapping(1, 49);
tree.release_mapping(75, 24);
tree.set_tag(0, 100, mtGC);
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);
// 1 2 3 4 5 6 7 8 9 10 11
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..........
// Legend:
// A - Test (reserved)
// . - free
VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)];
EXPECT_EQ(100, diff.reserve);
all_diff = tree.reserve_mapping(50, 25, rd_NMT_cs0);
// 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[NMTUtil::tag_to_index(mtTest)];
VMATree::SingleDiff diff2 = all_diff.tag[NMTUtil::tag_to_index(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);
// 1 2 3 4 5 6 7 8 9 10 11
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..........
// Legend:
// A - Test (reserved)
// . - free
VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)];
EXPECT_EQ(100, diff.reserve);
all_diff = tree.release_mapping(0, 100);
// 1 2 3 4 5 6 7 8 9 10 11
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
// ..............................................................................................................
// Legend:
diff = all_diff.tag[NMTUtil::tag_to_index(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);
// 1 2 3 4 5 6 7 8 9 10 11
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..........
// Legend:
// A - Test (reserved)
// . - free
VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)];
EXPECT_EQ(diff.reserve, 100);
all_diff = tree.commit_mapping(0, 100, rd_Test_cs0);
// 1 2 3 4 5 6 7 8 9 10 11
// 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa..........
// Legend:
// a - Test (committed)
// . - free
diff = all_diff.tag[NMTUtil::tag_to_index(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);
// 1 2
// 01234567890123456789
// AAAAAAAAAA..........
// Legend:
// A - Test (reserved)
// . - free
VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)];
EXPECT_EQ(diff.reserve, 10);
all_diff = tree.reserve_mapping(10, 10, rd_Test_cs0);
// 1 2 3
// 012345678901234567890123456789
// AAAAAAAAAAAAAAAAAAAA..........
// Legend:
// A - Test (reserved)
// . - free
diff = all_diff.tag[NMTUtil::tag_to_index(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);
// 1 2
// 01234567890123456789
// AAAAAAAAAA..........
// Legend:
// A - Test (reserved)
// . - free
VMATree::SingleDiff diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)];
EXPECT_EQ(diff.reserve, 10);
all_diff = tree.reserve_mapping(10, 10, rd_NMT_cs0);
// 1 2 3
// 012345678901234567890123456789
// AAAAAAAAAABBBBBBBBBB..........
// Legend:
// A - Test (reserved)
// B - Native Memory Tracking (reserved)
// . - free
diff = all_diff.tag[NMTUtil::tag_to_index(mtTest)];
EXPECT_EQ(0, diff.reserve);
diff = all_diff.tag[NMTUtil::tag_to_index(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;
Tree::RegionData rd_Test_cs0(NCS::StackIndex(), mtTest);
tree.commit_mapping(16, 16, rd_Test_cs0);
// 1 2 3 4
// 0123456789012345678901234567890123456789
// ................aaaaaaaaaaaaaaaa..........
// Legend:
// a - Test (committed)
// . - free
tree.commit_mapping(32, 32, rd_Test_cs0);
// 1 2 3 4 5 6 7
// 0123456789012345678901234567890123456789012345678901234567890123456789
// ................aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa..........
// Legend:
// a - Test (committed)
// . - free
VMATree::SummaryDiff diff = tree.commit_mapping(0, 64, rd_Test_cs0);
// 1 2 3 4 5 6 7
// 0123456789012345678901234567890123456789012345678901234567890123456789
// aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa..........
// Legend:
// a - Test (committed)
// . - free
EXPECT_EQ(16, diff.tag[NMTUtil::tag_to_index(mtTest)].commit);
EXPECT_EQ(16, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve);
}
}
// Exceedingly simple tracker for page-granular allocations
// Use it for testing consistency with VMATree.
struct SimpleVMATracker : public CHeapObj<mtTest> {
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();
}
}
VMATree::SummaryDiff do_it(Kind kind, size_t start, size_t size, NativeCallStack stack, MemTag mem_tag) {
assert(is_aligned(size, page_size) && is_aligned(start, page_size), "page alignment");
VMATree::SummaryDiff diff;
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[(int)old_info.mem_tag].reserve -= page_size;
} else if (old_info.kind == Committed) {
diff.tag[(int)old_info.mem_tag].reserve -= page_size;
diff.tag[(int)old_info.mem_tag].commit -= page_size;
}
if (kind == Reserved) {
diff.tag[(int)new_info.mem_tag].reserve += page_size;
} else if (kind == Committed) {
diff.tag[(int)new_info.mem_tag].reserve += page_size;
diff.tag[(int)new_info.mem_tag].commit += page_size;
}
// Overwrite old one with new
pages[i] = new_info;
}
return diff;
}
VMATree::SummaryDiff reserve(size_t start, size_t size, NativeCallStack stack, MemTag mem_tag) {
return do_it(Reserved, start, size, stack, mem_tag);
}
VMATree::SummaryDiff commit(size_t start, size_t size, NativeCallStack stack, MemTag mem_tag) {
return do_it(Committed, start, size, stack, mem_tag);
}
VMATree::SummaryDiff release(size_t start, size_t size) {
return do_it(Free, start, size, NativeCallStack(), mtNone);
}
};
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) {
simple_diff = tr->reserve(start, size, stack, mem_tag);
tree_diff = tree.reserve_mapping(start, size, data);
} else if (kind == SimpleVMATracker::Committed) {
simple_diff = tr->commit(start, size, stack, mem_tag);
tree_diff = tree.commit_mapping(start, size, data);
} else {
simple_diff = tr->release(start, size);
tree_diff = tree.release_mapping(start, size);
}
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::VMATreap& treap = this->treap(tree);
VMATree::TreapNode* startn = find(treap, start * page_size);
ASSERT_NE(nullptr, startn);
VMATree::TreapNode* endn = find(treap, (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::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);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr
VMATree::SummaryDiff diff = tree.commit_mapping(0, 25, rd_None_cs1, true);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// CCCCCCCCCCCCCCCCCCCCCCCCCrrrrrrrrrrrrrrrrrrrrrrrrr
EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve);
EXPECT_EQ(25, diff.tag[NMTUtil::tag_to_index(mtTest)].commit);
diff = tree.commit_mapping(30, 5, rd_None_cs1, true);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// CCCCCCCCCCCCCCCCCCCCCCCCCrrrrrCCCCCrrrrrrrrrrrrrrr
EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve);
EXPECT_EQ(5, diff.tag[NMTUtil::tag_to_index(mtTest)].commit);
diff = tree.uncommit_mapping(0, 25, rd_None_cs1);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrCCCCCrrrrrrrrrrrrrrr
EXPECT_EQ(0, diff.tag[NMTUtil::tag_to_index(mtTest)].reserve);
EXPECT_EQ(-25, diff.tag[NMTUtil::tag_to_index(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;
tree.reserve_mapping(0, 50, rd_Test_cs1); // 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, 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, 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, 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;
tree.reserve_mapping(0, 50, rd_Test_cs1); // 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, 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);
// 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);
// 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;
tree.reserve_mapping(0, 50, rd_Test_cs1);
tree.commit_mapping(10, 10, rd_None_cs2, true);
tree.commit_mapping(0, 5, rd_None_cs2, true);
tree.uncommit_mapping(0, 3, rd_None_cs2);
// 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);
// 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;
tree.reserve_mapping(0, 50, rd_Test_cs1);
tree.reserve_mapping(10, 10, call_stack_4);
// 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;
tree.commit_mapping(0, 50, rd_Test_cs1);
// 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;
tree.commit_mapping(0, 50, rd_None_cs2);
tree.reserve_mapping(0, 50, rd_Test_cs1);
// 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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// .....CCCCCCCCCCCCCCCCCCCC..........................
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// .....CCCCCCCCCCCCCCC...............................
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 15);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrrrrrrr..........CCCCCCCCCCCCCCCCCCCC...........
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// .....rrrrrCCCCCCCCCC...............................
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 10);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrr..CCCCCCCCCCCCCCCCCCCC........................
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrr..CCCCCCCCCCCCC...............................
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 13);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrrrrrrrCCCCCCCCCCCCCCCCCCCC.....................
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// CCCCCCCCCCCCCCCCCCCC...............................
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// .....CCCCCCCCCCCCCCCCCCCC..........................
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// .....CCCCCCCCCCCCCCC...............................
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 15);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// .....CCCCCCCCCCCCCCCCCCCC.....rrrrrrrrrr...........
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// .....CCCCCCCCCCCCCCCCCCCCrrrrr.....................
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// .....CCCCCCCCCCCCCCCCCCCC.....rrrrrrrrrr...........
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// .....CCCCCCCCCCCCCCC..........rrrrrrrrrr...........
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 15);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrrrrrrr.....CCCCCCCCCC.....rrrrrrrrrr...........
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 10);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrrrrrrr.....CCCCCCCCCCrrrrr.....................
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 10);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrr..CCCCCCCCCCCCCCCCCCCC...rrrrrrrrrr...........
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrr..CCCCCCCCCCCCC..........rrrrrrrrrr...........
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 13);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrrrrrrrCCCCCCCCCCCCCCC.....rrrrrrrrrr...........
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 15);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrrrrrrrCCCCCCCCCCCCCCCrrrrr.....................
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 15);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrrCCCCCCCCCCCCCCCCCCCC.....rrrrrrrrrr...........
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 20);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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, false);
// 1 2 3 4 5
// 012345678901234567890123456789012345678901234567890
// rrrrrCCCCCCCCCCCCCCC..........rrrrrrrrrr...........
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(mtTest)].commit, 15);
EXPECT_EQ(diff.tag[NMTUtil::tag_to_index(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);
}
}