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>
63 // CC-OFFNXT(G.FUD.06) perf critical
TryAllocateNewArena(size_t size)64 inline bool FrameAllocator<ALIGNMENT, USE_MEMSET>::TryAllocateNewArena(size_t size)
65 {
66 size_t arenaSize = GetNextArenaSize(size);
67 LOG_FRAME_ALLOCATOR(DEBUG) << "Try to allocate a new arena with size " << arenaSize;
68 FramesArena *newArena = AllocateArenaImpl(arenaSize);
69 if (newArena == nullptr) {
70 LOG_FRAME_ALLOCATOR(DEBUG) << "Couldn't get memory for a new arena";
71 arenaSizeNeedToGrow_ = false;
72 return false;
73 }
74 lastAllocArena_->LinkNext(newArena);
75 newArena->LinkPrev(lastAllocArena_);
76 lastAllocArena_ = newArena;
77 emptyArenasCount_++;
78 LOG_FRAME_ALLOCATOR(DEBUG) << "Successfully allocate new arena with addr " << std::hex << newArena;
79 return true;
80 }
81
82 template <Alignment ALIGNMENT, bool USE_MEMSET>
Alloc(size_t size)83 ALWAYS_INLINE inline void *FrameAllocator<ALIGNMENT, USE_MEMSET>::Alloc(size_t size)
84 {
85 ASSERT(AlignUp(size, GetAlignmentInBytes(ALIGNMENT)) == size);
86 // Try to get free memory from current arenas
87 void *mem = TryToAllocate(size);
88
89 if (UNLIKELY(mem == nullptr)) {
90 LOG_FRAME_ALLOCATOR(DEBUG) << "Can't allocate " << size << " bytes for a new frame in current arenas";
91 if (!TryAllocateNewArena(size)) {
92 LOG_FRAME_ALLOCATOR(DEBUG) << "Can't allocate a new arena, return nullptr";
93 return nullptr;
94 }
95 mem = TryToAllocate(size);
96 if (mem == nullptr) {
97 LOG_FRAME_ALLOCATOR(DEBUG) << "Can't allocate memory in a totally free arena, change default arenas sizes";
98 return nullptr;
99 }
100 }
101
102 ASSERT(AlignUp(ToUintPtr(mem), GetAlignmentInBytes(ALIGNMENT)) == ToUintPtr(mem));
103 LOG_FRAME_ALLOCATOR(DEBUG) << "Allocated memory at addr " << std::hex << mem;
104 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
105 if constexpr (USE_MEMSET) {
106 memset_s(mem, size, 0x00, size);
107 }
108 allocatedSize_ += size;
109 return mem;
110 }
111
112 template <Alignment ALIGNMENT, bool USE_MEMSET>
Free(void * mem)113 ALWAYS_INLINE inline void FrameAllocator<ALIGNMENT, USE_MEMSET>::Free(void *mem)
114 {
115 ASSERT(curArena_ != nullptr); // must has been initialized!
116 ASSERT(ToUintPtr(mem) == AlignUp(ToUintPtr(mem), GetAlignmentInBytes(ALIGNMENT)));
117 if (curArena_->InArena(mem)) {
118 allocatedSize_ -= ToUintPtr(curArena_->GetTop()) - ToUintPtr(mem);
119 curArena_->Free(mem);
120 } else {
121 ASSERT(curArena_->GetOccupiedSize() == 0);
122 ASSERT(curArena_->GetPrevArena() != nullptr);
123
124 curArena_ = curArena_->GetPrevArena();
125 ASSERT(curArena_->InArena(mem));
126 allocatedSize_ -= ToUintPtr(curArena_->GetTop()) - ToUintPtr(mem);
127 curArena_->Free(mem);
128 if (UNLIKELY((emptyArenasCount_ + 1) > FRAME_ALLOC_MAX_FREE_ARENAS_THRESHOLD)) {
129 FreeLastArena();
130 } else {
131 emptyArenasCount_++;
132 }
133 }
134 LOG_FRAME_ALLOCATOR(DEBUG) << "Free memory at addr " << std::hex << mem;
135 }
136
137 template <Alignment ALIGNMENT, bool USE_MEMSET>
138 // CC-OFFNXT(G.FUD.06) perf critical
TryToAllocate(size_t size)139 inline void *FrameAllocator<ALIGNMENT, USE_MEMSET>::TryToAllocate(size_t size)
140 {
141 // Try to allocate memory in the current arena:
142 ASSERT(curArena_ != nullptr);
143 void *mem = curArena_->Alloc(size);
144 if (LIKELY(mem != nullptr)) {
145 return mem;
146 }
147 // We don't have enough memory in current arena, try to allocate in the next one:
148 FramesArena *nextArena = curArena_->GetNextArena();
149 if (nextArena == nullptr) {
150 LOG_FRAME_ALLOCATOR(DEBUG) << "TryToPush failed - we don't have a free arena";
151 return nullptr;
152 }
153 mem = nextArena->Alloc(size);
154 if (LIKELY(mem != nullptr)) {
155 ASSERT(emptyArenasCount_ > 0);
156 emptyArenasCount_--;
157 curArena_ = nextArena;
158 return mem;
159 }
160 LOG_FRAME_ALLOCATOR(DEBUG) << "Couldn't allocate " << size << " bytes of memory in the totally free arena."
161 << " Change initial sizes of arenas";
162 return nullptr;
163 }
164
165 template <Alignment ALIGNMENT, bool USE_MEMSET>
GetNextArenaSize(size_t size)166 inline size_t FrameAllocator<ALIGNMENT, USE_MEMSET>::GetNextArenaSize(size_t size)
167 {
168 size_t requestedSize = size + sizeof(FramesArena) + GetAlignmentInBytes(ALIGNMENT);
169 if ((arenaSizeNeedToGrow_) || (biggestArenaSize_ < requestedSize)) {
170 biggestArenaSize_ += ARENA_SIZE_GREW_LEVEL;
171 if (biggestArenaSize_ < requestedSize) {
172 biggestArenaSize_ = RoundUp(requestedSize, ARENA_SIZE_GREW_LEVEL);
173 }
174 } else {
175 arenaSizeNeedToGrow_ = true;
176 }
177 return biggestArenaSize_;
178 }
179
180 template <Alignment ALIGNMENT, bool USE_MEMSET>
181 // CC-OFFNXT(G.FUD.06) perf critical
FreeLastArena()182 inline void FrameAllocator<ALIGNMENT, USE_MEMSET>::FreeLastArena()
183 {
184 ASSERT(lastAllocArena_ != nullptr);
185 FramesArena *arenaToFree = lastAllocArena_;
186 lastAllocArena_ = arenaToFree->GetPrevArena();
187 if (arenaToFree == curArena_) {
188 curArena_ = lastAllocArena_;
189 }
190 if (lastAllocArena_ == nullptr) {
191 ASSERT(lastAllocArena_ == curArena_);
192 // To fix clang tidy warning
193 // (it suggests that cur_arena_ can be not nullptr when
194 // last_alloc_arena_ is equal to nullptr)
195 curArena_ = lastAllocArena_;
196 LOG_FRAME_ALLOCATOR(DEBUG) << "Clear the last arena in the list";
197 } else {
198 lastAllocArena_->ClearNextLink();
199 }
200 LOG_FRAME_ALLOCATOR(DEBUG) << "Free the arena at addr " << std::hex << arenaToFree;
201 FreeArenaImpl(arenaToFree);
202 arenaSizeNeedToGrow_ = false;
203 }
204
205 template <Alignment ALIGNMENT, bool USE_MEMSET>
206 // CC-OFFNXT(G.FMT.07) project code style
207 // CC-OFFNXT(G.FUD.06) perf critical
208 inline typename FrameAllocator<ALIGNMENT, USE_MEMSET>::FramesArena *
AllocateArenaImpl(size_t size)209 FrameAllocator<ALIGNMENT, USE_MEMSET>::AllocateArenaImpl(size_t size)
210 {
211 FramesArena *newArena = nullptr;
212 if (!useMalloc_) {
213 ASSERT(memPoolAlloc_ != nullptr);
214 newArena = memPoolAlloc_->AllocArena<FramesArena>(size, spaceType_, AllocatorType::FRAME_ALLOCATOR, this);
215 } else {
216 auto mem = ark::os::mem::AlignedAlloc(GetAlignmentInBytes(ARENA_DEFAULT_ALIGNMENT), size);
217 if (mem != nullptr) {
218 auto arenaBuffOffs = AlignUp(sizeof(FramesArena), GetAlignmentInBytes(ARENA_DEFAULT_ALIGNMENT));
219 newArena = new (mem) FramesArena(size - arenaBuffOffs, ToVoidPtr(ToUintPtr(mem) + arenaBuffOffs));
220 }
221 }
222 return newArena;
223 }
224
225 template <Alignment ALIGNMENT, bool USE_MEMSET>
FreeArenaImpl(FramesArena * arena)226 inline void FrameAllocator<ALIGNMENT, USE_MEMSET>::FreeArenaImpl(FramesArena *arena)
227 {
228 ASSERT(arena != nullptr);
229 if (!useMalloc_) {
230 ASSERT(memPoolAlloc_ != nullptr);
231 memPoolAlloc_->FreeArena<FramesArena>(arena);
232 } else {
233 os::mem::AlignedFree(arena);
234 }
235 }
236
237 template <Alignment ALIGNMENT, bool USE_MEMSET>
Contains(void * mem)238 inline bool FrameAllocator<ALIGNMENT, USE_MEMSET>::Contains(void *mem)
239 {
240 auto curArena = curArena_;
241
242 while (curArena != nullptr) {
243 LOG_FRAME_ALLOCATOR(DEBUG) << "check InAllocator arena at addr " << std::hex << curArena;
244 if (curArena->InArena(mem)) {
245 return true;
246 }
247 curArena = curArena->GetPrevArena();
248 }
249 return false;
250 }
251
252 #undef LOG_FRAME_ALLOCATOR
253
254 } // namespace ark::mem
255 #endif // PANDA_RUNTIME_MEM_FRAME_ALLOCATOR_INL_H
256