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(ProfileMethodInfo(MethodReference(dex, method_idx)), 52 flags, 53 annotation, 54 /*is_test=*/ true); 55 } 56 57 static bool AddMethod( 58 ProfileCompilationInfo* info, 59 const DexFile* dex, 60 uint16_t method_idx, 61 const std::vector<ProfileInlineCache>& inline_caches, 62 const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { 63 return AddMethod(info, dex, method_idx, inline_caches, Hotness::kFlagHot, annotation); 64 } 65 66 static bool AddMethod( 67 ProfileCompilationInfo* info, 68 const DexFile* dex, 69 uint16_t method_idx, 70 const std::vector<ProfileInlineCache>& inline_caches, 71 Hotness::Flag flags, 72 const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { 73 return info->AddMethod(ProfileMethodInfo(MethodReference(dex, method_idx), inline_caches), 74 flags, 75 annotation, 76 /*is_test=*/ true); 77 } 78 79 static bool AddClass(ProfileCompilationInfo* info, 80 const DexFile* dex, 81 dex::TypeIndex type_index, 82 const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) { 83 return info->AddClass(*dex, type_index, annotation); 84 } 85 ProfileIndexMatchesDexFile(const ProfileCompilationInfo & info,ProfileIndexType profile_index,const DexFile * dex_file)86 static bool ProfileIndexMatchesDexFile(const ProfileCompilationInfo& info, 87 ProfileIndexType profile_index, 88 const DexFile* dex_file) { 89 DCHECK(dex_file != nullptr); 90 std::array<const DexFile*, 1u> dex_files{dex_file}; 91 return dex_file == info.FindDexFileForProfileIndex(profile_index, dex_files); 92 } 93 94 // 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)95 static bool EqualInlineCaches(const std::vector<ProfileMethodInfo::ProfileInlineCache>& expected, 96 const DexFile* dex_file, 97 const ProfileCompilationInfo::MethodHotness& actual_hotness, 98 const ProfileCompilationInfo& info) { 99 CHECK(actual_hotness.IsHot()); 100 CHECK(actual_hotness.GetInlineCacheMap() != nullptr); 101 const ProfileCompilationInfo::InlineCacheMap& actual = *actual_hotness.GetInlineCacheMap(); 102 if (expected.size() != actual.size()) { 103 return false; 104 } 105 // The `expected` data should be sorted by dex pc. 106 CHECK(std::is_sorted(expected.begin(), 107 expected.end(), 108 [](auto&& lhs, auto&& rhs) { return lhs.dex_pc < rhs.dex_pc; })); 109 // The `actual` data is a map sorted by dex pc, so we can just iterate over both. 110 auto expected_it = expected.begin(); 111 for (auto it = actual.begin(), end = actual.end(); it != end; ++it, ++expected_it) { 112 uint32_t dex_pc = it->first; 113 const ProfileCompilationInfo::DexPcData& dex_pc_data = it->second; 114 if (dex_pc != expected_it->dex_pc) { 115 return false; 116 } 117 if (dex_pc_data.is_missing_types != expected_it->is_missing_types) { 118 return false; 119 } else if (dex_pc_data.is_missing_types) { 120 continue; // The classes do not matter if we're missing some types. 121 } 122 // The `expected_it->is_megamorphic` is not initialized. Check the number of classes. 123 bool expected_is_megamorphic = 124 (expected_it->classes.size() >= ProfileCompilationInfo::kIndividualInlineCacheSize); 125 if (dex_pc_data.is_megamorphic != expected_is_megamorphic) { 126 return false; 127 } else if (dex_pc_data.is_megamorphic) { 128 continue; // The classes do not matter if the inline cache is megamorphic. 129 } 130 if (dex_pc_data.classes.size() != expected_it->classes.size()) { 131 return false; 132 } 133 for (dex::TypeIndex type_index : dex_pc_data.classes) { 134 if (std::none_of(expected_it->classes.begin(), 135 expected_it->classes.end(), 136 [&](const TypeReference& type_ref) { 137 if (type_ref.dex_file == dex_file) { 138 return type_index == type_ref.TypeIndex(); 139 } else { 140 const char* expected_descriptor = 141 type_ref.dex_file->GetTypeDescriptor(type_ref.TypeIndex()); 142 const char* descriptor = info.GetTypeDescriptor(dex_file, type_index); 143 return strcmp(expected_descriptor, descriptor) == 0; 144 } 145 })) { 146 return false; 147 } 148 } 149 } 150 return true; 151 } 152 153 protected: 154 static constexpr size_t kNumSharedTypes = 10u; 155 156 const DexFile* BuildDex(const std::string& location, 157 uint32_t location_checksum, 158 const std::string& class_descriptor, 159 size_t num_method_ids, 160 size_t num_class_ids = kNumSharedTypes + 1u) { 161 TestDexFileBuilder builder; 162 builder.AddType(class_descriptor); 163 CHECK_NE(num_class_ids, 0u); 164 size_t num_shared_ids = std::min(num_class_ids - 1u, kNumSharedTypes); 165 for (size_t shared_type_index = 0; shared_type_index != num_shared_ids; ++shared_type_index) { 166 builder.AddType("LSharedType" + std::to_string(shared_type_index) + ";"); 167 } 168 for (size_t i = 1u + num_shared_ids; i < num_class_ids; ++i) { 169 builder.AddType("LFiller" + std::to_string(i) + ";"); 170 } 171 for (size_t method_index = 0; method_index != num_method_ids; ++method_index) { 172 // Some tests add the maximum number of methods (`num_method_ids` is 2^16) and we 173 // do not want to waste memory with that many unique name strings (with identical 174 // proto id). So create up to num_shared_ids^2 proto ids and only 175 // num_method_ids/num_shared_ids^2 names. 176 size_t return_type_index = method_index % num_shared_ids; 177 size_t arg_type_index = (method_index / num_shared_ids) % num_shared_ids; 178 size_t method_name_index = (method_index / num_shared_ids) / num_shared_ids; 179 std::string return_type = "LSharedType" + std::to_string(return_type_index) + ";"; 180 std::string arg_type = "LSharedType" + std::to_string(arg_type_index) + ";"; 181 std::string signature = ART_FORMAT("({}){}", arg_type, return_type); 182 builder.AddMethod(class_descriptor, signature, "m" + std::to_string(method_name_index)); 183 } 184 storage.push_back(builder.Build(location, location_checksum)); 185 return storage.back().get(); 186 } 187 188 std::vector<std::unique_ptr<const DexFile>> storage; 189 }; 190 191 } // namespace art 192 193 #endif // ART_LIBPROFILE_PROFILE_PROFILE_TEST_HELPER_H_ 194