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