1 /* 2 * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2018, 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 28 #define LOG_PLEASE 29 30 #include "metaspace/metaspaceTestsCommon.hpp" 31 #include "metaspace/metaspace_rangehelpers.hpp" 32 33 static int test_node_id = 100000; // start high to make it stick out in logs. 34 35 36 37 class VirtualSpaceNodeTest { 38 39 // These counters are updated by the Node. 40 SizeCounter _counter_reserved_words; 41 SizeCounter _counter_committed_words; 42 CommitLimiter _commit_limiter; 43 VirtualSpaceNode* _node; 44 45 // These are my checks and counters. 46 const size_t _vs_word_size; 47 const size_t _commit_limit; 48 49 MetachunkList _root_chunks; 50 51 void verify() const { 52 53 ASSERT_EQ(_root_chunks.count() * metaspace::chunklevel::MAX_CHUNK_WORD_SIZE, 54 _node->used_words()); 55 56 ASSERT_GE(_commit_limit, _counter_committed_words.get()); 57 ASSERT_EQ(_commit_limiter.committed_words(), _counter_committed_words.get()); 58 59 // Since we know _counter_committed_words serves our single node alone, the counter has to 60 // match the number of bits in the node internal commit mask. 61 ASSERT_EQ(_counter_committed_words.get(), _node->committed_words()); 62 63 ASSERT_EQ(_counter_reserved_words.get(), _vs_word_size); 64 ASSERT_EQ(_counter_reserved_words.get(), _node->word_size()); 65 66 } 67 68 void lock_and_verify_node() { 69 #ifdef ASSERT 70 MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); 71 _node->verify_locked(true); 72 #endif 73 } 74 75 Metachunk* alloc_root_chunk() { 76 77 verify(); 78 79 const bool node_is_full = _node->used_words() == _node->word_size(); 80 Metachunk* c = NULL; 81 { 82 MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); 83 c = _node->allocate_root_chunk(); 84 } 85 86 lock_and_verify_node(); 87 88 if (node_is_full) { 89 90 EXPECT_NULL(c); 91 92 } else { 93 94 DEBUG_ONLY(c->verify(true);) 95 EXPECT_NOT_NULL(c); 96 EXPECT_TRUE(c->is_root_chunk()); 97 EXPECT_TRUE(c->is_free()); 98 EXPECT_EQ(c->word_size(), metaspace::chunklevel::MAX_CHUNK_WORD_SIZE); 99 100 EXPECT_TRUE(c->is_fully_uncommitted()); 101 102 EXPECT_TRUE(_node->contains(c->base())); 103 104 _root_chunks.add(c); 105 106 } 107 108 verify(); 109 110 return c; 111 112 } 113 114 bool commit_root_chunk(Metachunk* c, size_t request_commit_words) { 115 116 verify(); 117 118 const size_t committed_words_before = _counter_committed_words.get(); 119 120 bool rc = c->ensure_committed(request_commit_words); 121 122 verify(); 123 DEBUG_ONLY(c->verify(true);) 124 125 lock_and_verify_node(); 126 127 if (rc == false) { 128 129 // We must have hit the commit limit. 130 EXPECT_GE(committed_words_before + request_commit_words, _commit_limit); 131 132 } else { 133 134 // We should not have hit the commit limit. 135 EXPECT_LE(_counter_committed_words.get(), _commit_limit); 136 137 // We do not know how much we really committed - maybe nothing if the 138 // chunk had been committed before - but we know the numbers should have 139 // risen or at least stayed equal. 140 EXPECT_GE(_counter_committed_words.get(), committed_words_before); 141 142 // The chunk should be as far committed as was requested 143 EXPECT_GE(c->committed_words(), request_commit_words); 144 145 // Zap committed portion. 146 DEBUG_ONLY(zap_range(c->base(), c->committed_words());) 147 148 } 149 150 verify(); 151 152 return rc; 153 154 } // commit_root_chunk 155 156 void uncommit_chunk(Metachunk* c) { 157 158 verify(); 159 160 const size_t committed_words_before = _counter_committed_words.get(); 161 const size_t available_words_before = _commit_limiter.possible_expansion_words(); 162 163 c->uncommit(); 164 165 DEBUG_ONLY(c->verify(true);) 166 167 lock_and_verify_node(); 168 169 EXPECT_EQ(c->committed_words(), (size_t)0); 170 171 // Commit counter should have gone down (by exactly the size of the chunk) if chunk 172 // is larger than a commit granule. 173 // For smaller chunks, we do not know, but at least we know the commit size should not 174 // have gone up. 175 if (c->word_size() >= Settings::commit_granule_words()) { 176 177 EXPECT_EQ(_counter_committed_words.get(), committed_words_before - c->word_size()); 178 179 // also, commit number in commit limiter should have gone down, so we have more space 180 EXPECT_EQ(_commit_limiter.possible_expansion_words(), 181 available_words_before + c->word_size()); 182 183 } else { 184 185 EXPECT_LE(_counter_committed_words.get(), committed_words_before); 186 187 } 188 189 verify(); 190 191 } // uncommit_chunk 192 193 Metachunk* split_chunk_with_checks(Metachunk* c, chunklevel_t target_level, FreeChunkListVector* freelist) { 194 195 DEBUG_ONLY(c->verify(true);) 196 197 const chunklevel_t orig_level = c->level(); 198 assert(orig_level < target_level, "Sanity"); 199 DEBUG_ONLY(metaspace::chunklevel::check_valid_level(target_level);) 200 201 const int total_num_chunks_in_freelist_before = freelist->num_chunks(); 202 const size_t total_word_size_in_freelist_before = freelist->word_size(); 203 204 // freelist->print_on(tty); 205 206 // Split... 207 { 208 MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); 209 _node->split(target_level, c, freelist); 210 } 211 212 // freelist->print_on(tty); 213 214 EXPECT_NOT_NULL(c); 215 EXPECT_EQ(c->level(), target_level); 216 EXPECT_TRUE(c->is_free()); 217 218 // ... check that we get the proper amount of splinters. For every chunk split we expect one 219 // buddy chunk to appear of level + 1 (aka, half size). 220 size_t expected_wordsize_increase = 0; 221 int expected_num_chunks_increase = 0; 222 for (chunklevel_t l = orig_level + 1; l <= target_level; l ++) { 223 expected_wordsize_increase += metaspace::chunklevel::word_size_for_level(l); 224 expected_num_chunks_increase ++; 225 } 226 227 const int total_num_chunks_in_freelist_after = freelist->num_chunks(); 228 const size_t total_word_size_in_freelist_after = freelist->word_size(); 229 230 EXPECT_EQ(total_num_chunks_in_freelist_after, total_num_chunks_in_freelist_before + expected_num_chunks_increase); 231 EXPECT_EQ(total_word_size_in_freelist_after, total_word_size_in_freelist_before + expected_wordsize_increase); 232 233 return c; 234 235 } // end: split_chunk_with_checks 236 237 238 Metachunk* merge_chunk_with_checks(Metachunk* c, chunklevel_t expected_target_level, FreeChunkListVector* freelist) { 239 240 const chunklevel_t orig_level = c->level(); 241 assert(expected_target_level < orig_level, "Sanity"); 242 243 const int total_num_chunks_in_freelist_before = freelist->num_chunks(); 244 const size_t total_word_size_in_freelist_before = freelist->word_size(); 245 246 //freelist->print_on(tty); 247 248 Metachunk* result = NULL; 249 { 250 MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); 251 result = _node->merge(c, freelist); 252 } 253 EXPECT_NOT_NULL(result); 254 EXPECT_TRUE(result->level() == expected_target_level); 255 256 //freelist->print_on(tty); 257 258 // ... check that we merged in the proper amount of chunks. For every decreased level 259 // of the original chunk (each size doubling) we should see one buddy chunk swallowed up. 260 size_t expected_wordsize_decrease = 0; 261 int expected_num_chunks_decrease = 0; 262 for (chunklevel_t l = orig_level; l > expected_target_level; l --) { 263 expected_wordsize_decrease += metaspace::chunklevel::word_size_for_level(l); 264 expected_num_chunks_decrease ++; 265 } 266 267 const int total_num_chunks_in_freelist_after = freelist->num_chunks(); 268 const size_t total_word_size_in_freelist_after = freelist->word_size(); 269 270 EXPECT_EQ(total_num_chunks_in_freelist_after, total_num_chunks_in_freelist_before - expected_num_chunks_decrease); 271 EXPECT_EQ(total_word_size_in_freelist_after, total_word_size_in_freelist_before - expected_wordsize_decrease); 272 273 return result; 274 275 } // end: merge_chunk_with_checks 276 277 public: 278 279 VirtualSpaceNodeTest(size_t vs_word_size, size_t commit_limit) 280 : _counter_reserved_words(), _counter_committed_words(), _commit_limiter(commit_limit), 281 _node(NULL), _vs_word_size(vs_word_size), _commit_limit(commit_limit) 282 { 283 { 284 MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); 285 _node = VirtualSpaceNode::create_node(vs_word_size, &_commit_limiter, 286 &_counter_reserved_words, &_counter_committed_words); 287 EXPECT_EQ(_node->word_size(), vs_word_size); 288 } 289 EXPECT_TRUE(_commit_limiter.possible_expansion_words() == _commit_limit); 290 verify(); 291 } 292 293 ~VirtualSpaceNodeTest() { 294 { 295 MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); 296 delete _node; 297 } 298 // After the node is deleted, counters should be back to zero 299 // (we cannot use ASSERT/EXPECT here in the destructor) 300 assert(_counter_reserved_words.get() == 0, "Sanity"); 301 assert(_counter_committed_words.get() == 0, "Sanity"); 302 assert(_commit_limiter.committed_words() == 0, "Sanity"); 303 } 304 305 void test_simple() { 306 Metachunk* c = alloc_root_chunk(); 307 commit_root_chunk(c, Settings::commit_granule_words()); 308 commit_root_chunk(c, c->word_size()); 309 uncommit_chunk(c); 310 } 311 312 void test_exhaust_node() { 313 Metachunk* c = NULL; 314 bool rc = true; 315 do { 316 c = alloc_root_chunk(); 317 if (c != NULL) { 318 rc = commit_root_chunk(c, c->word_size()); 319 } 320 } while (c != NULL && rc); 321 } 322 323 void test_arbitrary_commits() { 324 325 assert(_commit_limit >= _vs_word_size, "For this test no commit limit."); 326 327 // Get a root chunk to have a committable region 328 Metachunk* c = alloc_root_chunk(); 329 ASSERT_NOT_NULL(c); 330 331 if (c->committed_words() > 0) { 332 c->uncommit(); 333 } 334 335 ASSERT_EQ(_node->committed_words(), (size_t)0); 336 ASSERT_EQ(_counter_committed_words.get(), (size_t)0); 337 338 TestMap testmap(c->word_size()); 339 assert(testmap.get_num_set() == 0, "Sanity"); 340 341 for (int run = 0; run < 1000; run ++) { 342 343 const size_t committed_words_before = testmap.get_num_set(); 344 ASSERT_EQ(_commit_limiter.committed_words(), committed_words_before); 345 ASSERT_EQ(_counter_committed_words.get(), committed_words_before); 346 347 // A random range 348 SizeRange r = SizeRange(c->word_size()).random_aligned_subrange(Settings::commit_granule_words()); 349 350 const size_t committed_words_in_range_before = 351 testmap.get_num_set(r.start(), r.end()); 352 353 const bool do_commit = IntRange(100).random_value() >= 50; 354 if (do_commit) { 355 356 //LOG("c " SIZE_FORMAT "," SIZE_FORMAT, r.start(), r.end()); 357 358 bool rc = false; 359 { 360 MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); 361 rc = _node->ensure_range_is_committed(c->base() + r.start(), r.size()); 362 } 363 364 // Test-zap 365 zap_range(c->base() + r.start(), r.size()); 366 367 // We should never reach commit limit since it is as large as the whole area. 368 ASSERT_TRUE(rc); 369 370 testmap.set_range(r.start(), r.end()); 371 372 } else { 373 374 //LOG("u " SIZE_FORMAT "," SIZE_FORMAT, r.start(), r.end()); 375 376 { 377 MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); 378 _node->uncommit_range(c->base() + r.start(), r.size()); 379 } 380 381 testmap.clear_range(r.start(), r.end()); 382 383 } 384 385 const size_t committed_words_after = testmap.get_num_set(); 386 387 ASSERT_EQ(_commit_limiter.committed_words(), committed_words_after); 388 ASSERT_EQ(_counter_committed_words.get(), committed_words_after); 389 390 verify(); 391 } 392 } 393 394 // Helper function for test_splitting_chunks_1 395 static void check_chunk_is_committed_at_least_up_to(const Metachunk* c, size_t& word_size) { 396 if (word_size >= c->word_size()) { 397 EXPECT_TRUE(c->is_fully_committed()); 398 word_size -= c->word_size(); 399 } else { 400 EXPECT_EQ(c->committed_words(), word_size); 401 word_size = 0; // clear remaining size if there is. 402 } 403 } 404 405 void test_split_and_merge_chunks() { 406 407 assert(_commit_limit >= _vs_word_size, "No commit limit here pls"); 408 409 // Allocate a root chunk and commit a random part of it. Then repeatedly split 410 // it and merge it back together; observe the committed regions of the split chunks. 411 412 Metachunk* c = alloc_root_chunk(); 413 414 if (c->committed_words() > 0) { 415 c->uncommit(); 416 } 417 418 // To capture split-off chunks. Note: it is okay to use this here as a temp object. 419 FreeChunkListVector freelist; 420 421 const int granules_per_root_chunk = (int)(c->word_size() / Settings::commit_granule_words()); 422 423 for (int granules_to_commit = 0; granules_to_commit < granules_per_root_chunk; granules_to_commit ++) { 424 425 const size_t words_to_commit = Settings::commit_granule_words() * granules_to_commit; 426 427 c->ensure_committed(words_to_commit); 428 429 ASSERT_EQ(c->committed_words(), words_to_commit); 430 ASSERT_EQ(_counter_committed_words.get(), words_to_commit); 431 ASSERT_EQ(_commit_limiter.committed_words(), words_to_commit); 432 433 const size_t committed_words_before = c->committed_words(); 434 435 verify(); 436 437 for (chunklevel_t target_level = LOWEST_CHUNK_LEVEL + 1; 438 target_level <= HIGHEST_CHUNK_LEVEL; target_level ++) { 439 440 // Split: 441 Metachunk* c2 = split_chunk_with_checks(c, target_level, &freelist); 442 c2->set_in_use(); 443 444 // Split smallest leftover chunk. 445 if (c2->level() < HIGHEST_CHUNK_LEVEL) { 446 447 Metachunk* c3 = freelist.remove_first(c2->level()); 448 ASSERT_NOT_NULL(c3); // Must exist since c2 must have a splinter buddy now. 449 450 Metachunk* c4 = split_chunk_with_checks(c3, HIGHEST_CHUNK_LEVEL, &freelist); 451 c4->set_in_use(); 452 453 // Merge it back. We expect this to merge up to c2's level, since c2 is in use. 454 c4->set_free(); 455 Metachunk* c5 = merge_chunk_with_checks(c4, c2->level(), &freelist); 456 ASSERT_NOT_NULL(c5); 457 freelist.add(c5); 458 459 } 460 461 // Merge c2 back. 462 c2->set_free(); 463 merge_chunk_with_checks(c2, LOWEST_CHUNK_LEVEL, &freelist); 464 465 // After all this splitting and combining committed size should not have changed. 466 ASSERT_EQ(c2->committed_words(), committed_words_before); 467 468 } 469 470 } 471 472 } // end: test_splitting_chunks 473 474 475 476 477 }; 478 479 480 481 TEST_VM(metaspace, virtual_space_node_test_basics) { 482 483 MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag); 484 485 const size_t word_size = metaspace::chunklevel::MAX_CHUNK_WORD_SIZE * 10; 486 487 SizeCounter scomm; 488 SizeCounter sres; 489 CommitLimiter cl (word_size * 2); // basically, no commit limiter. 490 491 VirtualSpaceNode* node = VirtualSpaceNode::create_node(word_size, &cl, &sres, &scomm); 492 ASSERT_NOT_NULL(node); 493 ASSERT_EQ(node->committed_words(), (size_t)0); 494 ASSERT_EQ(node->committed_words(), scomm.get()); 495 DEBUG_ONLY(node->verify_locked(true);) 496 497 bool b = node->ensure_range_is_committed(node->base(), node->word_size()); 498 ASSERT_TRUE(b); 499 ASSERT_EQ(node->committed_words(), word_size); 500 ASSERT_EQ(node->committed_words(), scomm.get()); 501 DEBUG_ONLY(node->verify_locked(true);) 502 zap_range(node->base(), node->word_size()); 503 504 node->uncommit_range(node->base(), node->word_size()); 505 ASSERT_EQ(node->committed_words(), (size_t)0); 506 ASSERT_EQ(node->committed_words(), scomm.get()); 507 DEBUG_ONLY(node->verify_locked(true);) 508 509 const int num_granules = (int)(word_size / Settings::commit_granule_words()); 510 for (int i = 1; i < num_granules; i += 4) { 511 b = node->ensure_range_is_committed(node->base(), i * Settings::commit_granule_words()); 512 ASSERT_TRUE(b); 513 ASSERT_EQ(node->committed_words(), i * Settings::commit_granule_words()); 514 ASSERT_EQ(node->committed_words(), scomm.get()); 515 DEBUG_ONLY(node->verify_locked(true);) 516 zap_range(node->base(), i * Settings::commit_granule_words()); 517 } 518 519 node->uncommit_range(node->base(), node->word_size()); 520 ASSERT_EQ(node->committed_words(), (size_t)0); 521 ASSERT_EQ(node->committed_words(), scomm.get()); 522 DEBUG_ONLY(node->verify_locked(true);) 523 524 } 525 526 527 // Note: we unfortunately need TEST_VM even though the system tested 528 // should be pretty independent since we need things like os::vm_page_size() 529 // which in turn need OS layer initialization. 530 TEST_VM(metaspace, virtual_space_node_test_1) { 531 VirtualSpaceNodeTest test(metaspace::chunklevel::MAX_CHUNK_WORD_SIZE, 532 metaspace::chunklevel::MAX_CHUNK_WORD_SIZE); 533 test.test_simple(); 534 } 535 536 TEST_VM(metaspace, virtual_space_node_test_2) { 537 // Should not hit commit limit 538 VirtualSpaceNodeTest test(3 * metaspace::chunklevel::MAX_CHUNK_WORD_SIZE, 539 3 * metaspace::chunklevel::MAX_CHUNK_WORD_SIZE); 540 test.test_simple(); 541 test.test_exhaust_node(); 542 } 543 544 TEST_VM(metaspace, virtual_space_node_test_3) { 545 double d = os::elapsedTime(); 546 // Test committing uncommitting arbitrary ranges 547 for (int run = 0; run < 100; run ++) { 548 VirtualSpaceNodeTest test(metaspace::chunklevel::MAX_CHUNK_WORD_SIZE, 549 metaspace::chunklevel::MAX_CHUNK_WORD_SIZE); 550 test.test_split_and_merge_chunks(); 551 } 552 double d2 = os::elapsedTime(); 553 LOG("%f", (d2-d)); 554 } 555 556 TEST_VM(metaspace, virtual_space_node_test_4) { 557 // Should hit commit limit 558 VirtualSpaceNodeTest test(10 * metaspace::chunklevel::MAX_CHUNK_WORD_SIZE, 559 3 * metaspace::chunklevel::MAX_CHUNK_WORD_SIZE); 560 test.test_exhaust_node(); 561 } 562 563 TEST_VM(metaspace, virtual_space_node_test_5) { 564 // Test committing uncommitting arbitrary ranges 565 VirtualSpaceNodeTest test(metaspace::chunklevel::MAX_CHUNK_WORD_SIZE, 566 metaspace::chunklevel::MAX_CHUNK_WORD_SIZE); 567 test.test_arbitrary_commits(); 568 } 569 570 TEST_VM(metaspace, virtual_space_node_test_7) { 571 // Test large allocation and freeing. 572 { 573 VirtualSpaceNodeTest test(metaspace::chunklevel::MAX_CHUNK_WORD_SIZE * 100, 574 metaspace::chunklevel::MAX_CHUNK_WORD_SIZE * 100); 575 test.test_exhaust_node(); 576 } 577 { 578 VirtualSpaceNodeTest test(metaspace::chunklevel::MAX_CHUNK_WORD_SIZE * 100, 579 metaspace::chunklevel::MAX_CHUNK_WORD_SIZE * 100); 580 test.test_exhaust_node(); 581 } 582 583 }