1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <gtest/gtest.h>
17
18 #include "libpandabase/utils/logger.h"
19 #include "libpandabase/mem/mem_config.h"
20 #include "libpandabase/mem/pool_manager.h"
21 #include "runtime/mem/heap_space.h"
22
23 namespace panda::mem::test {
24
25 class HeapSpaceTest : public testing::Test {
26 public:
HeapSpaceTest()27 explicit HeapSpaceTest()
28 {
29 // Logger::InitializeStdLogging(Logger::Level::DEBUG, Logger::Component::ALL);
30 };
31
~HeapSpaceTest()32 ~HeapSpaceTest()
33 {
34 // Logger::Destroy();
35 }
36
37 protected:
38 static constexpr size_t DEFAULT_TEST_HEAP_SIZE = 64_MB;
39 static constexpr size_t DEFAULT_TEST_YOUNG_SIZE = 4_MB;
40
GetCurrentMaxSize() const41 size_t GetCurrentMaxSize() const
42 {
43 return heap_space_->GetCurrentSize();
44 }
45
GetCurrentYoungMaxSize() const46 size_t GetCurrentYoungMaxSize() const
47 {
48 return gen_spaces_->GetCurrentMaxYoungSize();
49 }
50
GetCurrentTenuredMaxSize() const51 size_t GetCurrentTenuredMaxSize() const
52 {
53 return gen_spaces_->GetCurrentSize();
54 }
55
56 struct HeapSpaceHolder {
HeapSpaceHolderpanda::mem::test::HeapSpaceTest::HeapSpaceHolder57 HeapSpaceHolder(size_t initial_heap_size = DEFAULT_TEST_HEAP_SIZE,
58 size_t max_heap_size = DEFAULT_TEST_HEAP_SIZE, uint32_t min_percentage = 30U,
59 uint32_t max_percentage = 70U)
60 {
61 MemConfig::Initialize(max_heap_size, 0_MB, 0_MB, 0_MB, initial_heap_size);
62 PoolManager::Initialize();
63 HeapSpaceTest::heap_space_ = new HeapSpace();
64 HeapSpaceTest::heap_space_->Initialize(MemConfig::GetInitialHeapSizeLimit(), MemConfig::GetHeapSizeLimit(),
65 min_percentage, max_percentage);
66 }
67
~HeapSpaceHolderpanda::mem::test::HeapSpaceTest::HeapSpaceHolder68 ~HeapSpaceHolder() noexcept
69 {
70 delete HeapSpaceTest::heap_space_;
71 HeapSpaceTest::heap_space_ = nullptr;
72 PoolManager::Finalize();
73 MemConfig::Finalize();
74 }
75 };
76
77 struct GenerationalSpacesHolder {
GenerationalSpacesHolderpanda::mem::test::HeapSpaceTest::GenerationalSpacesHolder78 GenerationalSpacesHolder(size_t init_young_size = DEFAULT_TEST_YOUNG_SIZE,
79 size_t young_size = DEFAULT_TEST_YOUNG_SIZE,
80 size_t initial_heap_size = DEFAULT_TEST_HEAP_SIZE,
81 size_t max_heap_size = DEFAULT_TEST_HEAP_SIZE, uint32_t min_percentage = 0U,
82 uint32_t max_percentage = PERCENT_100_U32)
83 {
84 MemConfig::Initialize(max_heap_size, 0_MB, 0_MB, 0_MB, initial_heap_size);
85 PoolManager::Initialize();
86 HeapSpaceTest::gen_spaces_ = new GenerationalSpaces();
87 HeapSpaceTest::gen_spaces_->Initialize(init_young_size, true, young_size, true,
88 MemConfig::GetInitialHeapSizeLimit(), MemConfig::GetHeapSizeLimit(),
89 min_percentage, max_percentage);
90 }
91
~GenerationalSpacesHolderpanda::mem::test::HeapSpaceTest::GenerationalSpacesHolder92 ~GenerationalSpacesHolder() noexcept
93 {
94 delete HeapSpaceTest::gen_spaces_;
95 HeapSpaceTest::gen_spaces_ = nullptr;
96 PoolManager::Finalize();
97 MemConfig::Finalize();
98 }
99 };
100
101 static HeapSpace *heap_space_;
102 static GenerationalSpaces *gen_spaces_;
103 };
104
105 HeapSpace *HeapSpaceTest::heap_space_ = nullptr;
106 GenerationalSpaces *HeapSpaceTest::gen_spaces_ = nullptr;
107
TEST_F(HeapSpaceTest,AllocFreeAndCheckSizesTest)108 TEST_F(HeapSpaceTest, AllocFreeAndCheckSizesTest)
109 {
110 HeapSpaceHolder hsh;
111
112 auto pool_1 =
113 heap_space_->TryAllocPool(4_MB, SpaceType::SPACE_TYPE_OBJECT, AllocatorType::FREELIST_ALLOCATOR, nullptr);
114 ASSERT_NE(pool_1, NULLPOOL);
115 auto pool_2 =
116 heap_space_->TryAllocPool(6_MB, SpaceType::SPACE_TYPE_OBJECT, AllocatorType::FREELIST_ALLOCATOR, nullptr);
117 ASSERT_NE(pool_2, NULLPOOL);
118
119 ASSERT_EQ(heap_space_->GetHeapSize(), 10_MB);
120
121 heap_space_->FreePool(pool_1.GetMem(), pool_1.GetSize());
122 ASSERT_EQ(heap_space_->GetHeapSize(), 6_MB);
123 auto *arena_1 =
124 heap_space_->TryAllocArena(6_MB, SpaceType::SPACE_TYPE_OBJECT, AllocatorType::FREELIST_ALLOCATOR, nullptr);
125 ASSERT_NE(arena_1, nullptr);
126 ASSERT_EQ(heap_space_->GetHeapSize(), 12_MB);
127 heap_space_->FreePool(pool_2.GetMem(), pool_2.GetSize());
128 ASSERT_EQ(heap_space_->GetHeapSize(), 6_MB);
129 heap_space_->FreeArena(arena_1);
130 ASSERT_EQ(heap_space_->GetHeapSize(), 0_MB);
131 }
132
TEST_F(HeapSpaceTest,EmulateAllocBeforeAndAfterSTWGCTest)133 TEST_F(HeapSpaceTest, EmulateAllocBeforeAndAfterSTWGCTest)
134 {
135 static constexpr size_t INIT_HEAP_SIZE = 4_MB;
136 static constexpr size_t FIRST_POOL_SIZE = INIT_HEAP_SIZE / 2;
137 static constexpr size_t SECOND_POOL_SIZE = INIT_HEAP_SIZE * 3 / 4;
138 HeapSpaceHolder hsh(INIT_HEAP_SIZE, 2 * INIT_HEAP_SIZE, 0U, PERCENT_100_U32);
139
140 ASSERT_EQ(GetCurrentMaxSize(), INIT_HEAP_SIZE) << "Current heap limit must be equal initial heap size";
141 auto pool_1 = heap_space_->TryAllocPool(FIRST_POOL_SIZE, SpaceType::SPACE_TYPE_OBJECT,
142 AllocatorType::FREELIST_ALLOCATOR, nullptr);
143 ASSERT_NE(pool_1, NULLPOOL);
144 // -- Emulate simple GC
145 heap_space_->ComputeNewSize();
146 ASSERT_EQ(GetCurrentMaxSize(), INIT_HEAP_SIZE);
147 // --
148 auto pool_2 = heap_space_->TryAllocPool(SECOND_POOL_SIZE, SpaceType::SPACE_TYPE_OBJECT,
149 AllocatorType::FREELIST_ALLOCATOR, nullptr);
150 ASSERT_EQ(pool_2, NULLPOOL) << "We now can't allocate pool";
151 // -- Emulate simple GC
152 heap_space_->ComputeNewSize();
153 ASSERT_EQ(GetCurrentMaxSize(), FIRST_POOL_SIZE + SECOND_POOL_SIZE);
154 // --
155 pool_2 = heap_space_->TryAllocPool(SECOND_POOL_SIZE, SpaceType::SPACE_TYPE_OBJECT,
156 AllocatorType::FREELIST_ALLOCATOR, nullptr);
157 ASSERT_NE(pool_2, NULLPOOL);
158 heap_space_->FreePool(pool_1.GetMem(), pool_1.GetSize());
159 // -- Emulate simple GC
160 heap_space_->ComputeNewSize();
161 ASSERT_EQ(GetCurrentMaxSize(), FIRST_POOL_SIZE + SECOND_POOL_SIZE);
162 // --
163 heap_space_->FreePool(pool_2.GetMem(), pool_2.GetSize());
164 }
165
TEST_F(HeapSpaceTest,EmulateAllocBeforeAndDuringGenGCTest)166 TEST_F(HeapSpaceTest, EmulateAllocBeforeAndDuringGenGCTest)
167 {
168 static constexpr size_t INIT_HEAP_SIZE = 16_MB;
169 static constexpr size_t FIRST_POOL_SIZE = 4_MB;
170 static constexpr size_t SECOND_POOL_SIZE = 10_MB;
171 GenerationalSpacesHolder gsh(DEFAULT_TEST_YOUNG_SIZE, DEFAULT_TEST_YOUNG_SIZE, INIT_HEAP_SIZE, INIT_HEAP_SIZE * 2);
172 auto young_pool = gen_spaces_->AllocAlonePoolForYoung(SpaceType::SPACE_TYPE_OBJECT,
173 AllocatorType::BUMP_ALLOCATOR_WITH_TLABS, nullptr);
174 // Check young pool allocation
175 ASSERT_NE(young_pool, NULLPOOL);
176 ASSERT_EQ(young_pool.GetSize(), DEFAULT_TEST_YOUNG_SIZE);
177 // Check current heap space sizes before "runtime work"
178 ASSERT_EQ(GetCurrentYoungMaxSize(), DEFAULT_TEST_YOUNG_SIZE);
179 ASSERT_EQ(GetCurrentTenuredMaxSize(), INIT_HEAP_SIZE - DEFAULT_TEST_YOUNG_SIZE);
180
181 auto pool_1 = gen_spaces_->TryAllocPoolForYoung(FIRST_POOL_SIZE, SpaceType::SPACE_TYPE_OBJECT,
182 AllocatorType::FREELIST_ALLOCATOR, nullptr);
183 ASSERT_EQ(pool_1, NULLPOOL);
184 // -- Emulate simple GC
185 gen_spaces_->SetIsWorkGC(true);
186 pool_1 = gen_spaces_->TryAllocPoolForTenured(FIRST_POOL_SIZE, SpaceType::SPACE_TYPE_OBJECT,
187 AllocatorType::RUNSLOTS_ALLOCATOR, nullptr);
188 ASSERT_EQ(pool_1.GetSize(), FIRST_POOL_SIZE);
189 gen_spaces_->ComputeNewSize();
190 ASSERT_EQ(GetCurrentYoungMaxSize(), DEFAULT_TEST_YOUNG_SIZE);
191 ASSERT_EQ(GetCurrentTenuredMaxSize(), INIT_HEAP_SIZE - DEFAULT_TEST_YOUNG_SIZE);
192 // --
193 // Try too big pool, now no space for such pool
194 auto pool_2 = gen_spaces_->TryAllocPoolForTenured(SECOND_POOL_SIZE, SpaceType::SPACE_TYPE_OBJECT,
195 AllocatorType::FREELIST_ALLOCATOR, nullptr);
196 ASSERT_EQ(pool_2, NULLPOOL) << "Now no space for such pool, so we must have NULLPOOL";
197 // -- Emulate simple GC
198 gen_spaces_->SetIsWorkGC(true);
199 ASSERT_TRUE(gen_spaces_->CanAllocInSpace(false, SECOND_POOL_SIZE)) << "We can allocate pool during GC";
200 pool_2 = gen_spaces_->TryAllocPoolForTenured(SECOND_POOL_SIZE, SpaceType::SPACE_TYPE_OBJECT,
201 AllocatorType::FREELIST_ALLOCATOR, nullptr);
202 ASSERT_EQ(pool_2.GetSize(), SECOND_POOL_SIZE) << "We can allocate pool during GC";
203 ASSERT_EQ(GetCurrentTenuredMaxSize(), FIRST_POOL_SIZE + SECOND_POOL_SIZE);
204 gen_spaces_->ComputeNewSize();
205 ASSERT_EQ(GetCurrentTenuredMaxSize(), FIRST_POOL_SIZE + 2 * SECOND_POOL_SIZE);
206 // --
207 gen_spaces_->FreePool(pool_2.GetMem(), pool_2.GetSize());
208 gen_spaces_->FreePool(pool_1.GetMem(), pool_1.GetSize());
209 gen_spaces_->FreeYoungPool(young_pool.GetMem(), young_pool.GetSize());
210 }
211
TEST_F(HeapSpaceTest,SharedPoolTest)212 TEST_F(HeapSpaceTest, SharedPoolTest)
213 {
214 static constexpr size_t INIT_HEAP_SIZE = 32_MB;
215 static constexpr size_t INIT_YOUNG_SIZE = 2_MB;
216 static constexpr size_t MAX_YOUNG_SIZE = 8_MB;
217 static constexpr size_t SHARED_POOL_SIZE = 8_MB;
218 static constexpr size_t REGION_SIZE = 1_MB;
219 static constexpr bool IS_YOUNG = true;
220 GenerationalSpacesHolder gsh(INIT_YOUNG_SIZE, MAX_YOUNG_SIZE, INIT_HEAP_SIZE);
221 auto shared_pool = gen_spaces_->AllocSharedPool(SHARED_POOL_SIZE, SpaceType::SPACE_TYPE_OBJECT,
222 AllocatorType::REGION_ALLOCATOR, nullptr);
223 ASSERT_EQ(shared_pool.GetSize(), SHARED_POOL_SIZE);
224 ASSERT_EQ(GetCurrentYoungMaxSize(), INIT_YOUNG_SIZE);
225 ASSERT_EQ(GetCurrentTenuredMaxSize(), INIT_HEAP_SIZE - INIT_YOUNG_SIZE);
226 ASSERT_TRUE(gen_spaces_->CanAllocInSpace(IS_YOUNG, INIT_YOUNG_SIZE));
227 ASSERT_TRUE(gen_spaces_->CanAllocInSpace(!IS_YOUNG, INIT_YOUNG_SIZE));
228 gen_spaces_->IncreaseYoungOccupiedInSharedPool(REGION_SIZE);
229 gen_spaces_->IncreaseTenuredOccupiedInSharedPool(REGION_SIZE);
230 ASSERT_FALSE(gen_spaces_->CanAllocInSpace(IS_YOUNG, INIT_YOUNG_SIZE));
231 ASSERT_TRUE(gen_spaces_->CanAllocInSpace(!IS_YOUNG, INIT_YOUNG_SIZE));
232 auto pool_1 = gen_spaces_->TryAllocPoolForTenured(REGION_SIZE, SpaceType::SPACE_TYPE_OBJECT,
233 AllocatorType::REGION_ALLOCATOR, nullptr);
234 ASSERT_EQ(pool_1.GetSize(), REGION_SIZE);
235 gen_spaces_->ReduceYoungOccupiedInSharedPool(REGION_SIZE);
236 ASSERT_TRUE(gen_spaces_->CanAllocInSpace(IS_YOUNG, INIT_YOUNG_SIZE));
237 gen_spaces_->ReduceTenuredOccupiedInSharedPool(REGION_SIZE);
238 ASSERT_TRUE(gen_spaces_->CanAllocInSpace(IS_YOUNG, INIT_YOUNG_SIZE));
239 ASSERT_TRUE(gen_spaces_->CanAllocInSpace(!IS_YOUNG, INIT_YOUNG_SIZE));
240 gen_spaces_->FreeTenuredPool(pool_1.GetMem(), pool_1.GetSize());
241 gen_spaces_->FreeSharedPool(shared_pool.GetMem(), shared_pool.GetSize());
242 }
243
244 } // namespace panda::mem::test
245