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