1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
/*
* 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_
|