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> 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> 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> 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> 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: 83 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: 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 107 class 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 128 class 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 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 160 CompiledMethodStorage::~CompiledMethodStorage() { 161 // All done by member destructors. 162 } 163 164 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 177 const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCode( 178 const ArrayRef<const uint8_t>& code) { 179 return AllocateOrDeduplicateArray(code, &dedupe_code_); 180 } 181 182 void CompiledMethodStorage::ReleaseCode(const LengthPrefixedArray<uint8_t>* code) { 183 ReleaseArrayIfNotDeduplicated(code); 184 } 185 186 const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateVMapTable( 187 const ArrayRef<const uint8_t>& table) { 188 return AllocateOrDeduplicateArray(table, &dedupe_vmap_table_); 189 } 190 191 void CompiledMethodStorage::ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table) { 192 ReleaseArrayIfNotDeduplicated(table); 193 } 194 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 200 void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info) { 201 ReleaseArrayIfNotDeduplicated(cfi_info); 202 } 203 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 209 void CompiledMethodStorage::ReleaseLinkerPatches( 210 const LengthPrefixedArray<linker::LinkerPatch>* linker_patches) { 211 ReleaseArrayIfNotDeduplicated(linker_patches); 212 } 213 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::kBakerReadBarrierBranch: 220 custom_value1 = linker_patch.GetBakerCustomValue1(); 221 custom_value2 = linker_patch.GetBakerCustomValue2(); 222 break; 223 case linker::LinkerPatch::Type::kCallRelative: 224 // No custom values. 225 break; 226 default: 227 LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType(); 228 UNREACHABLE(); 229 } 230 return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2); 231 } 232 233 ArrayRef<const uint8_t> CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch, 234 /*out*/ std::string* debug_name) { 235 ThunkMapKey key = GetThunkMapKey(linker_patch); 236 MutexLock lock(Thread::Current(), thunk_map_lock_); 237 auto it = thunk_map_.find(key); 238 if (it != thunk_map_.end()) { 239 const ThunkMapValue& value = it->second; 240 if (debug_name != nullptr) { 241 *debug_name = value.GetDebugName(); 242 } 243 return value.GetCode(); 244 } else { 245 if (debug_name != nullptr) { 246 *debug_name = std::string(); 247 } 248 return ArrayRef<const uint8_t>(); 249 } 250 } 251 252 void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch, 253 ArrayRef<const uint8_t> code, 254 const std::string& debug_name) { 255 DCHECK(!code.empty()); 256 ThunkMapKey key = GetThunkMapKey(linker_patch); 257 std::vector<uint8_t, SwapAllocator<uint8_t>> code_copy( 258 code.begin(), code.end(), SwapAllocator<uint8_t>(swap_space_.get())); 259 ThunkMapValue value(std::move(code_copy), debug_name); 260 MutexLock lock(Thread::Current(), thunk_map_lock_); 261 // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry. 262 thunk_map_.emplace(key, std::move(value)); 263 } 264 265 } // namespace art 266