14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25 #include "precompiled.hpp"
26 #include "memory/allocation.hpp"
27 #include "nmt/memTag.hpp"
28 #include "nmt/nmtNativeCallStackStorage.hpp"
29 #include "nmt/vmatree.hpp"
30 #include "runtime/os.hpp"
31 #include "unittest.hpp"
32
33 using Tree = VMATree;
34 using Node = Tree::TreapNode;
35 using NCS = NativeCallStackStorage;
36
37 class NMTVMATreeTest : public testing::Test {
38 public:
39 NCS ncs;
40 constexpr static const int si_len = 2;
41 NCS::StackIndex si[si_len];
42 NativeCallStack stacks[si_len];
43
44 NMTVMATreeTest() : ncs(true) {
45 stacks[0] = make_stack(0xA);
46 stacks[1] = make_stack(0xB);
47 si[0] = ncs.push(stacks[0]);
48 si[1] = ncs.push(stacks[0]);
49 }
50
51 // Utilities
52
53 VMATree::TreapNode* treap_root(VMATree& tree) {
54 return tree._tree._root;
60
61 VMATree::TreapNode* find(VMATree::VMATreap& treap, const VMATree::position key) {
62 return treap.find(treap._root, key);
63 }
64
65 NativeCallStack make_stack(size_t a) {
66 NativeCallStack stack((address*)&a, 1);
67 return stack;
68 }
69
70 VMATree::StateType in_type_of(VMATree::TreapNode* x) {
71 return x->val().in.type();
72 }
73
74 VMATree::StateType out_type_of(VMATree::TreapNode* x) {
75 return x->val().out.type();
76 }
77
78 int count_nodes(Tree& tree) {
79 int count = 0;
80 treap(tree).visit_in_order([&](Node* x) {
81 ++count;
82 });
83 return count;
84 }
85
86 // Tests
87 // Adjacent reservations are merged if the properties match.
88 void adjacent_2_nodes(const VMATree::RegionData& rd) {
89 Tree tree;
90 for (int i = 0; i < 10; i++) {
91 tree.reserve_mapping(i * 100, 100, rd);
92 }
93 EXPECT_EQ(2, count_nodes(tree));
94
95 // Reserving the exact same space again should result in still having only 2 nodes
96 for (int i = 0; i < 10; i++) {
97 tree.reserve_mapping(i * 100, 100, rd);
98 }
99 EXPECT_EQ(2, count_nodes(tree));
100
113 for (int i = 0; i < 10; i++) {
114 tree.release_mapping(i * 100, 100);
115 }
116 EXPECT_EQ(nullptr, treap_root(tree));
117
118 // Other way around
119 tree.reserve_mapping(0, 100 * 10, rd);
120 for (int i = 9; i >= 0; i--) {
121 tree.release_mapping(i * 100, 100);
122 }
123 EXPECT_EQ(nullptr, treap_root(tree));
124 }
125
126 // Committing in a whole reserved range results in 2 nodes
127 void commit_whole(const VMATree::RegionData& rd) {
128 Tree tree;
129 tree.reserve_mapping(0, 100 * 10, rd);
130 for (int i = 0; i < 10; i++) {
131 tree.commit_mapping(i * 100, 100, rd);
132 }
133 treap(tree).visit_in_order([&](Node* x) {
134 VMATree::StateType in = in_type_of(x);
135 VMATree::StateType out = out_type_of(x);
136 EXPECT_TRUE((in == VMATree::StateType::Released && out == VMATree::StateType::Committed) ||
137 (in == VMATree::StateType::Committed && out == VMATree::StateType::Released));
138 });
139 EXPECT_EQ(2, count_nodes(tree));
140 }
141
142 // Committing in middle of reservation ends with a sequence of 4 nodes
143 void commit_middle(const VMATree::RegionData& rd) {
144 Tree tree;
145 tree.reserve_mapping(0, 100, rd);
146 tree.commit_mapping(50, 25, rd);
147
148 size_t found[16];
149 size_t wanted[4] = {0, 50, 75, 100};
150 auto exists = [&](size_t x) {
151 for (int i = 0; i < 4; i++) {
152 if (wanted[i] == x) return true;
153 }
154 return false;
155 };
156
157 int i = 0;
158 treap(tree).visit_in_order([&](Node* x) {
159 if (i < 16) {
160 found[i] = x->key();
161 }
162 i++;
163 });
164
165 ASSERT_EQ(4, i) << "0 - 50 - 75 - 100 nodes expected";
166 EXPECT_TRUE(exists(found[0]));
167 EXPECT_TRUE(exists(found[1]));
168 EXPECT_TRUE(exists(found[2]));
169 EXPECT_TRUE(exists(found[3]));
170 };
171 };
172
173
174
175 TEST_VM_F(NMTVMATreeTest, OverlappingReservationsResultInTwoNodes) {
176 VMATree::RegionData rd{si[0], mtTest};
177 Tree tree;
178 for (int i = 99; i >= 0; i--) {
194 commit_middle(rd);
195 commit_whole(rd);
196
197 { // Identical operation but different metadata should not merge
198 Tree tree;
199 VMATree::RegionData rd{si[0], mtTest };
200 VMATree::RegionData rd2{si[1], mtNMT };
201 tree.reserve_mapping(0, 100, rd);
202 tree.reserve_mapping(100, 100, rd2);
203
204 EXPECT_EQ(3, count_nodes(tree));
205 int found_nodes = 0;
206 }
207
208 { // Reserving after commit should overwrite commit
209 Tree tree;
210 VMATree::RegionData rd{si[0], mtTest };
211 VMATree::RegionData rd2{si[1], mtNMT };
212 tree.commit_mapping(50, 50, rd2);
213 tree.reserve_mapping(0, 100, rd);
214 treap(tree).visit_in_order([&](Node* x) {
215 EXPECT_TRUE(x->key() == 0 || x->key() == 100);
216 if (x->key() == 0) {
217 EXPECT_EQ(x->val().out.regiondata().mem_tag, mtTest);
218 }
219 });
220
221 EXPECT_EQ(2, count_nodes(tree));
222 }
223
224 { // Split a reserved region into two different reserved regions
225 Tree tree;
226 VMATree::RegionData rd{si[0], mtTest };
227 VMATree::RegionData rd2{si[1], mtNMT };
228 VMATree::RegionData rd3{si[0], mtNone };
229 tree.reserve_mapping(0, 100, rd);
230 tree.reserve_mapping(0, 50, rd2);
231 tree.reserve_mapping(50, 50, rd3);
232
233 EXPECT_EQ(3, count_nodes(tree));
234 }
235 { // One big reserve + release leaves an empty tree
236 Tree::RegionData rd{si[0], mtNMT};
237 Tree tree;
238 tree.reserve_mapping(0, 500000, rd);
239 tree.release_mapping(0, 500000);
240
241 EXPECT_EQ(nullptr, treap_root(tree));
242 }
243
244 { // A committed region inside of/replacing a reserved region
245 // should replace the reserved region's metadata.
246 Tree::RegionData rd{si[0], mtNMT};
247 VMATree::RegionData rd2{si[1], mtTest};
248 Tree tree;
249 tree.reserve_mapping(0, 100, rd);
250 tree.commit_mapping(0, 100, rd2);
251 treap(tree).visit_range_in_order(0, 99999, [&](Node* x) {
252 if (x->key() == 0) {
253 EXPECT_EQ(mtTest, x->val().out.regiondata().mem_tag);
254 }
255 if (x->key() == 100) {
256 EXPECT_EQ(mtTest, x->val().in.regiondata().mem_tag);
257 }
258 });
259 }
260
261 { // Attempting to reserve or commit an empty region should not change the tree.
262 Tree tree;
263 Tree::RegionData rd{si[0], mtNMT};
264 tree.reserve_mapping(0, 0, rd);
265 EXPECT_EQ(nullptr, treap_root(tree));
266 tree.commit_mapping(0, 0, rd);
267 EXPECT_EQ(nullptr, treap_root(tree));
268 }
269 }
270
271 // Tests for summary accounting
|
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 *
23 */
24
25 #include "precompiled.hpp"
26 #include "memory/allocation.hpp"
27 #include "nmt/memTag.hpp"
28 #include "nmt/nmtNativeCallStackStorage.hpp"
29 #include "nmt/vmatree.hpp"
30 #include "runtime/os.hpp"
31 #include "unittest.hpp"
32
33 using Tree = VMATree;
34 using TNode = Tree::TreapNode;
35 using NCS = NativeCallStackStorage;
36
37 class NMTVMATreeTest : public testing::Test {
38 public:
39 NCS ncs;
40 constexpr static const int si_len = 2;
41 NCS::StackIndex si[si_len];
42 NativeCallStack stacks[si_len];
43
44 NMTVMATreeTest() : ncs(true) {
45 stacks[0] = make_stack(0xA);
46 stacks[1] = make_stack(0xB);
47 si[0] = ncs.push(stacks[0]);
48 si[1] = ncs.push(stacks[0]);
49 }
50
51 // Utilities
52
53 VMATree::TreapNode* treap_root(VMATree& tree) {
54 return tree._tree._root;
60
61 VMATree::TreapNode* find(VMATree::VMATreap& treap, const VMATree::position key) {
62 return treap.find(treap._root, key);
63 }
64
65 NativeCallStack make_stack(size_t a) {
66 NativeCallStack stack((address*)&a, 1);
67 return stack;
68 }
69
70 VMATree::StateType in_type_of(VMATree::TreapNode* x) {
71 return x->val().in.type();
72 }
73
74 VMATree::StateType out_type_of(VMATree::TreapNode* x) {
75 return x->val().out.type();
76 }
77
78 int count_nodes(Tree& tree) {
79 int count = 0;
80 treap(tree).visit_in_order([&](TNode* x) {
81 ++count;
82 });
83 return count;
84 }
85
86 // Tests
87 // Adjacent reservations are merged if the properties match.
88 void adjacent_2_nodes(const VMATree::RegionData& rd) {
89 Tree tree;
90 for (int i = 0; i < 10; i++) {
91 tree.reserve_mapping(i * 100, 100, rd);
92 }
93 EXPECT_EQ(2, count_nodes(tree));
94
95 // Reserving the exact same space again should result in still having only 2 nodes
96 for (int i = 0; i < 10; i++) {
97 tree.reserve_mapping(i * 100, 100, rd);
98 }
99 EXPECT_EQ(2, count_nodes(tree));
100
113 for (int i = 0; i < 10; i++) {
114 tree.release_mapping(i * 100, 100);
115 }
116 EXPECT_EQ(nullptr, treap_root(tree));
117
118 // Other way around
119 tree.reserve_mapping(0, 100 * 10, rd);
120 for (int i = 9; i >= 0; i--) {
121 tree.release_mapping(i * 100, 100);
122 }
123 EXPECT_EQ(nullptr, treap_root(tree));
124 }
125
126 // Committing in a whole reserved range results in 2 nodes
127 void commit_whole(const VMATree::RegionData& rd) {
128 Tree tree;
129 tree.reserve_mapping(0, 100 * 10, rd);
130 for (int i = 0; i < 10; i++) {
131 tree.commit_mapping(i * 100, 100, rd);
132 }
133 treap(tree).visit_in_order([&](TNode* x) {
134 VMATree::StateType in = in_type_of(x);
135 VMATree::StateType out = out_type_of(x);
136 EXPECT_TRUE((in == VMATree::StateType::Released && out == VMATree::StateType::Committed) ||
137 (in == VMATree::StateType::Committed && out == VMATree::StateType::Released));
138 });
139 EXPECT_EQ(2, count_nodes(tree));
140 }
141
142 // Committing in middle of reservation ends with a sequence of 4 nodes
143 void commit_middle(const VMATree::RegionData& rd) {
144 Tree tree;
145 tree.reserve_mapping(0, 100, rd);
146 tree.commit_mapping(50, 25, rd);
147
148 size_t found[16];
149 size_t wanted[4] = {0, 50, 75, 100};
150 auto exists = [&](size_t x) {
151 for (int i = 0; i < 4; i++) {
152 if (wanted[i] == x) return true;
153 }
154 return false;
155 };
156
157 int i = 0;
158 treap(tree).visit_in_order([&](TNode* x) {
159 if (i < 16) {
160 found[i] = x->key();
161 }
162 i++;
163 });
164
165 ASSERT_EQ(4, i) << "0 - 50 - 75 - 100 nodes expected";
166 EXPECT_TRUE(exists(found[0]));
167 EXPECT_TRUE(exists(found[1]));
168 EXPECT_TRUE(exists(found[2]));
169 EXPECT_TRUE(exists(found[3]));
170 };
171 };
172
173
174
175 TEST_VM_F(NMTVMATreeTest, OverlappingReservationsResultInTwoNodes) {
176 VMATree::RegionData rd{si[0], mtTest};
177 Tree tree;
178 for (int i = 99; i >= 0; i--) {
194 commit_middle(rd);
195 commit_whole(rd);
196
197 { // Identical operation but different metadata should not merge
198 Tree tree;
199 VMATree::RegionData rd{si[0], mtTest };
200 VMATree::RegionData rd2{si[1], mtNMT };
201 tree.reserve_mapping(0, 100, rd);
202 tree.reserve_mapping(100, 100, rd2);
203
204 EXPECT_EQ(3, count_nodes(tree));
205 int found_nodes = 0;
206 }
207
208 { // Reserving after commit should overwrite commit
209 Tree tree;
210 VMATree::RegionData rd{si[0], mtTest };
211 VMATree::RegionData rd2{si[1], mtNMT };
212 tree.commit_mapping(50, 50, rd2);
213 tree.reserve_mapping(0, 100, rd);
214 treap(tree).visit_in_order([&](TNode* x) {
215 EXPECT_TRUE(x->key() == 0 || x->key() == 100);
216 if (x->key() == 0) {
217 EXPECT_EQ(x->val().out.regiondata().mem_tag, mtTest);
218 }
219 });
220
221 EXPECT_EQ(2, count_nodes(tree));
222 }
223
224 { // Split a reserved region into two different reserved regions
225 Tree tree;
226 VMATree::RegionData rd{si[0], mtTest };
227 VMATree::RegionData rd2{si[1], mtNMT };
228 VMATree::RegionData rd3{si[0], mtNone };
229 tree.reserve_mapping(0, 100, rd);
230 tree.reserve_mapping(0, 50, rd2);
231 tree.reserve_mapping(50, 50, rd3);
232
233 EXPECT_EQ(3, count_nodes(tree));
234 }
235 { // One big reserve + release leaves an empty tree
236 Tree::RegionData rd{si[0], mtNMT};
237 Tree tree;
238 tree.reserve_mapping(0, 500000, rd);
239 tree.release_mapping(0, 500000);
240
241 EXPECT_EQ(nullptr, treap_root(tree));
242 }
243
244 { // A committed region inside of/replacing a reserved region
245 // should replace the reserved region's metadata.
246 Tree::RegionData rd{si[0], mtNMT};
247 VMATree::RegionData rd2{si[1], mtTest};
248 Tree tree;
249 tree.reserve_mapping(0, 100, rd);
250 tree.commit_mapping(0, 100, rd2);
251 treap(tree).visit_range_in_order(0, 99999, [&](TNode* x) {
252 if (x->key() == 0) {
253 EXPECT_EQ(mtTest, x->val().out.regiondata().mem_tag);
254 }
255 if (x->key() == 100) {
256 EXPECT_EQ(mtTest, x->val().in.regiondata().mem_tag);
257 }
258 });
259 }
260
261 { // Attempting to reserve or commit an empty region should not change the tree.
262 Tree tree;
263 Tree::RegionData rd{si[0], mtNMT};
264 tree.reserve_mapping(0, 0, rd);
265 EXPECT_EQ(nullptr, treap_root(tree));
266 tree.commit_mapping(0, 0, rd);
267 EXPECT_EQ(nullptr, treap_root(tree));
268 }
269 }
270
271 // Tests for summary accounting
|