1 /*
  2  * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 #include "unittest.hpp"
 25 
 26 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
 27 #include "gc/shenandoah/mode/shenandoahMode.hpp"
 28 #include "gc/shenandoah/shenandoahOldGeneration.hpp"
 29 #include "gc/shenandoah/shenandoahThreadLocalData.hpp"
 30 
 31 #define SKIP_IF_NOT_SHENANDOAH() \
 32   if (!(UseShenandoahGC && ShenandoahHeap::heap()->mode()->is_generational())) {                 \
 33     tty->print_cr("skipped (run with -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational)");  \
 34     return;                                                                                      \
 35   }
 36 
 37 
 38 class ShenandoahOldGenerationTest : public ::testing::Test {
 39 protected:
 40   static const size_t INITIAL_PLAB_SIZE;
 41   static const size_t INITIAL_PLAB_PROMOTED;
 42 
 43   ShenandoahOldGeneration* old;
 44 
 45   ShenandoahOldGenerationTest()
 46     : old(nullptr)
 47   {
 48   }
 49 
 50   void SetUp() override {
 51     SKIP_IF_NOT_SHENANDOAH();
 52 
 53     ShenandoahHeap::heap()->lock()->lock(false);
 54 
 55     old = new ShenandoahOldGeneration(8, 1024 * 1024);
 56     old->set_promoted_reserve(512 * HeapWordSize);
 57     old->expend_promoted(256 * HeapWordSize);
 58     old->set_evacuation_reserve(512 * HeapWordSize);
 59 
 60     Thread* thread = Thread::current();
 61     ShenandoahThreadLocalData::reset_plab_promoted(thread);
 62     ShenandoahThreadLocalData::disable_plab_promotions(thread);
 63     ShenandoahThreadLocalData::set_plab_actual_size(thread, INITIAL_PLAB_SIZE);
 64     ShenandoahThreadLocalData::add_to_plab_promoted(thread, INITIAL_PLAB_PROMOTED);
 65   }
 66 
 67   void TearDown() override {
 68     if (UseShenandoahGC) {
 69       ShenandoahHeap::heap()->lock()->unlock();
 70       delete old;
 71     }
 72   }
 73 
 74   static bool promotions_enabled() {
 75     return ShenandoahThreadLocalData::allow_plab_promotions(Thread::current());
 76   }
 77 
 78   static size_t plab_size() {
 79     return ShenandoahThreadLocalData::get_plab_actual_size(Thread::current());
 80   }
 81 
 82   static size_t plab_promoted() {
 83     return ShenandoahThreadLocalData::get_plab_promoted(Thread::current());
 84   }
 85 };
 86 
 87 const size_t ShenandoahOldGenerationTest::INITIAL_PLAB_SIZE = 42;
 88 const size_t ShenandoahOldGenerationTest::INITIAL_PLAB_PROMOTED = 128;
 89 
 90 TEST_VM_F(ShenandoahOldGenerationTest, test_can_promote) {
 91   SKIP_IF_NOT_SHENANDOAH();
 92   EXPECT_TRUE(old->can_promote(128 * HeapWordSize)) << "Should have room to promote";
 93   EXPECT_FALSE(old->can_promote(384 * HeapWordSize)) << "Should not have room to promote";
 94 }
 95 
 96 TEST_VM_F(ShenandoahOldGenerationTest, test_can_allocate_plab_for_promotion) {
 97   SKIP_IF_NOT_SHENANDOAH();
 98   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(128, 128);
 99   EXPECT_TRUE(old->can_allocate(req)) << "Should have room to promote";
100 }
101 
102 TEST_VM_F(ShenandoahOldGenerationTest, test_can_allocate_plab_for_evacuation) {
103   SKIP_IF_NOT_SHENANDOAH();
104   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(384, 384);
105   EXPECT_FALSE(old->can_promote(req.size() * HeapWordSize)) << "No room for promotions";
106   EXPECT_TRUE(old->can_allocate(req)) << "Should have room to evacuate";
107 }
108 
109 TEST_VM_F(ShenandoahOldGenerationTest, test_cannot_allocate_plab) {
110   SKIP_IF_NOT_SHENANDOAH();
111   // Simulate having exhausted the evacuation reserve when request is too big to be promoted
112   old->set_evacuation_reserve(0);
113   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(384, 384);
114   EXPECT_FALSE(old->can_allocate(req)) << "No room for promotions or evacuations";
115 }
116 
117 TEST_VM_F(ShenandoahOldGenerationTest, test_can_allocate_for_shared_evacuation) {
118   SKIP_IF_NOT_SHENANDOAH();
119   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(768, ShenandoahAffiliation::OLD_GENERATION, false);
120   EXPECT_FALSE(old->can_promote(req.size() * HeapWordSize)) << "No room for promotion";
121   EXPECT_TRUE(old->can_allocate(req)) << "Should have room to evacuate shared (even though evacuation reserve is smaller than request)";
122 }
123 
124 TEST_VM_F(ShenandoahOldGenerationTest, test_cannot_allocate_for_shared_promotion) {
125   SKIP_IF_NOT_SHENANDOAH();
126   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(768, ShenandoahAffiliation::OLD_GENERATION, true);
127   EXPECT_FALSE(old->can_promote(req.size() * HeapWordSize)) << "No room for promotion";
128   EXPECT_FALSE(old->can_allocate(req)) << "No room to promote, should fall back to evacuation in young gen";
129 }
130 
131 TEST_VM_F(ShenandoahOldGenerationTest, test_expend_promoted) {
132   SKIP_IF_NOT_SHENANDOAH();
133   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(128, 128);
134 
135   // simulate the allocation
136   req.set_actual_size(128);
137 
138   size_t actual_size = req.actual_size() * HeapWordSize;
139   EXPECT_TRUE(old->can_promote(actual_size)) << "Should have room for promotion";
140 
141   size_t expended_before = old->get_promoted_expended();
142   old->configure_plab_for_current_thread(req);
143   size_t expended_after = old->get_promoted_expended();
144   EXPECT_EQ(expended_before + actual_size, expended_after) << "Should expend promotion reserve";
145   EXPECT_EQ(plab_promoted(), 0UL) << "Nothing promoted yet";
146   EXPECT_EQ(plab_size(), actual_size) << "New plab should be able to hold this much promotion";
147   EXPECT_TRUE(promotions_enabled()) << "Plab should be available for promotions";
148 }
149 
150 TEST_VM_F(ShenandoahOldGenerationTest, test_actual_size_exceeds_promotion_reserve) {
151   SKIP_IF_NOT_SHENANDOAH();
152   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_plab(128, 128);
153 
154   // simulate an allocation that exceeds the promotion reserve after allocation
155   req.set_actual_size(384);
156   EXPECT_FALSE(old->can_promote(req.actual_size() * HeapWordSize)) << "Should have room for promotion";
157 
158   size_t expended_before = old->get_promoted_expended();
159   old->configure_plab_for_current_thread(req);
160   size_t expended_after = old->get_promoted_expended();
161 
162   EXPECT_EQ(expended_before, expended_after) << "Did not promote, should not expend promotion";
163   EXPECT_EQ(plab_promoted(), 0UL) << "Cannot promote in new plab";
164   EXPECT_EQ(plab_size(), 0UL) << "Should not have space for promotions";
165   EXPECT_FALSE(promotions_enabled()) << "New plab can only be used for evacuations";
166 }
167 
168 TEST_VM_F(ShenandoahOldGenerationTest, test_shared_expends_promoted_but_does_not_change_plab) {
169   SKIP_IF_NOT_SHENANDOAH();
170   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(128, ShenandoahAffiliation::OLD_GENERATION, true);
171   req.set_actual_size(128);
172   size_t actual_size = req.actual_size() * HeapWordSize;
173 
174   size_t expended_before = old->get_promoted_expended();
175   old->configure_plab_for_current_thread(req);
176   size_t expended_after = old->get_promoted_expended();
177 
178   EXPECT_EQ(expended_before + actual_size, expended_after) << "Shared promotion still expends promotion";
179   EXPECT_EQ(plab_promoted(), INITIAL_PLAB_PROMOTED) << "Shared promotion should not count in plab";
180   EXPECT_EQ(plab_size(), INITIAL_PLAB_SIZE) << "Shared promotion should not change size of plab";
181   EXPECT_FALSE(promotions_enabled());
182 }
183 
184 TEST_VM_F(ShenandoahOldGenerationTest, test_shared_evacuation_has_no_side_effects) {
185   SKIP_IF_NOT_SHENANDOAH();
186   ShenandoahAllocRequest req = ShenandoahAllocRequest::for_shared_gc(128, ShenandoahAffiliation::OLD_GENERATION, false);
187   req.set_actual_size(128);
188 
189   size_t expended_before = old->get_promoted_expended();
190   old->configure_plab_for_current_thread(req);
191   size_t expended_after = old->get_promoted_expended();
192 
193   EXPECT_EQ(expended_before, expended_after) << "Not a promotion, should not expend promotion reserve";
194   EXPECT_EQ(plab_promoted(), INITIAL_PLAB_PROMOTED) << "Not a plab, should not have touched plab";
195   EXPECT_EQ(plab_size(), INITIAL_PLAB_SIZE) << "Not a plab, should not have touched plab";
196   EXPECT_FALSE(promotions_enabled());
197 }
198 
199 #undef SKIP_IF_NOT_SHENANDOAH