1 /*
  2  * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
  3  * Copyright (c) 2023 Red Hat, Inc. 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/classLoaderMetaspace.hpp"
 28 #include "memory/metaspace/freeBlocks.hpp"
 29 #include "memory/metaspace/metablock.inline.hpp"
 30 #include "memory/metaspace/metaspaceArena.hpp"
 31 #include "memory/metaspace/metaspaceSettings.hpp"
 32 #include "memory/metaspace/metaspaceStatistics.hpp"
 33 #include "memory/metaspace.hpp"
 34 #include "oops/klass.hpp"
 35 #include "runtime/mutex.hpp"
 36 #include "utilities/debug.hpp"
 37 #include "utilities/align.hpp"
 38 #include "utilities/globalDefinitions.hpp"
 39 
 40 #ifdef _LP64
 41 
 42 #define LOG_PLEASE
 43 #include "metaspaceGtestCommon.hpp"
 44 #include "metaspaceGtestContexts.hpp"
 45 #include "metaspaceGtestRangeHelpers.hpp"
 46 #include "metaspaceGtestSparseArray.hpp"
 47 
 48 #define HANDLE_FAILURE \
 49   if (testing::Test::HasFailure()) { \
 50     return; \
 51   }
 52 
 53 namespace metaspace {
 54 
 55 class ClmsTester {
 56 
 57   Mutex _lock;
 58   MetaspaceContext* _class_context;
 59   MetaspaceContext* _nonclass_context;
 60   ClassLoaderMetaspace* _clms;
 61   const size_t _klass_arena_alignment_words;
 62   unsigned _num_allocations;
 63 
 64   struct Deltas {
 65     int num_chunks_delta;
 66     ssize_t used_words_delta;
 67     int num_freeblocks_delta;
 68     ssize_t freeblocks_words_delta;
 69   };
 70 
 71   Deltas calc_deltas(const ArenaStats& before, const ArenaStats& after) {
 72     Deltas d;
 73     d.num_chunks_delta = after.totals()._num - before.totals()._num;
 74     d.used_words_delta = after.totals()._used_words - before.totals()._used_words;
 75     d.num_freeblocks_delta = (int)after._free_blocks_num - (int)before._free_blocks_num;
 76     d.freeblocks_words_delta = after._free_blocks_word_size - before._free_blocks_word_size;
 77     return d;
 78   }
 79 
 80 public:
 81 
 82   ClmsTester(size_t klass_alignment_words, Metaspace::MetaspaceType space_type,
 83              MetaspaceContext* class_context, MetaspaceContext* nonclass_context)
 84   : _lock(Monitor::nosafepoint, "CLMSTest_lock"),
 85     _class_context(class_context), _nonclass_context(nonclass_context),
 86     _clms(nullptr), _klass_arena_alignment_words(klass_alignment_words), _num_allocations(0) {
 87     _clms = new ClassLoaderMetaspace(&_lock, space_type, nonclass_context, class_context, klass_alignment_words);
 88   }
 89 
 90   ~ClmsTester() {
 91     delete _clms;
 92     EXPECT_EQ(_class_context->used_words(), (size_t)0);
 93     EXPECT_EQ(_nonclass_context->used_words(), (size_t)0);
 94   }
 95 
 96   MetaBlock allocate_and_check(size_t word_size, bool is_class) {
 97 
 98     // take stats before allocation
 99     ClmsStats stats_before;
100     _clms->add_to_statistics(&stats_before);
101 
102     // allocate
103     MetaWord* p = _clms->allocate(word_size, is_class ? Metaspace::ClassType : Metaspace::NonClassType);
104     _num_allocations ++;
105 
106     // take stats after allocation
107     ClmsStats stats_after;
108     _clms->add_to_statistics(&stats_after);
109 
110     // for less verbose testing:
111     const ArenaStats& ca_before = stats_before._arena_stats_class;
112     const ArenaStats& ca_after = stats_after._arena_stats_class;
113     const ArenaStats& nca_before = stats_before._arena_stats_nonclass;
114     const ArenaStats& nca_after = stats_after._arena_stats_nonclass;
115 
116     // deltas
117     const Deltas d_ca = calc_deltas(ca_before, ca_after);
118     const Deltas d_nca = calc_deltas(nca_before, nca_after);
119 
120 #define EXPECT_FREEBLOCKS_UNCHANGED(arena_prefix) \
121     EXPECT_EQ(d_##arena_prefix.num_freeblocks_delta, 0);  \
122     EXPECT_EQ(d_##arena_prefix.freeblocks_words_delta, (ssize_t)0);
123 
124 #define EXPECT_ARENA_UNCHANGED(arena_prefix) \
125     EXPECT_EQ(d_##arena_prefix.num_chunks_delta, 0);  \
126     EXPECT_EQ(d_##arena_prefix.used_words_delta, (ssize_t)0);
127 
128     if (p != nullptr) {
129 
130       MetaBlock bl(p, word_size);
131 
132       if (is_class) {
133 
134         EXPECT_TRUE(bl.is_aligned_base(_klass_arena_alignment_words));
135 
136         if (_num_allocations == 1) {
137           // first allocation: nonclass arena unchanged, class arena grows by 1 chunk and wordsize,
138           // class arena freeblocks unchanged
139           EXPECT_ARENA_UNCHANGED(nca);
140           EXPECT_FREEBLOCKS_UNCHANGED(nca);
141           EXPECT_EQ(d_ca.num_chunks_delta, 1);
142           EXPECT_EQ((size_t)d_ca.used_words_delta, word_size);
143           EXPECT_FREEBLOCKS_UNCHANGED(ca);
144           return bl;
145         }
146 
147         // Had this been taken from class arena freeblocks?
148         if (d_ca.num_freeblocks_delta == -1) {
149           // the class arena freeblocks should have gone down, and the non-class arena freeblocks may have gone
150           // up in case the block was larger than required
151           const size_t wordsize_block_taken = (size_t)(-d_ca.freeblocks_words_delta);
152           EXPECT_GE(wordsize_block_taken, word_size); // the block we took must be at least allocation size
153           const size_t expected_freeblock_remainder = wordsize_block_taken - word_size;
154           if (expected_freeblock_remainder > 0) {
155             // the remainder, if it existed, should have been added to nonclass freeblocks
156             EXPECT_EQ(d_nca.num_freeblocks_delta, 1);
157             EXPECT_EQ((size_t)d_nca.freeblocks_words_delta, expected_freeblock_remainder);
158           }
159           // finally, nothing should have happened in the arenas proper.
160           EXPECT_ARENA_UNCHANGED(ca);
161           EXPECT_ARENA_UNCHANGED(nca);
162           return bl;
163         }
164 
165         // block was taken from class arena proper
166 
167         // We expect allocation waste due to alignment, should have been added to the freeblocks
168         // of nonclass arena. Allocation waste can be 0. If no chunk turnover happened, it must be
169         // smaller than klass alignment, otherwise it can get as large as a commit granule.
170         const size_t max_expected_allocation_waste =
171             d_ca.num_chunks_delta == 0 ? (_klass_arena_alignment_words - 1) : Settings::commit_granule_words();
172         EXPECT_GE(d_ca.num_chunks_delta, 0);
173         EXPECT_LE(d_ca.num_chunks_delta, 1);
174         EXPECT_GE((size_t)d_ca.used_words_delta, word_size);
175         EXPECT_LE((size_t)d_ca.used_words_delta, word_size + max_expected_allocation_waste);
176         EXPECT_FREEBLOCKS_UNCHANGED(ca);
177         EXPECT_ARENA_UNCHANGED(nca);
178         if (max_expected_allocation_waste > 0) {
179           EXPECT_GE(d_nca.num_freeblocks_delta, 0);
180           EXPECT_LE(d_nca.num_freeblocks_delta, 1);
181           EXPECT_GE(d_nca.freeblocks_words_delta, 0);
182           EXPECT_LE((size_t)d_nca.freeblocks_words_delta, max_expected_allocation_waste);
183         } else {
184           EXPECT_FREEBLOCKS_UNCHANGED(nca);
185         }
186         return bl;
187 
188       } // end: is_class
189 
190       else
191 
192       {
193         // Nonclass arena allocation.
194         // Allocation waste can happen:
195         // - if we allocate from nonclass freeblocks, the block remainder
196         // - if we allocate from arena proper, by chunk turnover
197 
198         if (d_nca.freeblocks_words_delta < 0) {
199           // We allocated a block from the nonclass arena freeblocks.
200           const size_t wordsize_block_taken = (size_t)(-d_nca.freeblocks_words_delta);
201           EXPECT_EQ(wordsize_block_taken, word_size);
202           // The number of blocks may or may not have decreased (depending on whether there
203           // was a wastage block)
204           EXPECT_GE(d_nca.num_chunks_delta, -1);
205           EXPECT_LE(d_nca.num_chunks_delta, 0);
206           EXPECT_ARENA_UNCHANGED(nca);
207           EXPECT_ARENA_UNCHANGED(ca);
208           EXPECT_FREEBLOCKS_UNCHANGED(ca);
209           return bl;
210         }
211 
212         // We don't expect alignment waste. Only wastage happens at chunk turnover.
213         const size_t max_expected_allocation_waste =
214             d_nca.num_chunks_delta == 0 ? 0 : Settings::commit_granule_words();
215         EXPECT_ARENA_UNCHANGED(ca);
216         EXPECT_FREEBLOCKS_UNCHANGED(ca);
217         EXPECT_GE(d_nca.num_chunks_delta, 0);
218         EXPECT_LE(d_nca.num_chunks_delta, 1);
219         EXPECT_GE((size_t)d_nca.used_words_delta, word_size);
220         EXPECT_LE((size_t)d_nca.used_words_delta, word_size + max_expected_allocation_waste);
221         if (max_expected_allocation_waste == 0) {
222           EXPECT_FREEBLOCKS_UNCHANGED(nca);
223         }
224       }
225       return bl;
226 
227     } // end: allocation successful
228 
229     // allocation failed.
230     EXPECT_ARENA_UNCHANGED(ca);
231     EXPECT_FREEBLOCKS_UNCHANGED(ca);
232     EXPECT_ARENA_UNCHANGED(nca);
233     EXPECT_FREEBLOCKS_UNCHANGED(nca);
234 
235     return MetaBlock();
236   }
237 
238   MetaBlock allocate_expect_success(size_t word_size, bool is_class) {
239     MetaBlock bl = allocate_and_check(word_size, is_class);
240     EXPECT_TRUE(bl.is_nonempty());
241     return bl;
242   }
243 
244   MetaBlock allocate_expect_failure(size_t word_size, bool is_class) {
245     MetaBlock bl = allocate_and_check(word_size, is_class);
246     EXPECT_TRUE(bl.is_empty());
247     return bl;
248   }
249 
250   void deallocate_and_check(MetaBlock bl, bool is_class) {
251 
252     // take stats before deallocation
253     ClmsStats stats_before;
254     _clms->add_to_statistics(&stats_before);
255 
256     // allocate
257     _clms->deallocate(bl.base(), bl.word_size(), is_class);
258 
259     // take stats after deallocation
260     ClmsStats stats_after;
261     _clms->add_to_statistics(&stats_after);
262 
263     // for less verbose testing:
264     const ArenaStats& ca_before = stats_before._arena_stats_class;
265     const ArenaStats& ca_after = stats_after._arena_stats_class;
266     const ArenaStats& nca_before = stats_before._arena_stats_nonclass;
267     const ArenaStats& nca_after = stats_after._arena_stats_nonclass;
268 
269     // deltas
270     // deltas
271     const Deltas d_ca = calc_deltas(ca_before, ca_after);
272     const Deltas d_nca = calc_deltas(nca_before, nca_after);
273 
274     EXPECT_ARENA_UNCHANGED(ca);
275     EXPECT_ARENA_UNCHANGED(nca);
276     if (is_class) {
277       EXPECT_EQ(d_ca.num_freeblocks_delta, 1);
278       EXPECT_EQ((size_t)d_ca.freeblocks_words_delta, bl.word_size());
279       EXPECT_FREEBLOCKS_UNCHANGED(nca);
280     } else {
281       EXPECT_EQ(d_nca.num_freeblocks_delta, 1);
282       EXPECT_EQ((size_t)d_nca.freeblocks_words_delta, bl.word_size());
283       EXPECT_FREEBLOCKS_UNCHANGED(ca);
284     }
285   }
286 };
287 
288 static constexpr size_t klass_size = sizeof(Klass) / BytesPerWord;
289 
290 static void basic_test(size_t klass_arena_alignment) {
291   if (Settings::use_allocation_guard()) {
292     return;
293   }
294   MetaspaceGtestContext class_context, nonclass_context;
295   {
296     ClmsTester tester(klass_arena_alignment, Metaspace::StandardMetaspaceType, class_context.context(), nonclass_context.context());
297 
298     MetaBlock bl1 = tester.allocate_expect_success(klass_size, true);
299     HANDLE_FAILURE;
300 
301     MetaBlock bl2 = tester.allocate_expect_success(klass_size, true);
302     HANDLE_FAILURE;
303 
304     tester.deallocate_and_check(bl1, true);
305     HANDLE_FAILURE;
306 
307     MetaBlock bl3 = tester.allocate_expect_success(klass_size, true);
308     HANDLE_FAILURE;
309     EXPECT_EQ(bl3, bl1); // should have gotten the same block back from freelist
310 
311     MetaBlock bl4 = tester.allocate_expect_success(Metaspace::min_allocation_word_size, false);
312     HANDLE_FAILURE;
313 
314     MetaBlock bl5 = tester.allocate_expect_success(K, false);
315     HANDLE_FAILURE;
316 
317     tester.deallocate_and_check(bl5, false);
318     HANDLE_FAILURE;
319 
320     MetaBlock bl6 = tester.allocate_expect_success(K, false);
321     HANDLE_FAILURE;
322 
323     EXPECT_EQ(bl5, bl6); // should have gotten the same block back from freelist
324   }
325   EXPECT_EQ(class_context.used_words(), (size_t)0);
326   EXPECT_EQ(nonclass_context.used_words(), (size_t)0);
327   // we should have used exactly one commit granule (64K), not more, for each context
328   EXPECT_EQ(class_context.committed_words(), Settings::commit_granule_words());
329   EXPECT_EQ(nonclass_context.committed_words(), Settings::commit_granule_words());
330 }
331 
332 #define TEST_BASIC_N(n)               \
333 TEST_VM(metaspace, CLMS_basics_##n) { \
334   basic_test(n);            \
335 }
336 
337 TEST_BASIC_N(1)
338 TEST_BASIC_N(4)
339 TEST_BASIC_N(16)
340 TEST_BASIC_N(32)
341 TEST_BASIC_N(128)
342 
343 static void test_random(size_t klass_arena_alignment) {
344   if (Settings::use_allocation_guard()) {
345     return;
346   }
347 
348   MetaspaceGtestContext class_context, nonclass_context;
349   constexpr int max_allocations = 1024;
350   const SizeRange nonclass_alloc_range(Metaspace::min_allocation_alignment_words, 1024);
351   const SizeRange class_alloc_range(klass_size, 1024);
352   const IntRange one_out_of_ten(0, 10);
353   for (int runs = 9; runs >= 0; runs--) {
354     {
355       ClmsTester tester(64, Metaspace::StandardMetaspaceType, class_context.context(), nonclass_context.context());
356       struct LifeBlock {
357         MetaBlock bl;
358         bool is_class;
359       };
360       LifeBlock life_allocations[max_allocations];
361       for (int i = 0; i < max_allocations; i++) {
362         life_allocations[i].bl.reset();
363       }
364 
365       unsigned num_class_allocs = 0, num_nonclass_allocs = 0, num_class_deallocs = 0, num_nonclass_deallocs = 0;
366       for (int i = 0; i < 5000; i ++) {
367         const int slot = IntRange(0, max_allocations).random_value();
368         if (life_allocations[slot].bl.is_empty()) {
369           const bool is_class = one_out_of_ten.random_value() == 0;
370           const size_t word_size =
371               is_class ? class_alloc_range.random_value() : nonclass_alloc_range.random_value();
372           MetaBlock bl = tester.allocate_expect_success(word_size, is_class);
373           HANDLE_FAILURE;
374           life_allocations[slot].bl = bl;
375           life_allocations[slot].is_class = is_class;
376           if (is_class) {
377             num_class_allocs ++;
378           } else {
379             num_nonclass_allocs ++;
380           }
381         } else {
382           tester.deallocate_and_check(life_allocations[slot].bl, life_allocations[slot].is_class);
383           HANDLE_FAILURE;
384           life_allocations[slot].bl.reset();
385           if (life_allocations[slot].is_class) {
386             num_class_deallocs ++;
387           } else {
388             num_nonclass_deallocs ++;
389           }
390         }
391       }
392       LOG("num class allocs: %u, num nonclass allocs: %u, num class deallocs: %u, num nonclass deallocs: %u",
393           num_class_allocs, num_nonclass_allocs, num_class_deallocs, num_nonclass_deallocs);
394     }
395     EXPECT_EQ(class_context.used_words(), (size_t)0);
396     EXPECT_EQ(nonclass_context.used_words(), (size_t)0);
397     constexpr float fragmentation_factor = 3.0f;
398     const size_t max_expected_nonclass_committed = max_allocations * nonclass_alloc_range.highest() * fragmentation_factor;
399     const size_t max_expected_class_committed = max_allocations * class_alloc_range.highest() * fragmentation_factor;
400     // we should have used exactly one commit granule (64K), not more, for each context
401     EXPECT_LT(class_context.committed_words(), max_expected_class_committed);
402     EXPECT_LT(nonclass_context.committed_words(), max_expected_nonclass_committed);
403   }
404 }
405 
406 #define TEST_RANDOM_N(n)               \
407 TEST_VM(metaspace, CLMS_random_##n) {  \
408   test_random(n);                      \
409 }
410 
411 TEST_RANDOM_N(1)
412 TEST_RANDOM_N(4)
413 TEST_RANDOM_N(16)
414 TEST_RANDOM_N(32)
415 TEST_RANDOM_N(128)
416 
417 } // namespace metaspace
418 
419 #endif // _LP64