1 /*
   2  * Copyright (c) 2019, SAP. All rights reserved.
   3  * Copyright (c) 2019, Oracle and/or its affiliates. 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 #include "precompiled.hpp"
  26 
  27 #include "memory/allocation.hpp"
  28 #include "memory/metaspace/abstractPool.hpp"
  29 #include "runtime/os.hpp"
  30 #include "utilities/debug.hpp"
  31 #include "utilities/globalDefinitions.hpp"
  32 
  33 #include "unittest.hpp"
  34 
  35 #include <limits>
  36 
  37 // Test AbstractPool class
  38 
  39 template <class E, class I>
  40 class AbstractPoolTest {
  41 public:
  42 
  43   typedef metaspace::AbstractPool<E, I> PoolType;
  44 
  45 private:
  46 
  47   PoolType _pool;
  48   size_t _num_allocated;
  49 
  50   // Array of the same size as the pool max capacity; holds the allocated elements.
  51   E** _elems;
  52 
  53 
  54   // Helper function. Writes a canary into *E.
  55   static void mark_element(E* e, uint8_t v)             { *(uint8_t*)e = v; }
  56 
  57   // Helper function. Check expected canary in *E.
  58   static void check_element_mark(const E* e, uint8_t v) { uint8_t v2 = *(const uint8_t*)e; ASSERT_TRUE(v == v2); }
  59 
  60   // Given an element, check its index resolution.
  61   void check_element_to_index_translation(const E* e) {
  62     I idx = _pool->index_for_elem(e);
  63     ASSERT_TRUE(_pool->is_valid_index(idx));
  64     E* e2 = _pool->elem_at_index(idx);
  65     ASSERT_TRUE(e2 = e);
  66   }
  67 
  68   void attempt_free_at(size_t index) {
  69     if (_elems[index] == NULL) {
  70       return;
  71     }
  72 
  73     check_element_mark(_elems[index]);
  74     _pool.return_element(_elems[index]);
  75     _elems[index] = NULL;
  76 
  77     ASSERT_GT(_num_allocated, 0);
  78     _num_allocated --;
  79 
  80     ASSERT_EQ(_num_allocated, _pool.used());
  81   }
  82 
  83   void attempt_allocate_at(size_t index) {
  84 
  85     assert(index <= _pool.max_capacity(), "Sanity");
  86 
  87     if (_elems[index] != NULL) {
  88       return;
  89     }
  90 
  91     E* e = _pool.allocate_element();
  92     if (e == NULL) {
  93       ASSERT_TRUE(_pool.is_full());
  94       ASSERT_TRUE(_pool.used() == _pool.max_capacity());
  95     } else {
  96       mark_element(e, index & 0xFF);
  97       check_element_to_index_translation(e);
  98       _num_allocated ++;
  99       _elems[index] = e;
 100       ASSERT_EQ(_num_allocated, _pool.used());
 101     }
 102 
 103   }
 104 
 105   void attempt_allocate_or_free_at(size_t index) {
 106     if (_elems[index] == NULL) {
 107       attempt_allocate_at(index);
 108     } else {
 109       attempt_free_at(index);
 110     }
 111   }
 112 
 113   // Fill pool until exhaustion.
 114   void test_exhaustion() {
 115 
 116     for (size_t i = 0; i < _pool.max_capacity(); i ++) {
 117       attempt_allocate_at(i);
 118     }
 119 
 120     ASSERT_TRUE(_pool.used() == _pool.max_capacity());
 121     ASSERT_TRUE(_pool.is_full());
 122 
 123     DEBUG_ONLY(_pool.verify(true);)
 124 
 125   }
 126 
 127   // Fill pool until exhaustion.
 128   void test_draining() {
 129 
 130     for (size_t i = 0; i < _pool.max_capacity(); i ++) {
 131       attempt_free_at(i);
 132     }
 133 
 134     ASSERT_TRUE(_pool.used() == _pool.max_capacity());
 135     ASSERT_TRUE(_pool.is_empty());
 136 
 137     DEBUG_ONLY(_pool.verify(true);)
 138   }
 139 
 140   // Randomly allocate from the pool and free. Slight preference for allocation.
 141   void test_random_alloc_free(int num_iterations) {
 142 
 143     for (int iter = 0; iter < num_iterations; iter ++) {
 144       size_t index = (size_t)os::random() % _pool.max_capacity();
 145       attempt_allocate_or_free_at(index);
 146     }
 147 
 148     DEBUG_ONLY(_pool.verify(true);)
 149 
 150   }
 151 
 152   static void test_once() {
 153 
 154 
 155     const int max_for_index_type = (int) std::numeric_limits<I>::max();
 156 
 157     const size_t max = MAX2(os::random() % max_for_index_type, 2);
 158     const size_t inc = MAX2(max / ((os::random() % 100) + 1), (size_t)1);
 159 
 160     AbstractPoolTest<E, I> test(inc, max);
 161 
 162     test.test_random_alloc_free(100);
 163     test.test_exhaustion();
 164     test.test_draining();
 165     test.test_random_alloc_free(100);
 166 
 167   }
 168 
 169 
 170 public:
 171 
 172   AbstractPoolTest(size_t capacity_increase,
 173                    size_t max_capacity)
 174     : _pool("just-a-test-pool", max_capacity, capacity_increase),
 175       _num_allocated(0), _elems(NULL)
 176   {
 177     _elems = NEW_C_HEAP_ARRAY(E*, max_capacity, mtInternal);
 178     memset(_elems, 0, max_capacity * sizeof(E));
 179   }
 180 
 181   ~AbstractPoolTest() { FREE_C_HEAP_ARRAY(E*, _elems); }
 182 
 183   static void run_tests() {
 184     for (int i = 0; i < 1000; i ++) {
 185       test_once();
 186     }
 187   }
 188 
 189 };
 190 
 191 
 192 
 193 
 194 
 195 TEST(metaspace, pool1) {
 196   AbstractPoolTest<uint64_t, uint8_t>::run_tests();
 197 }
 198 
 199 TEST(metaspace, pool2) {
 200   AbstractPoolTest<uint64_t, uint16_t>::run_tests();
 201 }
 202 
 203 TEST(metaspace, pool3) {
 204   AbstractPoolTest<uint32_t, uint8_t>::run_tests();
 205 }
 206 
 207 struct S1 { uint16_t i1; uint16_t i2; uint16_t i3; };
 208 
 209 TEST(metaspace, pool4) {
 210   AbstractPoolTest<S1, uint8_t>::run_tests();
 211 }
 212 
 213 TEST(metaspace, pool5) {
 214   AbstractPoolTest<S1, uint16_t>::run_tests();
 215 }
 216 
 217 // crooked and unaligned :)
 218 
 219 struct S2 { char i [0x100];  };
 220 
 221 TEST(metaspace, pool6) {
 222   AbstractPoolTest<S2, uint8_t>::run_tests();
 223 }
 224 
 225 TEST(metaspace, pool7) {
 226   AbstractPoolTest<S2, uint16_t>::run_tests();
 227 }
 228 
 229 
 230 // crooked and unaligned :)
 231 /*
 232  *
 233  FOr now, excluded: see STATIC_ASSERT in AssertPool. We should not be able to define an AbstractPool where
 234  E has a smaller alignment requirement than I.
 235 
 236 struct S3 { char i [33];  };
 237 
 238 TEST(metaspace, pool8) {
 239   AbstractPoolTest<S3, uint8_t>::run_tests();
 240 }
 241 
 242 TEST(metaspace, pool9) {
 243   AbstractPoolTest<S3, uint16_t>::run_tests();
 244 }
 245 
 246 */
 247 
 248