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 #include "runtime/os.hpp"
  28 
  29 #include "metaspaceTestsCommon.hpp"
  30 #include "metaspace/metaspace_rangehelpers.hpp"
  31 
  32 static int get_random(int limit) { return os::random() % limit; }
  33 
  34 class CommitMaskTest {
  35   const MetaWord* const _base;
  36   const size_t _word_size;
  37 
  38   CommitMask _mask;
  39 
  40   void verify_mask() {
  41     // Note: we omit the touch test since we operate on fictional
  42     // memory
  43     DEBUG_ONLY(_mask.verify(false);)
  44   }
  45 
  46   // Return a random sub range within [_base.._base + word_size),
  47   // aligned to granule size
  48   const MetaWord* calc_random_subrange(size_t* p_word_size) {
  49     size_t l1 = get_random((int)_word_size);
  50     size_t l2 = get_random((int)_word_size);
  51     if (l1 > l2) {
  52       size_t l = l1;
  53       l1 = l2;
  54       l2 = l;
  55     }
  56     l1 = align_down(l1, Settings::commit_granule_words());
  57     l2 = align_up(l2, Settings::commit_granule_words());
  58 
  59     const MetaWord* p = _base + l1;
  60     const size_t len = l2 - l1;
  61 
  62     assert(p >= _base && p + len <= _base + _word_size,
  63            "Sanity");
  64     *p_word_size = len;
  65 
  66     return p;
  67   }
  68 
  69   void test1() {
  70 
  71     LOG("test1");
  72 
  73     // Commit everything
  74     size_t prior_committed = _mask.mark_range_as_committed(_base, _word_size);
  75     verify_mask();
  76     ASSERT_LE(prior_committed, _word_size); // We do not really know
  77 
  78     // Commit everything again, should be a noop
  79     prior_committed = _mask.mark_range_as_committed(_base, _word_size);
  80     verify_mask();
  81     ASSERT_EQ(prior_committed, _word_size);
  82 
  83     ASSERT_EQ(_mask.get_committed_size(),
  84               _word_size);
  85     ASSERT_EQ(_mask.get_committed_size_in_range(_base, _word_size),
  86               _word_size);
  87 
  88     for (const MetaWord* p = _base; p < _base + _word_size; p ++) {
  89       ASSERT_TRUE(_mask.is_committed_address(p));
  90     }
  91 
  92     // Now make an uncommitted hole
  93     size_t sr_word_size;
  94     const MetaWord* sr_base = calc_random_subrange(&sr_word_size);
  95     LOG("subrange " PTR_FORMAT "-" PTR_FORMAT ".",
  96         p2i(sr_base), p2i(sr_base + sr_word_size));
  97 
  98     size_t prior_uncommitted =
  99         _mask.mark_range_as_uncommitted(sr_base, sr_word_size);
 100     verify_mask();
 101     ASSERT_EQ(prior_uncommitted, (size_t)0);
 102 
 103     // Again, for fun, should be a noop now.
 104     prior_uncommitted = _mask.mark_range_as_uncommitted(sr_base, sr_word_size);
 105     verify_mask();
 106     ASSERT_EQ(prior_uncommitted, sr_word_size);
 107 
 108     ASSERT_EQ(_mask.get_committed_size_in_range(sr_base, sr_word_size),
 109               (size_t)0);
 110     ASSERT_EQ(_mask.get_committed_size(),
 111               _word_size - sr_word_size);
 112     ASSERT_EQ(_mask.get_committed_size_in_range(_base, _word_size),
 113               _word_size - sr_word_size);
 114     for (const MetaWord* p = _base; p < _base + _word_size; p ++) {
 115       if (p >= sr_base && p < sr_base + sr_word_size) {
 116         ASSERT_FALSE(_mask.is_committed_address(p));
 117       } else {
 118         ASSERT_TRUE(_mask.is_committed_address(p));
 119       }
 120     }
 121 
 122     // Recommit whole range
 123     prior_committed = _mask.mark_range_as_committed(_base, _word_size);
 124     verify_mask();
 125     ASSERT_EQ(prior_committed, _word_size - sr_word_size);
 126 
 127     ASSERT_EQ(_mask.get_committed_size_in_range(sr_base, sr_word_size),
 128               sr_word_size);
 129     ASSERT_EQ(_mask.get_committed_size(),
 130               _word_size);
 131     ASSERT_EQ(_mask.get_committed_size_in_range(_base, _word_size),
 132               _word_size);
 133     for (const MetaWord* p = _base; p < _base + _word_size; p ++) {
 134       ASSERT_TRUE(_mask.is_committed_address(p));
 135     }
 136 
 137   }
 138 
 139   void test2() {
 140 
 141     LOG("test2");
 142 
 143     // Uncommit everything
 144     size_t prior_uncommitted = _mask.mark_range_as_uncommitted(_base, _word_size);
 145     verify_mask();
 146     ASSERT_LE(prior_uncommitted, _word_size);
 147 
 148     // Uncommit everything again, should be a noop
 149     prior_uncommitted = _mask.mark_range_as_uncommitted(_base, _word_size);
 150     verify_mask();
 151     ASSERT_EQ(prior_uncommitted, _word_size);
 152 
 153     ASSERT_EQ(_mask.get_committed_size(),
 154         (size_t)0);
 155     ASSERT_EQ(_mask.get_committed_size_in_range(_base, _word_size),
 156         (size_t)0);
 157 
 158     // Now make an committed region
 159     size_t sr_word_size;
 160     const MetaWord* sr_base = calc_random_subrange(&sr_word_size);
 161     LOG("subrange " PTR_FORMAT "-" PTR_FORMAT ".",
 162         p2i(sr_base), p2i(sr_base + sr_word_size));
 163 
 164     ASSERT_EQ(_mask.get_committed_size_in_range(sr_base, sr_word_size),
 165               (size_t)0);
 166     for (const MetaWord* p = _base; p < _base + _word_size; p ++) {
 167       ASSERT_FALSE(_mask.is_committed_address(p));
 168     }
 169 
 170     size_t prior_committed = _mask.mark_range_as_committed(sr_base, sr_word_size);
 171     verify_mask();
 172     ASSERT_EQ(prior_committed, (size_t)0);
 173 
 174     // Again, for fun, should be a noop now.
 175     prior_committed = _mask.mark_range_as_committed(sr_base, sr_word_size);
 176     verify_mask();
 177     ASSERT_EQ(prior_committed, sr_word_size);
 178 
 179     ASSERT_EQ(_mask.get_committed_size_in_range(sr_base, sr_word_size),
 180         sr_word_size);
 181     ASSERT_EQ(_mask.get_committed_size(),
 182         sr_word_size);
 183     ASSERT_EQ(_mask.get_committed_size_in_range(_base, _word_size),
 184         sr_word_size);
 185     for (const MetaWord* p = _base; p < _base + _word_size; p ++) {
 186       if (p >= sr_base && p < sr_base + sr_word_size) {
 187         ASSERT_TRUE(_mask.is_committed_address(p));
 188       } else {
 189         ASSERT_FALSE(_mask.is_committed_address(p));
 190       }
 191     }
 192 
 193     // Re-uncommit whole range
 194     prior_uncommitted = _mask.mark_range_as_uncommitted(_base, _word_size);
 195     verify_mask();
 196     ASSERT_EQ(prior_uncommitted, _word_size - sr_word_size);
 197 
 198     EXPECT_EQ(_mask.get_committed_size_in_range(sr_base, sr_word_size),
 199         (size_t)0);
 200     EXPECT_EQ(_mask.get_committed_size(),
 201         (size_t)0);
 202     EXPECT_EQ(_mask.get_committed_size_in_range(_base, _word_size),
 203         (size_t)0);
 204     for (const MetaWord* p = _base; p < _base + _word_size; p ++) {
 205       ASSERT_FALSE(_mask.is_committed_address(p));
 206     }
 207 
 208   }
 209 
 210 
 211   void test3() {
 212 
 213     // arbitrary ranges are set and cleared and compared with the test map
 214     TestMap map(_word_size);
 215 
 216     _mask.clear_large();
 217 
 218     for (int run = 0; run < 100; run ++) {
 219 
 220       // A random range
 221       SizeRange r = SizeRange(_word_size).random_aligned_subrange(Settings::commit_granule_words());
 222 
 223       if (os::random() % 100 < 50) {
 224         _mask.mark_range_as_committed(_base + r.lowest(), r.size());
 225         map.set_range(r.lowest(), r.end());
 226       } else {
 227         _mask.mark_range_as_uncommitted(_base + r.lowest(), r.size());
 228         map.clear_range(r.lowest(), r.end());
 229       }
 230 
 231       ASSERT_EQ(_mask.get_committed_size(), (size_t)map.get_num_set());
 232 
 233       ASSERT_EQ(_mask.get_committed_size_in_range(_base + r.lowest(), r.size()),
 234                 (size_t)map.get_num_set(r.lowest(), r.end()));
 235 
 236     }
 237 
 238   }
 239 
 240 
 241 public:
 242 
 243   CommitMaskTest(const MetaWord* base, size_t size)
 244     : _base(base), _word_size(size), _mask(base, size)
 245   {}
 246 
 247   void test() {
 248     LOG("mask range: " PTR_FORMAT "-" PTR_FORMAT
 249          " (" SIZE_FORMAT " words).",
 250          p2i(_base), p2i(_base + _word_size), _word_size);
 251     for (int i = 0; i < 5; i ++) {
 252       test1(); test2(); test3();
 253     }
 254   }
 255 
 256 
 257 };
 258 
 259 TEST_VM(metaspace, commit_mask_basics) {
 260 
 261   const MetaWord* const base = (const MetaWord*) 0x100000;
 262 
 263   CommitMask mask1(base, Settings::commit_granule_words());
 264   ASSERT_EQ(mask1.size(), (BitMap::idx_t)1);
 265 
 266   CommitMask mask2(base, Settings::commit_granule_words() * 4);
 267   ASSERT_EQ(mask2.size(), (BitMap::idx_t)4);
 268 
 269   CommitMask mask3(base, Settings::commit_granule_words() * 43);
 270   ASSERT_EQ(mask3.size(), (BitMap::idx_t)43);
 271 
 272   mask3.mark_range_as_committed(base, Settings::commit_granule_words());
 273   mask3.mark_range_as_committed(base + (Settings::commit_granule_words() * 42), Settings::commit_granule_words());
 274 
 275   ASSERT_EQ(mask3.at(0), 1);
 276   for (int i = 1; i < 42; i ++) {
 277     ASSERT_EQ(mask3.at(i), 0);
 278   }
 279   ASSERT_EQ(mask3.at(42), 1);
 280 
 281 }
 282 
 283 TEST_VM(metaspace, commit_mask_small) {
 284 
 285   const MetaWord* const base = (const MetaWord*) 0x100000;
 286 
 287   CommitMaskTest test(base, Settings::commit_granule_words());
 288   test.test();
 289 
 290 }
 291 
 292 TEST_VM(metaspace, commit_mask_range) {
 293 
 294   const MetaWord* const base = (const MetaWord*) 0x100000;
 295   const size_t len = Settings::commit_granule_words() * 4;
 296   const MetaWord* const end = base + len;
 297   CommitMask mask(base, len);
 298 
 299   LOG("total range: " PTR_FORMAT "-" PTR_FORMAT "\n", p2i(base), p2i(end));
 300 
 301   size_t l = mask.mark_range_as_committed(base, len);
 302   ASSERT_LE(l, len);
 303 
 304   for (const MetaWord* p = base; p <= end - Settings::commit_granule_words();
 305        p += Settings::commit_granule_words()) {
 306     for (const MetaWord* p2 = p + Settings::commit_granule_words();
 307          p2 <= end; p2 += Settings::commit_granule_words()) {
 308       LOG(PTR_FORMAT "-" PTR_FORMAT "\n", p2i(p), p2i(p2));
 309       EXPECT_EQ(mask.get_committed_size_in_range(p, p2 - p),
 310                 (size_t)(p2 - p));
 311     }
 312   }
 313 
 314   l = mask.mark_range_as_uncommitted(base, len);
 315   ASSERT_EQ(l, (size_t)0);
 316 
 317   for (const MetaWord* p = base; p <= end - Settings::commit_granule_words();
 318        p += Settings::commit_granule_words()) {
 319     for (const MetaWord* p2 = p + Settings::commit_granule_words();
 320          p2 <= end; p2 += Settings::commit_granule_words()) {
 321       LOG(PTR_FORMAT "-" PTR_FORMAT "\n", p2i(p), p2i(p2));
 322       EXPECT_EQ(mask.get_committed_size_in_range(p, p2 - p),
 323                 (size_t)(0));
 324     }
 325   }
 326 
 327 }
 328 
 329 
 330 TEST_VM(metaspace, commit_mask_random) {
 331 
 332   for (int i = 0; i < 5; i ++) {
 333 
 334     // make up a range out of thin air
 335     const MetaWord* const base =
 336         align_down( (const MetaWord*) ((uintptr_t) os::random() * os::random()),
 337                     Settings::commit_granule_bytes());
 338     const size_t len = align_up( 1 + (os::random() % M),
 339                     Settings::commit_granule_words());
 340 
 341     CommitMaskTest test(base, len);
 342     test.test();
 343 
 344   }
 345 
 346 }