1 /*
2 * Copyright (c) 2021 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 #ifndef PANDA_RUNTIME_MEM_FRAME_ALLOCATOR_INL_H_
17 #define PANDA_RUNTIME_MEM_FRAME_ALLOCATOR_INL_H_
18
19 #include "runtime/mem/frame_allocator.h"
20
21 #include <cstring>
22
23 #include "libpandabase/mem/pool_manager.h"
24 #include "libpandabase/utils/logger.h"
25
26 namespace panda::mem {
27
28 using StackFrameAllocator = FrameAllocator<>;
29
30 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
31 #define LOG_FRAME_ALLOCATOR(level) LOG(level, ALLOC) << "FrameAllocator: "
32
33 template <Alignment AlignmenT, bool UseMemsetT>
FrameAllocator()34 inline FrameAllocator<AlignmenT, UseMemsetT>::FrameAllocator()
35 {
36 LOG_FRAME_ALLOCATOR(DEBUG) << "Initializing of FrameAllocator";
37 mem_pool_alloc_ = PoolManager::GetMmapMemPool();
38 cur_arena_ = mem_pool_alloc_->AllocArena<FramesArena>(FIRST_ARENA_SIZE, SpaceType::SPACE_TYPE_INTERNAL,
39 AllocatorType::FRAME_ALLOCATOR, this);
40 last_alloc_arena_ = cur_arena_;
41 biggest_arena_size_ = FIRST_ARENA_SIZE;
42 arena_size_need_to_grow_ = true;
43 LOG_FRAME_ALLOCATOR(INFO) << "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 mem_pool_alloc_->FreeArena<FramesArena>(last_alloc_arena_);
54 last_alloc_arena_ = prev_arena;
55 }
56 cur_arena_ = nullptr;
57 LOG_FRAME_ALLOCATOR(INFO) << "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 auto new_arena = mem_pool_alloc_->AllocArena<FramesArena>(arena_size, SpaceType::SPACE_TYPE_INTERNAL,
66 AllocatorType::FRAME_ALLOCATOR, this);
67 if (new_arena == nullptr) {
68 LOG_FRAME_ALLOCATOR(DEBUG) << "Couldn't get memory for a new arena";
69 arena_size_need_to_grow_ = false;
70 return false;
71 }
72 last_alloc_arena_->LinkNext(new_arena);
73 new_arena->LinkPrev(last_alloc_arena_);
74 last_alloc_arena_ = new_arena;
75 empty_arenas_count_++;
76 LOG_FRAME_ALLOCATOR(DEBUG) << "Successfully allocate new arena with addr " << std::hex << new_arena;
77 return true;
78 }
79
80 template <Alignment AlignmenT, bool UseMemsetT>
Alloc(size_t size)81 inline void *FrameAllocator<AlignmenT, UseMemsetT>::Alloc(size_t size)
82 {
83 ASSERT(AlignUp(size, GetAlignmentInBytes(AlignmenT)) == size);
84 // Try to get free memory from current arenas
85 void *mem = TryToAllocate(size);
86
87 if (UNLIKELY(mem == nullptr)) {
88 LOG_FRAME_ALLOCATOR(DEBUG) << "Can't allocate " << size << " bytes for a new frame in current arenas";
89 if (!TryAllocateNewArena(size)) {
90 LOG_FRAME_ALLOCATOR(DEBUG) << "Can't allocate a new arena, return nullptr";
91 return nullptr;
92 }
93 mem = TryToAllocate(size);
94 if (mem == nullptr) {
95 LOG_FRAME_ALLOCATOR(DEBUG) << "Can't allocate memory in a totally free arena, change default arenas sizes";
96 return nullptr;
97 }
98 }
99
100 ASSERT(AlignUp(ToUintPtr(mem), GetAlignmentInBytes(AlignmenT)) == ToUintPtr(mem));
101 LOG_FRAME_ALLOCATOR(INFO) << "Allocated memory at addr " << std::hex << mem;
102 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
103 if constexpr (UseMemsetT) {
104 (void)memset_s(mem, size, 0x00, size);
105 }
106 return mem;
107 }
108
109 template <Alignment AlignmenT, bool UseMemsetT>
Free(void * mem)110 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 cur_arena_->Free(mem);
116 } else {
117 ASSERT(cur_arena_->GetOccupiedSize() == 0);
118 ASSERT(cur_arena_->GetPrevArena() != nullptr);
119
120 cur_arena_ = cur_arena_->GetPrevArena();
121 ASSERT(cur_arena_->InArena(mem));
122 cur_arena_->Free(mem);
123 if (UNLIKELY((empty_arenas_count_ + 1) > FRAME_ALLOC_MAX_FREE_ARENAS_THRESHOLD)) {
124 FreeLastArena();
125 } else {
126 empty_arenas_count_++;
127 }
128 }
129 LOG_FRAME_ALLOCATOR(INFO) << "Free memory at addr " << std::hex << mem;
130 }
131
132 template <Alignment AlignmenT, bool UseMemsetT>
TryToAllocate(size_t size)133 inline void *FrameAllocator<AlignmenT, UseMemsetT>::TryToAllocate(size_t size)
134 {
135 // Try to allocate memory in the current arena:
136 ASSERT(cur_arena_ != nullptr);
137 void *mem = cur_arena_->Alloc(size);
138 if (LIKELY(mem != nullptr)) {
139 return mem;
140 }
141 // We don't have enough memory in current arena, try to allocate in the next one:
142 FramesArena *next_arena = cur_arena_->GetNextArena();
143 if (next_arena == nullptr) {
144 LOG_FRAME_ALLOCATOR(DEBUG) << "TryToPush failed - we don't have a free arena";
145 return nullptr;
146 }
147 mem = next_arena->Alloc(size);
148 if (LIKELY(mem != nullptr)) {
149 ASSERT(empty_arenas_count_ > 0);
150 empty_arenas_count_--;
151 cur_arena_ = next_arena;
152 return mem;
153 }
154 LOG_FRAME_ALLOCATOR(DEBUG) << "Couldn't allocate " << size << " bytes of memory in the totally free arena."
155 << " Change initial sizes of arenas";
156 return nullptr;
157 }
158
159 template <Alignment AlignmenT, bool UseMemsetT>
GetNextArenaSize(size_t size)160 inline size_t FrameAllocator<AlignmenT, UseMemsetT>::GetNextArenaSize(size_t size)
161 {
162 if (arena_size_need_to_grow_) {
163 biggest_arena_size_ += ARENA_SIZE_GREW_LEVEL;
164 if (biggest_arena_size_ < size) {
165 biggest_arena_size_ = RoundUp(size, ARENA_SIZE_GREW_LEVEL);
166 }
167 } else {
168 arena_size_need_to_grow_ = true;
169 }
170 return biggest_arena_size_;
171 }
172
173 template <Alignment AlignmenT, bool UseMemsetT>
FreeLastArena()174 inline void FrameAllocator<AlignmenT, UseMemsetT>::FreeLastArena()
175 {
176 ASSERT(last_alloc_arena_ != nullptr);
177 FramesArena *arena_to_free = last_alloc_arena_;
178 last_alloc_arena_ = arena_to_free->GetPrevArena();
179 if (arena_to_free == cur_arena_) {
180 cur_arena_ = last_alloc_arena_;
181 }
182 if (last_alloc_arena_ == nullptr) {
183 LOG_FRAME_ALLOCATOR(DEBUG) << "Clear the last arena in the list";
184 } else {
185 last_alloc_arena_->ClearNextLink();
186 }
187 LOG_FRAME_ALLOCATOR(DEBUG) << "Free the arena at addr " << std::hex << arena_to_free;
188 mem_pool_alloc_->FreeArena<FramesArena>(arena_to_free);
189 arena_size_need_to_grow_ = false;
190 }
191
192 template <Alignment AlignmenT, bool UseMemsetT>
Contains(void * mem)193 inline bool FrameAllocator<AlignmenT, UseMemsetT>::Contains(void *mem)
194 {
195 auto cur_arena = cur_arena_;
196 while (cur_arena != nullptr) {
197 LOG_FRAME_ALLOCATOR(DEBUG) << "check InAllocator arena at addr " << std::hex << cur_arena;
198 if (cur_arena->InArena(mem)) {
199 return true;
200 }
201 cur_arena = cur_arena->GetPrevArena();
202 }
203 return false;
204 }
205
206 #undef LOG_FRAME_ALLOCATOR
207
208 } // namespace panda::mem
209 #endif // PANDA_RUNTIME_MEM_FRAME_ALLOCATOR_INL_H_
210