1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ART_LIBPROFILE_PROFILE_PROFILE_TEST_HELPER_H_ 18 #define ART_LIBPROFILE_PROFILE_PROFILE_TEST_HELPER_H_ 19 20 #include <memory> 21 #include <vector> 22 23 #include "dex/test_dex_file_builder.h" 24 #include "profile/profile_compilation_info.h" 25 26 namespace art { 27 28 class ProfileTestHelper { 29 public: 30 ProfileTestHelper() = default; 31 32 using Hotness = ProfileCompilationInfo::MethodHotness; 33 using ProfileInlineCache = ProfileMethodInfo::ProfileInlineCache; 34 using ProfileSampleAnnotation = ProfileCompilationInfo::ProfileSampleAnnotation; 35 using ProfileIndexType = ProfileCompilationInfo::ProfileIndexType; 36 37 static bool AddMethod( 38 ProfileCompilationInfo* info, 39 const DexFile* dex, 40 uint16_t method_idx, 41 const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { 42 return AddMethod(info, dex, method_idx, Hotness::kFlagHot, annotation); 43 } 44 45 static bool AddMethod( 46 ProfileCompilationInfo* info, 47 const DexFile* dex, 48 uint16_t method_idx, 49 Hotness::Flag flags, 50 const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { 51 return info->AddMethod( 52 ProfileMethodInfo(MethodReference(dex, method_idx)), flags, annotation); 53 } 54 55 static bool AddMethod( 56 ProfileCompilationInfo* info, 57 const DexFile* dex, 58 uint16_t method_idx, 59 const std::vector<ProfileInlineCache>& inline_caches, 60 const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { 61 return AddMethod(info, dex, method_idx, inline_caches, Hotness::kFlagHot, annotation); 62 } 63 64 static bool AddMethod( 65 ProfileCompilationInfo* info, 66 const DexFile* dex, 67 uint16_t method_idx, 68 const std::vector<ProfileInlineCache>& inline_caches, 69 Hotness::Flag flags, 70 const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { 71 return info->AddMethod( 72 ProfileMethodInfo(MethodReference(dex, method_idx), inline_caches), flags, annotation); 73 } 74 75 static bool AddClass(ProfileCompilationInfo* info, 76 const DexFile* dex, 77 dex::TypeIndex type_index, 78 const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { 79 return info->AddClass(*dex, type_index, annotation); 80 } 81 ProfileIndexMatchesDexFile(const ProfileCompilationInfo & info,ProfileIndexType profile_index,const DexFile * dex_file)82 static bool ProfileIndexMatchesDexFile(const ProfileCompilationInfo& info, 83 ProfileIndexType profile_index, 84 const DexFile* dex_file) { 85 DCHECK(dex_file != nullptr); 86 std::array<const DexFile*, 1u> dex_files{dex_file}; 87 return dex_file == info.FindDexFileForProfileIndex(profile_index, dex_files); 88 } 89 90 // Compare different representations of inline caches for equality. EqualInlineCaches(const std::vector<ProfileMethodInfo::ProfileInlineCache> & expected,const DexFile * dex_file,const ProfileCompilationInfo::MethodHotness & actual_hotness,const ProfileCompilationInfo & info)91 static bool EqualInlineCaches(const std::vector<ProfileMethodInfo::ProfileInlineCache>& expected, 92 const DexFile* dex_file, 93 const ProfileCompilationInfo::MethodHotness& actual_hotness, 94 const ProfileCompilationInfo& info) { 95 CHECK(actual_hotness.IsHot()); 96 CHECK(actual_hotness.GetInlineCacheMap() != nullptr); 97 const ProfileCompilationInfo::InlineCacheMap& actual = *actual_hotness.GetInlineCacheMap(); 98 if (expected.size() != actual.size()) { 99 return false; 100 } 101 // The `expected` data should be sorted by dex pc. 102 CHECK(std::is_sorted(expected.begin(), 103 expected.end(), 104 [](auto&& lhs, auto&& rhs) { return lhs.dex_pc < rhs.dex_pc; })); 105 // The `actual` data is a map sorted by dex pc, so we can just iterate over both. 106 auto expected_it = expected.begin(); 107 for (auto it = actual.begin(), end = actual.end(); it != end; ++it, ++expected_it) { 108 uint32_t dex_pc = it->first; 109 const ProfileCompilationInfo::DexPcData& dex_pc_data = it->second; 110 if (dex_pc != expected_it->dex_pc) { 111 return false; 112 } 113 if (dex_pc_data.is_missing_types != expected_it->is_missing_types) { 114 return false; 115 } else if (dex_pc_data.is_missing_types) { 116 continue; // The classes do not matter if we're missing some types. 117 } 118 // The `expected_it->is_megamorphic` is not initialized. Check the number of classes. 119 bool expected_is_megamorphic = 120 (expected_it->classes.size() >= ProfileCompilationInfo::kIndividualInlineCacheSize); 121 if (dex_pc_data.is_megamorphic != expected_is_megamorphic) { 122 return false; 123 } else if (dex_pc_data.is_megamorphic) { 124 continue; // The classes do not matter if the inline cache is megamorphic. 125 } 126 if (dex_pc_data.classes.size() != expected_it->classes.size()) { 127 return false; 128 } 129 for (dex::TypeIndex type_index : dex_pc_data.classes) { 130 if (std::none_of(expected_it->classes.begin(), 131 expected_it->classes.end(), 132 [&](const TypeReference& type_ref) { 133 if (type_ref.dex_file == dex_file) { 134 return type_index == type_ref.TypeIndex(); 135 } else { 136 const char* expected_descriptor = 137 type_ref.dex_file->StringByTypeIdx(type_ref.TypeIndex()); 138 const char* descriptor = info.GetTypeDescriptor(dex_file, type_index); 139 return strcmp(expected_descriptor, descriptor) == 0; 140 } 141 })) { 142 return false; 143 } 144 } 145 } 146 return true; 147 } 148 149 protected: 150 static constexpr size_t kNumSharedTypes = 10u; 151 152 const DexFile* BuildDex(const std::string& location, 153 uint32_t location_checksum, 154 const std::string& class_descriptor, 155 size_t num_method_ids, 156 size_t num_class_ids = kNumSharedTypes + 1u) { 157 TestDexFileBuilder builder; 158 builder.AddType(class_descriptor); 159 CHECK_NE(num_class_ids, 0u); 160 size_t num_shared_ids = std::min(num_class_ids - 1u, kNumSharedTypes); 161 for (size_t shared_type_index = 0; shared_type_index != num_shared_ids; ++shared_type_index) { 162 builder.AddType("LSharedType" + std::to_string(shared_type_index) + ";"); 163 } 164 for (size_t i = 1u + num_shared_ids; i < num_class_ids; ++i) { 165 builder.AddType("LFiller" + std::to_string(i) + ";"); 166 } 167 for (size_t method_index = 0; method_index != num_method_ids; ++method_index) { 168 // Some tests add the maximum number of methods (`num_method_ids` is 2^16) and we 169 // do not want to waste memory with that many unique name strings (with identical 170 // proto id). So create up to num_shared_ids^2 proto ids and only 171 // num_method_ids/num_shared_ids^2 names. 172 size_t return_type_index = method_index % num_shared_ids; 173 size_t arg_type_index = (method_index / num_shared_ids) % num_shared_ids; 174 size_t method_name_index = (method_index / num_shared_ids) / num_shared_ids; 175 std::string return_type = "LSharedType" + std::to_string(return_type_index) + ";"; 176 std::string arg_type = "LSharedType" + std::to_string(arg_type_index) + ";"; 177 std::string signature = "(" + arg_type + ")" + return_type; 178 builder.AddMethod(class_descriptor, signature, "m" + std::to_string(method_name_index)); 179 } 180 storage.push_back(builder.Build(location, location_checksum)); 181 return storage.back().get(); 182 } 183 184 std::vector<std::unique_ptr<const DexFile>> storage; 185 }; 186 187 } // namespace art 188 189 #endif // ART_LIBPROFILE_PROFILE_PROFILE_TEST_HELPER_H_ 190