• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 <ostream>
19 
20 #include "compiled_method_storage.h"
21 
22 #include <android-base/logging.h>
23 
24 #include "base/data_hash.h"
25 #include "base/utils.h"
26 #include "compiled_method.h"
27 #include "linker/linker_patch.h"
28 #include "thread-current-inl.h"
29 #include "utils/dedupe_set-inl.h"
30 #include "utils/swap_space.h"
31 
32 namespace art {
33 
34 namespace {  // anonymous namespace
35 
36 template <typename T>
CopyArray(SwapSpace * swap_space,const ArrayRef<const T> & array)37 const LengthPrefixedArray<T>* CopyArray(SwapSpace* swap_space, const ArrayRef<const T>& array) {
38   DCHECK(!array.empty());
39   SwapAllocator<uint8_t> allocator(swap_space);
40   void* storage = allocator.allocate(LengthPrefixedArray<T>::ComputeSize(array.size()));
41   LengthPrefixedArray<T>* array_copy = new(storage) LengthPrefixedArray<T>(array.size());
42   std::copy(array.begin(), array.end(), array_copy->begin());
43   return array_copy;
44 }
45 
46 template <typename T>
ReleaseArray(SwapSpace * swap_space,const LengthPrefixedArray<T> * array)47 void ReleaseArray(SwapSpace* swap_space, const LengthPrefixedArray<T>* array) {
48   SwapAllocator<uint8_t> allocator(swap_space);
49   size_t size = LengthPrefixedArray<T>::ComputeSize(array->size());
50   array->~LengthPrefixedArray<T>();
51   allocator.deallocate(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(array)), size);
52 }
53 
54 }  // anonymous namespace
55 
56 template <typename T, typename DedupeSetType>
AllocateOrDeduplicateArray(const ArrayRef<const T> & data,DedupeSetType * dedupe_set)57 inline const LengthPrefixedArray<T>* CompiledMethodStorage::AllocateOrDeduplicateArray(
58     const ArrayRef<const T>& data,
59     DedupeSetType* dedupe_set) {
60   if (data.empty()) {
61     return nullptr;
62   } else if (!DedupeEnabled()) {
63     return CopyArray(swap_space_.get(), data);
64   } else {
65     return dedupe_set->Add(Thread::Current(), data);
66   }
67 }
68 
69 template <typename T>
ReleaseArrayIfNotDeduplicated(const LengthPrefixedArray<T> * array)70 inline void CompiledMethodStorage::ReleaseArrayIfNotDeduplicated(
71     const LengthPrefixedArray<T>* array) {
72   if (array != nullptr && !DedupeEnabled()) {
73     ReleaseArray(swap_space_.get(), array);
74   }
75 }
76 
77 template <typename ContentType>
78 class CompiledMethodStorage::DedupeHashFunc {
79  private:
80   static constexpr bool kUseMurmur3Hash = true;
81 
82  public:
operator ()(const ArrayRef<ContentType> & array) const83   size_t operator()(const ArrayRef<ContentType>& array) const {
84     return DataHash()(array);
85   }
86 };
87 
88 template <typename T>
89 class CompiledMethodStorage::LengthPrefixedArrayAlloc {
90  public:
LengthPrefixedArrayAlloc(SwapSpace * swap_space)91   explicit LengthPrefixedArrayAlloc(SwapSpace* swap_space)
92       : swap_space_(swap_space) {
93   }
94 
Copy(const ArrayRef<const T> & array)95   const LengthPrefixedArray<T>* Copy(const ArrayRef<const T>& array) {
96     return CopyArray(swap_space_, array);
97   }
98 
Destroy(const LengthPrefixedArray<T> * array)99   void Destroy(const LengthPrefixedArray<T>* array) {
100     ReleaseArray(swap_space_, array);
101   }
102 
103  private:
104   SwapSpace* const swap_space_;
105 };
106 
107 class CompiledMethodStorage::ThunkMapKey {
108  public:
ThunkMapKey(linker::LinkerPatch::Type type,uint32_t custom_value1,uint32_t custom_value2)109   ThunkMapKey(linker::LinkerPatch::Type type, uint32_t custom_value1, uint32_t custom_value2)
110       : type_(type), custom_value1_(custom_value1), custom_value2_(custom_value2) {}
111 
operator <(const ThunkMapKey & other) const112   bool operator<(const ThunkMapKey& other) const {
113     if (custom_value1_ != other.custom_value1_) {
114       return custom_value1_ < other.custom_value1_;
115     }
116     if (custom_value2_ != other.custom_value2_) {
117       return custom_value2_ < other.custom_value2_;
118     }
119     return type_ < other.type_;
120   }
121 
122  private:
123   linker::LinkerPatch::Type type_;
124   uint32_t custom_value1_;
125   uint32_t custom_value2_;
126 };
127 
128 class CompiledMethodStorage::ThunkMapValue {
129  public:
ThunkMapValue(std::vector<uint8_t,SwapAllocator<uint8_t>> && code,const std::string & debug_name)130   ThunkMapValue(std::vector<uint8_t, SwapAllocator<uint8_t>>&& code,
131                 const std::string& debug_name)
132       : code_(std::move(code)), debug_name_(debug_name) {}
133 
GetCode() const134   ArrayRef<const uint8_t> GetCode() const {
135     return ArrayRef<const uint8_t>(code_);
136   }
137 
GetDebugName() const138   const std::string& GetDebugName() const {
139     return debug_name_;
140   }
141 
142  private:
143   std::vector<uint8_t, SwapAllocator<uint8_t>> code_;
144   std::string debug_name_;
145 };
146 
CompiledMethodStorage(int swap_fd)147 CompiledMethodStorage::CompiledMethodStorage(int swap_fd)
148     : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
149       dedupe_enabled_(true),
150       dedupe_code_("dedupe code", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
151       dedupe_vmap_table_("dedupe vmap table",
152                          LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
153       dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
154       dedupe_linker_patches_("dedupe cfi info",
155                              LengthPrefixedArrayAlloc<linker::LinkerPatch>(swap_space_.get())),
156       thunk_map_lock_("thunk_map_lock"),
157       thunk_map_(std::less<ThunkMapKey>(), SwapAllocator<ThunkMapValueType>(swap_space_.get())) {
158 }
159 
~CompiledMethodStorage()160 CompiledMethodStorage::~CompiledMethodStorage() {
161   // All done by member destructors.
162 }
163 
DumpMemoryUsage(std::ostream & os,bool extended) const164 void CompiledMethodStorage::DumpMemoryUsage(std::ostream& os, bool extended) const {
165   if (swap_space_.get() != nullptr) {
166     const size_t swap_size = swap_space_->GetSize();
167     os << " swap=" << PrettySize(swap_size) << " (" << swap_size << "B)";
168   }
169   if (extended) {
170     Thread* self = Thread::Current();
171     os << "\nCode dedupe: " << dedupe_code_.DumpStats(self);
172     os << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(self);
173     os << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(self);
174   }
175 }
176 
DeduplicateCode(const ArrayRef<const uint8_t> & code)177 const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCode(
178     const ArrayRef<const uint8_t>& code) {
179   return AllocateOrDeduplicateArray(code, &dedupe_code_);
180 }
181 
ReleaseCode(const LengthPrefixedArray<uint8_t> * code)182 void CompiledMethodStorage::ReleaseCode(const LengthPrefixedArray<uint8_t>* code) {
183   ReleaseArrayIfNotDeduplicated(code);
184 }
185 
UniqueCodeEntries() const186 size_t CompiledMethodStorage::UniqueCodeEntries() const {
187   DCHECK(DedupeEnabled());
188   return dedupe_code_.Size(Thread::Current());
189 }
190 
DeduplicateVMapTable(const ArrayRef<const uint8_t> & table)191 const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateVMapTable(
192     const ArrayRef<const uint8_t>& table) {
193   return AllocateOrDeduplicateArray(table, &dedupe_vmap_table_);
194 }
195 
ReleaseVMapTable(const LengthPrefixedArray<uint8_t> * table)196 void CompiledMethodStorage::ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table) {
197   ReleaseArrayIfNotDeduplicated(table);
198 }
199 
UniqueVMapTableEntries() const200 size_t CompiledMethodStorage::UniqueVMapTableEntries() const {
201   DCHECK(DedupeEnabled());
202   return dedupe_vmap_table_.Size(Thread::Current());
203 }
204 
DeduplicateCFIInfo(const ArrayRef<const uint8_t> & cfi_info)205 const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCFIInfo(
206     const ArrayRef<const uint8_t>& cfi_info) {
207   return AllocateOrDeduplicateArray(cfi_info, &dedupe_cfi_info_);
208 }
209 
ReleaseCFIInfo(const LengthPrefixedArray<uint8_t> * cfi_info)210 void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info) {
211   ReleaseArrayIfNotDeduplicated(cfi_info);
212 }
213 
UniqueCFIInfoEntries() const214 size_t CompiledMethodStorage::UniqueCFIInfoEntries() const {
215   DCHECK(DedupeEnabled());
216   return dedupe_cfi_info_.Size(Thread::Current());
217 }
218 
DeduplicateLinkerPatches(const ArrayRef<const linker::LinkerPatch> & linker_patches)219 const LengthPrefixedArray<linker::LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches(
220     const ArrayRef<const linker::LinkerPatch>& linker_patches) {
221   return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_);
222 }
223 
ReleaseLinkerPatches(const LengthPrefixedArray<linker::LinkerPatch> * linker_patches)224 void CompiledMethodStorage::ReleaseLinkerPatches(
225     const LengthPrefixedArray<linker::LinkerPatch>* linker_patches) {
226   ReleaseArrayIfNotDeduplicated(linker_patches);
227 }
228 
UniqueLinkerPatchesEntries() const229 size_t CompiledMethodStorage::UniqueLinkerPatchesEntries() const {
230   DCHECK(DedupeEnabled());
231   return dedupe_linker_patches_.Size(Thread::Current());
232 }
233 
GetThunkMapKey(const linker::LinkerPatch & linker_patch)234 CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey(
235     const linker::LinkerPatch& linker_patch) {
236   uint32_t custom_value1 = 0u;
237   uint32_t custom_value2 = 0u;
238   switch (linker_patch.GetType()) {
239     case linker::LinkerPatch::Type::kCallEntrypoint:
240       custom_value1 = linker_patch.EntrypointOffset();
241       break;
242     case linker::LinkerPatch::Type::kBakerReadBarrierBranch:
243       custom_value1 = linker_patch.GetBakerCustomValue1();
244       custom_value2 = linker_patch.GetBakerCustomValue2();
245       break;
246     case linker::LinkerPatch::Type::kCallRelative:
247       // No custom values.
248       break;
249     default:
250       LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType();
251       UNREACHABLE();
252   }
253   return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2);
254 }
255 
CreateCompiledMethod(InstructionSet instruction_set,ArrayRef<const uint8_t> code,ArrayRef<const uint8_t> stack_map,ArrayRef<const uint8_t> cfi,ArrayRef<const linker::LinkerPatch> patches,bool is_intrinsic)256 CompiledMethod* CompiledMethodStorage::CreateCompiledMethod(
257     InstructionSet instruction_set,
258     ArrayRef<const uint8_t> code,
259     ArrayRef<const uint8_t> stack_map,
260     ArrayRef<const uint8_t> cfi,
261     ArrayRef<const linker::LinkerPatch> patches,
262     bool is_intrinsic) {
263   CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod(
264       this, instruction_set, code, stack_map, cfi, patches);
265   if (is_intrinsic) {
266     compiled_method->MarkAsIntrinsic();
267   }
268   return compiled_method;
269 }
270 
GetThunkCode(const linker::LinkerPatch & linker_patch,std::string * debug_name)271 ArrayRef<const uint8_t> CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch,
272                                                             /*out*/ std::string* debug_name) {
273   ThunkMapKey key = GetThunkMapKey(linker_patch);
274   MutexLock lock(Thread::Current(), thunk_map_lock_);
275   auto it = thunk_map_.find(key);
276   if (it != thunk_map_.end()) {
277     const ThunkMapValue& value = it->second;
278     if (debug_name != nullptr) {
279       *debug_name = value.GetDebugName();
280     }
281     return value.GetCode();
282   } else {
283     if (debug_name != nullptr) {
284       *debug_name = std::string();
285     }
286     return ArrayRef<const uint8_t>();
287   }
288 }
289 
SetThunkCode(const linker::LinkerPatch & linker_patch,ArrayRef<const uint8_t> code,const std::string & debug_name)290 void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch,
291                                          ArrayRef<const uint8_t> code,
292                                          const std::string& debug_name) {
293   DCHECK(!code.empty());
294   ThunkMapKey key = GetThunkMapKey(linker_patch);
295   std::vector<uint8_t, SwapAllocator<uint8_t>> code_copy(
296       code.begin(), code.end(), SwapAllocator<uint8_t>(swap_space_.get()));
297   ThunkMapValue value(std::move(code_copy), debug_name);
298   MutexLock lock(Thread::Current(), thunk_map_lock_);
299   // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry.
300   thunk_map_.emplace(key, std::move(value));
301 }
302 
303 }  // namespace art
304