1 /*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <algorithm>
18 #include <iomanip>
19 #include <numeric>
20
21 #include "arena_allocator.h"
22 #include "logging.h"
23 #include "mem_map.h"
24 #include "mutex.h"
25 #include "thread-inl.h"
26 #include "systrace.h"
27
28 namespace art {
29
30 static constexpr size_t kMemoryToolRedZoneBytes = 8;
31 constexpr size_t Arena::kDefaultSize;
32
33 template <bool kCount>
34 const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = {
35 "Misc ",
36 "SwitchTbl ",
37 "SlowPaths ",
38 "GrowBitMap ",
39 "STL ",
40 "GraphBuilder ",
41 "Graph ",
42 "BasicBlock ",
43 "BlockList ",
44 "RevPostOrder ",
45 "LinearOrder ",
46 "ConstantsMap ",
47 "Predecessors ",
48 "Successors ",
49 "Dominated ",
50 "Instruction ",
51 "InvokeInputs ",
52 "PhiInputs ",
53 "LoopInfo ",
54 "LIBackEdges ",
55 "TryCatchInf ",
56 "UseListNode ",
57 "Environment ",
58 "EnvVRegs ",
59 "EnvLocations ",
60 "LocSummary ",
61 "SsaBuilder ",
62 "MoveOperands ",
63 "CodeBuffer ",
64 "StackMaps ",
65 "Optimization ",
66 "GVN ",
67 "InductionVar ",
68 "BCE ",
69 "DCE ",
70 "LSE ",
71 "LICM ",
72 "SsaLiveness ",
73 "SsaPhiElim ",
74 "RefTypeProp ",
75 "SideEffects ",
76 "RegAllocator ",
77 "RegAllocVldt ",
78 "StackMapStm ",
79 "CodeGen ",
80 "Assembler ",
81 "ParallelMove ",
82 "GraphChecker ",
83 "Verifier ",
84 "CallingConv ",
85 };
86
87 template <bool kCount>
ArenaAllocatorStatsImpl()88 ArenaAllocatorStatsImpl<kCount>::ArenaAllocatorStatsImpl()
89 : num_allocations_(0u) {
90 std::fill_n(alloc_stats_, arraysize(alloc_stats_), 0u);
91 }
92
93 template <bool kCount>
Copy(const ArenaAllocatorStatsImpl & other)94 void ArenaAllocatorStatsImpl<kCount>::Copy(const ArenaAllocatorStatsImpl& other) {
95 num_allocations_ = other.num_allocations_;
96 std::copy(other.alloc_stats_, other.alloc_stats_ + arraysize(alloc_stats_), alloc_stats_);
97 }
98
99 template <bool kCount>
RecordAlloc(size_t bytes,ArenaAllocKind kind)100 void ArenaAllocatorStatsImpl<kCount>::RecordAlloc(size_t bytes, ArenaAllocKind kind) {
101 alloc_stats_[kind] += bytes;
102 ++num_allocations_;
103 }
104
105 template <bool kCount>
NumAllocations() const106 size_t ArenaAllocatorStatsImpl<kCount>::NumAllocations() const {
107 return num_allocations_;
108 }
109
110 template <bool kCount>
BytesAllocated() const111 size_t ArenaAllocatorStatsImpl<kCount>::BytesAllocated() const {
112 const size_t init = 0u; // Initial value of the correct type.
113 return std::accumulate(alloc_stats_, alloc_stats_ + arraysize(alloc_stats_), init);
114 }
115
116 template <bool kCount>
Dump(std::ostream & os,const Arena * first,ssize_t lost_bytes_adjustment) const117 void ArenaAllocatorStatsImpl<kCount>::Dump(std::ostream& os, const Arena* first,
118 ssize_t lost_bytes_adjustment) const {
119 size_t malloc_bytes = 0u;
120 size_t lost_bytes = 0u;
121 size_t num_arenas = 0u;
122 for (const Arena* arena = first; arena != nullptr; arena = arena->next_) {
123 malloc_bytes += arena->Size();
124 lost_bytes += arena->RemainingSpace();
125 ++num_arenas;
126 }
127 // The lost_bytes_adjustment is used to make up for the fact that the current arena
128 // may not have the bytes_allocated_ updated correctly.
129 lost_bytes += lost_bytes_adjustment;
130 const size_t bytes_allocated = BytesAllocated();
131 os << " MEM: used: " << bytes_allocated << ", allocated: " << malloc_bytes
132 << ", lost: " << lost_bytes << "\n";
133 size_t num_allocations = NumAllocations();
134 if (num_allocations != 0) {
135 os << "Number of arenas allocated: " << num_arenas << ", Number of allocations: "
136 << num_allocations << ", avg size: " << bytes_allocated / num_allocations << "\n";
137 }
138 os << "===== Allocation by kind\n";
139 static_assert(arraysize(kAllocNames) == kNumArenaAllocKinds, "arraysize of kAllocNames");
140 for (int i = 0; i < kNumArenaAllocKinds; i++) {
141 os << kAllocNames[i] << std::setw(10) << alloc_stats_[i] << "\n";
142 }
143 }
144
145 // Explicitly instantiate the used implementation.
146 template class ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations>;
147
DoMakeDefined(void * ptr,size_t size)148 void ArenaAllocatorMemoryTool::DoMakeDefined(void* ptr, size_t size) {
149 MEMORY_TOOL_MAKE_DEFINED(ptr, size);
150 }
151
DoMakeUndefined(void * ptr,size_t size)152 void ArenaAllocatorMemoryTool::DoMakeUndefined(void* ptr, size_t size) {
153 MEMORY_TOOL_MAKE_UNDEFINED(ptr, size);
154 }
155
DoMakeInaccessible(void * ptr,size_t size)156 void ArenaAllocatorMemoryTool::DoMakeInaccessible(void* ptr, size_t size) {
157 MEMORY_TOOL_MAKE_NOACCESS(ptr, size);
158 }
159
Arena()160 Arena::Arena() : bytes_allocated_(0), next_(nullptr) {
161 }
162
MallocArena(size_t size)163 MallocArena::MallocArena(size_t size) {
164 memory_ = reinterpret_cast<uint8_t*>(calloc(1, size));
165 CHECK(memory_ != nullptr); // Abort on OOM.
166 size_ = size;
167 }
168
~MallocArena()169 MallocArena::~MallocArena() {
170 free(reinterpret_cast<void*>(memory_));
171 }
172
MemMapArena(size_t size,bool low_4gb,const char * name)173 MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) {
174 std::string error_msg;
175 map_.reset(MemMap::MapAnonymous(
176 name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg));
177 CHECK(map_.get() != nullptr) << error_msg;
178 memory_ = map_->Begin();
179 size_ = map_->Size();
180 }
181
~MemMapArena()182 MemMapArena::~MemMapArena() {
183 // Destroys MemMap via std::unique_ptr<>.
184 }
185
Release()186 void MemMapArena::Release() {
187 if (bytes_allocated_ > 0) {
188 map_->MadviseDontNeedAndZero();
189 bytes_allocated_ = 0;
190 }
191 }
192
Reset()193 void Arena::Reset() {
194 if (bytes_allocated_ > 0) {
195 memset(Begin(), 0, bytes_allocated_);
196 bytes_allocated_ = 0;
197 }
198 }
199
ArenaPool(bool use_malloc,bool low_4gb,const char * name)200 ArenaPool::ArenaPool(bool use_malloc, bool low_4gb, const char* name)
201 : use_malloc_(use_malloc),
202 lock_("Arena pool lock", kArenaPoolLock),
203 free_arenas_(nullptr),
204 low_4gb_(low_4gb),
205 name_(name) {
206 if (low_4gb) {
207 CHECK(!use_malloc) << "low4gb must use map implementation";
208 }
209 if (!use_malloc) {
210 MemMap::Init();
211 }
212 }
213
~ArenaPool()214 ArenaPool::~ArenaPool() {
215 ReclaimMemory();
216 }
217
ReclaimMemory()218 void ArenaPool::ReclaimMemory() {
219 while (free_arenas_ != nullptr) {
220 auto* arena = free_arenas_;
221 free_arenas_ = free_arenas_->next_;
222 delete arena;
223 }
224 }
225
LockReclaimMemory()226 void ArenaPool::LockReclaimMemory() {
227 MutexLock lock(Thread::Current(), lock_);
228 ReclaimMemory();
229 }
230
AllocArena(size_t size)231 Arena* ArenaPool::AllocArena(size_t size) {
232 Thread* self = Thread::Current();
233 Arena* ret = nullptr;
234 {
235 MutexLock lock(self, lock_);
236 if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) {
237 ret = free_arenas_;
238 free_arenas_ = free_arenas_->next_;
239 }
240 }
241 if (ret == nullptr) {
242 ret = use_malloc_ ? static_cast<Arena*>(new MallocArena(size)) :
243 new MemMapArena(size, low_4gb_, name_);
244 }
245 ret->Reset();
246 return ret;
247 }
248
TrimMaps()249 void ArenaPool::TrimMaps() {
250 if (!use_malloc_) {
251 ScopedTrace trace(__PRETTY_FUNCTION__);
252 // Doesn't work for malloc.
253 MutexLock lock(Thread::Current(), lock_);
254 for (auto* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
255 arena->Release();
256 }
257 }
258 }
259
GetBytesAllocated() const260 size_t ArenaPool::GetBytesAllocated() const {
261 size_t total = 0;
262 MutexLock lock(Thread::Current(), lock_);
263 for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
264 total += arena->GetBytesAllocated();
265 }
266 return total;
267 }
268
FreeArenaChain(Arena * first)269 void ArenaPool::FreeArenaChain(Arena* first) {
270 if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
271 for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
272 MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
273 }
274 }
275 if (first != nullptr) {
276 Arena* last = first;
277 while (last->next_ != nullptr) {
278 last = last->next_;
279 }
280 Thread* self = Thread::Current();
281 MutexLock lock(self, lock_);
282 last->next_ = free_arenas_;
283 free_arenas_ = first;
284 }
285 }
286
BytesAllocated() const287 size_t ArenaAllocator::BytesAllocated() const {
288 return ArenaAllocatorStats::BytesAllocated();
289 }
290
BytesUsed() const291 size_t ArenaAllocator::BytesUsed() const {
292 size_t total = ptr_ - begin_;
293 if (arena_head_ != nullptr) {
294 for (Arena* cur_arena = arena_head_->next_; cur_arena != nullptr;
295 cur_arena = cur_arena->next_) {
296 total += cur_arena->GetBytesAllocated();
297 }
298 }
299 return total;
300 }
301
ArenaAllocator(ArenaPool * pool)302 ArenaAllocator::ArenaAllocator(ArenaPool* pool)
303 : pool_(pool),
304 begin_(nullptr),
305 end_(nullptr),
306 ptr_(nullptr),
307 arena_head_(nullptr) {
308 }
309
UpdateBytesAllocated()310 void ArenaAllocator::UpdateBytesAllocated() {
311 if (arena_head_ != nullptr) {
312 // Update how many bytes we have allocated into the arena so that the arena pool knows how
313 // much memory to zero out.
314 arena_head_->bytes_allocated_ = ptr_ - begin_;
315 }
316 }
317
AllocWithMemoryTool(size_t bytes,ArenaAllocKind kind)318 void* ArenaAllocator::AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind) {
319 // We mark all memory for a newly retrieved arena as inaccessible and then
320 // mark only the actually allocated memory as defined. That leaves red zones
321 // and padding between allocations marked as inaccessible.
322 size_t rounded_bytes = RoundUp(bytes + kMemoryToolRedZoneBytes, 8);
323 ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind);
324 uint8_t* ret;
325 if (UNLIKELY(rounded_bytes > static_cast<size_t>(end_ - ptr_))) {
326 ret = AllocFromNewArena(rounded_bytes);
327 uint8_t* noaccess_begin = ret + bytes;
328 uint8_t* noaccess_end;
329 if (ret == arena_head_->Begin()) {
330 DCHECK(ptr_ - rounded_bytes == ret);
331 noaccess_end = end_;
332 } else {
333 // We're still using the old arena but `ret` comes from a new one just after it.
334 DCHECK(arena_head_->next_ != nullptr);
335 DCHECK(ret == arena_head_->next_->Begin());
336 DCHECK_EQ(rounded_bytes, arena_head_->next_->GetBytesAllocated());
337 noaccess_end = arena_head_->next_->End();
338 }
339 MEMORY_TOOL_MAKE_NOACCESS(noaccess_begin, noaccess_end - noaccess_begin);
340 } else {
341 ret = ptr_;
342 ptr_ += rounded_bytes;
343 }
344 MEMORY_TOOL_MAKE_DEFINED(ret, bytes);
345 // Check that the memory is already zeroed out.
346 DCHECK(std::all_of(ret, ret + bytes, [](uint8_t val) { return val == 0u; }));
347 return ret;
348 }
349
~ArenaAllocator()350 ArenaAllocator::~ArenaAllocator() {
351 // Reclaim all the arenas by giving them back to the thread pool.
352 UpdateBytesAllocated();
353 pool_->FreeArenaChain(arena_head_);
354 }
355
AllocFromNewArena(size_t bytes)356 uint8_t* ArenaAllocator::AllocFromNewArena(size_t bytes) {
357 Arena* new_arena = pool_->AllocArena(std::max(Arena::kDefaultSize, bytes));
358 DCHECK(new_arena != nullptr);
359 DCHECK_LE(bytes, new_arena->Size());
360 if (static_cast<size_t>(end_ - ptr_) > new_arena->Size() - bytes) {
361 // The old arena has more space remaining than the new one, so keep using it.
362 // This can happen when the requested size is over half of the default size.
363 DCHECK(arena_head_ != nullptr);
364 new_arena->bytes_allocated_ = bytes; // UpdateBytesAllocated() on the new_arena.
365 new_arena->next_ = arena_head_->next_;
366 arena_head_->next_ = new_arena;
367 } else {
368 UpdateBytesAllocated();
369 new_arena->next_ = arena_head_;
370 arena_head_ = new_arena;
371 // Update our internal data structures.
372 begin_ = new_arena->Begin();
373 ptr_ = begin_ + bytes;
374 end_ = new_arena->End();
375 }
376 return new_arena->Begin();
377 }
378
Contains(const void * ptr) const379 bool ArenaAllocator::Contains(const void* ptr) const {
380 if (ptr >= begin_ && ptr < end_) {
381 return true;
382 }
383 for (const Arena* cur_arena = arena_head_; cur_arena != nullptr; cur_arena = cur_arena->next_) {
384 if (cur_arena->Contains(ptr)) {
385 return true;
386 }
387 }
388 return false;
389 }
390
MemStats(const char * name,const ArenaAllocatorStats * stats,const Arena * first_arena,ssize_t lost_bytes_adjustment)391 MemStats::MemStats(const char* name, const ArenaAllocatorStats* stats, const Arena* first_arena,
392 ssize_t lost_bytes_adjustment)
393 : name_(name),
394 stats_(stats),
395 first_arena_(first_arena),
396 lost_bytes_adjustment_(lost_bytes_adjustment) {
397 }
398
Dump(std::ostream & os) const399 void MemStats::Dump(std::ostream& os) const {
400 os << name_ << " stats:\n";
401 stats_->Dump(os, first_arena_, lost_bytes_adjustment_);
402 }
403
404 // Dump memory usage stats.
GetMemStats() const405 MemStats ArenaAllocator::GetMemStats() const {
406 ssize_t lost_bytes_adjustment =
407 (arena_head_ == nullptr) ? 0 : (end_ - ptr_) - arena_head_->RemainingSpace();
408 return MemStats("ArenaAllocator", this, arena_head_, lost_bytes_adjustment);
409 }
410
411 } // namespace art
412