diff options
author | 2017-01-30 19:30:44 -0800 | |
---|---|---|
committer | 2017-02-16 13:52:56 -0800 | |
commit | 940eb0c00ef531dd9a0a68dbd61e377832e81eb4 (patch) | |
tree | c65e43a702bb781b7a2f4ebedfb6b3d131fd8312 | |
parent | 9fb10fb39bcb3d9a4dc7e16f8c1d38dcc112639c (diff) |
Add inline caches to offline profiles
Add support for inline caches in profiles:
- extract inline caches from the jit cache when the profile saver
queries the hot methods
- bump profile version to support the new data
- add new tests
- inline caches are only supported for same-apk calls (including
multidex)
Test: m art-test-host-gtest-profile_compilation_info_test
Bug: 32434870
Change-Id: I38b4ca0a54568d2224765ff76023baef1b8fd1a2
-rw-r--r-- | dexlayout/dexlayout_test.cc | 2 | ||||
-rw-r--r-- | profman/profman.cc | 2 | ||||
-rw-r--r-- | runtime/jit/jit_code_cache.cc | 31 | ||||
-rw-r--r-- | runtime/jit/jit_code_cache.h | 3 | ||||
-rw-r--r-- | runtime/jit/profile_compilation_info.cc | 581 | ||||
-rw-r--r-- | runtime/jit/profile_compilation_info.h | 270 | ||||
-rw-r--r-- | runtime/jit/profile_compilation_info_test.cc | 343 | ||||
-rw-r--r-- | runtime/jit/profile_saver.cc | 12 | ||||
-rw-r--r-- | runtime/jit/profiling_info.h | 2 | ||||
-rw-r--r-- | runtime/safe_map.h | 10 |
10 files changed, 1111 insertions, 145 deletions
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc index da1e1d26dc..2d85e8fe7b 100644 --- a/dexlayout/dexlayout_test.cc +++ b/dexlayout/dexlayout_test.cc @@ -41,7 +41,7 @@ static const char kDexFileLayoutInputDex[] = "AAAAdQEAAAAQAAABAAAAjAEAAA=="; static const char kDexFileLayoutInputProfile[] = - "cHJvADAwMgABAAsAAAABAPUpbf5jbGFzc2VzLmRleAEA"; + "cHJvADAwMwABCwABAAAAAAD1KW3+Y2xhc3Nlcy5kZXgBAA=="; static const char kDexFileLayoutExpectedOutputDex[] = "ZGV4CjAzNQD1KW3+B8NAB0f2A/ZVIBJ0aHrGIqcpVTAUAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAH" diff --git a/profman/profman.cc b/profman/profman.cc index 8f35a76b6d..a42e4f1db1 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -593,7 +593,7 @@ class ProfMan FINAL { } // Generate the profile data structure. ProfileCompilationInfo info; - std::vector<MethodReference> methods; // No methods for now. + std::vector<ProfileMethodInfo> methods; // No methods for now. info.AddMethodsAndClasses(methods, resolved_class_set); // Write the profile file. CHECK(info.Save(fd)); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index 60ab275641..c226a38299 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -1245,15 +1245,40 @@ void* JitCodeCache::MoreCore(const void* mspace, intptr_t increment) NO_THREAD_S } void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_locations, - std::vector<MethodReference>& methods) { + std::vector<ProfileMethodInfo>& methods) { ScopedTrace trace(__FUNCTION__); MutexLock mu(Thread::Current(), lock_); for (const ProfilingInfo* info : profiling_infos_) { ArtMethod* method = info->GetMethod(); const DexFile* dex_file = method->GetDexFile(); - if (ContainsElement(dex_base_locations, dex_file->GetBaseLocation())) { - methods.emplace_back(dex_file, method->GetDexMethodIndex()); + if (!ContainsElement(dex_base_locations, dex_file->GetBaseLocation())) { + // Skip dex files which are not profiled. + continue; } + std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches; + for (size_t i = 0; i < info->number_of_inline_caches_; ++i) { + std::vector<ProfileMethodInfo::ProfileClassReference> profile_classes; + const InlineCache& cache = info->cache_[i]; + for (size_t k = 0; k < InlineCache::kIndividualCacheSize; k++) { + mirror::Class* cls = cache.classes_[k].Read(); + if (cls == nullptr) { + break; + } + const DexFile& class_dex_file = cls->GetDexFile(); + dex::TypeIndex type_index = cls->GetDexTypeIndex(); + if (ContainsElement(dex_base_locations, class_dex_file.GetBaseLocation())) { + // Only consider classes from the same apk (including multidex). + profile_classes.emplace_back(/*ProfileMethodInfo::ProfileClassReference*/ + &class_dex_file, type_index); + } + } + if (!profile_classes.empty()) { + inline_caches.emplace_back(/*ProfileMethodInfo::ProfileInlineCache*/ + cache.dex_pc_, profile_classes); + } + } + methods.emplace_back(/*ProfileMethodInfo*/ + dex_file, method->GetDexMethodIndex(), inline_caches); } } diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h index b5e31769ab..33a792fe75 100644 --- a/runtime/jit/jit_code_cache.h +++ b/runtime/jit/jit_code_cache.h @@ -30,6 +30,7 @@ #include "method_reference.h" #include "oat_file.h" #include "object_callbacks.h" +#include "profile_compilation_info.h" #include "safe_map.h" #include "thread_pool.h" @@ -192,7 +193,7 @@ class JitCodeCache { // Adds to `methods` all profiled methods which are part of any of the given dex locations. void GetProfiledMethods(const std::set<std::string>& dex_base_locations, - std::vector<MethodReference>& methods) + std::vector<ProfileMethodInfo>& methods) REQUIRES(!lock_) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 54fc0386e1..5638ce1160 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -37,7 +37,7 @@ namespace art { const uint8_t ProfileCompilationInfo::kProfileMagic[] = { 'p', 'r', 'o', '\0' }; -const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '2', '\0' }; +const uint8_t ProfileCompilationInfo::kProfileVersion[] = { '0', '0', '3', '\0' }; // inline caches static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; @@ -46,6 +46,25 @@ static constexpr uint16_t kMaxDexFileKeyLength = PATH_MAX; // using the same test profile. static constexpr bool kDebugIgnoreChecksum = false; +static constexpr uint8_t kMegamorphicEncoding = 7; + +static_assert(sizeof(InlineCache::kIndividualCacheSize) == sizeof(uint8_t), + "InlineCache::kIndividualCacheSize does not have the expect type size"); +static_assert(InlineCache::kIndividualCacheSize < kMegamorphicEncoding, + "InlineCache::kIndividualCacheSize is larger than expected"); + +void ProfileCompilationInfo::DexPcData::AddClass(uint16_t dex_profile_idx, + const dex::TypeIndex& type_idx) { + if (is_megamorphic) { + return; + } + classes.emplace(dex_profile_idx, type_idx); + if (classes.size() >= InlineCache::kIndividualCacheSize) { + is_megamorphic = true; + classes.clear(); + } +} + // Transform the actual dex location into relative paths. // Note: this is OK because we don't store profiles of different apps into the same file. // Apps with split apks don't cause trouble because each split has a different name and will not @@ -62,12 +81,10 @@ std::string ProfileCompilationInfo::GetProfileDexFileKey(const std::string& dex_ } bool ProfileCompilationInfo::AddMethodsAndClasses( - const std::vector<MethodReference>& methods, + const std::vector<ProfileMethodInfo>& methods, const std::set<DexCacheResolvedClasses>& resolved_classes) { - for (const MethodReference& method : methods) { - if (!AddMethodIndex(GetProfileDexFileKey(method.dex_file->GetLocation()), - method.dex_file->GetLocationChecksum(), - method.dex_method_index)) { + for (const ProfileMethodInfo& method : methods) { + if (!AddMethod(method)) { return false; } } @@ -170,29 +187,40 @@ static void AddUintToBuffer(std::vector<uint8_t>* buffer, T value) { } static constexpr size_t kLineHeaderSize = - 3 * sizeof(uint16_t) + // method_set.size + class_set.size + dex_location.size - sizeof(uint32_t); // checksum + 2 * sizeof(uint16_t) + // class_set.size + dex_location.size + 2 * sizeof(uint32_t); // method_map.size + checksum /** * Serialization format: - * magic,version,number_of_lines - * dex_location1,number_of_methods1,number_of_classes1,dex_location_checksum1, \ - * method_id11,method_id12...,class_id1,class_id2... - * dex_location2,number_of_methods2,number_of_classes2,dex_location_checksum2, \ - * method_id21,method_id22...,,class_id1,class_id2... + * magic,version,number_of_dex_files + * dex_location1,number_of_classes1,methods_region_size,dex_location_checksum1, \ + * method_encoding_11,method_encoding_12...,class_id1,class_id2... + * dex_location2,number_of_classes2,methods_region_size,dex_location_checksum2, \ + * method_encoding_21,method_encoding_22...,,class_id1,class_id2... * ..... + * The method_encoding is: + * method_id,number_of_inline_caches,inline_cache1,inline_cache2... + * The inline_cache is: + * dex_pc,[M|dex_map_size], dex_profile_index,class_id1,class_id2...,dex_profile_index2,... + * dex_map_size is the number of dex_indeces that follows. + * Classes are grouped per their dex files and the line + * `dex_profile_index,class_id1,class_id2...,dex_profile_index2,...` encodes the + * mapping from `dex_profile_index` to the set of classes `class_id1,class_id2...` + * M stands for megamorphic and it's encoded as the byte kMegamorphicEncoding. + * When present, there will be no class ids following. **/ bool ProfileCompilationInfo::Save(int fd) { ScopedTrace trace(__PRETTY_FUNCTION__); DCHECK_GE(fd, 0); - // Cache at most 5KB before writing. - static constexpr size_t kMaxSizeToKeepBeforeWriting = 5 * KB; + // Cache at most 50KB before writing. + static constexpr size_t kMaxSizeToKeepBeforeWriting = 50 * KB; // Use a vector wrapper to avoid keeping track of offsets when we add elements. std::vector<uint8_t> buffer; WriteBuffer(fd, kProfileMagic, sizeof(kProfileMagic)); WriteBuffer(fd, kProfileVersion, sizeof(kProfileVersion)); - AddUintToBuffer(&buffer, static_cast<uint16_t>(info_.size())); + DCHECK_LE(info_.size(), std::numeric_limits<uint8_t>::max()); + AddUintToBuffer(&buffer, static_cast<uint8_t>(info_.size())); for (const auto& it : info_) { if (buffer.size() > kMaxSizeToKeepBeforeWriting) { @@ -203,9 +231,9 @@ bool ProfileCompilationInfo::Save(int fd) { } const std::string& dex_location = it.first; const DexFileData& dex_data = it.second; - if (dex_data.method_set.empty() && dex_data.class_set.empty()) { - continue; - } + + // Note that we allow dex files without any methods or classes, so that + // inline caches can refer valid dex files. if (dex_location.size() >= kMaxDexFileKeyLength) { LOG(WARNING) << "DexFileKey exceeds allocated limit"; @@ -214,42 +242,128 @@ bool ProfileCompilationInfo::Save(int fd) { // Make sure that the buffer has enough capacity to avoid repeated resizings // while we add data. + uint32_t methods_region_size = GetMethodsRegionSize(dex_data); size_t required_capacity = buffer.size() + kLineHeaderSize + dex_location.size() + - sizeof(uint16_t) * (dex_data.class_set.size() + dex_data.method_set.size()); + sizeof(uint16_t) * dex_data.class_set.size() + + methods_region_size; buffer.reserve(required_capacity); - DCHECK_LE(dex_location.size(), std::numeric_limits<uint16_t>::max()); - DCHECK_LE(dex_data.method_set.size(), std::numeric_limits<uint16_t>::max()); DCHECK_LE(dex_data.class_set.size(), std::numeric_limits<uint16_t>::max()); AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_location.size())); - AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.method_set.size())); AddUintToBuffer(&buffer, static_cast<uint16_t>(dex_data.class_set.size())); + AddUintToBuffer(&buffer, methods_region_size); // uint32_t AddUintToBuffer(&buffer, dex_data.checksum); // uint32_t AddStringToBuffer(&buffer, dex_location); - for (auto method_it : dex_data.method_set) { - AddUintToBuffer(&buffer, method_it); + for (const auto& method_it : dex_data.method_map) { + AddUintToBuffer(&buffer, method_it.first); + AddInlineCacheToBuffer(&buffer, method_it.second); } - for (auto class_id : dex_data.class_set) { + for (const auto& class_id : dex_data.class_set) { AddUintToBuffer(&buffer, class_id.index_); } - DCHECK_EQ(required_capacity, buffer.size()) + + DCHECK_LE(required_capacity, buffer.size()) << "Failed to add the expected number of bytes in the buffer"; } return WriteBuffer(fd, buffer.data(), buffer.size()); } +void ProfileCompilationInfo::AddInlineCacheToBuffer(std::vector<uint8_t>* buffer, + const InlineCacheMap& inline_cache_map) { + // Add inline cache map size. + AddUintToBuffer(buffer, static_cast<uint16_t>(inline_cache_map.size())); + if (inline_cache_map.size() == 0) { + return; + } + for (const auto& inline_cache_it : inline_cache_map) { + uint16_t dex_pc = inline_cache_it.first; + const DexPcData dex_pc_data = inline_cache_it.second; + const ClassSet& classes = dex_pc_data.classes; + + // Add the dex pc. + AddUintToBuffer(buffer, dex_pc); + + if (dex_pc_data.is_megamorphic) { + // Add the megamorphic encoding if needed and continue. + // If megamorphic, we don't add the rest of the classes. + AddUintToBuffer(buffer, kMegamorphicEncoding); + continue; + } + + DCHECK_LT(classes.size(), InlineCache::kIndividualCacheSize); + DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes"; + + SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map; + // Group the classes by dex. We expect that most of the classes will come from + // the same dex, so this will be more efficient than encoding the dex index + // for each class reference. + GroupClassesByDex(classes, &dex_to_classes_map); + // Add the dex map size. + AddUintToBuffer(buffer, static_cast<uint8_t>(dex_to_classes_map.size())); + for (const auto& dex_it : dex_to_classes_map) { + uint8_t dex_profile_index = dex_it.first; + const std::vector<dex::TypeIndex>& dex_classes = dex_it.second; + // Add the dex profile index. + AddUintToBuffer(buffer, dex_profile_index); + // Add the the number of classes for each dex profile index. + AddUintToBuffer(buffer, static_cast<uint8_t>(dex_classes.size())); + for (size_t i = 0; i < dex_classes.size(); i++) { + // Add the type index of the classes. + AddUintToBuffer(buffer, dex_classes[i].index_); + } + } + } +} + +uint32_t ProfileCompilationInfo::GetMethodsRegionSize(const DexFileData& dex_data) { + // ((uint16_t)method index + (uint16_t)inline cache size) * number of methods + uint32_t size = 2 * sizeof(uint16_t) * dex_data.method_map.size(); + for (const auto& method_it : dex_data.method_map) { + const InlineCacheMap& inline_cache = method_it.second; + size += sizeof(uint16_t) * inline_cache.size(); // dex_pc + for (const auto& inline_cache_it : inline_cache) { + const ClassSet& classes = inline_cache_it.second.classes; + SafeMap<uint8_t, std::vector<dex::TypeIndex>> dex_to_classes_map; + GroupClassesByDex(classes, &dex_to_classes_map); + size += sizeof(uint8_t); // dex_to_classes_map size + for (const auto& dex_it : dex_to_classes_map) { + size += sizeof(uint8_t); // dex profile index + size += sizeof(uint8_t); // number of classes + const std::vector<dex::TypeIndex>& dex_classes = dex_it.second; + size += sizeof(uint16_t) * dex_classes.size(); // the actual classes + } + } + } + return size; +} + +void ProfileCompilationInfo::GroupClassesByDex( + const ClassSet& classes, + /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map) { + for (const auto& classes_it : classes) { + auto dex_it = dex_to_classes_map->FindOrAdd(classes_it.dex_profile_index); + dex_it->second.push_back(classes_it.type_index); + } +} + ProfileCompilationInfo::DexFileData* ProfileCompilationInfo::GetOrAddDexFileData( const std::string& dex_location, uint32_t checksum) { - auto info_it = info_.find(dex_location); - if (info_it == info_.end()) { - info_it = info_.Put(dex_location, DexFileData(checksum)); + auto info_it = info_.FindOrAdd(dex_location, DexFileData(checksum, info_.size())); + if (info_.size() > std::numeric_limits<uint8_t>::max()) { + // Allow only 255 dex files to be profiled. This allows us to save bytes + // when encoding. The number is well above what we expect for normal applications. + if (kIsDebugBuild) { + LOG(WARNING) << "Exceeded the maximum number of dex files (255). Something went wrong"; + } + info_.erase(dex_location); + return nullptr; } if (info_it->second.checksum != checksum) { LOG(WARNING) << "Checksum mismatch for dex " << dex_location; @@ -270,13 +384,65 @@ bool ProfileCompilationInfo::AddResolvedClasses(const DexCacheResolvedClasses& c } bool ProfileCompilationInfo::AddMethodIndex(const std::string& dex_location, - uint32_t checksum, - uint16_t method_idx) { - DexFileData* const data = GetOrAddDexFileData(dex_location, checksum); - if (data == nullptr) { + uint32_t dex_checksum, + uint16_t method_index) { + return AddMethod(dex_location, dex_checksum, method_index, OfflineProfileMethodInfo()); +} + +bool ProfileCompilationInfo::AddMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t method_index, + const OfflineProfileMethodInfo& pmi) { + DexFileData* const data = GetOrAddDexFileData( + GetProfileDexFileKey(dex_location), + dex_checksum); + if (data == nullptr) { // checksum mismatch return false; } - data->method_set.insert(method_idx); + auto inline_cache_it = data->method_map.FindOrAdd(method_index); + for (const auto& pmi_inline_cache_it : pmi.inline_caches) { + uint16_t pmi_ic_dex_pc = pmi_inline_cache_it.first; + const DexPcData& pmi_ic_dex_pc_data = pmi_inline_cache_it.second; + auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(pmi_ic_dex_pc); + if (pmi_ic_dex_pc_data.is_megamorphic) { + dex_pc_data_it->second.SetMegamorphic(); + continue; + } + for (const ClassReference& class_ref : pmi_ic_dex_pc_data.classes) { + const DexReference& dex_ref = pmi.dex_references[class_ref.dex_profile_index]; + DexFileData* class_dex_data = GetOrAddDexFileData( + GetProfileDexFileKey(dex_ref.dex_location), + dex_ref.dex_checksum); + if (class_dex_data == nullptr) { // checksum mismatch + return false; + } + dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index); + } + } + return true; +} + +bool ProfileCompilationInfo::AddMethod(const ProfileMethodInfo& pmi) { + DexFileData* const data = GetOrAddDexFileData( + GetProfileDexFileKey(pmi.dex_file->GetLocation()), + pmi.dex_file->GetLocationChecksum()); + if (data == nullptr) { // checksum mismatch + return false; + } + auto inline_cache_it = data->method_map.FindOrAdd(pmi.dex_method_index); + + for (const ProfileMethodInfo::ProfileInlineCache& cache : pmi.inline_caches) { + for (const ProfileMethodInfo::ProfileClassReference& class_ref : cache.classes) { + DexFileData* class_dex_data = GetOrAddDexFileData( + GetProfileDexFileKey(class_ref.dex_file->GetLocation()), + class_ref.dex_file->GetLocationChecksum()); + if (class_dex_data == nullptr) { // checksum mismatch + return false; + } + auto dex_pc_data_it = inline_cache_it->second.FindOrAdd(cache.dex_pc); + dex_pc_data_it->second.AddClass(class_dex_data->profile_index, class_ref.type_index); + } + } return true; } @@ -291,21 +457,79 @@ bool ProfileCompilationInfo::AddClassIndex(const std::string& dex_location, return true; } -bool ProfileCompilationInfo::ProcessLine(SafeBuffer& line_buffer, - uint16_t method_set_size, - uint16_t class_set_size, - uint32_t checksum, - const std::string& dex_location) { - for (uint16_t i = 0; i < method_set_size; i++) { - uint16_t method_idx = line_buffer.ReadUintAndAdvance<uint16_t>(); - if (!AddMethodIndex(dex_location, checksum, method_idx)) { +#define READ_UINT(type, buffer, dest, error) \ + do { \ + if (!buffer.ReadUintAndAdvance<type>(&dest)) { \ + *error = "Could not read "#dest; \ + return false; \ + } \ + } \ + while (false) + +bool ProfileCompilationInfo::ReadInlineCache(SafeBuffer& buffer, + uint8_t number_of_dex_files, + /*out*/ InlineCacheMap* inline_cache, + /*out*/ std::string* error) { + uint16_t inline_cache_size; + READ_UINT(uint16_t, buffer, inline_cache_size, error); + for (; inline_cache_size > 0; inline_cache_size--) { + uint16_t dex_pc; + uint8_t dex_to_classes_map_size; + READ_UINT(uint16_t, buffer, dex_pc, error); + READ_UINT(uint8_t, buffer, dex_to_classes_map_size, error); + auto dex_pc_data_it = inline_cache->FindOrAdd(dex_pc); + if (dex_to_classes_map_size == kMegamorphicEncoding) { + dex_pc_data_it->second.SetMegamorphic(); + continue; + } + for (; dex_to_classes_map_size > 0; dex_to_classes_map_size--) { + uint8_t dex_profile_index; + uint8_t dex_classes_size; + READ_UINT(uint8_t, buffer, dex_profile_index, error); + READ_UINT(uint8_t, buffer, dex_classes_size, error); + if (dex_profile_index >= number_of_dex_files) { + *error = "dex_profile_index out of bounds "; + *error += std::to_string(dex_profile_index) + " " + std::to_string(number_of_dex_files); + return false; + } + for (; dex_classes_size > 0; dex_classes_size--) { + uint16_t type_index; + READ_UINT(uint16_t, buffer, type_index, error); + dex_pc_data_it->second.AddClass(dex_profile_index, dex::TypeIndex(type_index)); + } + } + } + return true; +} + +bool ProfileCompilationInfo::ReadMethods(SafeBuffer& buffer, + uint8_t number_of_dex_files, + const ProfileLineHeader& line_header, + /*out*/std::string* error) { + while (buffer.HasMoreData()) { + DexFileData* const data = GetOrAddDexFileData(line_header.dex_location, line_header.checksum); + uint16_t method_index; + READ_UINT(uint16_t, buffer, method_index, error); + + auto it = data->method_map.FindOrAdd(method_index); + if (!ReadInlineCache(buffer, number_of_dex_files, &(it->second), error)) { return false; } } - for (uint16_t i = 0; i < class_set_size; i++) { - uint16_t type_idx = line_buffer.ReadUintAndAdvance<uint16_t>(); - if (!AddClassIndex(dex_location, checksum, dex::TypeIndex(type_idx))) { + return true; +} + +bool ProfileCompilationInfo::ReadClasses(SafeBuffer& buffer, + uint16_t classes_to_read, + const ProfileLineHeader& line_header, + /*out*/std::string* error) { + for (uint16_t i = 0; i < classes_to_read; i++) { + uint16_t type_index; + READ_UINT(uint16_t, buffer, type_index, error); + if (!AddClassIndex(line_header.dex_location, + line_header.checksum, + dex::TypeIndex(type_index))) { return false; } } @@ -324,15 +548,17 @@ static int testEOF(int fd) { // Reads an uint value previously written with AddUintToBuffer. template <typename T> -T ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance() { +bool ProfileCompilationInfo::SafeBuffer::ReadUintAndAdvance(/*out*/T* value) { static_assert(std::is_unsigned<T>::value, "Type is not unsigned"); - CHECK_LE(ptr_current_ + sizeof(T), ptr_end_); - T value = 0; + if (ptr_current_ + sizeof(T) > ptr_end_) { + return false; + } + *value = 0; for (size_t i = 0; i < sizeof(T); i++) { - value += ptr_current_[i] << (i * kBitsPerByte); + *value += ptr_current_[i] << (i * kBitsPerByte); } ptr_current_ += sizeof(T); - return value; + return true; } bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, size_t data_size) { @@ -346,6 +572,10 @@ bool ProfileCompilationInfo::SafeBuffer::CompareAndAdvance(const uint8_t* data, return false; } +bool ProfileCompilationInfo::SafeBuffer::HasMoreData() { + return ptr_current_ < ptr_end_; +} + ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::FillFromFd( int fd, const std::string& source, @@ -369,13 +599,13 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::SafeBuffer::Fil ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHeader( int fd, - /*out*/uint16_t* number_of_lines, + /*out*/uint8_t* number_of_dex_files, /*out*/std::string* error) { // Read magic and version const size_t kMagicVersionSize = sizeof(kProfileMagic) + sizeof(kProfileVersion) + - sizeof(uint16_t); // number of lines + sizeof(uint8_t); // number of dex files SafeBuffer safe_buffer(kMagicVersionSize); @@ -392,24 +622,38 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileHead *error = "Profile version mismatch"; return kProfileLoadVersionMismatch; } - *number_of_lines = safe_buffer.ReadUintAndAdvance<uint16_t>(); + if (!safe_buffer.ReadUintAndAdvance<uint8_t>(number_of_dex_files)) { + *error = "Cannot read the number of dex files"; + return kProfileLoadBadData; + } return kProfileLoadSuccess; } +bool ProfileCompilationInfo::ReadProfileLineHeaderElements(SafeBuffer& buffer, + /*out*/uint16_t* dex_location_size, + /*out*/ProfileLineHeader* line_header, + /*out*/std::string* error) { + READ_UINT(uint16_t, buffer, *dex_location_size, error); + READ_UINT(uint16_t, buffer, line_header->class_set_size, error); + READ_UINT(uint32_t, buffer, line_header->method_region_size_bytes, error); + READ_UINT(uint32_t, buffer, line_header->checksum, error); + return true; +} + ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLineHeader( int fd, /*out*/ProfileLineHeader* line_header, /*out*/std::string* error) { SafeBuffer header_buffer(kLineHeaderSize); - ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileHeader", error); + ProfileLoadSatus status = header_buffer.FillFromFd(fd, "ReadProfileLineHeader", error); if (status != kProfileLoadSuccess) { return status; } - uint16_t dex_location_size = header_buffer.ReadUintAndAdvance<uint16_t>(); - line_header->method_set_size = header_buffer.ReadUintAndAdvance<uint16_t>(); - line_header->class_set_size = header_buffer.ReadUintAndAdvance<uint16_t>(); - line_header->checksum = header_buffer.ReadUintAndAdvance<uint32_t>(); + uint16_t dex_location_size; + if (!ReadProfileLineHeaderElements(header_buffer, &dex_location_size, line_header, error)) { + return kProfileLoadBadData; + } if (dex_location_size == 0 || dex_location_size > kMaxDexFileKeyLength) { *error = "DexFileKey has an invalid size: " + @@ -429,37 +673,38 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::ReadProfileLine( int fd, + uint8_t number_of_dex_files, const ProfileLineHeader& line_header, /*out*/std::string* error) { - // Make sure that we don't try to read everything in memory (in case the profile if full). - // Split readings in chunks of at most 10kb. - static constexpr uint16_t kMaxNumberOfEntriesToRead = 5120; - uint16_t methods_left_to_read = line_header.method_set_size; - uint16_t classes_left_to_read = line_header.class_set_size; + if (GetOrAddDexFileData(line_header.dex_location, line_header.checksum) == nullptr) { + *error = "Error when reading profile file line header: checksum mismatch for " + + line_header.dex_location; + return kProfileLoadBadData; + } - while ((methods_left_to_read > 0) || (classes_left_to_read > 0)) { - uint16_t methods_to_read = std::min(kMaxNumberOfEntriesToRead, methods_left_to_read); - uint16_t max_classes_to_read = kMaxNumberOfEntriesToRead - methods_to_read; - uint16_t classes_to_read = std::min(max_classes_to_read, classes_left_to_read); + { + SafeBuffer buffer(line_header.method_region_size_bytes); + ProfileLoadSatus status = buffer.FillFromFd(fd, "ReadProfileLineMethods", error); + if (status != kProfileLoadSuccess) { + return status; + } - size_t line_size = sizeof(uint16_t) * (methods_to_read + classes_to_read); - SafeBuffer line_buffer(line_size); + if (!ReadMethods(buffer, number_of_dex_files, line_header, error)) { + return kProfileLoadBadData; + } + } - ProfileLoadSatus status = line_buffer.FillFromFd(fd, "ReadProfileLine", error); + { + SafeBuffer buffer(sizeof(uint16_t) * line_header.class_set_size); + ProfileLoadSatus status = buffer.FillFromFd(fd, "ReadProfileLineClasses", error); if (status != kProfileLoadSuccess) { return status; } - if (!ProcessLine(line_buffer, - methods_to_read, - classes_to_read, - line_header.checksum, - line_header.dex_location)) { - *error = "Error when reading profile file line"; + if (!ReadClasses(buffer, line_header.class_set_size, line_header, error)) { return kProfileLoadBadData; } - methods_left_to_read -= methods_to_read; - classes_left_to_read -= classes_to_read; } + return kProfileLoadSuccess; } @@ -470,7 +715,7 @@ bool ProfileCompilationInfo::Load(int fd) { if (status == kProfileLoadSuccess) { return true; } else { - PLOG(WARNING) << "Error when reading profile " << error; + LOG(WARNING) << "Error when reading profile: " << error; return false; } } @@ -490,15 +735,16 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal( if (stat_buffer.st_size == 0) { return kProfileLoadSuccess; } - // Read profile header: magic + version + number_of_lines. - uint16_t number_of_lines; - ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_lines, error); + // Read profile header: magic + version + number_of_dex_files. + uint8_t number_of_dex_files; + ProfileLoadSatus status = ReadProfileHeader(fd, &number_of_dex_files, error); if (status != kProfileLoadSuccess) { return status; } - while (number_of_lines > 0) { + for (uint8_t k = 0; k < number_of_dex_files; k++) { ProfileLineHeader line_header; + // First, read the line header to get the amount of data we need to read. status = ReadProfileLineHeader(fd, &line_header, error); if (status != kProfileLoadSuccess) { @@ -506,11 +752,10 @@ ProfileCompilationInfo::ProfileLoadSatus ProfileCompilationInfo::LoadInternal( } // Now read the actual profile line. - status = ReadProfileLine(fd, line_header, error); + status = ReadProfileLine(fd, number_of_dex_files, line_header, error); if (status != kProfileLoadSuccess) { return status; } - number_of_lines--; } // Check that we read everything and that profiles don't contain junk data. @@ -538,37 +783,112 @@ bool ProfileCompilationInfo::MergeWith(const ProfileCompilationInfo& other) { } } // All checksums match. Import the data. + + // The other profile might have a different indexing of dex files. + // That is because each dex files gets a 'dex_profile_index' on a first come first served basis. + // That means that the order in with the methods are added to the profile matters for the + // actual indices. + // The reason we cannot rely on the actual multidex index is that a single profile may store + // data from multiple splits. This means that a profile may contain a classes2.dex from split-A + // and one from split-B. + + // First, build a mapping from other_dex_profile_index to this_dex_profile_index. + // This will make sure that the ClassReferences will point to the correct dex file. + SafeMap<uint8_t, uint8_t> dex_profile_index_remap; + for (const auto& other_it : other.info_) { + const std::string& other_dex_location = other_it.first; + const DexFileData& other_dex_data = other_it.second; + auto info_it = info_.FindOrAdd(other_dex_location, DexFileData(other_dex_data.checksum, 0)); + const DexFileData& dex_data = info_it->second; + dex_profile_index_remap.Put(other_dex_data.profile_index, dex_data.profile_index); + } + + // Merge the actual profile data. for (const auto& other_it : other.info_) { const std::string& other_dex_location = other_it.first; const DexFileData& other_dex_data = other_it.second; auto info_it = info_.find(other_dex_location); - if (info_it == info_.end()) { - info_it = info_.Put(other_dex_location, DexFileData(other_dex_data.checksum)); - } - info_it->second.method_set.insert(other_dex_data.method_set.begin(), - other_dex_data.method_set.end()); + DCHECK(info_it != info_.end()); + + // Merge the classes. info_it->second.class_set.insert(other_dex_data.class_set.begin(), other_dex_data.class_set.end()); + + // Merge the methods and the inline caches. + for (const auto& other_method_it : other_dex_data.method_map) { + uint16_t other_method_index = other_method_it.first; + auto method_it = info_it->second.method_map.FindOrAdd(other_method_index); + const auto& other_inline_cache = other_method_it.second; + for (const auto& other_ic_it : other_inline_cache) { + uint16_t other_dex_pc = other_ic_it.first; + const ClassSet& other_class_set = other_ic_it.second.classes; + auto class_set = method_it->second.FindOrAdd(other_dex_pc); + for (const auto& class_it : other_class_set) { + class_set->second.AddClass(dex_profile_index_remap.Get( + class_it.dex_profile_index), class_it.type_index); + } + } + } } return true; } +static bool ChecksumMatch(uint32_t dex_file_checksum, uint32_t checksum) { + return kDebugIgnoreChecksum || dex_file_checksum == checksum; +} + static bool ChecksumMatch(const DexFile& dex_file, uint32_t checksum) { - return kDebugIgnoreChecksum || dex_file.GetLocationChecksum() == checksum; + return ChecksumMatch(dex_file.GetLocationChecksum(), checksum); } bool ProfileCompilationInfo::ContainsMethod(const MethodReference& method_ref) const { - auto info_it = info_.find(GetProfileDexFileKey(method_ref.dex_file->GetLocation())); + return FindMethod(method_ref.dex_file->GetLocation(), + method_ref.dex_file->GetLocationChecksum(), + method_ref.dex_method_index) != nullptr; +} + +const ProfileCompilationInfo::InlineCacheMap* +ProfileCompilationInfo::FindMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t dex_method_index) const { + auto info_it = info_.find(GetProfileDexFileKey(dex_location)); if (info_it != info_.end()) { - if (!ChecksumMatch(*method_ref.dex_file, info_it->second.checksum)) { - return false; + if (!ChecksumMatch(dex_checksum, info_it->second.checksum)) { + return nullptr; } - const std::set<uint16_t>& methods = info_it->second.method_set; - return methods.find(method_ref.dex_method_index) != methods.end(); + const MethodMap& methods = info_it->second.method_map; + const auto method_it = methods.find(dex_method_index); + return method_it == methods.end() ? nullptr : &(method_it->second); } - return false; + return nullptr; } +void ProfileCompilationInfo::DexFileToProfileIndex( + /*out*/std::vector<DexReference>* dex_references) const { + dex_references->resize(info_.size()); + for (const auto& info_it : info_) { + DexReference& dex_ref = (*dex_references)[info_it.second.profile_index]; + dex_ref.dex_location = info_it.first; + dex_ref.dex_checksum = info_it.second.checksum; + } +} + +bool ProfileCompilationInfo::GetMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t dex_method_index, + /*out*/OfflineProfileMethodInfo* pmi) const { + const InlineCacheMap* inline_caches = FindMethod(dex_location, dex_checksum, dex_method_index); + if (inline_caches == nullptr) { + return false; + } + + DexFileToProfileIndex(&pmi->dex_references); + // TODO(calin): maybe expose a direct pointer to avoid copying + pmi->inline_caches = *inline_caches; + return true; +} + + bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const { auto info_it = info_.find(GetProfileDexFileKey(dex_file.GetLocation())); if (info_it != info_.end()) { @@ -584,7 +904,7 @@ bool ProfileCompilationInfo::ContainsClass(const DexFile& dex_file, dex::TypeInd uint32_t ProfileCompilationInfo::GetNumberOfMethods() const { uint32_t total = 0; for (const auto& it : info_) { - total += it.second.method_set.size(); + total += it.second.method_map.size(); } return total; } @@ -645,19 +965,34 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* } } os << "\n\tmethods: "; - for (const auto method_it : dex_data.method_set) { + for (const auto method_it : dex_data.method_map) { if (dex_file != nullptr) { - os << "\n\t\t" << dex_file->PrettyMethod(method_it, true); + os << "\n\t\t" << dex_file->PrettyMethod(method_it.first, true); } else { - os << method_it << ","; + os << method_it.first; + } + + os << "["; + for (const auto& inline_cache_it : method_it.second) { + os << "{" << std::hex << inline_cache_it.first << std::dec << ":"; + if (inline_cache_it.second.is_megamorphic) { + os << "M"; + } else { + for (const ClassReference& class_ref : inline_cache_it.second.classes) { + os << "(" << static_cast<uint32_t>(class_ref.dex_profile_index) + << "," << class_ref.type_index.index_ << ")"; + } + } + os << "}"; } + os << "], "; } os << "\n\tclasses: "; for (const auto class_it : dex_data.class_set) { if (dex_file != nullptr) { os << "\n\t\t" << dex_file->PrettyType(class_it); } else { - os << class_it << ","; + os << class_it.index_ << ","; } } } @@ -762,4 +1097,44 @@ bool ProfileCompilationInfo::GenerateTestProfile(int fd, return info.Save(fd); } +bool ProfileCompilationInfo::OfflineProfileMethodInfo::operator==( + const OfflineProfileMethodInfo& other) const { + if (inline_caches.size() != other.inline_caches.size()) { + return false; + } + + // We can't use a simple equality test because we need to match the dex files + // of the inline caches which might have different profile indices. + for (const auto& inline_cache_it : inline_caches) { + uint16_t dex_pc = inline_cache_it.first; + const DexPcData dex_pc_data = inline_cache_it.second; + const auto other_it = other.inline_caches.find(dex_pc); + if (other_it == other.inline_caches.end()) { + return false; + } + const DexPcData& other_dex_pc_data = other_it->second; + if (dex_pc_data.is_megamorphic != other_dex_pc_data.is_megamorphic) { + return false; + } + for (const ClassReference& class_ref : dex_pc_data.classes) { + bool found = false; + for (const ClassReference& other_class_ref : other_dex_pc_data.classes) { + CHECK_LE(class_ref.dex_profile_index, dex_references.size()); + CHECK_LE(other_class_ref.dex_profile_index, other.dex_references.size()); + const DexReference& dex_ref = dex_references[class_ref.dex_profile_index]; + const DexReference& other_dex_ref = other.dex_references[other_class_ref.dex_profile_index]; + if (class_ref.type_index == other_class_ref.type_index && + dex_ref == other_dex_ref) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + } + return true; +} + } // namespace art diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index 758b46d74a..4bfbfcd287 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -31,6 +31,40 @@ namespace art { /** + * Convenient class to pass around profile information (including inline caches) + * without the need to hold GC-able objects. + */ +struct ProfileMethodInfo { + struct ProfileClassReference { + ProfileClassReference(const DexFile* dex, const dex::TypeIndex& index) + : dex_file(dex), type_index(index) {} + + const DexFile* dex_file; + const dex::TypeIndex type_index; + }; + + struct ProfileInlineCache { + ProfileInlineCache(uint32_t pc, const std::vector<ProfileClassReference>& profile_classes) + : dex_pc(pc), classes(profile_classes) {} + + const uint32_t dex_pc; + const std::vector<ProfileClassReference> classes; + }; + + ProfileMethodInfo(const DexFile* dex, uint32_t method_index) + : dex_file(dex), dex_method_index(method_index) {} + + ProfileMethodInfo(const DexFile* dex, + uint32_t method_index, + const std::vector<ProfileInlineCache>& caches) + : dex_file(dex), dex_method_index(method_index), inline_caches(caches) {} + + const DexFile* dex_file; + const uint32_t dex_method_index; + const std::vector<ProfileInlineCache> inline_caches; +}; + +/** * Profile information in a format suitable to be queried by the compiler and * performing profile guided compilation. * It is a serialize-friendly format based on information collected by the @@ -42,34 +76,130 @@ class ProfileCompilationInfo { static const uint8_t kProfileMagic[]; static const uint8_t kProfileVersion[]; + // Data structures for encoding the offline representation of inline caches. + // This is exposed as public in order to make it available to dex2oat compilations + // (see compiler/optimizing/inliner.cc). + + // A dex location together with its checksum. + struct DexReference { + DexReference() {} + + DexReference(const std::string& location, uint32_t checksum) + : dex_location(location), dex_checksum(checksum) {} + + bool operator==(const DexReference& other) const { + return dex_checksum == other.dex_checksum && dex_location == other.dex_location; + } + + std::string dex_location; + uint32_t dex_checksum; + }; + + // Encodes a class reference in the profile. + // The owning dex file is encoded as the index (dex_profile_index) it has in the + // profile rather than as a full DexRefence(location,checksum). + // This avoids excessive string copying when managing the profile data. + // The dex_profile_index is an index in either of: + // - OfflineProfileMethodInfo#dex_references vector (public use) + // - DexFileData#profile_index (internal use). + // Note that the dex_profile_index is not necessary the multidex index. + // We cannot rely on the actual multidex index because a single profile may store + // data from multiple splits. This means that a profile may contain a classes2.dex from split-A + // and one from split-B. + struct ClassReference { + ClassReference(uint8_t dex_profile_idx, const dex::TypeIndex& type_idx) : + dex_profile_index(dex_profile_idx), type_index(type_idx) {} + + bool operator==(const ClassReference& other) const { + return dex_profile_index == other.dex_profile_index && type_index == other.type_index; + } + bool operator<(const ClassReference& other) const { + return dex_profile_index == other.dex_profile_index + ? type_index < other.type_index + : dex_profile_index < other.dex_profile_index; + } + + uint8_t dex_profile_index; // the index of the owning dex in the profile info + dex::TypeIndex type_index; // the type index of the class + }; + + // The set of classes that can be found at a given dex pc. + using ClassSet = std::set<ClassReference>; + + // Encodes the actual inline cache for a given dex pc (whether or not the receiver is + // megamorphic and its possible types). + // If the receiver is megamorphic the set of classes will be empty. + struct DexPcData { + DexPcData() : is_megamorphic(false) {} + void AddClass(uint16_t dex_profile_idx, const dex::TypeIndex& type_idx); + void SetMegamorphic() { + is_megamorphic = true; + classes.clear(); + } + bool operator==(const DexPcData& other) const { + return is_megamorphic == other.is_megamorphic && classes == other.classes; + } + + bool is_megamorphic; + ClassSet classes; + }; + + // The inline cache map: DexPc -> DexPcData. + using InlineCacheMap = SafeMap<uint16_t, DexPcData>; + + // Encodes the full set of inline caches for a given method. + // The dex_references vector is indexed according to the ClassReference::dex_profile_index. + // i.e. the dex file of any ClassReference present in the inline caches can be found at + // dex_references[ClassReference::dex_profile_index]. + struct OfflineProfileMethodInfo { + bool operator==(const OfflineProfileMethodInfo& other) const; + + std::vector<DexReference> dex_references; + InlineCacheMap inline_caches; + }; + + // Public methods to create, extend or query the profile. + // Add the given methods and classes to the current profile object. - bool AddMethodsAndClasses(const std::vector<MethodReference>& methods, + bool AddMethodsAndClasses(const std::vector<ProfileMethodInfo>& methods, const std::set<DexCacheResolvedClasses>& resolved_classes); - // Loads profile information from the given file descriptor. + + // Load profile information from the given file descriptor. bool Load(int fd); + // Merge the data from another ProfileCompilationInfo into the current object. bool MergeWith(const ProfileCompilationInfo& info); - // Saves the profile data to the given file descriptor. + + // Save the profile data to the given file descriptor. bool Save(int fd); - // Loads and merges profile information from the given file into the current + + // Load and merge profile information from the given file into the current // object and tries to save it back to disk. // If `force` is true then the save will go through even if the given file // has bad data or its version does not match. In this cases the profile content // is ignored. bool MergeAndSave(const std::string& filename, uint64_t* bytes_written, bool force); - // Returns the number of methods that were profiled. + // Return the number of methods that were profiled. uint32_t GetNumberOfMethods() const; - // Returns the number of resolved classes that were profiled. + + // Return the number of resolved classes that were profiled. uint32_t GetNumberOfResolvedClasses() const; - // Returns true if the method reference is present in the profiling info. + // Return true if the method reference is present in the profiling info. bool ContainsMethod(const MethodReference& method_ref) const; - // Returns true if the class's type is present in the profiling info. + // Return true if the class's type is present in the profiling info. bool ContainsClass(const DexFile& dex_file, dex::TypeIndex type_idx) const; - // Dumps all the loaded profile info into a string and returns it. + // Return true if the method is present in the profiling info. + // If the method is found, `pmi` is populated with its inline caches. + bool GetMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t dex_method_index, + /*out*/OfflineProfileMethodInfo* pmi) const; + + // Dump all the loaded profile info into a string and returns it. // If dex_files is not null then the method indices will be resolved to their // names. // This is intended for testing and debugging. @@ -80,26 +210,35 @@ class ProfileCompilationInfo { void GetClassNames(const std::vector<std::unique_ptr<const DexFile>>* dex_files, std::set<std::string>* class_names) const; + void GetClassNames(const std::vector<const DexFile*>* dex_files, std::set<std::string>* class_names) const; + // Perform an equality test with the `other` profile information. bool Equals(const ProfileCompilationInfo& other); - static std::string GetProfileDexFileKey(const std::string& dex_location); - - // Returns the class descriptors for all of the classes in the profiles' class sets. + // Return the class descriptors for all of the classes in the profiles' class sets. // Note the dex location is actually the profile key, the caller needs to call back in to the // profile info stuff to generate a map back to the dex location. std::set<DexCacheResolvedClasses> GetResolvedClasses() const; - // Clears the resolved classes from the current object. + // Clear the resolved classes from the current object. void ClearResolvedClasses(); + // Return the profile key associated with the given dex location. + static std::string GetProfileDexFileKey(const std::string& dex_location); + + // Generate a test profile which will contain a percentage of the total maximum + // number of methods and classes (method_ratio and class_ratio). static bool GenerateTestProfile(int fd, uint16_t number_of_dex_files, uint16_t method_ratio, uint16_t class_ratio); + // Check that the given profile method info contain the same data. + static bool Equals(const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi1, + const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi2); + private: enum ProfileLoadSatus { kProfileLoadIOError, @@ -108,30 +247,71 @@ class ProfileCompilationInfo { kProfileLoadSuccess }; + // Maps a method dex index to its inline cache. + using MethodMap = SafeMap<uint16_t, InlineCacheMap>; + + // Internal representation of the profile information belonging to a dex file. struct DexFileData { - explicit DexFileData(uint32_t location_checksum) : checksum(location_checksum) {} + DexFileData(uint32_t location_checksum, uint16_t index) + : profile_index(index), checksum(location_checksum) {} + // The profile index of this dex file (matches ClassReference#dex_profile_index) + uint8_t profile_index; + // The dex checksum uint32_t checksum; - std::set<uint16_t> method_set; + // The methonds' profile information + MethodMap method_map; + // The classes which have been profiled. Note that these don't necessarily include + // all the classes that can be found in the inline caches reference. std::set<dex::TypeIndex> class_set; bool operator==(const DexFileData& other) const { - return checksum == other.checksum && method_set == other.method_set; + return checksum == other.checksum && method_map == other.method_map; } }; + // Maps dex file to their profile information. using DexFileToProfileInfoMap = SafeMap<const std::string, DexFileData>; + // Return the profile data for the given dex location or null if the dex location + // already exists but has a different checksum DexFileData* GetOrAddDexFileData(const std::string& dex_location, uint32_t checksum); + + // Add a method index to the profile (without inline caches). bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx); + + // Add a method to the profile using its online representation (containing runtime structures). + bool AddMethod(const ProfileMethodInfo& pmi); + + // Add a method to the profile using its offline representation. + // This is mostly used to facilitate testing. + bool AddMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t method_index, + const OfflineProfileMethodInfo& pmi); + + // Add a class index to the profile. bool AddClassIndex(const std::string& dex_location, uint32_t checksum, dex::TypeIndex type_idx); + + // Add all classes from the given dex cache to the the profile. bool AddResolvedClasses(const DexCacheResolvedClasses& classes); + // Search for the given method in the profile. + // If found, its inline cache map is returned, otherwise the method returns null. + const InlineCacheMap* FindMethod(const std::string& dex_location, + uint32_t dex_checksum, + uint16_t dex_method_index) const; + + // Encode the known dex_files into a vector. The index of a dex_reference will + // be the same as the profile index of the dex file (used to encode the ClassReferences). + void DexFileToProfileIndex(/*out*/std::vector<DexReference>* dex_references) const; + // Parsing functionality. + // The information present in the header of each profile line. struct ProfileLineHeader { std::string dex_location; - uint16_t method_set_size; uint16_t class_set_size; + uint32_t method_region_size_bytes; uint32_t checksum; }; @@ -150,12 +330,15 @@ class ProfileCompilationInfo { // Reads an uint value (high bits to low bits) and advances the current pointer // with the number of bits read. - template <typename T> T ReadUintAndAdvance(); + template <typename T> bool ReadUintAndAdvance(/*out*/ T* value); // Compares the given data with the content current pointer. If the contents are // equal it advances the current pointer by data_size. bool CompareAndAdvance(const uint8_t* data, size_t data_size); + // Returns true if the buffer has more data to read. + bool HasMoreData(); + // Get the underlying raw buffer. uint8_t* Get() { return storage_.get(); } @@ -165,24 +348,63 @@ class ProfileCompilationInfo { uint8_t* ptr_end_; }; + // Entry point for profile loding functionality. ProfileLoadSatus LoadInternal(int fd, std::string* error); + // Read the profile header from the given fd and store the number of profile + // lines into number_of_dex_files. ProfileLoadSatus ReadProfileHeader(int fd, - /*out*/uint16_t* number_of_lines, + /*out*/uint8_t* number_of_dex_files, /*out*/std::string* error); + // Read the header of a profile line from the given fd. ProfileLoadSatus ReadProfileLineHeader(int fd, /*out*/ProfileLineHeader* line_header, /*out*/std::string* error); + + // Read individual elements from the profile line header. + bool ReadProfileLineHeaderElements(SafeBuffer& buffer, + /*out*/uint16_t* dex_location_size, + /*out*/ProfileLineHeader* line_header, + /*out*/std::string* error); + + // Read a single profile line from the given fd. ProfileLoadSatus ReadProfileLine(int fd, + uint8_t number_of_dex_files, const ProfileLineHeader& line_header, /*out*/std::string* error); - bool ProcessLine(SafeBuffer& line_buffer, - uint16_t method_set_size, - uint16_t class_set_size, - uint32_t checksum, - const std::string& dex_location); + // Read all the classes from the buffer into the profile `info_` structure. + bool ReadClasses(SafeBuffer& buffer, + uint16_t classes_to_read, + const ProfileLineHeader& line_header, + /*out*/std::string* error); + + // Read all the methods from the buffer into the profile `info_` structure. + bool ReadMethods(SafeBuffer& buffer, + uint8_t number_of_dex_files, + const ProfileLineHeader& line_header, + /*out*/std::string* error); + + // Read the inline cache encoding from line_bufer into inline_cache. + bool ReadInlineCache(SafeBuffer& buffer, + uint8_t number_of_dex_files, + /*out*/InlineCacheMap* inline_cache, + /*out*/std::string* error); + + // Encode the inline cache into the given buffer. + void AddInlineCacheToBuffer(std::vector<uint8_t>* buffer, + const InlineCacheMap& inline_cache); + + // Return the number of bytes needed to encode the profile information + // for the methods in dex_data. + uint32_t GetMethodsRegionSize(const DexFileData& dex_data); + + // Group `classes` by their owning dex profile index and put the result in + // `dex_to_classes_map`. + void GroupClassesByDex( + const ClassSet& classes, + /*out*/SafeMap<uint8_t, std::vector<dex::TypeIndex>>* dex_to_classes_map); friend class ProfileCompilationInfoTest; friend class CompilerDriverProfileTest; diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index 835a5f3495..93b47acf46 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -57,6 +57,14 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { return info->AddMethodIndex(dex_location, checksum, method_index); } + bool AddMethod(const std::string& dex_location, + uint32_t checksum, + uint16_t method_index, + const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi, + ProfileCompilationInfo* info) { + return info->AddMethod(dex_location, checksum, method_index, pmi); + } + bool AddClass(const std::string& dex_location, uint32_t checksum, uint16_t class_index, @@ -73,17 +81,132 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { const std::vector<ArtMethod*>& methods, const std::set<DexCacheResolvedClasses>& resolved_classes) { ProfileCompilationInfo info; - std::vector<MethodReference> method_refs; + std::vector<ProfileMethodInfo> profile_methods; + ScopedObjectAccess soa(Thread::Current()); + for (ArtMethod* method : methods) { + profile_methods.emplace_back(method->GetDexFile(), method->GetDexMethodIndex()); + } + if (!info.AddMethodsAndClasses(profile_methods, resolved_classes)) { + return false; + } + if (info.GetNumberOfMethods() != profile_methods.size()) { + return false; + } + return info.MergeAndSave(filename, nullptr, false); + } + + // Saves the given art methods to a profile backed by 'filename' and adds + // some fake inline caches to it. The added inline caches are returned in + // the out map `profile_methods_map`. + bool SaveProfilingInfoWithFakeInlineCaches( + const std::string& filename, + const std::vector<ArtMethod*>& methods, + /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) { + ProfileCompilationInfo info; + std::vector<ProfileMethodInfo> profile_methods; ScopedObjectAccess soa(Thread::Current()); for (ArtMethod* method : methods) { - method_refs.emplace_back(method->GetDexFile(), method->GetDexMethodIndex()); + std::vector<ProfileMethodInfo::ProfileInlineCache> caches; + // Monomorphic + for (uint16_t dex_pc = 0; dex_pc < 1; dex_pc++) { + std::vector<ProfileMethodInfo::ProfileClassReference> classes; + classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0)); + caches.emplace_back(dex_pc, classes); + } + // Polymorphic + for (uint16_t dex_pc = 1; dex_pc < 2; dex_pc++) { + std::vector<ProfileMethodInfo::ProfileClassReference> classes; + for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) { + classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); + } + caches.emplace_back(dex_pc, classes); + } + // Megamorphic + for (uint16_t dex_pc = 2; dex_pc < 3; dex_pc++) { + std::vector<ProfileMethodInfo::ProfileClassReference> classes; + for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) { + classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); + } + caches.emplace_back(dex_pc, classes); + } + ProfileMethodInfo pmi(method->GetDexFile(), method->GetDexMethodIndex(), caches); + profile_methods.push_back(pmi); + profile_methods_map->Put(method, pmi); + } + + if (!info.AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>())) { + return false; } - if (!info.AddMethodsAndClasses(method_refs, resolved_classes)) { + if (info.GetNumberOfMethods() != profile_methods.size()) { return false; } return info.MergeAndSave(filename, nullptr, false); } + ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo( + const ProfileMethodInfo& pmi) { + ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi; + SafeMap<DexFile*, uint8_t> dex_map; // dex files to profile index + for (const auto& inline_cache : pmi.inline_caches) { + for (const auto& class_ref : inline_cache.classes) { + uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file), + static_cast<uint8_t>(dex_map.size()))->second; + offline_pmi.inline_caches + .FindOrAdd(inline_cache.dex_pc)->second + .AddClass(dex_profile_index, class_ref.type_index); + if (dex_profile_index >= offline_pmi.dex_references.size()) { + // This is a new dex. + const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey( + class_ref.dex_file->GetLocation()); + offline_pmi.dex_references.emplace_back(dex_key, + class_ref.dex_file->GetLocationChecksum()); + } + } + } + return offline_pmi; + } + + // Creates an offline profile used for testing inline caches. + ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() { + ProfileCompilationInfo::OfflineProfileMethodInfo pmi; + + pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); + pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2); + pmi.dex_references.emplace_back("dex_location3", /* checksum */ 3); + + // Monomorphic + for (uint16_t dex_pc = 0; dex_pc < 1; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data; + dex_pc_data.AddClass(0, dex::TypeIndex(0)); + pmi.inline_caches.Put(dex_pc, dex_pc_data); + } + // Polymorphic + for (uint16_t dex_pc = 1; dex_pc < 2; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data; + dex_pc_data.AddClass(0, dex::TypeIndex(0)); + dex_pc_data.AddClass(1, dex::TypeIndex(1)); + dex_pc_data.AddClass(2, dex::TypeIndex(2)); + + pmi.inline_caches.Put(dex_pc, dex_pc_data); + } + // Megamorphic + for (uint16_t dex_pc = 2; dex_pc < 3; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data; + dex_pc_data.is_megamorphic = true; + pmi.inline_caches.Put(dex_pc, dex_pc_data); + } + + return pmi; + } + + void MakeMegamorphic(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) { + for (auto it : pmi->inline_caches) { + for (uint16_t k = 0; k <= 2 * InlineCache::kIndividualCacheSize; k++) { + it.second.AddClass(0, dex::TypeIndex(k)); + } + } + } + // Cannot sizeof the actual arrays so hardcode the values here. // They should not change anyway. static constexpr int kProfileMagicSize = 4; @@ -235,12 +358,12 @@ TEST_F(ProfileCompilationInfoTest, SaveEmpty) { TEST_F(ProfileCompilationInfoTest, LoadEmpty) { ScratchFile profile; - ProfileCompilationInfo empyt_info; + ProfileCompilationInfo empty_info; ProfileCompilationInfo loaded_info; ASSERT_TRUE(profile.GetFile()->ResetOffset()); ASSERT_TRUE(loaded_info.Load(GetFd(profile))); - ASSERT_TRUE(loaded_info.Equals(empyt_info)); + ASSERT_TRUE(loaded_info.Equals(empty_info)); } TEST_F(ProfileCompilationInfoTest, BadMagic) { @@ -324,4 +447,214 @@ TEST_F(ProfileCompilationInfoTest, UnexpectedContent) { ASSERT_FALSE(loaded_info.Load(GetFd(profile))); } +TEST_F(ProfileCompilationInfoTest, SaveInlineCaches) { + ScratchFile profile; + + ProfileCompilationInfo saved_info; + ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo(); + + // Add methods with inline caches. + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + // Add a method which is part of the same dex file as one of the + // class from the inline caches. + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info)); + // Add a method which is outside the set of dex files. + ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info)); + } + + ASSERT_TRUE(saved_info.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + // Check that we get back what we saved. + ProfileCompilationInfo loaded_info; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(loaded_info.Load(GetFd(profile))); + + ASSERT_TRUE(loaded_info.Equals(saved_info)); + + ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1; + ASSERT_TRUE(loaded_info.GetMethod("dex_location1", + /* checksum */ 1, + /* method_idx */ 3, + &loaded_pmi1)); + ASSERT_TRUE(loaded_pmi1 == pmi); + ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi2; + ASSERT_TRUE(loaded_info.GetMethod("dex_location4", + /* checksum */ 4, + /* method_idx */ 3, + &loaded_pmi2)); + ASSERT_TRUE(loaded_pmi2 == pmi); +} + +TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) { + ScratchFile profile; + + ProfileCompilationInfo saved_info; + ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo(); + + // Add methods with inline caches. + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info)); + } + + ASSERT_TRUE(saved_info.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + // Make the inline caches megamorphic and add them to the profile again. + ProfileCompilationInfo saved_info_extra; + ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo(); + MakeMegamorphic(&pmi_extra); + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra)); + } + + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(saved_info_extra.Save(GetFd(profile))); + ASSERT_EQ(0, profile.GetFile()->Flush()); + + // Merge the profiles so that we have the same view as the file. + ASSERT_TRUE(saved_info.MergeWith(saved_info_extra)); + + // Check that we get back what we saved. + ProfileCompilationInfo loaded_info; + ASSERT_TRUE(profile.GetFile()->ResetOffset()); + ASSERT_TRUE(loaded_info.Load(GetFd(profile))); + + ASSERT_TRUE(loaded_info.Equals(saved_info)); + + ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1; + ASSERT_TRUE(loaded_info.GetMethod("dex_location1", + /* checksum */ 1, + /* method_idx */ 3, + &loaded_pmi1)); + ASSERT_TRUE(loaded_pmi1 == pmi_extra); +} + +TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) { + ScratchFile profile; + + Thread* self = Thread::Current(); + jobject class_loader; + { + ScopedObjectAccess soa(self); + class_loader = LoadDex("ProfileTestMultiDex"); + } + ASSERT_NE(class_loader, nullptr); + + // Save virtual methods from Main. + std::set<DexCacheResolvedClasses> resolved_classes; + std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;"); + + SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map; + ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches( + profile.GetFilename(), main_methods, &profile_methods_map)); + + // Check that what we saved is in the profile. + ProfileCompilationInfo info; + ASSERT_TRUE(info.Load(GetFd(profile))); + ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size()); + { + ScopedObjectAccess soa(self); + for (ArtMethod* m : main_methods) { + ASSERT_TRUE(info.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); + const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second; + ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi; + ASSERT_TRUE(info.GetMethod(m->GetDexFile()->GetLocation(), + m->GetDexFile()->GetLocationChecksum(), + m->GetDexMethodIndex(), + &offline_pmi)); + ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi = + ConvertProfileMethodInfo(pmi); + ASSERT_EQ(converted_pmi, offline_pmi); + } + } +} + +TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCahce) { + ScratchFile profile; + + ProfileCompilationInfo info; + ProfileCompilationInfo::OfflineProfileMethodInfo pmi1 = GetOfflineProfileMethodInfo(); + ProfileCompilationInfo::OfflineProfileMethodInfo pmi2 = GetOfflineProfileMethodInfo(); + // Modify the checksum to trigger a mismatch. + pmi2.dex_references[0].dex_checksum++; + + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /*method_idx*/ 0, pmi1, &info)); + ASSERT_FALSE(AddMethod("dex_location2", /* checksum */ 2, /*method_idx*/ 0, pmi2, &info)); +} + +// Verify that profiles behave correctly even if the methods are added in a different +// order and with a different dex profile indices for the dex files. +TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) { + ScratchFile profile; + + ProfileCompilationInfo info; + ProfileCompilationInfo info_reindexed; + + ProfileCompilationInfo::OfflineProfileMethodInfo pmi; + pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); + pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2); + for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data; + dex_pc_data.AddClass(0, dex::TypeIndex(0)); + dex_pc_data.AddClass(1, dex::TypeIndex(1)); + pmi.inline_caches.Put(dex_pc, dex_pc_data); + } + + ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed; + pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2); + pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1); + for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data; + dex_pc_data.AddClass(1, dex::TypeIndex(0)); + dex_pc_data.AddClass(0, dex::TypeIndex(1)); + pmi_reindexed.inline_caches.Put(dex_pc, dex_pc_data); + } + + // Profile 1 and Profile 2 get the same methods but in different order. + // This will trigger a different dex numbers. + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &info)); + ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &info)); + } + + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ASSERT_TRUE(AddMethod( + "dex_location2", /* checksum */ 2, method_idx, pmi_reindexed, &info_reindexed)); + ASSERT_TRUE(AddMethod( + "dex_location1", /* checksum */ 1, method_idx, pmi_reindexed, &info_reindexed)); + } + + ProfileCompilationInfo info_backup = info; + ASSERT_TRUE(info.MergeWith(info_reindexed)); + // Merging should have no effect as we're adding the exact same stuff. + ASSERT_TRUE(info.Equals(info_backup)); + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi1; + ASSERT_TRUE(info.GetMethod("dex_location1", + /* checksum */ 1, + /* method_idx */ method_idx, + &loaded_pmi1)); + ASSERT_TRUE(loaded_pmi1 == pmi); + ProfileCompilationInfo::OfflineProfileMethodInfo loaded_pmi2; + ASSERT_TRUE(info.GetMethod("dex_location2", + /* checksum */ 2, + /* method_idx */ method_idx, + &loaded_pmi2)); + ASSERT_TRUE(loaded_pmi2 == pmi); + } +} + +TEST_F(ProfileCompilationInfoTest, AddMoreDexFileThanLimit) { + ProfileCompilationInfo info; + // Save a few methods. + for (uint16_t i = 0; i < std::numeric_limits<uint8_t>::max(); i++) { + std::string dex_location = std::to_string(i); + ASSERT_TRUE(AddMethod(dex_location, /* checksum */ 1, /* method_idx */ i, &info)); + } + // We only support at most 255 dex files. + ASSERT_FALSE(AddMethod( + /*dex_location*/ "256", /* checksum */ 1, /* method_idx */ 0, &info)); +} + } // namespace art diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc index 025d10ccc0..61e6c4126a 100644 --- a/runtime/jit/profile_saver.cc +++ b/runtime/jit/profile_saver.cc @@ -236,10 +236,10 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { std::set<DexCacheResolvedClasses> resolved_classes_for_location; const std::string& filename = it.first; const std::set<std::string>& locations = it.second; - std::vector<MethodReference> methods_for_location; + std::vector<ProfileMethodInfo> profile_methods_for_location; for (const MethodReference& ref : methods) { if (locations.find(ref.dex_file->GetBaseLocation()) != locations.end()) { - methods_for_location.push_back(ref); + profile_methods_for_location.emplace_back(ref.dex_file, ref.dex_method_index); } } for (const DexCacheResolvedClasses& classes : resolved_classes) { @@ -253,7 +253,7 @@ void ProfileSaver::FetchAndCacheResolvedClassesAndMethods() { } } ProfileCompilationInfo* info = GetCachedProfiledInfo(filename); - info->AddMethodsAndClasses(methods_for_location, resolved_classes_for_location); + info->AddMethodsAndClasses(profile_methods_for_location, resolved_classes_for_location); total_number_of_profile_entries_cached += resolved_classes_for_location.size(); } max_number_of_profile_entries_cached_ = std::max( @@ -280,15 +280,15 @@ bool ProfileSaver::ProcessProfilingInfo(uint16_t* new_methods) { } const std::string& filename = it.first; const std::set<std::string>& locations = it.second; - std::vector<MethodReference> methods; + std::vector<ProfileMethodInfo> profile_methods; { ScopedObjectAccess soa(Thread::Current()); - jit_code_cache_->GetProfiledMethods(locations, methods); + jit_code_cache_->GetProfiledMethods(locations, profile_methods); total_number_of_code_cache_queries_++; } ProfileCompilationInfo* cached_info = GetCachedProfiledInfo(filename); - cached_info->AddMethodsAndClasses(methods, std::set<DexCacheResolvedClasses>()); + cached_info->AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>()); int64_t delta_number_of_methods = cached_info->GetNumberOfMethods() - static_cast<int64_t>(last_save_number_of_methods_); diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h index 1c58a83679..f42a8da8fa 100644 --- a/runtime/jit/profiling_info.h +++ b/runtime/jit/profiling_info.h @@ -39,7 +39,7 @@ class Class; // Once the classes_ array is full, we consider the INVOKE to be megamorphic. class InlineCache { public: - static constexpr uint16_t kIndividualCacheSize = 5; + static constexpr uint8_t kIndividualCacheSize = 5; private: uint32_t dex_pc_; diff --git a/runtime/safe_map.h b/runtime/safe_map.h index 49f80f31a8..e638fdb504 100644 --- a/runtime/safe_map.h +++ b/runtime/safe_map.h @@ -137,6 +137,16 @@ class SafeMap { return it->second; } + iterator FindOrAdd(const K& k, const V& v) { + iterator it = find(k); + return it == end() ? Put(k, v) : it; + } + + iterator FindOrAdd(const K& k) { + iterator it = find(k); + return it == end() ? Put(k, V()) : it; + } + bool Equals(const Self& rhs) const { return map_ == rhs.map_; } |