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 <numeric>
19 
20 #include "arena_allocator.h"
21 #include "base/logging.h"
22 #include "base/mutex.h"
23 #include "thread-inl.h"
24 #include <memcheck/memcheck.h>
25 
26 namespace art {
27 
28 // Memmap is a bit slower than malloc according to my measurements.
29 static constexpr bool kUseMemMap = false;
30 static constexpr bool kUseMemSet = true && kUseMemMap;
31 static constexpr size_t kValgrindRedZoneBytes = 8;
32 constexpr size_t Arena::kDefaultSize;
33 
34 template <bool kCount>
35 const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = {
36   "Misc       ",
37   "BasicBlock ",
38   "LIR        ",
39   "LIR masks  ",
40   "MIR        ",
41   "DataFlow   ",
42   "GrowList   ",
43   "GrowBitMap ",
44   "Dalvik2SSA ",
45   "DebugInfo  ",
46   "Successor  ",
47   "RegAlloc   ",
48   "Data       ",
49   "Preds      ",
50   "STL        ",
51 };
52 
53 template <bool kCount>
ArenaAllocatorStatsImpl()54 ArenaAllocatorStatsImpl<kCount>::ArenaAllocatorStatsImpl()
55     : num_allocations_(0u) {
56   std::fill_n(alloc_stats_, arraysize(alloc_stats_), 0u);
57 }
58 
59 template <bool kCount>
Copy(const ArenaAllocatorStatsImpl & other)60 void ArenaAllocatorStatsImpl<kCount>::Copy(const ArenaAllocatorStatsImpl& other) {
61   num_allocations_ = other.num_allocations_;
62   std::copy(other.alloc_stats_, other.alloc_stats_ + arraysize(alloc_stats_), alloc_stats_);
63 }
64 
65 template <bool kCount>
RecordAlloc(size_t bytes,ArenaAllocKind kind)66 void ArenaAllocatorStatsImpl<kCount>::RecordAlloc(size_t bytes, ArenaAllocKind kind) {
67   alloc_stats_[kind] += bytes;
68   ++num_allocations_;
69 }
70 
71 template <bool kCount>
NumAllocations() const72 size_t ArenaAllocatorStatsImpl<kCount>::NumAllocations() const {
73   return num_allocations_;
74 }
75 
76 template <bool kCount>
BytesAllocated() const77 size_t ArenaAllocatorStatsImpl<kCount>::BytesAllocated() const {
78   const size_t init = 0u;  // Initial value of the correct type.
79   return std::accumulate(alloc_stats_, alloc_stats_ + arraysize(alloc_stats_), init);
80 }
81 
82 template <bool kCount>
Dump(std::ostream & os,const Arena * first,ssize_t lost_bytes_adjustment) const83 void ArenaAllocatorStatsImpl<kCount>::Dump(std::ostream& os, const Arena* first,
84                                            ssize_t lost_bytes_adjustment) const {
85   size_t malloc_bytes = 0u;
86   size_t lost_bytes = 0u;
87   size_t num_arenas = 0u;
88   for (const Arena* arena = first; arena != nullptr; arena = arena->next_) {
89     malloc_bytes += arena->Size();
90     lost_bytes += arena->RemainingSpace();
91     ++num_arenas;
92   }
93   // The lost_bytes_adjustment is used to make up for the fact that the current arena
94   // may not have the bytes_allocated_ updated correctly.
95   lost_bytes += lost_bytes_adjustment;
96   const size_t bytes_allocated = BytesAllocated();
97   os << " MEM: used: " << bytes_allocated << ", allocated: " << malloc_bytes
98      << ", lost: " << lost_bytes << "\n";
99   size_t num_allocations = NumAllocations();
100   if (num_allocations != 0) {
101     os << "Number of arenas allocated: " << num_arenas << ", Number of allocations: "
102        << num_allocations << ", avg size: " << bytes_allocated / num_allocations << "\n";
103   }
104   os << "===== Allocation by kind\n";
105   COMPILE_ASSERT(arraysize(kAllocNames) == kNumArenaAllocKinds, check_arraysize_kAllocNames);
106   for (int i = 0; i < kNumArenaAllocKinds; i++) {
107       os << kAllocNames[i] << std::setw(10) << alloc_stats_[i] << "\n";
108   }
109 }
110 
111 // Explicitly instantiate the used implementation.
112 template class ArenaAllocatorStatsImpl<kArenaAllocatorCountAllocations>;
113 
Arena(size_t size)114 Arena::Arena(size_t size)
115     : bytes_allocated_(0),
116       map_(nullptr),
117       next_(nullptr) {
118   if (kUseMemMap) {
119     std::string error_msg;
120     map_ = MemMap::MapAnonymous("dalvik-arena", NULL, size, PROT_READ | PROT_WRITE, false,
121                                 &error_msg);
122     CHECK(map_ != nullptr) << error_msg;
123     memory_ = map_->Begin();
124     size_ = map_->Size();
125   } else {
126     memory_ = reinterpret_cast<uint8_t*>(calloc(1, size));
127     size_ = size;
128   }
129 }
130 
~Arena()131 Arena::~Arena() {
132   if (kUseMemMap) {
133     delete map_;
134   } else {
135     free(reinterpret_cast<void*>(memory_));
136   }
137 }
138 
Reset()139 void Arena::Reset() {
140   if (bytes_allocated_) {
141     if (kUseMemSet || !kUseMemMap) {
142       memset(Begin(), 0, bytes_allocated_);
143     } else {
144       map_->MadviseDontNeedAndZero();
145     }
146     bytes_allocated_ = 0;
147   }
148 }
149 
ArenaPool()150 ArenaPool::ArenaPool()
151     : lock_("Arena pool lock"),
152       free_arenas_(nullptr) {
153 }
154 
~ArenaPool()155 ArenaPool::~ArenaPool() {
156   while (free_arenas_ != nullptr) {
157     auto* arena = free_arenas_;
158     free_arenas_ = free_arenas_->next_;
159     delete arena;
160   }
161 }
162 
AllocArena(size_t size)163 Arena* ArenaPool::AllocArena(size_t size) {
164   Thread* self = Thread::Current();
165   Arena* ret = nullptr;
166   {
167     MutexLock lock(self, lock_);
168     if (free_arenas_ != nullptr && LIKELY(free_arenas_->Size() >= size)) {
169       ret = free_arenas_;
170       free_arenas_ = free_arenas_->next_;
171     }
172   }
173   if (ret == nullptr) {
174     ret = new Arena(size);
175   }
176   ret->Reset();
177   return ret;
178 }
179 
GetBytesAllocated() const180 size_t ArenaPool::GetBytesAllocated() const {
181   size_t total = 0;
182   MutexLock lock(Thread::Current(), lock_);
183   for (Arena* arena = free_arenas_; arena != nullptr; arena = arena->next_) {
184     total += arena->GetBytesAllocated();
185   }
186   return total;
187 }
188 
FreeArenaChain(Arena * first)189 void ArenaPool::FreeArenaChain(Arena* first) {
190   if (UNLIKELY(RUNNING_ON_VALGRIND > 0)) {
191     for (Arena* arena = first; arena != nullptr; arena = arena->next_) {
192       VALGRIND_MAKE_MEM_UNDEFINED(arena->memory_, arena->bytes_allocated_);
193     }
194   }
195   if (first != nullptr) {
196     Arena* last = first;
197     while (last->next_ != nullptr) {
198       last = last->next_;
199     }
200     Thread* self = Thread::Current();
201     MutexLock lock(self, lock_);
202     last->next_ = free_arenas_;
203     free_arenas_ = first;
204   }
205 }
206 
BytesAllocated() const207 size_t ArenaAllocator::BytesAllocated() const {
208   return ArenaAllocatorStats::BytesAllocated();
209 }
210 
ArenaAllocator(ArenaPool * pool)211 ArenaAllocator::ArenaAllocator(ArenaPool* pool)
212   : pool_(pool),
213     begin_(nullptr),
214     end_(nullptr),
215     ptr_(nullptr),
216     arena_head_(nullptr),
217     running_on_valgrind_(RUNNING_ON_VALGRIND > 0) {
218 }
219 
UpdateBytesAllocated()220 void ArenaAllocator::UpdateBytesAllocated() {
221   if (arena_head_ != nullptr) {
222     // Update how many bytes we have allocated into the arena so that the arena pool knows how
223     // much memory to zero out.
224     arena_head_->bytes_allocated_ = ptr_ - begin_;
225   }
226 }
227 
AllocValgrind(size_t bytes,ArenaAllocKind kind)228 void* ArenaAllocator::AllocValgrind(size_t bytes, ArenaAllocKind kind) {
229   size_t rounded_bytes = RoundUp(bytes + kValgrindRedZoneBytes, 8);
230   if (UNLIKELY(ptr_ + rounded_bytes > end_)) {
231     // Obtain a new block.
232     ObtainNewArenaForAllocation(rounded_bytes);
233     if (UNLIKELY(ptr_ == nullptr)) {
234       return nullptr;
235     }
236   }
237   ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind);
238   uint8_t* ret = ptr_;
239   ptr_ += rounded_bytes;
240   // Check that the memory is already zeroed out.
241   for (uint8_t* ptr = ret; ptr < ptr_; ++ptr) {
242     CHECK_EQ(*ptr, 0U);
243   }
244   VALGRIND_MAKE_MEM_NOACCESS(ret + bytes, rounded_bytes - bytes);
245   return ret;
246 }
247 
~ArenaAllocator()248 ArenaAllocator::~ArenaAllocator() {
249   // Reclaim all the arenas by giving them back to the thread pool.
250   UpdateBytesAllocated();
251   pool_->FreeArenaChain(arena_head_);
252 }
253 
ObtainNewArenaForAllocation(size_t allocation_size)254 void ArenaAllocator::ObtainNewArenaForAllocation(size_t allocation_size) {
255   UpdateBytesAllocated();
256   Arena* new_arena = pool_->AllocArena(std::max(Arena::kDefaultSize, allocation_size));
257   new_arena->next_ = arena_head_;
258   arena_head_ = new_arena;
259   // Update our internal data structures.
260   ptr_ = begin_ = new_arena->Begin();
261   end_ = new_arena->End();
262 }
263 
MemStats(const char * name,const ArenaAllocatorStats * stats,const Arena * first_arena,ssize_t lost_bytes_adjustment)264 MemStats::MemStats(const char* name, const ArenaAllocatorStats* stats, const Arena* first_arena,
265                    ssize_t lost_bytes_adjustment)
266     : name_(name),
267       stats_(stats),
268       first_arena_(first_arena),
269       lost_bytes_adjustment_(lost_bytes_adjustment) {
270 }
271 
Dump(std::ostream & os) const272 void MemStats::Dump(std::ostream& os) const {
273   os << name_ << " stats:\n";
274   stats_->Dump(os, first_arena_, lost_bytes_adjustment_);
275 }
276 
277 // Dump memory usage stats.
GetMemStats() const278 MemStats ArenaAllocator::GetMemStats() const {
279   ssize_t lost_bytes_adjustment =
280       (arena_head_ == nullptr) ? 0 : (end_ - ptr_) - arena_head_->RemainingSpace();
281   return MemStats("ArenaAllocator", this, arena_head_, lost_bytes_adjustment);
282 }
283 
284 }  // namespace art
285