| /* |
| * Copyright (C) 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef ART_LIBPROFILE_PROFILE_PROFILE_TEST_HELPER_H_ |
| #define ART_LIBPROFILE_PROFILE_PROFILE_TEST_HELPER_H_ |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "dex/test_dex_file_builder.h" |
| #include "profile/profile_compilation_info.h" |
| |
| namespace art { |
| |
| class ProfileTestHelper { |
| public: |
| ProfileTestHelper() = default; |
| |
| using Hotness = ProfileCompilationInfo::MethodHotness; |
| using ProfileInlineCache = ProfileMethodInfo::ProfileInlineCache; |
| using ProfileSampleAnnotation = ProfileCompilationInfo::ProfileSampleAnnotation; |
| using ProfileIndexType = ProfileCompilationInfo::ProfileIndexType; |
| |
| static bool AddMethod( |
| ProfileCompilationInfo* info, |
| const DexFile* dex, |
| uint16_t method_idx, |
| const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { |
| return AddMethod(info, dex, method_idx, Hotness::kFlagHot, annotation); |
| } |
| |
| static bool AddMethod( |
| ProfileCompilationInfo* info, |
| const DexFile* dex, |
| uint16_t method_idx, |
| Hotness::Flag flags, |
| const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { |
| return info->AddMethod( |
| ProfileMethodInfo(MethodReference(dex, method_idx)), flags, annotation); |
| } |
| |
| static bool AddMethod( |
| ProfileCompilationInfo* info, |
| const DexFile* dex, |
| uint16_t method_idx, |
| const std::vector<ProfileInlineCache>& inline_caches, |
| const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { |
| return AddMethod(info, dex, method_idx, inline_caches, Hotness::kFlagHot, annotation); |
| } |
| |
| static bool AddMethod( |
| ProfileCompilationInfo* info, |
| const DexFile* dex, |
| uint16_t method_idx, |
| const std::vector<ProfileInlineCache>& inline_caches, |
| Hotness::Flag flags, |
| const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { |
| return info->AddMethod( |
| ProfileMethodInfo(MethodReference(dex, method_idx), inline_caches), flags, annotation); |
| } |
| |
| static bool AddClass(ProfileCompilationInfo* info, |
| const DexFile* dex, |
| dex::TypeIndex type_index, |
| const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { |
| return info->AddClass(*dex, type_index, annotation); |
| } |
| |
| static bool ProfileIndexMatchesDexFile(const ProfileCompilationInfo& info, |
| ProfileIndexType profile_index, |
| const DexFile* dex_file) { |
| DCHECK(dex_file != nullptr); |
| std::array<const DexFile*, 1u> dex_files{dex_file}; |
| return dex_file == info.FindDexFileForProfileIndex(profile_index, dex_files); |
| } |
| |
| // Compare different representations of inline caches for equality. |
| static bool EqualInlineCaches(const std::vector<ProfileMethodInfo::ProfileInlineCache>& expected, |
| const DexFile* dex_file, |
| const ProfileCompilationInfo::MethodHotness& actual_hotness, |
| const ProfileCompilationInfo& info) { |
| CHECK(actual_hotness.IsHot()); |
| CHECK(actual_hotness.GetInlineCacheMap() != nullptr); |
| const ProfileCompilationInfo::InlineCacheMap& actual = *actual_hotness.GetInlineCacheMap(); |
| if (expected.size() != actual.size()) { |
| return false; |
| } |
| // The `expected` data should be sorted by dex pc. |
| CHECK(std::is_sorted(expected.begin(), |
| expected.end(), |
| [](auto&& lhs, auto&& rhs) { return lhs.dex_pc < rhs.dex_pc; })); |
| // The `actual` data is a map sorted by dex pc, so we can just iterate over both. |
| auto expected_it = expected.begin(); |
| for (auto it = actual.begin(), end = actual.end(); it != end; ++it, ++expected_it) { |
| uint32_t dex_pc = it->first; |
| const ProfileCompilationInfo::DexPcData& dex_pc_data = it->second; |
| if (dex_pc != expected_it->dex_pc) { |
| return false; |
| } |
| if (dex_pc_data.is_missing_types != expected_it->is_missing_types) { |
| return false; |
| } else if (dex_pc_data.is_missing_types) { |
| continue; // The classes do not matter if we're missing some types. |
| } |
| // The `expected_it->is_megamorphic` is not initialized. Check the number of classes. |
| bool expected_is_megamorphic = |
| (expected_it->classes.size() >= ProfileCompilationInfo::kIndividualInlineCacheSize); |
| if (dex_pc_data.is_megamorphic != expected_is_megamorphic) { |
| return false; |
| } else if (dex_pc_data.is_megamorphic) { |
| continue; // The classes do not matter if the inline cache is megamorphic. |
| } |
| if (dex_pc_data.classes.size() != expected_it->classes.size()) { |
| return false; |
| } |
| for (dex::TypeIndex type_index : dex_pc_data.classes) { |
| if (std::none_of(expected_it->classes.begin(), |
| expected_it->classes.end(), |
| [&](const TypeReference& type_ref) { |
| if (type_ref.dex_file == dex_file) { |
| return type_index == type_ref.TypeIndex(); |
| } else { |
| const char* expected_descriptor = |
| type_ref.dex_file->StringByTypeIdx(type_ref.TypeIndex()); |
| const char* descriptor = info.GetTypeDescriptor(dex_file, type_index); |
| return strcmp(expected_descriptor, descriptor) == 0; |
| } |
| })) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| protected: |
| static constexpr size_t kNumSharedTypes = 10u; |
| |
| const DexFile* BuildDex(const std::string& location, |
| uint32_t location_checksum, |
| const std::string& class_descriptor, |
| size_t num_method_ids, |
| size_t num_class_ids = kNumSharedTypes + 1u) { |
| TestDexFileBuilder builder; |
| builder.AddType(class_descriptor); |
| CHECK_NE(num_class_ids, 0u); |
| size_t num_shared_ids = std::min(num_class_ids - 1u, kNumSharedTypes); |
| for (size_t shared_type_index = 0; shared_type_index != num_shared_ids; ++shared_type_index) { |
| builder.AddType("LSharedType" + std::to_string(shared_type_index) + ";"); |
| } |
| for (size_t i = 1u + num_shared_ids; i < num_class_ids; ++i) { |
| builder.AddType("LFiller" + std::to_string(i) + ";"); |
| } |
| for (size_t method_index = 0; method_index != num_method_ids; ++method_index) { |
| // Some tests add the maximum number of methods (`num_method_ids` is 2^16) and we |
| // do not want to waste memory with that many unique name strings (with identical |
| // proto id). So create up to num_shared_ids^2 proto ids and only |
| // num_method_ids/num_shared_ids^2 names. |
| size_t return_type_index = method_index % num_shared_ids; |
| size_t arg_type_index = (method_index / num_shared_ids) % num_shared_ids; |
| size_t method_name_index = (method_index / num_shared_ids) / num_shared_ids; |
| std::string return_type = "LSharedType" + std::to_string(return_type_index) + ";"; |
| std::string arg_type = "LSharedType" + std::to_string(arg_type_index) + ";"; |
| std::string signature = "(" + arg_type + ")" + return_type; |
| builder.AddMethod(class_descriptor, signature, "m" + std::to_string(method_name_index)); |
| } |
| storage.push_back(builder.Build(location, location_checksum)); |
| return storage.back().get(); |
| } |
| |
| std::vector<std::unique_ptr<const DexFile>> storage; |
| }; |
| |
| } // namespace art |
| |
| #endif // ART_LIBPROFILE_PROFILE_PROFILE_TEST_HELPER_H_ |