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