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