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 <cstddef>
19 #include <iomanip>
20 #include <numeric>
21
22 #include "arena_allocator.h"
23 #include "logging.h"
24 #include "mem_map.h"
25 #include "mutex.h"
26 #include "thread-inl.h"
27 #include "systrace.h"
28
29 namespace art {
30
31 constexpr size_t kMemoryToolRedZoneBytes = 8;
32 constexpr size_t Arena::kDefaultSize;
33
34 template <bool kCount>
35 const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = {
36 "Misc ",
37 "SwitchTbl ",
38 "SlowPaths ",
39 "GrowBitMap ",
40 "STL ",
41 "GraphBuilder ",
42 "Graph ",
43 "BasicBlock ",
44 "BlockList ",
45 "RevPostOrder ",
46 "LinearOrder ",
47 "ConstantsMap ",
48 "Predecessors ",
49 "Successors ",
50 "Dominated ",
51 "Instruction ",
52 "InvokeInputs ",
53 "PhiInputs ",
54 "LoopInfo ",
55 "LIBackEdges ",
56 "TryCatchInf ",
57 "UseListNode ",
58 "Environment ",
59 "EnvVRegs ",
60 "EnvLocations ",
61 "LocSummary ",
62 "SsaBuilder ",
63 "MoveOperands ",
64 "CodeBuffer ",
65 "StackMaps ",
66 "Optimization ",
67 "GVN ",
68 "InductionVar ",
69 "BCE ",
70 "DCE ",
71 "LSE ",
72 "LICM ",
73 "LoopOpt ",
74 "SsaLiveness ",
75 "SsaPhiElim ",
76 "RefTypeProp ",
77 "SideEffects ",
78 "RegAllocator ",
79 "RegAllocVldt ",
80 "StackMapStm ",
81 "VectorNode ",
82 "CodeGen ",
83 "Assembler ",
84 "ParallelMove ",
85 "GraphChecker ",
86 "Verifier ",
87 "CallingConv ",
88 "CHA ",
89 "Scheduler ",
90 "Profile ",
91 };
92
93 template <bool kCount>
ArenaAllocatorStatsImpl()94 ArenaAllocatorStatsImpl<kCount>::ArenaAllocatorStatsImpl()
95 : num_allocations_(0u),
96 alloc_stats_(kNumArenaAllocKinds, 0u) {
97 }
98
99 template <bool kCount>
Copy(const ArenaAllocatorStatsImpl & other)100 void ArenaAllocatorStatsImpl<kCount>::Copy(const ArenaAllocatorStatsImpl& other) {
101 num_allocations_ = other.num_allocations_;
102 std::copy_n(other.alloc_stats_.begin(), kNumArenaAllocKinds, alloc_stats_.begin());
103 }
104
105 template <bool kCount>
RecordAlloc(size_t bytes,ArenaAllocKind kind)106 void ArenaAllocatorStatsImpl<kCount>::RecordAlloc(size_t bytes, ArenaAllocKind kind) {
107 alloc_stats_[kind] += bytes;
108 ++num_allocations_;
109 }
110
111 template <bool kCount>
NumAllocations() const112 size_t ArenaAllocatorStatsImpl<kCount>::NumAllocations() const {
113 return num_allocations_;
114 }
115
116 template <bool kCount>
BytesAllocated() const117 size_t ArenaAllocatorStatsImpl<kCount>::BytesAllocated() const {
118 const size_t init = 0u; // Initial value of the correct type.
119 return std::accumulate(alloc_stats_.begin(), alloc_stats_.end(), init);
120 }
121
122 template <bool kCount>
Dump(std::ostream & os,const Arena * first,ssize_t lost_bytes_adjustment) const123 void ArenaAllocatorStatsImpl<kCount>::Dump(std::ostream& os, const Arena* first,
124 ssize_t lost_bytes_adjustment) const {
125 size_t malloc_bytes = 0u;
126 size_t lost_bytes = 0u;
127 size_t num_arenas = 0u;
128 for (const Arena* arena = first; arena != nullptr; arena = arena->next_) {
129 malloc_bytes += arena->Size();
130 lost_bytes += arena->RemainingSpace();
131 ++num_arenas;
132 }
133 // The lost_bytes_adjustment is used to make up for the fact that the current arena
134 // may not have the bytes_allocated_ updated correctly.
135 lost_bytes += lost_bytes_adjustment;
136 const size_t bytes_allocated = BytesAllocated();
137 os << " MEM: used: " << bytes_allocated << ", allocated: " << malloc_bytes
138 << ", lost: " << lost_bytes << "\n";
139 size_t num_allocations = NumAllocations();
140 if (num_allocations != 0) {
141 os << "Number of arenas allocated: " << num_arenas << ", Number of allocations: "
142 << num_allocations << ", avg size: " << bytes_allocated / num_allocations << "\n";
143 }
144 os << "===== Allocation by kind\n";
145 static_assert(arraysize(kAllocNames) == kNumArenaAllocKinds, "arraysize of kAllocNames");
146 for (int i = 0; i < kNumArenaAllocKinds; i++) {
147 os << kAllocNames[i] << std::setw(10) << alloc_stats_[i] << "\n";
148 }
149 }
150
151 #pragma GCC diagnostic push
152 #if __clang_major__ >= 4
153 #pragma GCC diagnostic ignored "-Winstantiation-after-specialization"
154 #endif
155 // We're going to use ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations> which needs
156 // to be explicitly instantiated if kArenaAllocatorCountAllocations is true. Explicit
157 // instantiation of the specialization ArenaAllocatorStatsImpl<false> does not do anything
158 // but requires the warning "-Winstantiation-after-specialization" to be turned off.
159 //
160 // To avoid bit-rot of the ArenaAllocatorStatsImpl<true>, instantiate it also in debug builds
161 // (but keep the unnecessary code out of release builds) as we do not usually compile with
162 // kArenaAllocatorCountAllocations set to true.
163 template class ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations || kIsDebugBuild>;
164 #pragma GCC diagnostic pop
165
DoMakeDefined(void * ptr,size_t size)166 void ArenaAllocatorMemoryTool::DoMakeDefined(void* ptr, size_t size) {
167 MEMORY_TOOL_MAKE_DEFINED(ptr, size);
168 }
169
DoMakeUndefined(void * ptr,size_t size)170 void ArenaAllocatorMemoryTool::DoMakeUndefined(void* ptr, size_t size) {
171 MEMORY_TOOL_MAKE_UNDEFINED(ptr, size);
172 }
173
DoMakeInaccessible(void * ptr,size_t size)174 void ArenaAllocatorMemoryTool::DoMakeInaccessible(void* ptr, size_t size) {
175 MEMORY_TOOL_MAKE_NOACCESS(ptr, size);
176 }
177
Arena()178 Arena::Arena() : bytes_allocated_(0), memory_(nullptr), size_(0), next_(nullptr) {
179 }
180
181 class MallocArena FINAL : public Arena {
182 public:
183 explicit MallocArena(size_t size = Arena::kDefaultSize);
184 virtual ~MallocArena();
185 private:
RequiredOverallocation()186 static constexpr size_t RequiredOverallocation() {
187 return (alignof(std::max_align_t) < ArenaAllocator::kArenaAlignment)
188 ? ArenaAllocator::kArenaAlignment - alignof(std::max_align_t)
189 : 0u;
190 }
191
192 uint8_t* unaligned_memory_;
193 };
194
MallocArena(size_t size)195 MallocArena::MallocArena(size_t size) {
196 // We need to guarantee kArenaAlignment aligned allocation for the new arena.
197 // TODO: Use std::aligned_alloc() when it becomes available with C++17.
198 constexpr size_t overallocation = RequiredOverallocation();
199 unaligned_memory_ = reinterpret_cast<uint8_t*>(calloc(1, size + overallocation));
200 CHECK(unaligned_memory_ != nullptr); // Abort on OOM.
201 DCHECK_ALIGNED(unaligned_memory_, alignof(std::max_align_t));
202 if (overallocation == 0u) {
203 memory_ = unaligned_memory_;
204 } else {
205 memory_ = AlignUp(unaligned_memory_, ArenaAllocator::kArenaAlignment);
206 if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
207 size_t head = memory_ - unaligned_memory_;
208 size_t tail = overallocation - head;
209 MEMORY_TOOL_MAKE_NOACCESS(unaligned_memory_, head);
210 MEMORY_TOOL_MAKE_NOACCESS(memory_ + size, tail);
211 }
212 }
213 DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
214 size_ = size;
215 }
216
~MallocArena()217 MallocArena::~MallocArena() {
218 constexpr size_t overallocation = RequiredOverallocation();
219 if (overallocation != 0u && UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
220 size_t head = memory_ - unaligned_memory_;
221 size_t tail = overallocation - head;
222 MEMORY_TOOL_MAKE_UNDEFINED(unaligned_memory_, head);
223 MEMORY_TOOL_MAKE_UNDEFINED(memory_ + size_, tail);
224 }
225 free(reinterpret_cast<void*>(unaligned_memory_));
226 }
227
228 class MemMapArena FINAL : public Arena {
229 public:
230 MemMapArena(size_t size, bool low_4gb, const char* name);
231 virtual ~MemMapArena();
232 void Release() OVERRIDE;
233
234 private:
235 std::unique_ptr<MemMap> map_;
236 };
237
MemMapArena(size_t size,bool low_4gb,const char * name)238 MemMapArena::MemMapArena(size_t size, bool low_4gb, const char* name) {
239 // Round up to a full page as that's the smallest unit of allocation for mmap()
240 // and we want to be able to use all memory that we actually allocate.
241 size = RoundUp(size, kPageSize);
242 std::string error_msg;
243 map_.reset(MemMap::MapAnonymous(
244 name, nullptr, size, PROT_READ | PROT_WRITE, low_4gb, false, &error_msg));
245 CHECK(map_.get() != nullptr) << error_msg;
246 memory_ = map_->Begin();
247 static_assert(ArenaAllocator::kArenaAlignment <= kPageSize,
248 "Arena should not need stronger alignment than kPageSize.");
249 DCHECK_ALIGNED(memory_, ArenaAllocator::kArenaAlignment);
250 size_ = map_->Size();
251 }
252
~MemMapArena()253 MemMapArena::~MemMapArena() {
254 // Destroys MemMap via std::unique_ptr<>.
255 }
256
Release()257 void MemMapArena::Release() {
258 if (bytes_allocated_ > 0) {
259 map_->MadviseDontNeedAndZero();
260 bytes_allocated_ = 0;
261 }
262 }
263
Reset()264 void Arena::Reset() {
265 if (bytes_allocated_ > 0) {
266 memset(Begin(), 0, bytes_allocated_);
267 bytes_allocated_ = 0;
268 }
269 }
270
ArenaPool(bool use_malloc,bool low_4gb,const char * name)271 ArenaPool::ArenaPool(bool use_malloc, bool low_4gb, const char* name)
272 : use_malloc_(use_malloc),
273 lock_("Arena pool lock", kArenaPoolLock),
274 free_arenas_(nullptr),
275 low_4gb_(low_4gb),
276 name_(name) {
277 if (low_4gb) {
278 CHECK(!use_malloc) << "low4gb must use map implementation";
279 }
280 if (!use_malloc) {
281 MemMap::Init();
282 }
283 }
284
~ArenaPool()285 ArenaPool::~ArenaPool() {
286 ReclaimMemory();
287 }
288
ReclaimMemory()289 void ArenaPool::ReclaimMemory() {
290 while (free_arenas_ != nullptr) {
291 auto* arena = free_arenas_;
292 free_arenas_ = free_arenas_->next_;
293 delete arena;
294 }
295 }
296
LockReclaimMemory()297 void ArenaPool::LockReclaimMemory() {
298 MutexLock lock(Thread::Current(), lock_);
299 ReclaimMemory();
300 }
301
AllocArena(size_t size)302 Arena* ArenaPool::AllocArena(size_t size) {
303 Thread* self = Thread::Current();
304 Arena* ret = nullptr;
305 {
306 MutexLock lock(self, lock_);
307 if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) {
308 ret = free_arenas_;
309 free_arenas_ = free_arenas_->next_;
310 }
311 }
312 if (ret == nullptr) {
313 ret = use_malloc_ ? static_cast<Arena*>(new MallocArena(size)) :
314 new MemMapArena(size, low_4gb_, name_);
315 }
316 ret->Reset();
317 return ret;
318 }
319
TrimMaps()320 void ArenaPool::TrimMaps() {
321 if (!use_malloc_) {
322 ScopedTrace trace(__PRETTY_FUNCTION__);
323 // Doesn't work for malloc.
324 MutexLock lock(Thread::Current(), lock_);
325 for (auto* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
326 arena->Release();
327 }
328 }
329 }
330
GetBytesAllocated() const331 size_t ArenaPool::GetBytesAllocated() const {
332 size_t total = 0;
333 MutexLock lock(Thread::Current(), lock_);
334 for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
335 total += arena->GetBytesAllocated();
336 }
337 return total;
338 }
339
FreeArenaChain(Arena * first)340 void ArenaPool::FreeArenaChain(Arena* first) {
341 if (UNLIKELY(RUNNING_ON_MEMORY_TOOL > 0)) {
342 for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
343 MEMORY_TOOL_MAKE_UNDEFINED(arena->memory_, arena->bytes_allocated_);
344 }
345 }
346 if (first != nullptr) {
347 Arena* last = first;
348 while (last->next_ != nullptr) {
349 last = last->next_;
350 }
351 Thread* self = Thread::Current();
352 MutexLock lock(self, lock_);
353 last->next_ = free_arenas_;
354 free_arenas_ = first;
355 }
356 }
357
BytesAllocated() const358 size_t ArenaAllocator::BytesAllocated() const {
359 return ArenaAllocatorStats::BytesAllocated();
360 }
361
BytesUsed() const362 size_t ArenaAllocator::BytesUsed() const {
363 size_t total = ptr_ - begin_;
364 if (arena_head_ != nullptr) {
365 for (Arena* cur_arena = arena_head_->next_; cur_arena != nullptr;
366 cur_arena = cur_arena->next_) {
367 total += cur_arena->GetBytesAllocated();
368 }
369 }
370 return total;
371 }
372
ArenaAllocator(ArenaPool * pool)373 ArenaAllocator::ArenaAllocator(ArenaPool* pool)
374 : pool_(pool),
375 begin_(nullptr),
376 end_(nullptr),
377 ptr_(nullptr),
378 arena_head_(nullptr) {
379 }
380
UpdateBytesAllocated()381 void ArenaAllocator::UpdateBytesAllocated() {
382 if (arena_head_ != nullptr) {
383 // Update how many bytes we have allocated into the arena so that the arena pool knows how
384 // much memory to zero out.
385 arena_head_->bytes_allocated_ = ptr_ - begin_;
386 }
387 }
388
AllocWithMemoryTool(size_t bytes,ArenaAllocKind kind)389 void* ArenaAllocator::AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind) {
390 // We mark all memory for a newly retrieved arena as inaccessible and then
391 // mark only the actually allocated memory as defined. That leaves red zones
392 // and padding between allocations marked as inaccessible.
393 size_t rounded_bytes = RoundUp(bytes + kMemoryToolRedZoneBytes, 8);
394 ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind);
395 uint8_t* ret;
396 if (UNLIKELY(rounded_bytes > static_cast<size_t>(end_ - ptr_))) {
397 ret = AllocFromNewArenaWithMemoryTool(rounded_bytes);
398 } else {
399 ret = ptr_;
400 ptr_ += rounded_bytes;
401 }
402 MEMORY_TOOL_MAKE_DEFINED(ret, bytes);
403 // Check that the memory is already zeroed out.
404 DCHECK(std::all_of(ret, ret + bytes, [](uint8_t val) { return val == 0u; }));
405 return ret;
406 }
407
AllocWithMemoryToolAlign16(size_t bytes,ArenaAllocKind kind)408 void* ArenaAllocator::AllocWithMemoryToolAlign16(size_t bytes, ArenaAllocKind kind) {
409 // We mark all memory for a newly retrieved arena as inaccessible and then
410 // mark only the actually allocated memory as defined. That leaves red zones
411 // and padding between allocations marked as inaccessible.
412 size_t rounded_bytes = bytes + kMemoryToolRedZoneBytes;
413 DCHECK_ALIGNED(rounded_bytes, 8); // `bytes` is 16-byte aligned, red zone is 8-byte aligned.
414 uintptr_t padding =
415 ((reinterpret_cast<uintptr_t>(ptr_) + 15u) & 15u) - reinterpret_cast<uintptr_t>(ptr_);
416 ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind);
417 uint8_t* ret;
418 if (UNLIKELY(padding + rounded_bytes > static_cast<size_t>(end_ - ptr_))) {
419 static_assert(kArenaAlignment >= 16, "Expecting sufficient alignment for new Arena.");
420 ret = AllocFromNewArenaWithMemoryTool(rounded_bytes);
421 } else {
422 ptr_ += padding; // Leave padding inaccessible.
423 ret = ptr_;
424 ptr_ += rounded_bytes;
425 }
426 MEMORY_TOOL_MAKE_DEFINED(ret, bytes);
427 // Check that the memory is already zeroed out.
428 DCHECK(std::all_of(ret, ret + bytes, [](uint8_t val) { return val == 0u; }));
429 return ret;
430 }
431
~ArenaAllocator()432 ArenaAllocator::~ArenaAllocator() {
433 // Reclaim all the arenas by giving them back to the thread pool.
434 UpdateBytesAllocated();
435 pool_->FreeArenaChain(arena_head_);
436 }
437
AllocFromNewArena(size_t bytes)438 uint8_t* ArenaAllocator::AllocFromNewArena(size_t bytes) {
439 Arena* new_arena = pool_->AllocArena(std::max(Arena::kDefaultSize, bytes));
440 DCHECK(new_arena != nullptr);
441 DCHECK_LE(bytes, new_arena->Size());
442 if (static_cast<size_t>(end_ - ptr_) > new_arena->Size() - bytes) {
443 // The old arena has more space remaining than the new one, so keep using it.
444 // This can happen when the requested size is over half of the default size.
445 DCHECK(arena_head_ != nullptr);
446 new_arena->bytes_allocated_ = bytes; // UpdateBytesAllocated() on the new_arena.
447 new_arena->next_ = arena_head_->next_;
448 arena_head_->next_ = new_arena;
449 } else {
450 UpdateBytesAllocated();
451 new_arena->next_ = arena_head_;
452 arena_head_ = new_arena;
453 // Update our internal data structures.
454 begin_ = new_arena->Begin();
455 DCHECK_ALIGNED(begin_, kAlignment);
456 ptr_ = begin_ + bytes;
457 end_ = new_arena->End();
458 }
459 return new_arena->Begin();
460 }
461
AllocFromNewArenaWithMemoryTool(size_t bytes)462 uint8_t* ArenaAllocator::AllocFromNewArenaWithMemoryTool(size_t bytes) {
463 uint8_t* ret = AllocFromNewArena(bytes);
464 uint8_t* noaccess_begin = ret + bytes;
465 uint8_t* noaccess_end;
466 if (ret == arena_head_->Begin()) {
467 DCHECK(ptr_ - bytes == ret);
468 noaccess_end = end_;
469 } else {
470 // We're still using the old arena but `ret` comes from a new one just after it.
471 DCHECK(arena_head_->next_ != nullptr);
472 DCHECK(ret == arena_head_->next_->Begin());
473 DCHECK_EQ(bytes, arena_head_->next_->GetBytesAllocated());
474 noaccess_end = arena_head_->next_->End();
475 }
476 MEMORY_TOOL_MAKE_NOACCESS(noaccess_begin, noaccess_end - noaccess_begin);
477 return ret;
478 }
479
Contains(const void * ptr) const480 bool ArenaAllocator::Contains(const void* ptr) const {
481 if (ptr >= begin_ && ptr < end_) {
482 return true;
483 }
484 for (const Arena* cur_arena = arena_head_; cur_arena != nullptr; cur_arena = cur_arena->next_) {
485 if (cur_arena->Contains(ptr)) {
486 return true;
487 }
488 }
489 return false;
490 }
491
MemStats(const char * name,const ArenaAllocatorStats * stats,const Arena * first_arena,ssize_t lost_bytes_adjustment)492 MemStats::MemStats(const char* name,
493 const ArenaAllocatorStats* stats,
494 const Arena* first_arena,
495 ssize_t lost_bytes_adjustment)
496 : name_(name),
497 stats_(stats),
498 first_arena_(first_arena),
499 lost_bytes_adjustment_(lost_bytes_adjustment) {
500 }
501
Dump(std::ostream & os) const502 void MemStats::Dump(std::ostream& os) const {
503 os << name_ << " stats:\n";
504 stats_->Dump(os, first_arena_, lost_bytes_adjustment_);
505 }
506
507 // Dump memory usage stats.
GetMemStats() const508 MemStats ArenaAllocator::GetMemStats() const {
509 ssize_t lost_bytes_adjustment =
510 (arena_head_ == nullptr) ? 0 : (end_ - ptr_) - arena_head_->RemainingSpace();
511 return MemStats("ArenaAllocator", this, arena_head_, lost_bytes_adjustment);
512 }
513
514 } // namespace art
515