/* * 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 #include #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& 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& 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 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& 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> storage; }; } // namespace art #endif // ART_LIBPROFILE_PROFILE_PROFILE_TEST_HELPER_H_