Path: blob/master/test/hotspot/gtest/metaspace/test_blocktree.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/blockTree.hpp"27#include "memory/metaspace/counters.hpp"28#include "memory/resourceArea.hpp"29// #define LOG_PLEASE30#include "metaspaceGtestCommon.hpp"3132using metaspace::BlockTree;33using metaspace::MemRangeCounter;3435// Small helper. Given a 0-terminated array of sizes, a feeder buffer and a tree,36// add blocks of these sizes to the tree in the order they appear in the array.37static void create_nodes(const size_t sizes[], FeederBuffer& fb, BlockTree& bt) {38for (int i = 0; sizes[i] > 0; i ++) {39size_t s = sizes[i];40MetaWord* p = fb.get(s);41bt.add_block(p, s);42}43}4445#define CHECK_BT_CONTENT(bt, expected_num, expected_size) { \46EXPECT_EQ(bt.count(), (unsigned)expected_num); \47EXPECT_EQ(bt.total_size(), (size_t)expected_size); \48if (expected_num == 0) { \49EXPECT_TRUE(bt.is_empty()); \50} else { \51EXPECT_FALSE(bt.is_empty()); \52} \53}5455TEST_VM(metaspace, BlockTree_basic) {5657BlockTree bt;58CHECK_BT_CONTENT(bt, 0, 0);5960size_t real_size = 0;61MetaWord* p = NULL;62MetaWord arr[10000];6364ASSERT_LE(BlockTree::MinWordSize, (size_t)6); // Sanity check. Adjust if Node is changed.6566const size_t minws = BlockTree::MinWordSize;6768// remove_block from empty tree should yield nothing69p = bt.remove_block(minws, &real_size);70EXPECT_NULL(p);71EXPECT_0(real_size);72CHECK_BT_CONTENT(bt, 0, 0);7374// Add some blocks and retrieve them right away.75size_t sizes[] = {76minws, // smallest possible77minws + 10,781024,794711,80081};8283for (int i = 0; sizes[i] > 0; i++) {84bt.add_block(arr, sizes[i]);85CHECK_BT_CONTENT(bt, 1, sizes[i]);8687DEBUG_ONLY(bt.verify();)8889MetaWord* p = bt.remove_block(sizes[i], &real_size);90EXPECT_EQ(p, arr);91EXPECT_EQ(real_size, (size_t)sizes[i]);92CHECK_BT_CONTENT(bt, 0, 0);93}9495}9697// Helper for test_find_nearest_fit_with_tree.98// Out of an array of sizes return the closest upper match to a requested size.99// Returns SIZE_MAX if none found.100static size_t helper_find_nearest_fit(const size_t sizes[], size_t request_size) {101size_t best = SIZE_MAX;102for (int i = 0; sizes[i] > 0; i++) {103if (sizes[i] >= request_size && sizes[i] < best) {104best = sizes[i];105}106}107return best;108}109110// Given a sequence of (0-terminated) sizes, add blocks of those sizes to the tree in the order given. Then, ask111// for a request size and check that it is the expected result.112static void test_find_nearest_fit_with_tree(const size_t sizes[], size_t request_size) {113114BlockTree bt;115FeederBuffer fb(4 * K);116117create_nodes(sizes, fb, bt);118119DEBUG_ONLY(bt.verify();)120121size_t expected_size = helper_find_nearest_fit(sizes, request_size);122size_t real_size = 0;123MetaWord* p = bt.remove_block(request_size, &real_size);124125if (expected_size != SIZE_MAX) {126EXPECT_NOT_NULL(p);127EXPECT_EQ(real_size, expected_size);128} else {129EXPECT_NULL(p);130EXPECT_0(real_size);131}132133LOG(SIZE_FORMAT ": " SIZE_FORMAT ".", request_size, real_size);134135}136137TEST_VM(metaspace, BlockTree_find_nearest_fit) {138139// Test tree for test_find_nearest_fit looks like this140// 30141// / \142// / \143// / \144// 17 50145// / \ / \146// / \ / \147// 10 28 32 51148// \149// 35150151static const size_t sizes[] = {15230, 17, 10, 28,15350, 32, 51, 35,1540 // stop155};156157BlockTree bt;158FeederBuffer fb(4 * K);159160create_nodes(sizes, fb, bt);161162for (int i = BlockTree::MinWordSize; i <= 60; i ++) {163test_find_nearest_fit_with_tree(sizes, i);164}165166}167168// Test repeated adding and removing of blocks of the same size, which169// should exercise the list-part of the tree.170TEST_VM(metaspace, BlockTree_basic_siblings)171{172BlockTree bt;173FeederBuffer fb(4 * K);174175CHECK_BT_CONTENT(bt, 0, 0);176177const size_t test_size = BlockTree::MinWordSize;178const int num = 10;179180for (int i = 0; i < num; i++) {181bt.add_block(fb.get(test_size), test_size);182CHECK_BT_CONTENT(bt, i + 1, (i + 1) * test_size);183}184185DEBUG_ONLY(bt.verify();)186187for (int i = num; i > 0; i --) {188size_t real_size = 4711;189MetaWord* p = bt.remove_block(test_size, &real_size);190EXPECT_TRUE(fb.is_valid_pointer(p));191EXPECT_EQ(real_size, (size_t)test_size);192CHECK_BT_CONTENT(bt, i - 1, (i - 1) * test_size);193}194195}196197#ifdef ASSERT198TEST_VM(metaspace, BlockTree_print_test) {199200static const size_t sizes[] = {20130, 17, 10, 28,20250, 32, 51, 35,2030 // stop204};205206BlockTree bt;207FeederBuffer fb(4 * K);208209create_nodes(sizes, fb, bt);210211ResourceMark rm;212213stringStream ss;214bt.print_tree(&ss);215216LOG("%s", ss.as_string());217}218219// Test that an overwritten node would result in an assert and a printed tree220TEST_VM_ASSERT_MSG(metaspace, BlockTree_overwriter_test, ".*failed: Invalid node") {221static const size_t sizes1[] = { 30, 17, 0 };222static const size_t sizes2[] = { 12, 12, 0 };223224BlockTree bt;225FeederBuffer fb(4 * K);226227// some nodes...228create_nodes(sizes1, fb, bt);229230// a node we will break...231MetaWord* p_broken = fb.get(12);232bt.add_block(p_broken, 12);233234// some more nodes...235create_nodes(sizes2, fb, bt);236237// overwrite node memory (only the very first byte), then verify tree.238// Verification should catch the broken canary, print the tree,239// then assert.240LOG("Will break node at " PTR_FORMAT ".", p2i(p_broken));241tty->print_cr("Death test, please ignore the following \"Invalid node\" printout.");242*((char*)p_broken) = '\0';243bt.verify();244}245#endif246247class BlockTreeTest {248249FeederBuffer _fb;250251BlockTree _bt[2];252MemRangeCounter _cnt[2];253254RandSizeGenerator _rgen;255256#define CHECK_COUNTERS \257CHECK_BT_CONTENT(_bt[0], _cnt[0].count(), _cnt[0].total_size()) \258CHECK_BT_CONTENT(_bt[1], _cnt[1].count(), _cnt[1].total_size())259260#define CHECK_COUNTERS_ARE_0 \261CHECK_BT_CONTENT(_bt[0], 0, 0) \262CHECK_BT_CONTENT(_bt[1], 0, 0)263264#ifdef ASSERT265void verify_trees() {266_bt[0].verify();267_bt[1].verify();268}269#endif270271enum feeding_pattern_t {272scatter = 1,273left_right = 2,274right_left = 3275};276277// Feed the whole feeder buffer to the trees, according to feeding_pattern.278void feed_all(feeding_pattern_t feeding_pattern) {279280MetaWord* p = NULL;281unsigned added = 0;282283// If we feed in small graining, we cap the number of blocks to limit test duration.284const unsigned max_blocks = 2000;285286size_t old_feeding_size = feeding_pattern == right_left ? _rgen.max() : _rgen.min();287do {288size_t s = 0;289switch (feeding_pattern) {290case scatter:291// fill completely random292s =_rgen.get();293break;294case left_right:295// fill in ascending order to provoke a misformed tree.296s = MIN2(_rgen.get(), old_feeding_size);297old_feeding_size = s;298break;299case right_left:300// same, but descending.301s = MAX2(_rgen.get(), old_feeding_size);302old_feeding_size = s;303break;304}305306// Get a block from the feeder buffer; feed it alternatingly to either tree.307p = _fb.get(s);308if (p != NULL) {309int which = added % 2;310added++;311_bt[which].add_block(p, s);312_cnt[which].add(s);313CHECK_COUNTERS314}315} while (p != NULL && added < max_blocks);316317DEBUG_ONLY(verify_trees();)318319// Trees should contain the same number of nodes (+-1)320EXPECT_TRUE(_bt[0].count() == _bt[1].count() ||321_bt[0].count() == _bt[1].count() + 1);322323}324325void ping_pong_loop(int iterations) {326327// We loop and in each iteration randomly retrieve a block from one tree and add it to another.328for (int i = 0; i < iterations; i++) {329int taker = 0;330int giver = 1;331if ((os::random() % 10) > 5) {332giver = 0; taker = 1;333}334size_t s =_rgen.get();335size_t real_size = 0;336MetaWord* p = _bt[giver].remove_block(s, &real_size);337if (p != NULL) {338ASSERT_TRUE(_fb.is_valid_range(p, real_size));339ASSERT_GE(real_size, s);340_bt[taker].add_block(p, real_size);341_cnt[giver].sub(real_size);342_cnt[taker].add(real_size);343CHECK_COUNTERS;344}345346#ifdef ASSERT347if (true) {//i % 1000 == 0) {348verify_trees();349}350#endif351}352}353354// Drain the trees. While draining, observe the order of the drained items.355void drain_all() {356357for (int which = 0; which < 2; which++) {358BlockTree* bt = _bt + which;359size_t last_size = 0;360while (!bt->is_empty()) {361362// We only query for the minimal size. Actually returned size should be363// monotonously growing since remove_block should always return the closest fit.364size_t real_size = 4711;365MetaWord* p = bt->remove_block(BlockTree::MinWordSize, &real_size);366ASSERT_TRUE(_fb.is_valid_range(p, real_size));367368ASSERT_GE(real_size, last_size);369last_size = real_size;370371_cnt[which].sub(real_size);372CHECK_COUNTERS;373374DEBUG_ONLY(bt->verify();)375376}377}378379}380381void test(feeding_pattern_t feeding_pattern) {382383CHECK_COUNTERS_ARE_0384385feed_all(feeding_pattern);386387LOG("Blocks in circulation: bt1=%d:" SIZE_FORMAT ", bt2=%d:" SIZE_FORMAT ".",388_bt[0].count(), _bt[0].total_size(),389_bt[1].count(), _bt[1].total_size());390391ping_pong_loop(5000);392393LOG("After Pingpong: bt1=%d:" SIZE_FORMAT ", bt2=%d:" SIZE_FORMAT ".",394_bt[0].count(), _bt[0].total_size(),395_bt[1].count(), _bt[1].total_size());396397drain_all();398399CHECK_COUNTERS_ARE_0400}401402public:403404BlockTreeTest(size_t min_word_size, size_t max_word_size) :405_fb(2 * M),406_bt(),407_rgen(min_word_size, max_word_size)408{409CHECK_COUNTERS;410DEBUG_ONLY(verify_trees();)411}412413void test_scatter() { test(scatter); }414void test_right_left() { test(right_left); }415void test_left_right() { test(left_right); }416417};418419#define DO_TEST(name, feedingpattern, min, max) \420TEST_VM(metaspace, BlockTree_##name##_##feedingpattern) { \421BlockTreeTest btt(min, max); \422btt.test_##feedingpattern(); \423}424425#define DO_TEST_ALL_PATTERNS(name, min, max) \426DO_TEST(name, scatter, min, max) \427DO_TEST(name, right_left, min, max) \428DO_TEST(name, left_right, min, max)429430DO_TEST_ALL_PATTERNS(wide, BlockTree::MinWordSize, 128 * K);431DO_TEST_ALL_PATTERNS(narrow, BlockTree::MinWordSize, 16)432DO_TEST_ALL_PATTERNS(129, BlockTree::MinWordSize, 129)433DO_TEST_ALL_PATTERNS(4K, BlockTree::MinWordSize, 4*K)434435436437