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