diff options
| -rw-r--r-- | runtime/jit/profile_compilation_info.cc | 62 | ||||
| -rw-r--r-- | runtime/jit/profile_compilation_info.h | 38 | ||||
| -rw-r--r-- | runtime/jit/profile_compilation_info_test.cc | 169 |
3 files changed, 246 insertions, 23 deletions
diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 4bf2895723..de4d02edaf 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -775,8 +775,14 @@ bool ProfileCompilationInfo::ReadInlineCache( for (; dex_classes_size > 0; dex_classes_size--) { uint16_t type_index; READ_UINT(uint16_t, buffer, type_index, error); - dex_pc_data->AddClass(dex_profile_index_remap.Get(dex_profile_index), - dex::TypeIndex(type_index)); + auto it = dex_profile_index_remap.find(dex_profile_index); + if (it == dex_profile_index_remap.end()) { + // If we don't have an index that's because the dex file was filtered out when loading. + // Set missing types on the dex pc data. + dex_pc_data->SetIsMissingTypes(); + } else { + dex_pc_data->AddClass(it->second, dex::TypeIndex(type_index)); + } } } } @@ -1036,10 +1042,11 @@ ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::ReadProfileLin // TODO(calin): Fix this API. ProfileCompilationInfo::Load should be static and // return a unique pointer to a ProfileCompilationInfo upon success. -bool ProfileCompilationInfo::Load(int fd, bool merge_classes) { +bool ProfileCompilationInfo::Load( + int fd, bool merge_classes, const ProfileLoadFilterFn& filter_fn) { std::string error; - ProfileLoadStatus status = LoadInternal(fd, &error, merge_classes); + ProfileLoadStatus status = LoadInternal(fd, &error, merge_classes, filter_fn); if (status == kProfileLoadSuccess) { return true; @@ -1245,7 +1252,10 @@ bool ProfileCompilationInfo::ProfileSource::HasEmptyContent() const { // TODO(calin): fail fast if the dex checksums don't match. ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::LoadInternal( - int32_t fd, std::string* error, bool merge_classes) { + int32_t fd, + std::string* error, + bool merge_classes, + const ProfileLoadFilterFn& filter_fn) { ScopedTrace trace(__PRETTY_FUNCTION__); DCHECK_GE(fd, 0); @@ -1327,20 +1337,29 @@ ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::LoadInternal( } SafeMap<uint8_t, uint8_t> dex_profile_index_remap; - if (!RemapProfileIndex(profile_line_headers, &dex_profile_index_remap)) { + if (!RemapProfileIndex(profile_line_headers, filter_fn, &dex_profile_index_remap)) { return kProfileLoadBadData; } for (uint8_t k = 0; k < number_of_dex_files; k++) { - // Now read the actual profile line. - status = ReadProfileLine(uncompressed_data, - number_of_dex_files, - profile_line_headers[k], - dex_profile_index_remap, - merge_classes, - error); - if (status != kProfileLoadSuccess) { - return status; + if (!filter_fn(profile_line_headers[k].dex_location, profile_line_headers[k].checksum)) { + // We have to skip the line. Advanced the current pointer of the buffer. + size_t profile_line_size = + profile_line_headers[k].class_set_size + + profile_line_headers[k].method_region_size_bytes + + DexFileData::ComputeBitmapStorage(profile_line_headers[k].num_method_ids); + uncompressed_data.Advance(profile_line_size); + } else { + // Now read the actual profile line. + status = ReadProfileLine(uncompressed_data, + number_of_dex_files, + profile_line_headers[k], + dex_profile_index_remap, + merge_classes, + error); + if (status != kProfileLoadSuccess) { + return status; + } } } @@ -1355,12 +1374,16 @@ ProfileCompilationInfo::ProfileLoadStatus ProfileCompilationInfo::LoadInternal( bool ProfileCompilationInfo::RemapProfileIndex( const std::vector<ProfileLineHeader>& profile_line_headers, + const ProfileLoadFilterFn& filter_fn, /*out*/SafeMap<uint8_t, uint8_t>* dex_profile_index_remap) { // First verify that all checksums match. This will avoid adding garbage to // the current profile info. // Note that the number of elements should be very small, so this should not // be a performance issue. for (const ProfileLineHeader other_profile_line_header : profile_line_headers) { + if (!filter_fn(other_profile_line_header.dex_location, other_profile_line_header.checksum)) { + continue; + } // verify_checksum is false because we want to differentiate between a missing dex data and // a mismatched checksum. const DexFileData* dex_data = FindDexData(other_profile_line_header.dex_location, @@ -1374,6 +1397,9 @@ bool ProfileCompilationInfo::RemapProfileIndex( // All checksums match. Import the data. uint32_t num_dex_files = static_cast<uint32_t>(profile_line_headers.size()); for (uint32_t i = 0; i < num_dex_files; i++) { + if (!filter_fn(profile_line_headers[i].dex_location, profile_line_headers[i].checksum)) { + continue; + } const DexFileData* dex_data = GetOrAddDexFileData(profile_line_headers[i].dex_location, profile_line_headers[i].checksum, profile_line_headers[i].num_method_ids); @@ -2054,4 +2080,10 @@ bool ProfileCompilationInfo::UpdateProfileKeys( return true; } +bool ProfileCompilationInfo::ProfileFilterFnAcceptAll( + const std::string& dex_location ATTRIBUTE_UNUSED, + uint32_t checksum ATTRIBUTE_UNUSED) { + return true; +} + } // namespace art diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index 350ce9ed8d..1973f3f09e 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -306,7 +306,19 @@ class ProfileCompilationInfo { // Load or Merge profile information from the given file descriptor. // If the current profile is non-empty the load will fail. // If merge_classes is set to false, classes will not be merged/loaded. - bool Load(int fd, bool merge_classes = true); + // If filter_fn is present, it will be used to filter out profile data belonging + // to dex file which do not comply with the filter + // (i.e. for which filter_fn(dex_location, dex_checksum) is false). + using ProfileLoadFilterFn = std::function<bool(const std::string&, uint32_t)>; + // Profile filter method which accepts all dex locations. + // This is convenient to use when we need to accept all locations without repeating the same + // lambda. + static bool ProfileFilterFnAcceptAll(const std::string& dex_location, uint32_t checksum); + + bool Load( + int fd, + bool merge_classes = true, + const ProfileLoadFilterFn& filter_fn = ProfileFilterFnAcceptAll); // Verify integrity of the profile file with the provided dex files. // If there exists a DexData object which maps to a dex_file, then it verifies that: @@ -459,14 +471,21 @@ class ProfileCompilationInfo { class_set(std::less<dex::TypeIndex>(), allocator->Adapter(kArenaAllocProfile)), num_method_ids(num_methods), bitmap_storage(allocator->Adapter(kArenaAllocProfile)) { - const size_t num_bits = num_method_ids * kBitmapIndexCount; - bitmap_storage.resize(RoundUp(num_bits, kBitsPerByte) / kBitsPerByte); + bitmap_storage.resize(ComputeBitmapStorage(num_method_ids)); if (!bitmap_storage.empty()) { method_bitmap = - BitMemoryRegion(MemoryRegion(&bitmap_storage[0], bitmap_storage.size()), 0, num_bits); + BitMemoryRegion(MemoryRegion( + &bitmap_storage[0], bitmap_storage.size()), 0, ComputeBitmapBits(num_method_ids)); } } + static size_t ComputeBitmapBits(uint32_t num_method_ids) { + return num_method_ids * kBitmapIndexCount; + } + static size_t ComputeBitmapStorage(uint32_t num_method_ids) { + return RoundUp(ComputeBitmapBits(num_method_ids), kBitsPerByte) / kBitsPerByte; + } + bool operator==(const DexFileData& other) const { return checksum == other.checksum && method_map == other.method_map; } @@ -689,10 +708,12 @@ class ProfileCompilationInfo { /*out*/ std::unique_ptr<ProfileSource>* source, /*out*/ std::string* error); - // Entry point for profile loding functionality. - ProfileLoadStatus LoadInternal(int32_t fd, - std::string* error, - bool merge_classes = true); + // Entry point for profile loading functionality. + ProfileLoadStatus LoadInternal( + int32_t fd, + std::string* error, + bool merge_classes = true, + const ProfileLoadFilterFn& filter_fn = ProfileFilterFnAcceptAll); // Read the profile header from the given fd and store the number of profile // lines into number_of_dex_files. @@ -736,6 +757,7 @@ class ProfileCompilationInfo { // The method generates mapping of profile indices while merging a new profile // data into current data. It returns true, if the mapping was successful. bool RemapProfileIndex(const std::vector<ProfileLineHeader>& profile_line_headers, + const ProfileLoadFilterFn& filter_fn, /*out*/SafeMap<uint8_t, uint8_t>* dex_profile_index_remap); // Read the inline cache encoding from line_bufer into inline_cache. diff --git a/runtime/jit/profile_compilation_info_test.cc b/runtime/jit/profile_compilation_info_test.cc index b4265d1a28..4ac11ee422 100644 --- a/runtime/jit/profile_compilation_info_test.cc +++ b/runtime/jit/profile_compilation_info_test.cc @@ -314,6 +314,10 @@ class ProfileCompilationInfoTest : public CommonRuntimeTest { } } + bool IsEmpty(const ProfileCompilationInfo& info) { + return info.IsEmpty(); + } + // Cannot sizeof the actual arrays so hard code the values here. // They should not change anyway. static constexpr int kProfileMagicSize = 4; @@ -1124,4 +1128,169 @@ TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyFail) { ASSERT_FALSE(info.UpdateProfileKeys(dex_files)); } +TEST_F(ProfileCompilationInfoTest, FilteredLoading) { + 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)); + ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, 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()); + + // Filter out dex locations. Keep only dex_location1 and dex_location2. + ProfileCompilationInfo::ProfileLoadFilterFn filter_fn = + [](const std::string& dex_location, uint32_t checksum) -> bool { + return (dex_location == "dex_location1" && checksum == 1) + || (dex_location == "dex_location3" && checksum == 3); + }; + ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn)); + + // Verify that we filtered out locations during load. + + // Dex location 2 and 4 should have been filtered out + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + ASSERT_TRUE(nullptr == loaded_info.GetMethod("dex_location2", /* checksum */ 2, method_idx)); + ASSERT_TRUE(nullptr == loaded_info.GetMethod("dex_location4", /* checksum */ 4, method_idx)); + } + + // Dex location 1 should have all all the inline caches referencing dex location 2 set to + // missing types. + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + // The methods for dex location 1 should be in the profile data. + std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 = + loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ method_idx); + ASSERT_TRUE(loaded_pmi1 != nullptr); + + // Verify the inline cache. + // Everything should be as constructed by GetOfflineProfileMethodInfo with the exception + // of the inline caches referring types from dex_location2. + // These should be set to IsMissingType. + ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); + + // Monomorphic types should remain the same as dex_location1 was kept. + for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get()); + dex_pc_data.AddClass(0, dex::TypeIndex(0)); + ic_map->Put(dex_pc, dex_pc_data); + } + // Polymorphic inline cache should have been transformed to IsMissingType due to + // the removal of dex_location2. + for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get()); + dex_pc_data.SetIsMissingTypes(); + ic_map->Put(dex_pc, dex_pc_data); + } + + // Megamorphic are not affected by removal of dex files. + for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get()); + dex_pc_data.SetIsMegamorphic(); + ic_map->Put(dex_pc, dex_pc_data); + } + // Missing types are not affected be removal of dex files. + for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) { + ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get()); + dex_pc_data.SetIsMissingTypes(); + ic_map->Put(dex_pc, dex_pc_data); + } + + ProfileCompilationInfo::OfflineProfileMethodInfo expected_pmi(ic_map); + + // The dex references should not have dex_location2 in the list. + expected_pmi.dex_references.emplace_back("dex_location1", /* checksum */1, kMaxMethodIds); + expected_pmi.dex_references.emplace_back("dex_location3", /* checksum */3, kMaxMethodIds); + + // Now check that we get back what we expect. + ASSERT_TRUE(*loaded_pmi1 == expected_pmi); + } +} + +TEST_F(ProfileCompilationInfoTest, FilteredLoadingRemoveAll) { + 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)); + ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, 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()); + + // Remove all elements. + ProfileCompilationInfo::ProfileLoadFilterFn filter_fn = + [](const std::string&, uint32_t) -> bool { return false; }; + ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn)); + + // Verify that we filtered out everything. + ASSERT_TRUE(IsEmpty(loaded_info)); +} + +TEST_F(ProfileCompilationInfoTest, FilteredLoadingKeepAll) { + 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()); + + // Keep all elements. + ProfileCompilationInfo::ProfileLoadFilterFn filter_fn = + [](const std::string&, uint32_t) -> bool { return true; }; + ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn)); + + + ASSERT_TRUE(loaded_info.Equals(saved_info)); + + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 = + loaded_info.GetMethod("dex_location1", /* checksum */ 1, method_idx); + ASSERT_TRUE(loaded_pmi1 != nullptr); + ASSERT_TRUE(*loaded_pmi1 == pmi); + } + for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { + std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 = + loaded_info.GetMethod("dex_location4", /* checksum */ 4, method_idx); + ASSERT_TRUE(loaded_pmi2 != nullptr); + ASSERT_TRUE(*loaded_pmi2 == pmi); + } +} + } // namespace art |