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 
DeduplicateVMapTable(const ArrayRef<const uint8_t> & table)186 const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateVMapTable(
187     const ArrayRef<const uint8_t>& table) {
188   return AllocateOrDeduplicateArray(table, &dedupe_vmap_table_);
189 }
190 
ReleaseVMapTable(const LengthPrefixedArray<uint8_t> * table)191 void CompiledMethodStorage::ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table) {
192   ReleaseArrayIfNotDeduplicated(table);
193 }
194 
DeduplicateCFIInfo(const ArrayRef<const uint8_t> & cfi_info)195 const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCFIInfo(
196     const ArrayRef<const uint8_t>& cfi_info) {
197   return AllocateOrDeduplicateArray(cfi_info, &dedupe_cfi_info_);
198 }
199 
ReleaseCFIInfo(const LengthPrefixedArray<uint8_t> * cfi_info)200 void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info) {
201   ReleaseArrayIfNotDeduplicated(cfi_info);
202 }
203 
DeduplicateLinkerPatches(const ArrayRef<const linker::LinkerPatch> & linker_patches)204 const LengthPrefixedArray<linker::LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches(
205     const ArrayRef<const linker::LinkerPatch>& linker_patches) {
206   return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_);
207 }
208 
ReleaseLinkerPatches(const LengthPrefixedArray<linker::LinkerPatch> * linker_patches)209 void CompiledMethodStorage::ReleaseLinkerPatches(
210     const LengthPrefixedArray<linker::LinkerPatch>* linker_patches) {
211   ReleaseArrayIfNotDeduplicated(linker_patches);
212 }
213 
GetThunkMapKey(const linker::LinkerPatch & linker_patch)214 CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey(
215     const linker::LinkerPatch& linker_patch) {
216   uint32_t custom_value1 = 0u;
217   uint32_t custom_value2 = 0u;
218   switch (linker_patch.GetType()) {
219     case linker::LinkerPatch::Type::kCallEntrypoint:
220       custom_value1 = linker_patch.EntrypointOffset();
221       break;
222     case linker::LinkerPatch::Type::kBakerReadBarrierBranch:
223       custom_value1 = linker_patch.GetBakerCustomValue1();
224       custom_value2 = linker_patch.GetBakerCustomValue2();
225       break;
226     case linker::LinkerPatch::Type::kCallRelative:
227       // No custom values.
228       break;
229     default:
230       LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType();
231       UNREACHABLE();
232   }
233   return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2);
234 }
235 
GetThunkCode(const linker::LinkerPatch & linker_patch,std::string * debug_name)236 ArrayRef<const uint8_t> CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch,
237                                                             /*out*/ std::string* debug_name) {
238   ThunkMapKey key = GetThunkMapKey(linker_patch);
239   MutexLock lock(Thread::Current(), thunk_map_lock_);
240   auto it = thunk_map_.find(key);
241   if (it != thunk_map_.end()) {
242     const ThunkMapValue& value = it->second;
243     if (debug_name != nullptr) {
244       *debug_name = value.GetDebugName();
245     }
246     return value.GetCode();
247   } else {
248     if (debug_name != nullptr) {
249       *debug_name = std::string();
250     }
251     return ArrayRef<const uint8_t>();
252   }
253 }
254 
SetThunkCode(const linker::LinkerPatch & linker_patch,ArrayRef<const uint8_t> code,const std::string & debug_name)255 void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch,
256                                          ArrayRef<const uint8_t> code,
257                                          const std::string& debug_name) {
258   DCHECK(!code.empty());
259   ThunkMapKey key = GetThunkMapKey(linker_patch);
260   std::vector<uint8_t, SwapAllocator<uint8_t>> code_copy(
261       code.begin(), code.end(), SwapAllocator<uint8_t>(swap_space_.get()));
262   ThunkMapValue value(std::move(code_copy), debug_name);
263   MutexLock lock(Thread::Current(), thunk_map_lock_);
264   // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry.
265   thunk_map_.emplace(key, std::move(value));
266 }
267 
268 }  // namespace art
269