1 /**
2 * Copyright (c) 2021-2024 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 <ctime>
17
18 #include "gtest/gtest.h"
19 #include "runtime/mem/alloc_config.h"
20 #include "runtime/mem/bump-allocator-inl.h"
21 #include "libpandabase/test_utilities.h"
22
23 namespace ark::mem {
24
25 template <bool USE_TLABS>
26 using NonObjectBumpAllocator =
27 BumpPointerAllocator<EmptyMemoryConfig, BumpPointerAllocatorLockConfig::CommonLock, USE_TLABS>;
28
29 class BumpAllocatorTest : public testing::Test {
30 public:
BumpAllocatorTest()31 BumpAllocatorTest()
32 {
33 // Logger::InitializeStdLogging(Logger::Level::DEBUG, Logger::Component::ALL);
34 #ifdef PANDA_NIGHTLY_TEST_ON
35 seed_ = std::time(NULL);
36 #else
37 // NOLINTNEXTLINE(readability-magic-numbers)
38 seed_ = 0x0BADDEAD;
39 #endif
40 srand(seed_);
41 ark::mem::MemConfig::Initialize(0, 8_MB, 0, 0, 0, 0);
42 PoolManager::Initialize();
43 }
44
~BumpAllocatorTest()45 ~BumpAllocatorTest() override
46 {
47 for (auto i : allocatedMemMmap_) {
48 ark::os::mem::UnmapRaw(std::get<0>(i), std::get<1>(i));
49 }
50 for (auto i : allocatedArenas_) {
51 delete i;
52 }
53 PoolManager::Finalize();
54 ark::mem::MemConfig::Finalize();
55 // Logger::Destroy();
56 }
57
58 NO_COPY_SEMANTIC(BumpAllocatorTest);
59 NO_MOVE_SEMANTIC(BumpAllocatorTest);
60
61 protected:
AllocateArena(size_t size)62 Arena *AllocateArena(size_t size)
63 {
64 void *mem = ark::os::mem::MapRWAnonymousRaw(size);
65 ASAN_UNPOISON_MEMORY_REGION(mem, size);
66 std::pair<void *, size_t> newPair {mem, size};
67 allocatedMemMmap_.push_back(newPair);
68 auto arena = new Arena(size, mem);
69 allocatedArenas_.push_back(arena);
70 return arena;
71 }
72
73 template <class AllocType, size_t ELEMENTS_COUNT>
AllocInCommonBuffer(NonObjectBumpAllocator<true> & allocator,std::array<AllocType *,ELEMENTS_COUNT> & elements)74 void AllocInCommonBuffer(NonObjectBumpAllocator<true> &allocator, std::array<AllocType *, ELEMENTS_COUNT> &elements)
75 {
76 for (size_t i = 0; i < ELEMENTS_COUNT; ++i) {
77 elements[i] = static_cast<AllocType *>(allocator.Alloc(sizeof(AllocType)));
78 ASSERT_TRUE(elements[i] != nullptr);
79 *elements[i] = AllocType(i) % std::numeric_limits<AllocType>::max();
80 }
81 }
82
83 template <class AllocType, size_t ELEMENTS_COUNT>
AllocInTLAB(TLAB * tlab,std::array<AllocType *,ELEMENTS_COUNT> & elements)84 void AllocInTLAB(TLAB *tlab, std::array<AllocType *, ELEMENTS_COUNT> &elements)
85 {
86 for (size_t i = 0; i < ELEMENTS_COUNT; ++i) {
87 elements[i] = static_cast<AllocType *>(tlab->Alloc(sizeof(AllocType)));
88 ASSERT_TRUE(elements[i] != nullptr);
89 *elements[i] = AllocType(i) % std::numeric_limits<AllocType>::max();
90 }
91 }
92
93 template <class AllocType, size_t ELEMENTS_COUNT>
CheckAlloc(std::array<AllocType *,ELEMENTS_COUNT> & elements)94 void CheckAlloc(std::array<AllocType *, ELEMENTS_COUNT> &elements)
95 {
96 size_t mask = DEFAULT_ALIGNMENT_IN_BYTES - 1;
97 for (size_t i = 0; i < ELEMENTS_COUNT; ++i) {
98 ASSERT_NE(elements[i], nullptr) << "value of i: " << i;
99 ASSERT_EQ(reinterpret_cast<size_t>(elements[i]) & mask, static_cast<size_t>(0)) << "value of i: " << i;
100 ASSERT_EQ(*elements[i], AllocType(i) % std::numeric_limits<AllocType>::max()) << "value of i: " << i;
101 }
102 }
103
104 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
105 unsigned seed_ {};
106
107 private:
108 std::vector<std::pair<void *, size_t>> allocatedMemMmap_;
109 std::vector<Arena *> allocatedArenas_;
110 };
111
DEATH_TEST_F(BumpAllocatorTest,AlignedAlloc)112 DEATH_TEST_F(BumpAllocatorTest, AlignedAlloc)
113 {
114 testing::FLAGS_gtest_death_test_style = "threadsafe";
115 constexpr size_t BUFF_SIZE = 1_MB;
116 constexpr size_t ARRAY_SIZE = 1024;
117 auto pool = PoolManager::GetMmapMemPool()->AllocPool(BUFF_SIZE, SpaceType::SPACE_TYPE_INTERNAL,
118 AllocatorType::BUMP_ALLOCATOR);
119 mem::MemStatsType memStats;
120 NonObjectBumpAllocator<false> bpAllocator(pool, SpaceType::SPACE_TYPE_INTERNAL, &memStats);
121 Alignment align = DEFAULT_ALIGNMENT;
122 std::array<int *, ARRAY_SIZE> arr {};
123
124 size_t mask = GetAlignmentInBytes(align) - 1;
125
126 // Allocations
127 srand(seed_);
128 for (size_t i = 0; i < ARRAY_SIZE; ++i) {
129 arr[i] = static_cast<int *>(bpAllocator.Alloc(sizeof(int), align));
130 // NOLINTNEXTLINE(cert-msc50-cpp)
131 *arr[i] = rand() % std::numeric_limits<int>::max();
132 }
133
134 // Allocations checking
135 srand(seed_);
136 for (size_t i = 0; i < ARRAY_SIZE; ++i) {
137 ASSERT_NE(arr[i], nullptr) << "value of i: " << i << ", align: " << align << ", seed:" << seed_;
138 ASSERT_EQ(reinterpret_cast<size_t>(arr[i]) & mask, static_cast<size_t>(0))
139 << "value of i: " << i << ", align: " << align << ", seed:" << seed_;
140 // NOLINTNEXTLINE(cert-msc50-cpp)
141 ASSERT_EQ(*arr[i], rand() % std::numeric_limits<int>::max())
142 << "value of i: " << i << ", align: " << align << ", seed:" << seed_;
143 }
144 static_assert(LOG_ALIGN_MAX != DEFAULT_ALIGNMENT, "We expect minimal alignment != DEFAULT_ALIGNMENT");
145 void *ptr;
146 #ifndef NDEBUG
147 EXPECT_DEATH_IF_SUPPORTED(ptr = bpAllocator.Alloc(sizeof(int), LOG_ALIGN_MAX), "alignment == DEFAULT_ALIGNMENT")
148 << ", seed:" << seed_;
149 #endif
150 ptr = bpAllocator.Alloc(1_MB);
151 ASSERT_EQ(ptr, nullptr) << "Here Alloc with allocation size = 1 MB should return nullptr"
152 << ", seed:" << seed_;
153 }
154
TEST_F(BumpAllocatorTest,CreateTLABAndAlloc)155 TEST_F(BumpAllocatorTest, CreateTLABAndAlloc)
156 {
157 using AllocType = uint64_t;
158 static_assert(sizeof(AllocType) % DEFAULT_ALIGNMENT_IN_BYTES == 0);
159 constexpr size_t TLAB_SIZE = 1_MB;
160 constexpr size_t COMMON_BUFFER_SIZE = 1_MB;
161 constexpr size_t TLAB_ALLOC_COUNT_SIZE = TLAB_SIZE / sizeof(AllocType);
162 constexpr size_t COMMON_ALLOC_COUNT_SIZE = COMMON_BUFFER_SIZE / sizeof(AllocType);
163
164 std::array<AllocType *, TLAB_ALLOC_COUNT_SIZE> tlabElements {};
165 std::array<AllocType *, COMMON_ALLOC_COUNT_SIZE> commonElements {};
166 auto pool = PoolManager::GetMmapMemPool()->AllocPool(TLAB_SIZE + COMMON_BUFFER_SIZE, SpaceType::SPACE_TYPE_INTERNAL,
167 AllocatorType::BUMP_ALLOCATOR);
168 mem::MemStatsType memStats;
169 NonObjectBumpAllocator<true> allocator(pool, SpaceType::SPACE_TYPE_OBJECT, &memStats, 1U);
170 {
171 AllocInCommonBuffer(allocator, commonElements);
172 TLAB *tlab = allocator.CreateNewTLAB(TLAB_SIZE);
173 ASSERT_TRUE(tlab != nullptr) << ", seed:" << seed_;
174 ASSERT_TRUE(allocator.CreateNewTLAB(TLAB_SIZE) == nullptr) << ", seed:" << seed_;
175 AllocInTLAB(tlab, tlabElements);
176 // Check that we don't have memory in the buffer:
177 ASSERT_TRUE(allocator.Alloc(sizeof(AllocType)) == nullptr);
178 ASSERT_TRUE(tlab->Alloc(sizeof(AllocType)) == nullptr);
179 CheckAlloc(commonElements);
180 CheckAlloc(tlabElements);
181 }
182 allocator.Reset();
183 {
184 TLAB *tlab = allocator.CreateNewTLAB(TLAB_SIZE);
185 ASSERT_TRUE(tlab != nullptr) << ", seed:" << seed_;
186 ASSERT_TRUE(allocator.CreateNewTLAB(TLAB_SIZE) == nullptr) << ", seed:" << seed_;
187 AllocInTLAB(tlab, tlabElements);
188 AllocInCommonBuffer(allocator, commonElements);
189 // Check that we don't have memory in the buffer:
190 ASSERT_TRUE(allocator.Alloc(sizeof(AllocType)) == nullptr);
191 ASSERT_TRUE(tlab->Alloc(sizeof(AllocType)) == nullptr);
192 CheckAlloc(tlabElements);
193 CheckAlloc(commonElements);
194 }
195 }
196
TEST_F(BumpAllocatorTest,CreateTooManyTLABS)197 TEST_F(BumpAllocatorTest, CreateTooManyTLABS)
198 {
199 constexpr size_t TLAB_SIZE = 1_MB;
200 constexpr size_t TLAB_COUNT = 3;
201 auto pool = PoolManager::GetMmapMemPool()->AllocPool(TLAB_SIZE * TLAB_COUNT, SpaceType::SPACE_TYPE_INTERNAL,
202 AllocatorType::BUMP_ALLOCATOR);
203 mem::MemStatsType memStats;
204 NonObjectBumpAllocator<true> allocator(pool, SpaceType::SPACE_TYPE_OBJECT, &memStats, TLAB_COUNT - 1);
205 {
206 for (size_t i = 0; i < TLAB_COUNT - 1; i++) {
207 TLAB *tlab = allocator.CreateNewTLAB(TLAB_SIZE);
208 ASSERT_TRUE(tlab != nullptr) << ", seed:" << seed_;
209 }
210 TLAB *tlab = allocator.CreateNewTLAB(TLAB_SIZE);
211 ASSERT_TRUE(tlab == nullptr) << ", seed:" << seed_;
212 }
213 allocator.Reset();
214 {
215 for (size_t i = 0; i < TLAB_COUNT - 1; i++) {
216 TLAB *tlab = allocator.CreateNewTLAB(TLAB_SIZE);
217 ASSERT_TRUE(tlab != nullptr) << ", seed:" << seed_;
218 }
219 TLAB *tlab = allocator.CreateNewTLAB(TLAB_SIZE);
220 ASSERT_TRUE(tlab == nullptr) << ", seed:" << seed_;
221 }
222 }
223
224 } // namespace ark::mem
225