Path: blob/master/test/hotspot/gtest/metaspace/test_chunkManager_stress.cpp
41144 views
/*1* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.2* Copyright (c) 2020 SAP SE. All rights reserved.3* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.4*5* This code is free software; you can redistribute it and/or modify it6* under the terms of the GNU General Public License version 2 only, as7* published by the Free Software Foundation.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 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 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*23*/2425#include "precompiled.hpp"26#include "memory/metaspace/chunkManager.hpp"27#include "memory/metaspace/metaspaceSettings.hpp"28#include "memory/metaspace/virtualSpaceList.hpp"29//#define LOG_PLEASE30#include "metaspaceGtestCommon.hpp"31#include "metaspaceGtestContexts.hpp"32#include "metaspaceGtestRangeHelpers.hpp"33#include "metaspaceGtestSparseArray.hpp"3435using metaspace::ChunkManager;36using metaspace::Settings;3738class ChunkManagerRandomChunkAllocTest {3940static const size_t max_footprint_words = 8 * M;4142ChunkGtestContext _context;4344// All allocated live chunks45typedef SparseArray<Metachunk*> SparseArrayOfChunks;46SparseArrayOfChunks _chunks;4748const ChunkLevelRange _chunklevel_range;49const float _commit_factor;5051// Depending on a probability pattern, come up with a reasonable limit to number of live chunks52static int max_num_live_chunks(ChunkLevelRange r, float commit_factor) {53// Assuming we allocate only the largest type of chunk, committed to the fullest commit factor,54// how many chunks can we accomodate before hitting max_footprint_words?55const size_t largest_chunk_size = word_size_for_level(r.lowest());56int max_chunks = (max_footprint_words * commit_factor) / largest_chunk_size;57// .. but cap at (min) 50 and (max) 100058max_chunks = MIN2(1000, max_chunks);59max_chunks = MAX2(50, max_chunks);60return max_chunks;61}6263// Return true if, after an allocation error happened, a reserve error seems likely.64bool could_be_reserve_error() {65return _context.vslist().is_full();66}6768// Return true if, after an allocation error happened, a commit error seems likely.69bool could_be_commit_error(size_t additional_word_size) {7071// could it be commit limit hit?7273if (Settings::new_chunks_are_fully_committed()) {74// For all we know we may have just failed to fully-commit a new root chunk.75additional_word_size = MAX_CHUNK_WORD_SIZE;76}7778// Note that this is difficult to verify precisely, since there are79// several layers of truth:80// a) at the lowest layer (RootChunkArea) we have a bitmap of committed granules;81// b) at the vslist layer, we keep running counters of committed/reserved words;82// c) at the chunk layer, we keep a commit watermark (committed_words).83//84// (a) should mirror reality.85// (a) and (b) should be precisely in sync. This is tested by86// VirtualSpaceList::verify().87// (c) can be, by design, imprecise (too low).88//89// Here, I check (b) and trust it to be correct. We also call vslist::verify().90DEBUG_ONLY(_context.verify();)9192const size_t commit_add = align_up(additional_word_size, Settings::commit_granule_words());93if (_context.commit_limit() <= (commit_add + _context.vslist().committed_words())) {94return true;95}9697return false;9899}100101// Given a chunk level and a factor, return a random commit size.102static size_t random_committed_words(chunklevel_t lvl, float commit_factor) {103const size_t sz = word_size_for_level(lvl) * commit_factor;104if (sz < 2) {105return 0;106}107return MIN2(SizeRange(sz).random_value(), sz);108}109110//// Chunk allocation ////111112// Given an slot index, allocate a random chunk and set it into that slot. Slot must be empty.113// Returns false if allocation fails.114bool allocate_random_chunk_at(int slot) {115116DEBUG_ONLY(_chunks.check_slot_is_null(slot);)117118const ChunkLevelRange r = _chunklevel_range.random_subrange();119const chunklevel_t pref_level = r.lowest();120const chunklevel_t max_level = r.highest();121const size_t min_committed = random_committed_words(max_level, _commit_factor);122123Metachunk* c = NULL;124_context.alloc_chunk(&c, r.lowest(), r.highest(), min_committed);125if (c == NULL) {126EXPECT_TRUE(could_be_reserve_error() ||127could_be_commit_error(min_committed));128LOG("Alloc chunk at %d failed.", slot);129return false;130}131132_chunks.set_at(slot, c);133134LOG("Allocated chunk at %d: " METACHUNK_FORMAT ".", slot, METACHUNK_FORMAT_ARGS(c));135136return true;137138}139140// Allocates a random number of random chunks141bool allocate_random_chunks() {142int to_alloc = 1 + IntRange(MAX2(1, _chunks.size() / 8)).random_value();143bool success = true;144int slot = _chunks.first_null_slot();145while (to_alloc > 0 && slot != -1 && success) {146success = allocate_random_chunk_at(slot);147slot = _chunks.next_null_slot(slot);148to_alloc --;149}150return success && to_alloc == 0;151}152153bool fill_all_slots_with_random_chunks() {154bool success = true;155for (int slot = _chunks.first_null_slot();156slot != -1 && success; slot = _chunks.next_null_slot(slot)) {157success = allocate_random_chunk_at(slot);158}159return success;160}161162//// Chunk return ////163164// Given an slot index, return the chunk in that slot to the chunk manager.165void return_chunk_at(int slot) {166Metachunk* c = _chunks.at(slot);167LOG("Returning chunk at %d: " METACHUNK_FORMAT ".", slot, METACHUNK_FORMAT_ARGS(c));168_context.return_chunk(c);169_chunks.set_at(slot, NULL);170}171172// return a random number of chunks (at most a quarter of the full slot range)173void return_random_chunks() {174int to_free = 1 + IntRange(MAX2(1, _chunks.size() / 8)).random_value();175int index = _chunks.first_non_null_slot();176while (to_free > 0 && index != -1) {177return_chunk_at(index);178index = _chunks.next_non_null_slot(index);179to_free --;180}181}182183void return_all_chunks() {184for (int slot = _chunks.first_non_null_slot();185slot != -1; slot = _chunks.next_non_null_slot(slot)) {186return_chunk_at(slot);187}188}189190// adjust test if we change levels191STATIC_ASSERT(HIGHEST_CHUNK_LEVEL == CHUNK_LEVEL_1K);192STATIC_ASSERT(LOWEST_CHUNK_LEVEL == CHUNK_LEVEL_4M);193194void one_test() {195196fill_all_slots_with_random_chunks();197_chunks.shuffle();198199IntRange rand(100);200201for (int j = 0; j < 1000; j++) {202203bool force_alloc = false;204bool force_free = true;205206bool do_alloc =207force_alloc ? true :208(force_free ? false : rand.random_value() >= 50);209force_alloc = force_free = false;210211if (do_alloc) {212if (!allocate_random_chunks()) {213force_free = true;214}215} else {216return_random_chunks();217}218219_chunks.shuffle();220221}222223return_all_chunks();224225}226227public:228229// A test with no limits230ChunkManagerRandomChunkAllocTest(ChunkLevelRange r, float commit_factor) :231_context(),232_chunks(max_num_live_chunks(r, commit_factor)),233_chunklevel_range(r),234_commit_factor(commit_factor)235{}236237// A test with no reserve limit but commit limit238ChunkManagerRandomChunkAllocTest(size_t commit_limit,239ChunkLevelRange r, float commit_factor) :240_context(commit_limit),241_chunks(max_num_live_chunks(r, commit_factor)),242_chunklevel_range(r),243_commit_factor(commit_factor)244{}245246// A test with both reserve and commit limit247// ChunkManagerRandomChunkAllocTest(size_t commit_limit, size_t reserve_limit,248// ChunkLevelRange r, float commit_factor)249// : _helper(commit_limit, reserve_limit),250// _chunks(max_num_live_chunks(r, commit_factor)),251// _chunklevel_range(r),252// _commit_factor(commit_factor)253// {}254255void do_tests() {256const int num_runs = 5;257for (int n = 0; n < num_runs; n++) {258one_test();259}260}261262};263264#define DEFINE_TEST(name, range, commit_factor) \265TEST_VM(metaspace, chunkmanager_random_alloc_##name) { \266ChunkManagerRandomChunkAllocTest test(range, commit_factor); \267test.do_tests(); \268}269270DEFINE_TEST(test_nolimit_1, ChunkLevelRanges::small_chunks(), 0.0f)271DEFINE_TEST(test_nolimit_2, ChunkLevelRanges::small_chunks(), 0.5f)272DEFINE_TEST(test_nolimit_3, ChunkLevelRanges::small_chunks(), 1.0f)273274DEFINE_TEST(test_nolimit_4, ChunkLevelRanges::all_chunks(), 0.0f)275DEFINE_TEST(test_nolimit_5, ChunkLevelRanges::all_chunks(), 0.5f)276DEFINE_TEST(test_nolimit_6, ChunkLevelRanges::all_chunks(), 1.0f)277278#define DEFINE_TEST_2(name, range, commit_factor) \279TEST_VM(metaspace, chunkmanager_random_alloc_##name) { \280const size_t commit_limit = 256 * K; \281ChunkManagerRandomChunkAllocTest test(commit_limit, range, commit_factor); \282test.do_tests(); \283}284285DEFINE_TEST_2(test_with_limit_1, ChunkLevelRanges::small_chunks(), 0.0f)286DEFINE_TEST_2(test_with_limit_2, ChunkLevelRanges::small_chunks(), 0.5f)287DEFINE_TEST_2(test_with_limit_3, ChunkLevelRanges::small_chunks(), 1.0f)288289DEFINE_TEST_2(test_with_limit_4, ChunkLevelRanges::all_chunks(), 0.0f)290DEFINE_TEST_2(test_with_limit_5, ChunkLevelRanges::all_chunks(), 0.5f)291DEFINE_TEST_2(test_with_limit_6, ChunkLevelRanges::all_chunks(), 1.0f)292293294295