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