1 /*
2 * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3 * Copyright (c) 2020 SAP SE. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 *
24 */
25
26 #include "precompiled.hpp"
27 #include "memory/metaspace/blockTree.hpp"
28 #include "memory/metaspace/counters.hpp"
29 #include "memory/resourceArea.hpp"
30 // #define LOG_PLEASE
31 #include "metaspaceGtestCommon.hpp"
32
33 using metaspace::BlockTree;
34 using metaspace::MemRangeCounter;
35
36 // Small helper. Given a 0-terminated array of sizes, a feeder buffer and a tree,
37 // add blocks of these sizes to the tree in the order they appear in the array.
create_nodes(const size_t sizes[],FeederBuffer & fb,BlockTree & bt)38 static void create_nodes(const size_t sizes[], FeederBuffer& fb, BlockTree& bt) {
39 for (int i = 0; sizes[i] > 0; i ++) {
40 size_t s = sizes[i];
41 MetaWord* p = fb.get(s);
42 bt.add_block(p, s);
43 }
44 }
45
46 #define CHECK_BT_CONTENT(bt, expected_num, expected_size) { \
47 EXPECT_EQ(bt.count(), (unsigned)expected_num); \
48 EXPECT_EQ(bt.total_size(), (size_t)expected_size); \
49 if (expected_num == 0) { \
50 EXPECT_TRUE(bt.is_empty()); \
51 } else { \
52 EXPECT_FALSE(bt.is_empty()); \
53 } \
54 }
55
TEST_VM(metaspace,BlockTree_basic)56 TEST_VM(metaspace, BlockTree_basic) {
57
58 BlockTree bt;
59 CHECK_BT_CONTENT(bt, 0, 0);
60
61 size_t real_size = 0;
62 MetaWord* p = NULL;
63 MetaWord arr[10000];
64
65 ASSERT_LE(BlockTree::MinWordSize, (size_t)6); // Sanity check. Adjust if Node is changed.
66
67 const size_t minws = BlockTree::MinWordSize;
68
69 // remove_block from empty tree should yield nothing
70 p = bt.remove_block(minws, &real_size);
71 EXPECT_NULL(p);
72 EXPECT_0(real_size);
73 CHECK_BT_CONTENT(bt, 0, 0);
74
75 // Add some blocks and retrieve them right away.
76 size_t sizes[] = {
77 minws, // smallest possible
78 minws + 10,
79 1024,
80 4711,
81 0
82 };
83
84 for (int i = 0; sizes[i] > 0; i++) {
85 bt.add_block(arr, sizes[i]);
86 CHECK_BT_CONTENT(bt, 1, sizes[i]);
87
88 DEBUG_ONLY(bt.verify();)
89
90 MetaWord* p = bt.remove_block(sizes[i], &real_size);
91 EXPECT_EQ(p, arr);
92 EXPECT_EQ(real_size, (size_t)sizes[i]);
93 CHECK_BT_CONTENT(bt, 0, 0);
94 }
95
96 }
97
98 // Helper for test_find_nearest_fit_with_tree.
99 // Out of an array of sizes return the closest upper match to a requested size.
100 // Returns SIZE_MAX if none found.
helper_find_nearest_fit(const size_t sizes[],size_t request_size)101 static size_t helper_find_nearest_fit(const size_t sizes[], size_t request_size) {
102 size_t best = SIZE_MAX;
103 for (int i = 0; sizes[i] > 0; i++) {
104 if (sizes[i] >= request_size && sizes[i] < best) {
105 best = sizes[i];
106 }
107 }
108 return best;
109 }
110
111 // Given a sequence of (0-terminated) sizes, add blocks of those sizes to the tree in the order given. Then, ask
112 // for a request size and check that it is the expected result.
test_find_nearest_fit_with_tree(const size_t sizes[],size_t request_size)113 static void test_find_nearest_fit_with_tree(const size_t sizes[], size_t request_size) {
114
115 BlockTree bt;
116 FeederBuffer fb(4 * K);
117
118 create_nodes(sizes, fb, bt);
119
120 DEBUG_ONLY(bt.verify();)
121
122 size_t expected_size = helper_find_nearest_fit(sizes, request_size);
123 size_t real_size = 0;
124 MetaWord* p = bt.remove_block(request_size, &real_size);
125
126 if (expected_size != SIZE_MAX) {
127 EXPECT_NOT_NULL(p);
128 EXPECT_EQ(real_size, expected_size);
129 } else {
130 EXPECT_NULL(p);
131 EXPECT_0(real_size);
132 }
133
134 LOG(SIZE_FORMAT ": " SIZE_FORMAT ".", request_size, real_size);
135
136 }
137
TEST_VM(metaspace,BlockTree_find_nearest_fit)138 TEST_VM(metaspace, BlockTree_find_nearest_fit) {
139
140 // Test tree for test_find_nearest_fit looks like this
141 // 30
142 // / \
143 // / \
144 // / \
145 // 17 50
146 // / \ / \
147 // / \ / \
148 // 10 28 32 51
149 // \
150 // 35
151
152 static const size_t sizes[] = {
153 30, 17, 10, 28,
154 50, 32, 51, 35,
155 0 // stop
156 };
157
158 BlockTree bt;
159 FeederBuffer fb(4 * K);
160
161 create_nodes(sizes, fb, bt);
162
163 for (int i = BlockTree::MinWordSize; i <= 60; i ++) {
164 test_find_nearest_fit_with_tree(sizes, i);
165 }
166
167 }
168
169 // Test repeated adding and removing of blocks of the same size, which
170 // should exercise the list-part of the tree.
TEST_VM(metaspace,BlockTree_basic_siblings)171 TEST_VM(metaspace, BlockTree_basic_siblings)
172 {
173 BlockTree bt;
174 FeederBuffer fb(4 * K);
175
176 CHECK_BT_CONTENT(bt, 0, 0);
177
178 const size_t test_size = BlockTree::MinWordSize;
179 const int num = 10;
180
181 for (int i = 0; i < num; i++) {
182 bt.add_block(fb.get(test_size), test_size);
183 CHECK_BT_CONTENT(bt, i + 1, (i + 1) * test_size);
184 }
185
186 DEBUG_ONLY(bt.verify();)
187
188 for (int i = num; i > 0; i --) {
189 size_t real_size = 4711;
190 MetaWord* p = bt.remove_block(test_size, &real_size);
191 EXPECT_TRUE(fb.is_valid_pointer(p));
192 EXPECT_EQ(real_size, (size_t)test_size);
193 CHECK_BT_CONTENT(bt, i - 1, (i - 1) * test_size);
194 }
195
196 }
197
198 #ifdef ASSERT
TEST_VM(metaspace,BlockTree_print_test)199 TEST_VM(metaspace, BlockTree_print_test) {
200
201 static const size_t sizes[] = {
202 30, 17, 10, 28,
203 50, 32, 51, 35,
204 0 // stop
205 };
206
207 BlockTree bt;
208 FeederBuffer fb(4 * K);
209
210 create_nodes(sizes, fb, bt);
211
212 ResourceMark rm;
213
214 stringStream ss;
215 bt.print_tree(&ss);
216
217 LOG("%s", ss.as_string());
218 }
219
220 // Test that an overwritten node would result in an assert and a printed tree
221 TEST_VM_ASSERT_MSG(metaspace, BlockTree_overwriter_test, "Invalid node") {
222 static const size_t sizes1[] = { 30, 17, 0 };
223 static const size_t sizes2[] = { 12, 12, 0 };
224
225 BlockTree bt;
226 FeederBuffer fb(4 * K);
227
228 // some nodes...
229 create_nodes(sizes1, fb, bt);
230
231 // a node we will break...
232 MetaWord* p_broken = fb.get(12);
233 bt.add_block(p_broken, 12);
234
235 // some more nodes...
236 create_nodes(sizes2, fb, bt);
237
238 // overwrite node memory (only the very first byte), then verify tree.
239 // Verification should catch the broken canary, print the tree,
240 // then assert.
241 LOG("Will break node at " PTR_FORMAT ".", p2i(p_broken));
242 tty->print_cr("Death test, please ignore the following \"Invalid node\" printout.");
243 *((char*)p_broken) = '\0';
244 bt.verify();
245 }
246 #endif
247
248 class BlockTreeTest {
249
250 FeederBuffer _fb;
251
252 BlockTree _bt[2];
253 MemRangeCounter _cnt[2];
254
255 RandSizeGenerator _rgen;
256
257 #define CHECK_COUNTERS \
258 CHECK_BT_CONTENT(_bt[0], _cnt[0].count(), _cnt[0].total_size()) \
259 CHECK_BT_CONTENT(_bt[1], _cnt[1].count(), _cnt[1].total_size())
260
261 #define CHECK_COUNTERS_ARE_0 \
262 CHECK_BT_CONTENT(_bt[0], 0, 0) \
263 CHECK_BT_CONTENT(_bt[1], 0, 0)
264
265 #ifdef ASSERT
verify_trees()266 void verify_trees() {
267 _bt[0].verify();
268 _bt[1].verify();
269 }
270 #endif
271
272 enum feeding_pattern_t {
273 scatter = 1,
274 left_right = 2,
275 right_left = 3
276 };
277
278 // Feed the whole feeder buffer to the trees, according to feeding_pattern.
feed_all(feeding_pattern_t feeding_pattern)279 void feed_all(feeding_pattern_t feeding_pattern) {
280
281 MetaWord* p = NULL;
282 unsigned added = 0;
283
284 // If we feed in small graining, we cap the number of blocks to limit test duration.
285 const unsigned max_blocks = 2000;
286
287 size_t old_feeding_size = feeding_pattern == right_left ? _rgen.max() : _rgen.min();
288 do {
289 size_t s = 0;
290 switch (feeding_pattern) {
291 case scatter:
292 // fill completely random
293 s =_rgen.get();
294 break;
295 case left_right:
296 // fill in ascending order to provoke a misformed tree.
297 s = MIN2(_rgen.get(), old_feeding_size);
298 old_feeding_size = s;
299 break;
300 case right_left:
301 // same, but descending.
302 s = MAX2(_rgen.get(), old_feeding_size);
303 old_feeding_size = s;
304 break;
305 }
306
307 // Get a block from the feeder buffer; feed it alternatingly to either tree.
308 p = _fb.get(s);
309 if (p != NULL) {
310 int which = added % 2;
311 added++;
312 _bt[which].add_block(p, s);
313 _cnt[which].add(s);
314 CHECK_COUNTERS
315 }
316 } while (p != NULL && added < max_blocks);
317
318 DEBUG_ONLY(verify_trees();)
319
320 // Trees should contain the same number of nodes (+-1)
321 EXPECT_TRUE(_bt[0].count() == _bt[1].count() ||
322 _bt[0].count() == _bt[1].count() + 1);
323
324 }
325
ping_pong_loop(int iterations)326 void ping_pong_loop(int iterations) {
327
328 // We loop and in each iteration randomly retrieve a block from one tree and add it to another.
329 for (int i = 0; i < iterations; i++) {
330 int taker = 0;
331 int giver = 1;
332 if ((os::random() % 10) > 5) {
333 giver = 0; taker = 1;
334 }
335 size_t s =_rgen.get();
336 size_t real_size = 0;
337 MetaWord* p = _bt[giver].remove_block(s, &real_size);
338 if (p != NULL) {
339 ASSERT_TRUE(_fb.is_valid_range(p, real_size));
340 ASSERT_GE(real_size, s);
341 _bt[taker].add_block(p, real_size);
342 _cnt[giver].sub(real_size);
343 _cnt[taker].add(real_size);
344 CHECK_COUNTERS;
345 }
346
347 #ifdef ASSERT
348 if (true) {//i % 1000 == 0) {
349 verify_trees();
350 }
351 #endif
352 }
353 }
354
355 // Drain the trees. While draining, observe the order of the drained items.
drain_all()356 void drain_all() {
357
358 for (int which = 0; which < 2; which++) {
359 BlockTree* bt = _bt + which;
360 size_t last_size = 0;
361 while (!bt->is_empty()) {
362
363 // We only query for the minimal size. Actually returned size should be
364 // monotonously growing since remove_block should always return the closest fit.
365 size_t real_size = 4711;
366 MetaWord* p = bt->remove_block(BlockTree::MinWordSize, &real_size);
367 ASSERT_TRUE(_fb.is_valid_range(p, real_size));
368
369 ASSERT_GE(real_size, last_size);
370 last_size = real_size;
371
372 _cnt[which].sub(real_size);
373 CHECK_COUNTERS;
374
375 DEBUG_ONLY(bt->verify();)
376
377 }
378 }
379
380 }
381
test(feeding_pattern_t feeding_pattern)382 void test(feeding_pattern_t feeding_pattern) {
383
384 CHECK_COUNTERS_ARE_0
385
386 feed_all(feeding_pattern);
387
388 LOG("Blocks in circulation: bt1=%d:" SIZE_FORMAT ", bt2=%d:" SIZE_FORMAT ".",
389 _bt[0].count(), _bt[0].total_size(),
390 _bt[1].count(), _bt[1].total_size());
391
392 ping_pong_loop(5000);
393
394 LOG("After Pingpong: bt1=%d:" SIZE_FORMAT ", bt2=%d:" SIZE_FORMAT ".",
395 _bt[0].count(), _bt[0].total_size(),
396 _bt[1].count(), _bt[1].total_size());
397
398 drain_all();
399
400 CHECK_COUNTERS_ARE_0
401 }
402
403 public:
404
BlockTreeTest(size_t min_word_size,size_t max_word_size)405 BlockTreeTest(size_t min_word_size, size_t max_word_size) :
406 _fb(2 * M),
407 _bt(),
408 _rgen(min_word_size, max_word_size)
409 {
410 CHECK_COUNTERS;
411 DEBUG_ONLY(verify_trees();)
412 }
413
test_scatter()414 void test_scatter() { test(scatter); }
test_right_left()415 void test_right_left() { test(right_left); }
test_left_right()416 void test_left_right() { test(left_right); }
417
418 };
419
420 #define DO_TEST(name, feedingpattern, min, max) \
421 TEST_VM(metaspace, BlockTree_##name##_##feedingpattern) { \
422 BlockTreeTest btt(min, max); \
423 btt.test_##feedingpattern(); \
424 }
425
426 #define DO_TEST_ALL_PATTERNS(name, min, max) \
427 DO_TEST(name, scatter, min, max) \
428 DO_TEST(name, right_left, min, max) \
429 DO_TEST(name, left_right, min, max)
430
431 DO_TEST_ALL_PATTERNS(wide, BlockTree::MinWordSize, 128 * K);
432 DO_TEST_ALL_PATTERNS(narrow, BlockTree::MinWordSize, 16)
433 DO_TEST_ALL_PATTERNS(129, BlockTree::MinWordSize, 129)
434 DO_TEST_ALL_PATTERNS(4K, BlockTree::MinWordSize, 4*K)
435
436