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