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