blob: 9410620d262de7a697e7146cc6790d0193dc17d8 [file] [log] [blame]
/*
* 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_