• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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