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 "bump_pointer_space.h"
18 #include "bump_pointer_space-inl.h"
19 #include "mirror/class-inl.h"
20 #include "mirror/object-inl.h"
21 #include "thread_list.h"
22 
23 namespace art {
24 namespace gc {
25 namespace space {
26 
Create(const std::string & name,size_t capacity)27 BumpPointerSpace* BumpPointerSpace::Create(const std::string& name, size_t capacity) {
28   capacity = RoundUp(capacity, kPageSize);
29   std::string error_msg;
30   MemMap mem_map = MemMap::MapAnonymous(name.c_str(),
31                                         capacity,
32                                         PROT_READ | PROT_WRITE,
33                                         /*low_4gb=*/ true,
34                                         &error_msg);
35   if (!mem_map.IsValid()) {
36     LOG(ERROR) << "Failed to allocate pages for alloc space (" << name << ") of size "
37         << PrettySize(capacity) << " with message " << error_msg;
38     return nullptr;
39   }
40   return new BumpPointerSpace(name, std::move(mem_map));
41 }
42 
CreateFromMemMap(const std::string & name,MemMap && mem_map)43 BumpPointerSpace* BumpPointerSpace::CreateFromMemMap(const std::string& name, MemMap&& mem_map) {
44   return new BumpPointerSpace(name, std::move(mem_map));
45 }
46 
BumpPointerSpace(const std::string & name,uint8_t * begin,uint8_t * limit)47 BumpPointerSpace::BumpPointerSpace(const std::string& name, uint8_t* begin, uint8_t* limit)
48     : ContinuousMemMapAllocSpace(name,
49                                  MemMap::Invalid(),
50                                  begin,
51                                  begin,
52                                  limit,
53                                  kGcRetentionPolicyAlwaysCollect),
54       growth_end_(limit),
55       objects_allocated_(0), bytes_allocated_(0),
56       block_lock_("Block lock"),
57       main_block_size_(0),
58       num_blocks_(0) {
59 }
60 
BumpPointerSpace(const std::string & name,MemMap && mem_map)61 BumpPointerSpace::BumpPointerSpace(const std::string& name, MemMap&& mem_map)
62     : ContinuousMemMapAllocSpace(name,
63                                  std::move(mem_map),
64                                  mem_map.Begin(),
65                                  mem_map.Begin(),
66                                  mem_map.End(),
67                                  kGcRetentionPolicyAlwaysCollect),
68       growth_end_(mem_map_.End()),
69       objects_allocated_(0), bytes_allocated_(0),
70       block_lock_("Block lock", kBumpPointerSpaceBlockLock),
71       main_block_size_(0),
72       num_blocks_(0) {
73 }
74 
Clear()75 void BumpPointerSpace::Clear() {
76   // Release the pages back to the operating system.
77   if (!kMadviseZeroes) {
78     memset(Begin(), 0, Limit() - Begin());
79   }
80   CHECK_NE(madvise(Begin(), Limit() - Begin(), MADV_DONTNEED), -1) << "madvise failed";
81   // Reset the end of the space back to the beginning, we move the end forward as we allocate
82   // objects.
83   SetEnd(Begin());
84   objects_allocated_.store(0, std::memory_order_relaxed);
85   bytes_allocated_.store(0, std::memory_order_relaxed);
86   growth_end_ = Limit();
87   {
88     MutexLock mu(Thread::Current(), block_lock_);
89     num_blocks_ = 0;
90     main_block_size_ = 0;
91   }
92 }
93 
Dump(std::ostream & os) const94 void BumpPointerSpace::Dump(std::ostream& os) const {
95   os << GetName() << " "
96       << reinterpret_cast<void*>(Begin()) << "-" << reinterpret_cast<void*>(End()) << " - "
97       << reinterpret_cast<void*>(Limit());
98 }
99 
GetNextObject(mirror::Object * obj)100 mirror::Object* BumpPointerSpace::GetNextObject(mirror::Object* obj) {
101   const uintptr_t position = reinterpret_cast<uintptr_t>(obj) + obj->SizeOf();
102   return reinterpret_cast<mirror::Object*>(RoundUp(position, kAlignment));
103 }
104 
RevokeThreadLocalBuffers(Thread * thread)105 size_t BumpPointerSpace::RevokeThreadLocalBuffers(Thread* thread) {
106   MutexLock mu(Thread::Current(), block_lock_);
107   RevokeThreadLocalBuffersLocked(thread);
108   return 0U;
109 }
110 
RevokeAllThreadLocalBuffers()111 size_t BumpPointerSpace::RevokeAllThreadLocalBuffers() {
112   Thread* self = Thread::Current();
113   MutexLock mu(self, *Locks::runtime_shutdown_lock_);
114   MutexLock mu2(self, *Locks::thread_list_lock_);
115   // TODO: Not do a copy of the thread list?
116   std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
117   for (Thread* thread : thread_list) {
118     RevokeThreadLocalBuffers(thread);
119   }
120   return 0U;
121 }
122 
AssertThreadLocalBuffersAreRevoked(Thread * thread)123 void BumpPointerSpace::AssertThreadLocalBuffersAreRevoked(Thread* thread) {
124   if (kIsDebugBuild) {
125     MutexLock mu(Thread::Current(), block_lock_);
126     DCHECK(!thread->HasTlab());
127   }
128 }
129 
AssertAllThreadLocalBuffersAreRevoked()130 void BumpPointerSpace::AssertAllThreadLocalBuffersAreRevoked() {
131   if (kIsDebugBuild) {
132     Thread* self = Thread::Current();
133     MutexLock mu(self, *Locks::runtime_shutdown_lock_);
134     MutexLock mu2(self, *Locks::thread_list_lock_);
135     // TODO: Not do a copy of the thread list?
136     std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
137     for (Thread* thread : thread_list) {
138       AssertThreadLocalBuffersAreRevoked(thread);
139     }
140   }
141 }
142 
UpdateMainBlock()143 void BumpPointerSpace::UpdateMainBlock() {
144   DCHECK_EQ(num_blocks_, 0U);
145   main_block_size_ = Size();
146 }
147 
148 // Returns the start of the storage.
AllocBlock(size_t bytes)149 uint8_t* BumpPointerSpace::AllocBlock(size_t bytes) {
150   bytes = RoundUp(bytes, kAlignment);
151   if (!num_blocks_) {
152     UpdateMainBlock();
153   }
154   uint8_t* storage = reinterpret_cast<uint8_t*>(
155       AllocNonvirtualWithoutAccounting(bytes + sizeof(BlockHeader)));
156   if (LIKELY(storage != nullptr)) {
157     BlockHeader* header = reinterpret_cast<BlockHeader*>(storage);
158     header->size_ = bytes;  // Write out the block header.
159     storage += sizeof(BlockHeader);
160     ++num_blocks_;
161   }
162   return storage;
163 }
164 
GetSweepCallback()165 accounting::ContinuousSpaceBitmap::SweepCallback* BumpPointerSpace::GetSweepCallback() {
166   UNIMPLEMENTED(FATAL);
167   UNREACHABLE();
168 }
169 
GetBytesAllocated()170 uint64_t BumpPointerSpace::GetBytesAllocated() {
171   // Start out pre-determined amount (blocks which are not being allocated into).
172   uint64_t total = static_cast<uint64_t>(bytes_allocated_.load(std::memory_order_relaxed));
173   Thread* self = Thread::Current();
174   MutexLock mu(self, *Locks::runtime_shutdown_lock_);
175   MutexLock mu2(self, *Locks::thread_list_lock_);
176   std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
177   MutexLock mu3(Thread::Current(), block_lock_);
178   // If we don't have any blocks, we don't have any thread local buffers. This check is required
179   // since there can exist multiple bump pointer spaces which exist at the same time.
180   if (num_blocks_ > 0) {
181     for (Thread* thread : thread_list) {
182       total += thread->GetThreadLocalBytesAllocated();
183     }
184   }
185   return total;
186 }
187 
GetObjectsAllocated()188 uint64_t BumpPointerSpace::GetObjectsAllocated() {
189   // Start out pre-determined amount (blocks which are not being allocated into).
190   uint64_t total = static_cast<uint64_t>(objects_allocated_.load(std::memory_order_relaxed));
191   Thread* self = Thread::Current();
192   MutexLock mu(self, *Locks::runtime_shutdown_lock_);
193   MutexLock mu2(self, *Locks::thread_list_lock_);
194   std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
195   MutexLock mu3(Thread::Current(), block_lock_);
196   // If we don't have any blocks, we don't have any thread local buffers. This check is required
197   // since there can exist multiple bump pointer spaces which exist at the same time.
198   if (num_blocks_ > 0) {
199     for (Thread* thread : thread_list) {
200       total += thread->GetThreadLocalObjectsAllocated();
201     }
202   }
203   return total;
204 }
205 
RevokeThreadLocalBuffersLocked(Thread * thread)206 void BumpPointerSpace::RevokeThreadLocalBuffersLocked(Thread* thread) {
207   objects_allocated_.fetch_add(thread->GetThreadLocalObjectsAllocated(), std::memory_order_relaxed);
208   bytes_allocated_.fetch_add(thread->GetThreadLocalBytesAllocated(), std::memory_order_relaxed);
209   thread->ResetTlab();
210 }
211 
AllocNewTlab(Thread * self,size_t bytes)212 bool BumpPointerSpace::AllocNewTlab(Thread* self, size_t bytes) {
213   MutexLock mu(Thread::Current(), block_lock_);
214   RevokeThreadLocalBuffersLocked(self);
215   uint8_t* start = AllocBlock(bytes);
216   if (start == nullptr) {
217     return false;
218   }
219   self->SetTlab(start, start + bytes, start + bytes);
220   return true;
221 }
222 
LogFragmentationAllocFailure(std::ostream & os,size_t)223 void BumpPointerSpace::LogFragmentationAllocFailure(std::ostream& os,
224                                                     size_t /* failed_alloc_bytes */) {
225   size_t max_contiguous_allocation = Limit() - End();
226   os << "; failed due to fragmentation (largest possible contiguous allocation "
227      <<  max_contiguous_allocation << " bytes)";
228   // Caller's job to print failed_alloc_bytes.
229 }
230 
AllocationSizeNonvirtual(mirror::Object * obj,size_t * usable_size)231 size_t BumpPointerSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable_size) {
232   size_t num_bytes = obj->SizeOf();
233   if (usable_size != nullptr) {
234     *usable_size = RoundUp(num_bytes, kAlignment);
235   }
236   return num_bytes;
237 }
238 
239 }  // namespace space
240 }  // namespace gc
241 }  // namespace art
242