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/chunkManager.hpp"
28 #include "memory/metaspace/counters.hpp"
29 #include "memory/metaspace/metaspaceArena.hpp"
30 #include "memory/metaspace/metaspaceArenaGrowthPolicy.hpp"
31 #include "memory/metaspace/metaspaceStatistics.hpp"
32 #include "runtime/mutexLocker.hpp"
33 #include "utilities/debug.hpp"
34 #include "utilities/globalDefinitions.hpp"
35 //#define LOG_PLEASE
36 #include "metaspaceGtestCommon.hpp"
37 #include "metaspaceGtestContexts.hpp"
38 #include "metaspaceGtestSparseArray.hpp"
39 
40 using metaspace::ArenaGrowthPolicy;
41 using metaspace::ChunkManager;
42 using metaspace::IntCounter;
43 using metaspace::MemRangeCounter;
44 using metaspace::MetaspaceArena;
45 using metaspace::SizeAtomicCounter;
46 using metaspace::ArenaStats;
47 using metaspace::InUseChunkStats;
48 
49 // Little randomness helper
fifty_fifty()50 static bool fifty_fifty() {
51   return IntRange(100).random_value() < 50;
52 }
53 
54 // See metaspaceArena.cpp : needed for predicting commit sizes.
55 namespace metaspace {
56   extern size_t get_raw_word_size_for_requested_word_size(size_t net_word_size);
57 }
58 
59 // A MetaspaceArenaTestBed contains a single MetaspaceArena and its lock.
60 // It keeps track of allocations done from this MetaspaceArena.
61 class MetaspaceArenaTestBed : public CHeapObj<mtInternal> {
62 
63   MetaspaceArena* _arena;
64 
65   Mutex* _lock;
66 
67   const SizeRange _allocation_range;
68   size_t _size_of_last_failed_allocation;
69 
70   // We keep track of all allocations done thru the MetaspaceArena to
71   // later check for overwriters.
72   struct allocation_t {
73     allocation_t* next;
74     MetaWord* p; // NULL if deallocated
75     size_t word_size;
markMetaspaceArenaTestBed::allocation_t76     void mark() {
77       mark_range(p, word_size);
78     }
verifyMetaspaceArenaTestBed::allocation_t79     void verify() const {
80       if (p != NULL) {
81         check_marked_range(p, word_size);
82       }
83     }
84   };
85 
86   allocation_t* _allocations;
87 
88   // We count how much we did allocate and deallocate
89   MemRangeCounter _alloc_count;
90   MemRangeCounter _dealloc_count;
91 
92   // Check statistics returned by MetaspaceArena::add_to_statistics() against what
93   // we know we allocated. This is a bit flaky since MetaspaceArena has internal
94   // overhead.
verify_arena_statistics() const95   void verify_arena_statistics() const {
96 
97     ArenaStats stats;
98     _arena->add_to_statistics(&stats);
99     InUseChunkStats in_use_stats = stats.totals();
100 
101     assert(_dealloc_count.total_size() <= _alloc_count.total_size() &&
102            _dealloc_count.count() <= _alloc_count.count(), "Sanity");
103 
104     // Check consistency of stats
105     ASSERT_GE(in_use_stats._word_size, in_use_stats._committed_words);
106     ASSERT_EQ(in_use_stats._committed_words,
107               in_use_stats._used_words + in_use_stats._free_words + in_use_stats._waste_words);
108     ASSERT_GE(in_use_stats._used_words, stats._free_blocks_word_size);
109 
110     // Note: reasons why the outside alloc counter and the inside used counter can differ:
111     // - alignment/padding of allocations
112     // - inside used counter contains blocks in free list
113     // - free block list splinter threshold
114 
115     // Since what we deallocated may have been given back to us in a following allocation,
116     // we only know fore sure we allocated what we did not give back.
117     const size_t at_least_allocated = _alloc_count.total_size() - _dealloc_count.total_size();
118 
119     // At most we allocated this:
120     const size_t max_word_overhead_per_alloc = 4;
121     const size_t at_most_allocated = _alloc_count.total_size() + max_word_overhead_per_alloc * _alloc_count.count();
122 
123     ASSERT_LE(at_least_allocated, in_use_stats._used_words - stats._free_blocks_word_size);
124     ASSERT_GE(at_most_allocated, in_use_stats._used_words - stats._free_blocks_word_size);
125 
126   }
127 
128 public:
129 
arena()130   MetaspaceArena* arena() { return _arena; }
131 
MetaspaceArenaTestBed(ChunkManager * cm,const ArenaGrowthPolicy * alloc_sequence,SizeAtomicCounter * used_words_counter,SizeRange allocation_range)132   MetaspaceArenaTestBed(ChunkManager* cm, const ArenaGrowthPolicy* alloc_sequence,
133                         SizeAtomicCounter* used_words_counter, SizeRange allocation_range) :
134     _arena(NULL),
135     _lock(NULL),
136     _allocation_range(allocation_range),
137     _size_of_last_failed_allocation(0),
138     _allocations(NULL),
139     _alloc_count(),
140     _dealloc_count()
141   {
142     _lock = new Mutex(Monitor::native, "gtest-MetaspaceArenaTestBed-lock", false, Monitor::_safepoint_check_never);
143     // Lock during space creation, since this is what happens in the VM too
144     //  (see ClassLoaderData::metaspace_non_null(), which we mimick here).
145     MutexLocker ml(_lock,  Mutex::_no_safepoint_check_flag);
146     _arena = new MetaspaceArena(cm, alloc_sequence, _lock, used_words_counter, "gtest-MetaspaceArenaTestBed-sm");
147   }
148 
~MetaspaceArenaTestBed()149   ~MetaspaceArenaTestBed() {
150 
151     verify_arena_statistics();
152 
153     allocation_t* a = _allocations;
154     while (a != NULL) {
155       allocation_t* b = a->next;
156       a->verify();
157       FREE_C_HEAP_OBJ(a);
158       a = b;
159     }
160 
161     DEBUG_ONLY(_arena->verify();)
162 
163     // Delete MetaspaceArena. That should clean up all metaspace.
164     delete _arena;
165     delete _lock;
166 
167   }
168 
words_allocated() const169   size_t words_allocated() const        { return _alloc_count.total_size(); }
num_allocations() const170   int num_allocations() const           { return _alloc_count.count(); }
171 
size_of_last_failed_allocation() const172   size_t size_of_last_failed_allocation() const { return _size_of_last_failed_allocation; }
173 
174   // Allocate a random amount. Return false if the allocation failed.
checked_random_allocate()175   bool checked_random_allocate() {
176     size_t word_size = 1 + _allocation_range.random_value();
177     MetaWord* p = _arena->allocate(word_size);
178     if (p != NULL) {
179       EXPECT_TRUE(is_aligned(p, sizeof(MetaWord)));
180       allocation_t* a = NEW_C_HEAP_OBJ(allocation_t, mtInternal);
181       a->word_size = word_size;
182       a->p = p;
183       a->mark();
184       a->next = _allocations;
185       _allocations = a;
186       _alloc_count.add(word_size);
187       if ((_alloc_count.count() % 20) == 0) {
188         verify_arena_statistics();
189         DEBUG_ONLY(_arena->verify();)
190       }
191       return true;
192     } else {
193       _size_of_last_failed_allocation = word_size;
194     }
195     return false;
196   }
197 
198   // Deallocate a random allocation
checked_random_deallocate()199   void checked_random_deallocate() {
200     allocation_t* a = _allocations;
201     while (a && a->p != NULL && os::random() % 10 != 0) {
202       a = a->next;
203     }
204     if (a != NULL && a->p != NULL) {
205       a->verify();
206       _arena->deallocate(a->p, a->word_size);
207       _dealloc_count.add(a->word_size);
208       a->p = NULL; a->word_size = 0;
209       if ((_dealloc_count.count() % 20) == 0) {
210         verify_arena_statistics();
211         DEBUG_ONLY(_arena->verify();)
212       }
213     }
214   }
215 
216 }; // End: MetaspaceArenaTestBed
217 
218 class MetaspaceArenaTest {
219 
220   MetaspaceGtestContext _context;
221 
222   SizeAtomicCounter _used_words_counter;
223 
224   SparseArray<MetaspaceArenaTestBed*> _testbeds;
225   IntCounter _num_beds;
226 
227   //////// Bed creation, destruction ///////
228 
create_new_test_bed_at(int slotindex,const ArenaGrowthPolicy * growth_policy,SizeRange allocation_range)229   void create_new_test_bed_at(int slotindex, const ArenaGrowthPolicy* growth_policy, SizeRange allocation_range) {
230     DEBUG_ONLY(_testbeds.check_slot_is_null(slotindex));
231     MetaspaceArenaTestBed* bed = new MetaspaceArenaTestBed(&_context.cm(), growth_policy,
232                                                        &_used_words_counter, allocation_range);
233     _testbeds.set_at(slotindex, bed);
234     _num_beds.increment();
235   }
236 
create_random_test_bed_at(int slotindex)237   void create_random_test_bed_at(int slotindex) {
238     SizeRange allocation_range(1, 100); // randomize too?
239     const ArenaGrowthPolicy* growth_policy = ArenaGrowthPolicy::policy_for_space_type(
240         (fifty_fifty() ? Metaspace::StandardMetaspaceType : Metaspace::ReflectionMetaspaceType),
241          fifty_fifty());
242     create_new_test_bed_at(slotindex, growth_policy, allocation_range);
243    }
244 
245   // Randomly create a random test bed at a random slot, and return its slot index
246   // (returns false if we reached max number of test beds)
create_random_test_bed()247   bool create_random_test_bed() {
248     const int slot = _testbeds.random_null_slot_index();
249     if (slot != -1) {
250       create_random_test_bed_at(slot);
251     }
252     return slot;
253   }
254 
255   // Create test beds for all slots
create_all_test_beds()256   void create_all_test_beds() {
257     for (int slot = 0; slot < _testbeds.size(); slot++) {
258       if (_testbeds.slot_is_null(slot)) {
259         create_random_test_bed_at(slot);
260       }
261     }
262   }
263 
delete_test_bed_at(int slotindex)264   void delete_test_bed_at(int slotindex) {
265     DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex));
266     MetaspaceArenaTestBed* bed = _testbeds.at(slotindex);
267     delete bed; // This will return all its memory to the chunk manager
268     _testbeds.set_at(slotindex, NULL);
269     _num_beds.decrement();
270   }
271 
272   // Randomly delete a random test bed at a random slot
273   // Return false if there are no test beds to delete.
delete_random_test_bed()274   bool delete_random_test_bed() {
275     const int slotindex = _testbeds.random_non_null_slot_index();
276     if (slotindex != -1) {
277       delete_test_bed_at(slotindex);
278       return true;
279     }
280     return false;
281   }
282 
283   // Delete all test beds.
delete_all_test_beds()284   void delete_all_test_beds() {
285     for (int slot = _testbeds.first_non_null_slot(); slot != -1; slot = _testbeds.next_non_null_slot(slot)) {
286       delete_test_bed_at(slot);
287     }
288   }
289 
290   //////// Allocating metaspace from test beds ///////
291 
random_allocate_from_testbed(int slotindex)292   bool random_allocate_from_testbed(int slotindex) {
293     DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex);)
294     MetaspaceArenaTestBed* bed = _testbeds.at(slotindex);
295     bool success = bed->checked_random_allocate();
296     if (success == false) {
297       // We must have hit a limit.
298       EXPECT_LT(_context.commit_limiter().possible_expansion_words(),
299                 metaspace::get_raw_word_size_for_requested_word_size(bed->size_of_last_failed_allocation()));
300     }
301     return success;
302   }
303 
304   // Allocate multiple times random sizes from a single MetaspaceArena.
random_allocate_multiple_times_from_testbed(int slotindex,int num_allocations)305   bool random_allocate_multiple_times_from_testbed(int slotindex, int num_allocations) {
306     bool success = true;
307     int n = 0;
308     while (success && n < num_allocations) {
309       success = random_allocate_from_testbed(slotindex);
310       n++;
311     }
312     return success;
313   }
314 
315   // Allocate multiple times random sizes from a single random MetaspaceArena.
random_allocate_random_times_from_random_testbed()316   bool random_allocate_random_times_from_random_testbed() {
317     int slot = _testbeds.random_non_null_slot_index();
318     bool success = false;
319     if (slot != -1) {
320       const int n = IntRange(5, 20).random_value();
321       success = random_allocate_multiple_times_from_testbed(slot, n);
322     }
323     return success;
324   }
325 
326   /////// Deallocating from testbed ///////////////////
327 
deallocate_from_testbed(int slotindex)328   void deallocate_from_testbed(int slotindex) {
329     DEBUG_ONLY(_testbeds.check_slot_is_not_null(slotindex);)
330     MetaspaceArenaTestBed* bed = _testbeds.at(slotindex);
331     bed->checked_random_deallocate();
332   }
333 
deallocate_from_random_testbed()334   void deallocate_from_random_testbed() {
335     int slot = _testbeds.random_non_null_slot_index();
336     if (slot != -1) {
337       deallocate_from_testbed(slot);
338     }
339   }
340 
341   /////// Stats ///////////////////////////////////////
342 
get_total_number_of_allocations() const343   int get_total_number_of_allocations() const {
344     int sum = 0;
345     for (int i = _testbeds.first_non_null_slot(); i != -1; i = _testbeds.next_non_null_slot(i)) {
346       sum += _testbeds.at(i)->num_allocations();
347     }
348     return sum;
349   }
350 
get_total_words_allocated() const351   size_t get_total_words_allocated() const {
352     size_t sum = 0;
353     for (int i = _testbeds.first_non_null_slot(); i != -1; i = _testbeds.next_non_null_slot(i)) {
354       sum += _testbeds.at(i)->words_allocated();
355     }
356     return sum;
357   }
358 
359 public:
360 
MetaspaceArenaTest(size_t commit_limit,int num_testbeds)361   MetaspaceArenaTest(size_t commit_limit, int num_testbeds)
362     : _context(commit_limit),
363       _testbeds(num_testbeds),
364       _num_beds()
365   {}
366 
~MetaspaceArenaTest()367   ~MetaspaceArenaTest () {
368 
369     delete_all_test_beds();
370 
371   }
372 
373   //////////////// Tests ////////////////////////
374 
test()375   void test() {
376 
377     // In a big loop, randomly chose one of these actions
378     // - creating a test bed (simulates a new loader creation)
379     // - allocating from a test bed (simulates allocating metaspace for a loader)
380     // - (rarely) deallocate (simulates metaspace deallocation, e.g. class redefinitions)
381     // - delete a test bed (simulates collection of a loader and subsequent return of metaspace to freelists)
382 
383     const int iterations = 10000;
384 
385     // Lets have a ceiling on number of words allocated (this is independent from the commit limit)
386     const size_t max_allocation_size = 8 * M;
387 
388     bool force_bed_deletion = false;
389 
390     for (int niter = 0; niter < iterations; niter++) {
391 
392       const int r = IntRange(100).random_value();
393 
394       if (force_bed_deletion || r < 10) {
395 
396         force_bed_deletion = false;
397         delete_random_test_bed();
398 
399       } else if (r < 20 || _num_beds.get() < (unsigned)_testbeds.size() / 2) {
400 
401         create_random_test_bed();
402 
403       } else if (r < 95) {
404 
405         // If allocation fails, we hit the commit limit and should delete some beds first
406         force_bed_deletion = ! random_allocate_random_times_from_random_testbed();
407 
408       } else {
409 
410         // Note: does not affect the used words counter.
411         deallocate_from_random_testbed();
412 
413       }
414 
415       // If we are close to our quota, start bed deletion
416       if (_used_words_counter.get() >= max_allocation_size) {
417 
418         force_bed_deletion = true;
419 
420       }
421 
422     }
423 
424   }
425 
426 };
427 
428 // 32 parallel MetaspaceArena objects, random allocating without commit limit
TEST_VM(metaspace,MetaspaceArena_random_allocs_32_beds_no_commit_limit)429 TEST_VM(metaspace, MetaspaceArena_random_allocs_32_beds_no_commit_limit) {
430   MetaspaceArenaTest test(max_uintx, 32);
431   test.test();
432 }
433 
434 // 32 parallel Metaspace arena objects, random allocating with commit limit
TEST_VM(metaspace,MetaspaceArena_random_allocs_32_beds_with_commit_limit)435 TEST_VM(metaspace, MetaspaceArena_random_allocs_32_beds_with_commit_limit) {
436   MetaspaceArenaTest test(2 * M, 32);
437   test.test();
438 }
439 
440 // A single MetaspaceArena, random allocating without commit limit. This should exercise
441 //  chunk enlargement since allocation is undisturbed.
TEST_VM(metaspace,MetaspaceArena_random_allocs_1_bed_no_commit_limit)442 TEST_VM(metaspace, MetaspaceArena_random_allocs_1_bed_no_commit_limit) {
443   MetaspaceArenaTest test(max_uintx, 1);
444   test.test();
445 }
446 
447