1 /*
2  * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3  * Copyright (c) 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 "memory/metaspace/counters.hpp"
28 #include "memory/metaspace/freeBlocks.hpp"
29 //#define LOG_PLEASE
30 #include "metaspaceGtestCommon.hpp"
31 
32 using metaspace::FreeBlocks;
33 using metaspace::SizeCounter;
34 
35 #define CHECK_CONTENT(fb, num_blocks_expected, word_size_expected) \
36 { \
37   if (word_size_expected > 0) { \
38     EXPECT_FALSE(fb.is_empty()); \
39   } else { \
40     EXPECT_TRUE(fb.is_empty()); \
41   } \
42   EXPECT_EQ(fb.total_size(), (size_t)word_size_expected); \
43   EXPECT_EQ(fb.count(), (int)num_blocks_expected); \
44 }
45 
46 class FreeBlocksTest {
47 
48   FeederBuffer _fb;
49   FreeBlocks _freeblocks;
50 
51   // random generator for block feeding
52   RandSizeGenerator _rgen_feeding;
53 
54   // random generator for allocations (and, hence, deallocations)
55   RandSizeGenerator _rgen_allocations;
56 
57   SizeCounter _allocated_words;
58 
59   struct allocation_t {
60     allocation_t* next;
61     size_t word_size;
62     MetaWord* p;
63   };
64 
65   // Array of the same size as the pool max capacity; holds the allocated elements.
66   allocation_t* _allocations;
67 
68   int _num_allocs;
69   int _num_deallocs;
70   int _num_feeds;
71 
feed_some()72   bool feed_some() {
73     size_t word_size = _rgen_feeding.get();
74     MetaWord* p = _fb.get(word_size);
75     if (p != NULL) {
76       _freeblocks.add_block(p, word_size);
77       return true;
78     }
79     return false;
80   }
81 
deallocate_top()82   void deallocate_top() {
83 
84     allocation_t* a = _allocations;
85     if (a != NULL) {
86       _allocations = a->next;
87       check_marked_range(a->p, a->word_size);
88       _freeblocks.add_block(a->p, a->word_size);
89       delete a;
90       DEBUG_ONLY(_freeblocks.verify();)
91     }
92   }
93 
allocate()94   bool allocate() {
95 
96     size_t word_size = MAX2(_rgen_allocations.get(), _freeblocks.MinWordSize);
97     MetaWord* p = _freeblocks.remove_block(word_size);
98     if (p != NULL) {
99       _allocated_words.increment_by(word_size);
100       allocation_t* a = new allocation_t;
101       a->p = p; a->word_size = word_size;
102       a->next = _allocations;
103       _allocations = a;
104       DEBUG_ONLY(_freeblocks.verify();)
105       mark_range(p, word_size);
106       return true;
107     }
108     return false;
109   }
110 
test_all_marked_ranges()111   void test_all_marked_ranges() {
112     for (allocation_t* a = _allocations; a != NULL; a = a->next) {
113       check_marked_range(a->p, a->word_size);
114     }
115   }
116 
test_loop()117   void test_loop() {
118     // We loop and in each iteration execute one of three operations:
119     // - allocation from fbl
120     // - deallocation to fbl of a previously allocated block
121     // - feeding a new larger block into the fbl (mimicks chunk retiring)
122     // When we have fed all large blocks into the fbl (feedbuffer empty), we
123     //  switch to draining the fbl completely (only allocs)
124     bool forcefeed = false;
125     bool draining = false;
126     bool stop = false;
127     int iter = 100000; // safety stop
128     while (!stop && iter > 0) {
129       iter --;
130       int surprise = (int)os::random() % 10;
131       if (!draining && (surprise >= 7 || forcefeed)) {
132         forcefeed = false;
133         if (feed_some()) {
134           _num_feeds++;
135         } else {
136           // We fed all input memory into the fbl. Now lets proceed until the fbl is drained.
137           draining = true;
138         }
139       } else if (!draining && surprise < 1) {
140         deallocate_top();
141         _num_deallocs++;
142       } else {
143         if (allocate()) {
144           _num_allocs++;
145         } else {
146           if (draining) {
147             stop = _freeblocks.total_size() < 512;
148           } else {
149             forcefeed = true;
150           }
151         }
152       }
153       if ((iter % 1000) == 0) {
154         DEBUG_ONLY(_freeblocks.verify();)
155         test_all_marked_ranges();
156         LOG("a %d (" SIZE_FORMAT "), d %d, f %d", _num_allocs, _allocated_words.get(), _num_deallocs, _num_feeds);
157 #ifdef LOG_PLEASE
158         _freeblocks.print(tty, true);
159         tty->cr();
160 #endif
161       }
162     }
163 
164     // Drain
165 
166   }
167 
168 public:
169 
FreeBlocksTest(size_t avg_alloc_size)170   FreeBlocksTest(size_t avg_alloc_size) :
171     _fb(512 * K), _freeblocks(),
172     _rgen_feeding(128, 4096),
173     _rgen_allocations(avg_alloc_size / 4, avg_alloc_size * 2, 0.01f, avg_alloc_size / 3, avg_alloc_size * 30),
174     _allocations(NULL),
175     _num_allocs(0),
176     _num_deallocs(0),
177     _num_feeds(0)
178   {
179     CHECK_CONTENT(_freeblocks, 0, 0);
180     // some initial feeding
181     _freeblocks.add_block(_fb.get(1024), 1024);
182     CHECK_CONTENT(_freeblocks, 1, 1024);
183   }
184 
test_small_allocations()185   static void test_small_allocations() {
186     FreeBlocksTest test(10);
187     test.test_loop();
188   }
189 
test_medium_allocations()190   static void test_medium_allocations() {
191     FreeBlocksTest test(30);
192     test.test_loop();
193   }
194 
test_large_allocations()195   static void test_large_allocations() {
196     FreeBlocksTest test(150);
197     test.test_loop();
198   }
199 
200 };
201 
TEST_VM(metaspace,freeblocks_basics)202 TEST_VM(metaspace, freeblocks_basics) {
203 
204   FreeBlocks fbl;
205   MetaWord tmp[1024];
206   CHECK_CONTENT(fbl, 0, 0);
207 
208   fbl.add_block(tmp, 1024);
209   DEBUG_ONLY(fbl.verify();)
210   ASSERT_FALSE(fbl.is_empty());
211   CHECK_CONTENT(fbl, 1, 1024);
212 
213   MetaWord* p = fbl.remove_block(1024);
214   EXPECT_EQ(p, tmp);
215   DEBUG_ONLY(fbl.verify();)
216   CHECK_CONTENT(fbl, 0, 0);
217 
218 }
219 
TEST_VM(metaspace,freeblocks_small)220 TEST_VM(metaspace, freeblocks_small) {
221   FreeBlocksTest::test_small_allocations();
222 }
223 
TEST_VM(metaspace,freeblocks_medium)224 TEST_VM(metaspace, freeblocks_medium) {
225   FreeBlocksTest::test_medium_allocations();
226 }
227 
TEST_VM(metaspace,freeblocks_large)228 TEST_VM(metaspace, freeblocks_large) {
229   FreeBlocksTest::test_large_allocations();
230 }
231 
232