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 #ifndef PANDA_RUNTIME_MEM_FRAME_ALLOCATOR_INL_H
16 #define PANDA_RUNTIME_MEM_FRAME_ALLOCATOR_INL_H
17
18 #include "runtime/mem/frame_allocator.h"
19
20 #include <cstring>
21
22 #include "libpandabase/mem/pool_manager.h"
23 #include "libpandabase/utils/logger.h"
24 #include "libpandabase/mem/stack_like_allocator-inl.h"
25
26 namespace ark::mem {
27
28 using StackFrameAllocator = StackLikeAllocator<>;
29
30 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
31 #define LOG_FRAME_ALLOCATOR(level) LOG(level, ALLOC) << "FrameAllocator: "
32
33 template <Alignment ALIGNMENT, bool USE_MEMSET>
FrameAllocator(bool useMalloc,SpaceType spaceType)34 inline FrameAllocator<ALIGNMENT, USE_MEMSET>::FrameAllocator(bool useMalloc, SpaceType spaceType)
35 : useMalloc_(useMalloc), spaceType_(spaceType)
36 {
37 LOG_FRAME_ALLOCATOR(DEBUG) << "Initializing of FrameAllocator";
38 if (!useMalloc_) {
39 memPoolAlloc_ = PoolManager::GetMmapMemPool();
40 }
41 curArena_ = AllocateArenaImpl(FIRST_ARENA_SIZE);
42 lastAllocArena_ = curArena_;
43 biggestArenaSize_ = FIRST_ARENA_SIZE;
44 arenaSizeNeedToGrow_ = true;
45 LOG_FRAME_ALLOCATOR(DEBUG) << "Initializing of FrameAllocator finished";
46 }
47
48 template <Alignment ALIGNMENT, bool USE_MEMSET>
~FrameAllocator()49 inline FrameAllocator<ALIGNMENT, USE_MEMSET>::~FrameAllocator()
50 {
51 LOG_FRAME_ALLOCATOR(DEBUG) << "Destroying of FrameAllocator";
52 while (lastAllocArena_ != nullptr) {
53 LOG_FRAME_ALLOCATOR(DEBUG) << "Free arena at addr " << std::hex << lastAllocArena_;
54 FramesArena *prevArena = lastAllocArena_->GetPrevArena();
55 FreeArenaImpl(lastAllocArena_);
56 lastAllocArena_ = prevArena;
57 }
58 curArena_ = nullptr;
59 LOG_FRAME_ALLOCATOR(DEBUG) << "Destroying of FrameAllocator finished";
60 }
61
62 template <Alignment ALIGNMENT, bool USE_MEMSET>
TryAllocateNewArena(size_t size)63 inline bool FrameAllocator<ALIGNMENT, USE_MEMSET>::TryAllocateNewArena(size_t size)
64 {
65 size_t arenaSize = GetNextArenaSize(size);
66 LOG_FRAME_ALLOCATOR(DEBUG) << "Try to allocate a new arena with size " << arenaSize;
67 FramesArena *newArena = AllocateArenaImpl(arenaSize);
68 if (newArena == nullptr) {
69 LOG_FRAME_ALLOCATOR(DEBUG) << "Couldn't get memory for a new arena";
70 arenaSizeNeedToGrow_ = false;
71 return false;
72 }
73 lastAllocArena_->LinkNext(newArena);
74 newArena->LinkPrev(lastAllocArena_);
75 lastAllocArena_ = newArena;
76 emptyArenasCount_++;
77 LOG_FRAME_ALLOCATOR(DEBUG) << "Successfully allocate new arena with addr " << std::hex << newArena;
78 return true;
79 }
80
81 template <Alignment ALIGNMENT, bool USE_MEMSET>
Alloc(size_t size)82 ALWAYS_INLINE inline void *FrameAllocator<ALIGNMENT, USE_MEMSET>::Alloc(size_t size)
83 {
84 ASSERT(AlignUp(size, GetAlignmentInBytes(ALIGNMENT)) == size);
85 // Try to get free memory from current arenas
86 void *mem = TryToAllocate(size);
87
88 if (UNLIKELY(mem == nullptr)) {
89 LOG_FRAME_ALLOCATOR(DEBUG) << "Can't allocate " << size << " bytes for a new frame in current arenas";
90 if (!TryAllocateNewArena(size)) {
91 LOG_FRAME_ALLOCATOR(DEBUG) << "Can't allocate a new arena, return nullptr";
92 return nullptr;
93 }
94 mem = TryToAllocate(size);
95 if (mem == nullptr) {
96 LOG_FRAME_ALLOCATOR(DEBUG) << "Can't allocate memory in a totally free arena, change default arenas sizes";
97 return nullptr;
98 }
99 }
100
101 ASSERT(AlignUp(ToUintPtr(mem), GetAlignmentInBytes(ALIGNMENT)) == ToUintPtr(mem));
102 LOG_FRAME_ALLOCATOR(DEBUG) << "Allocated memory at addr " << std::hex << mem;
103 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
104 if constexpr (USE_MEMSET) {
105 memset_s(mem, size, 0x00, size);
106 }
107 allocatedSize_ += size;
108 return mem;
109 }
110
111 template <Alignment ALIGNMENT, bool USE_MEMSET>
Free(void * mem)112 ALWAYS_INLINE inline void FrameAllocator<ALIGNMENT, USE_MEMSET>::Free(void *mem)
113 {
114 ASSERT(curArena_ != nullptr); // must has been initialized!
115 ASSERT(ToUintPtr(mem) == AlignUp(ToUintPtr(mem), GetAlignmentInBytes(ALIGNMENT)));
116 if (curArena_->InArena(mem)) {
117 allocatedSize_ -= ToUintPtr(curArena_->GetTop()) - ToUintPtr(mem);
118 curArena_->Free(mem);
119 } else {
120 ASSERT(curArena_->GetOccupiedSize() == 0);
121 ASSERT(curArena_->GetPrevArena() != nullptr);
122
123 curArena_ = curArena_->GetPrevArena();
124 ASSERT(curArena_->InArena(mem));
125 allocatedSize_ -= ToUintPtr(curArena_->GetTop()) - ToUintPtr(mem);
126 curArena_->Free(mem);
127 if (UNLIKELY((emptyArenasCount_ + 1) > FRAME_ALLOC_MAX_FREE_ARENAS_THRESHOLD)) {
128 FreeLastArena();
129 } else {
130 emptyArenasCount_++;
131 }
132 }
133 LOG_FRAME_ALLOCATOR(DEBUG) << "Free memory at addr " << std::hex << mem;
134 }
135
136 template <Alignment ALIGNMENT, bool USE_MEMSET>
TryToAllocate(size_t size)137 inline void *FrameAllocator<ALIGNMENT, USE_MEMSET>::TryToAllocate(size_t size)
138 {
139 // Try to allocate memory in the current arena:
140 ASSERT(curArena_ != nullptr);
141 void *mem = curArena_->Alloc(size);
142 if (LIKELY(mem != nullptr)) {
143 return mem;
144 }
145 // We don't have enough memory in current arena, try to allocate in the next one:
146 FramesArena *nextArena = curArena_->GetNextArena();
147 if (nextArena == nullptr) {
148 LOG_FRAME_ALLOCATOR(DEBUG) << "TryToPush failed - we don't have a free arena";
149 return nullptr;
150 }
151 mem = nextArena->Alloc(size);
152 if (LIKELY(mem != nullptr)) {
153 ASSERT(emptyArenasCount_ > 0);
154 emptyArenasCount_--;
155 curArena_ = nextArena;
156 return mem;
157 }
158 LOG_FRAME_ALLOCATOR(DEBUG) << "Couldn't allocate " << size << " bytes of memory in the totally free arena."
159 << " Change initial sizes of arenas";
160 return nullptr;
161 }
162
163 template <Alignment ALIGNMENT, bool USE_MEMSET>
GetNextArenaSize(size_t size)164 inline size_t FrameAllocator<ALIGNMENT, USE_MEMSET>::GetNextArenaSize(size_t size)
165 {
166 size_t requestedSize = size + sizeof(FramesArena) + GetAlignmentInBytes(ALIGNMENT);
167 if ((arenaSizeNeedToGrow_) || (biggestArenaSize_ < requestedSize)) {
168 biggestArenaSize_ += ARENA_SIZE_GREW_LEVEL;
169 if (biggestArenaSize_ < requestedSize) {
170 biggestArenaSize_ = RoundUp(requestedSize, ARENA_SIZE_GREW_LEVEL);
171 }
172 } else {
173 arenaSizeNeedToGrow_ = true;
174 }
175 return biggestArenaSize_;
176 }
177
178 template <Alignment ALIGNMENT, bool USE_MEMSET>
FreeLastArena()179 inline void FrameAllocator<ALIGNMENT, USE_MEMSET>::FreeLastArena()
180 {
181 ASSERT(lastAllocArena_ != nullptr);
182 FramesArena *arenaToFree = lastAllocArena_;
183 lastAllocArena_ = arenaToFree->GetPrevArena();
184 if (arenaToFree == curArena_) {
185 curArena_ = lastAllocArena_;
186 }
187 if (lastAllocArena_ == nullptr) {
188 ASSERT(lastAllocArena_ == curArena_);
189 // To fix clang tidy warning
190 // (it suggests that cur_arena_ can be not nullptr when
191 // last_alloc_arena_ is equal to nullptr)
192 curArena_ = lastAllocArena_;
193 LOG_FRAME_ALLOCATOR(DEBUG) << "Clear the last arena in the list";
194 } else {
195 lastAllocArena_->ClearNextLink();
196 }
197 LOG_FRAME_ALLOCATOR(DEBUG) << "Free the arena at addr " << std::hex << arenaToFree;
198 FreeArenaImpl(arenaToFree);
199 arenaSizeNeedToGrow_ = false;
200 }
201
202 template <Alignment ALIGNMENT, bool USE_MEMSET>
203 inline typename FrameAllocator<ALIGNMENT, USE_MEMSET>::FramesArena *
AllocateArenaImpl(size_t size)204 FrameAllocator<ALIGNMENT, USE_MEMSET>::AllocateArenaImpl(size_t size)
205 {
206 FramesArena *newArena = nullptr;
207 if (!useMalloc_) {
208 ASSERT(memPoolAlloc_ != nullptr);
209 newArena = memPoolAlloc_->AllocArena<FramesArena>(size, spaceType_, AllocatorType::FRAME_ALLOCATOR, this);
210 } else {
211 auto mem = ark::os::mem::AlignedAlloc(GetAlignmentInBytes(ARENA_DEFAULT_ALIGNMENT), size);
212 if (mem != nullptr) {
213 auto arenaBuffOffs = AlignUp(sizeof(FramesArena), GetAlignmentInBytes(ARENA_DEFAULT_ALIGNMENT));
214 newArena = new (mem) FramesArena(size - arenaBuffOffs, ToVoidPtr(ToUintPtr(mem) + arenaBuffOffs));
215 }
216 }
217 return newArena;
218 }
219
220 template <Alignment ALIGNMENT, bool USE_MEMSET>
FreeArenaImpl(FramesArena * arena)221 inline void FrameAllocator<ALIGNMENT, USE_MEMSET>::FreeArenaImpl(FramesArena *arena)
222 {
223 ASSERT(arena != nullptr);
224 if (!useMalloc_) {
225 ASSERT(memPoolAlloc_ != nullptr);
226 memPoolAlloc_->FreeArena<FramesArena>(arena);
227 } else {
228 os::mem::AlignedFree(arena);
229 }
230 }
231
232 template <Alignment ALIGNMENT, bool USE_MEMSET>
Contains(void * mem)233 inline bool FrameAllocator<ALIGNMENT, USE_MEMSET>::Contains(void *mem)
234 {
235 auto curArena = curArena_;
236
237 while (curArena != nullptr) {
238 LOG_FRAME_ALLOCATOR(DEBUG) << "check InAllocator arena at addr " << std::hex << curArena;
239 if (curArena->InArena(mem)) {
240 return true;
241 }
242 curArena = curArena->GetPrevArena();
243 }
244 return false;
245 }
246
247 #undef LOG_FRAME_ALLOCATOR
248
249 } // namespace ark::mem
250 #endif // PANDA_RUNTIME_MEM_FRAME_ALLOCATOR_INL_H
251