blob: d56b135aca3cbf51cc5f8add65f5018937d7f3ab [file] [log] [blame]
Vladimir Marko35831e82015-09-11 11:59:18 +01001/*
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
Andreas Gampe57943812017-12-06 21:39:13 -080022#include <android-base/logging.h>
23
Vladimir Marko54159c62018-06-20 14:30:08 +010024#include "base/data_hash.h"
David Sehrc431b9d2018-03-02 12:01:51 -080025#include "base/utils.h"
Vladimir Marko35831e82015-09-11 11:59:18 +010026#include "compiled_method.h"
Vladimir Markod8dbc8d2017-09-20 13:37:47 +010027#include "linker/linker_patch.h"
Andreas Gampeb486a982017-06-01 13:45:54 -070028#include "thread-current-inl.h"
Vladimir Marko35831e82015-09-11 11:59:18 +010029#include "utils/dedupe_set-inl.h"
30#include "utils/swap_space.h"
31
32namespace art {
33
34namespace { // anonymous namespace
35
36template <typename T>
37const 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
46template <typename T>
47void 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
56template <typename T, typename DedupeSetType>
57inline 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
69template <typename T>
70inline void CompiledMethodStorage::ReleaseArrayIfNotDeduplicated(
71 const LengthPrefixedArray<T>* array) {
72 if (array != nullptr && !DedupeEnabled()) {
73 ReleaseArray(swap_space_.get(), array);
74 }
75}
76
77template <typename ContentType>
78class CompiledMethodStorage::DedupeHashFunc {
79 private:
80 static constexpr bool kUseMurmur3Hash = true;
81
82 public:
83 size_t operator()(const ArrayRef<ContentType>& array) const {
Vladimir Marko54159c62018-06-20 14:30:08 +010084 return DataHash()(array);
Vladimir Marko35831e82015-09-11 11:59:18 +010085 }
86};
87
88template <typename T>
89class CompiledMethodStorage::LengthPrefixedArrayAlloc {
90 public:
91 explicit LengthPrefixedArrayAlloc(SwapSpace* swap_space)
92 : swap_space_(swap_space) {
93 }
94
95 const LengthPrefixedArray<T>* Copy(const ArrayRef<const T>& array) {
96 return CopyArray(swap_space_, array);
97 }
98
99 void Destroy(const LengthPrefixedArray<T>* array) {
100 ReleaseArray(swap_space_, array);
101 }
102
103 private:
104 SwapSpace* const swap_space_;
105};
106
Vladimir Markoca1e0382018-04-11 09:58:41 +0000107class CompiledMethodStorage::ThunkMapKey {
108 public:
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
112 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
128class CompiledMethodStorage::ThunkMapValue {
129 public:
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
134 ArrayRef<const uint8_t> GetCode() const {
135 return ArrayRef<const uint8_t>(code_);
136 }
137
138 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
Vladimir Marko35831e82015-09-11 11:59:18 +0100147CompiledMethodStorage::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())),
Mathieu Chartiercbcedbf2017-03-12 22:24:50 -0700151 dedupe_method_info_("dedupe method info",
152 LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
Vladimir Marko35831e82015-09-11 11:59:18 +0100153 dedupe_vmap_table_("dedupe vmap table",
154 LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
Vladimir Marko35831e82015-09-11 11:59:18 +0100155 dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
156 dedupe_linker_patches_("dedupe cfi info",
Vladimir Markoca1e0382018-04-11 09:58:41 +0000157 LengthPrefixedArrayAlloc<linker::LinkerPatch>(swap_space_.get())),
158 thunk_map_lock_("thunk_map_lock"),
159 thunk_map_(std::less<ThunkMapKey>(), SwapAllocator<ThunkMapValueType>(swap_space_.get())) {
Vladimir Marko35831e82015-09-11 11:59:18 +0100160}
161
162CompiledMethodStorage::~CompiledMethodStorage() {
163 // All done by member destructors.
164}
165
166void CompiledMethodStorage::DumpMemoryUsage(std::ostream& os, bool extended) const {
167 if (swap_space_.get() != nullptr) {
Anton Kirilovdd9473b2016-01-28 15:08:01 +0000168 const size_t swap_size = swap_space_->GetSize();
169 os << " swap=" << PrettySize(swap_size) << " (" << swap_size << "B)";
Vladimir Marko35831e82015-09-11 11:59:18 +0100170 }
171 if (extended) {
172 Thread* self = Thread::Current();
173 os << "\nCode dedupe: " << dedupe_code_.DumpStats(self);
Vladimir Marko35831e82015-09-11 11:59:18 +0100174 os << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(self);
Vladimir Marko35831e82015-09-11 11:59:18 +0100175 os << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(self);
176 }
177}
178
179const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCode(
180 const ArrayRef<const uint8_t>& code) {
181 return AllocateOrDeduplicateArray(code, &dedupe_code_);
182}
183
184void CompiledMethodStorage::ReleaseCode(const LengthPrefixedArray<uint8_t>* code) {
185 ReleaseArrayIfNotDeduplicated(code);
186}
187
Mathieu Chartiercbcedbf2017-03-12 22:24:50 -0700188const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateMethodInfo(
189 const ArrayRef<const uint8_t>& src_map) {
190 return AllocateOrDeduplicateArray(src_map, &dedupe_method_info_);
Vladimir Marko35831e82015-09-11 11:59:18 +0100191}
192
Mathieu Chartiercbcedbf2017-03-12 22:24:50 -0700193void CompiledMethodStorage::ReleaseMethodInfo(const LengthPrefixedArray<uint8_t>* method_info) {
194 ReleaseArrayIfNotDeduplicated(method_info);
Vladimir Marko35831e82015-09-11 11:59:18 +0100195}
196
Vladimir Marko35831e82015-09-11 11:59:18 +0100197const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateVMapTable(
198 const ArrayRef<const uint8_t>& table) {
199 return AllocateOrDeduplicateArray(table, &dedupe_vmap_table_);
200}
201
202void CompiledMethodStorage::ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table) {
203 ReleaseArrayIfNotDeduplicated(table);
204}
205
Vladimir Marko35831e82015-09-11 11:59:18 +0100206const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCFIInfo(
207 const ArrayRef<const uint8_t>& cfi_info) {
208 return AllocateOrDeduplicateArray(cfi_info, &dedupe_cfi_info_);
209}
210
211void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info) {
212 ReleaseArrayIfNotDeduplicated(cfi_info);
213}
214
Vladimir Markod8dbc8d2017-09-20 13:37:47 +0100215const LengthPrefixedArray<linker::LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches(
216 const ArrayRef<const linker::LinkerPatch>& linker_patches) {
Vladimir Marko35831e82015-09-11 11:59:18 +0100217 return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_);
218}
219
220void CompiledMethodStorage::ReleaseLinkerPatches(
Vladimir Markod8dbc8d2017-09-20 13:37:47 +0100221 const LengthPrefixedArray<linker::LinkerPatch>* linker_patches) {
Vladimir Marko35831e82015-09-11 11:59:18 +0100222 ReleaseArrayIfNotDeduplicated(linker_patches);
223}
224
Vladimir Markoca1e0382018-04-11 09:58:41 +0000225CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey(
226 const linker::LinkerPatch& linker_patch) {
227 uint32_t custom_value1 = 0u;
228 uint32_t custom_value2 = 0u;
229 switch (linker_patch.GetType()) {
230 case linker::LinkerPatch::Type::kBakerReadBarrierBranch:
231 custom_value1 = linker_patch.GetBakerCustomValue1();
232 custom_value2 = linker_patch.GetBakerCustomValue2();
233 break;
234 case linker::LinkerPatch::Type::kCallRelative:
235 // No custom values.
236 break;
237 default:
238 LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType();
239 UNREACHABLE();
240 }
241 return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2);
242}
243
244ArrayRef<const uint8_t> CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch,
245 /*out*/ std::string* debug_name) {
246 ThunkMapKey key = GetThunkMapKey(linker_patch);
247 MutexLock lock(Thread::Current(), thunk_map_lock_);
248 auto it = thunk_map_.find(key);
249 if (it != thunk_map_.end()) {
250 const ThunkMapValue& value = it->second;
251 if (debug_name != nullptr) {
252 *debug_name = value.GetDebugName();
253 }
254 return value.GetCode();
255 } else {
256 if (debug_name != nullptr) {
257 *debug_name = std::string();
258 }
259 return ArrayRef<const uint8_t>();
260 }
261}
262
263void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch,
264 ArrayRef<const uint8_t> code,
265 const std::string& debug_name) {
266 DCHECK(!code.empty());
267 ThunkMapKey key = GetThunkMapKey(linker_patch);
268 std::vector<uint8_t, SwapAllocator<uint8_t>> code_copy(
269 code.begin(), code.end(), SwapAllocator<uint8_t>(swap_space_.get()));
270 ThunkMapValue value(std::move(code_copy), debug_name);
271 MutexLock lock(Thread::Current(), thunk_map_lock_);
272 // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry.
273 thunk_map_.emplace(key, std::move(value));
274}
275
Vladimir Marko35831e82015-09-11 11:59:18 +0100276} // namespace art