xref: /aosp_15_r20/art/runtime/jit/profiling_info_test.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1 /*
2  * Copyright (C) 2016 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 #include "profiling_info.h"
18 
19 #include <gtest/gtest.h>
20 #include <stdio.h>
21 
22 #include "art_method-inl.h"
23 #include "base/unix_file/fd_file.h"
24 #include "class_linker-inl.h"
25 #include "common_runtime_test.h"
26 #include "dex/dex_file.h"
27 #include "dex/dex_file_loader.h"
28 #include "dex/method_reference.h"
29 #include "dex/type_reference.h"
30 #include "handle_scope-inl.h"
31 #include "linear_alloc.h"
32 #include "mirror/class-inl.h"
33 #include "mirror/class_loader.h"
34 #include "profile/profile_compilation_info.h"
35 #include "profile/profile_test_helper.h"
36 #include "scoped_thread_state_change-inl.h"
37 
38 namespace art HIDDEN {
39 
40 using Hotness = ProfileCompilationInfo::MethodHotness;
41 
42 class ProfileCompilationInfoTest : public CommonRuntimeTest {
43  public:
PostRuntimeCreate()44   void PostRuntimeCreate() override {
45     allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
46   }
47 
48  protected:
GetVirtualMethods(jobject class_loader,const char * clazz)49   std::vector<ArtMethod*> GetVirtualMethods(jobject class_loader, const char* clazz) {
50     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
51     Thread* self = Thread::Current();
52     ScopedObjectAccess soa(self);
53     StackHandleScope<1> hs(self);
54     Handle<mirror::ClassLoader> h_loader(
55         hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader()));
56     ObjPtr<mirror::Class> klass = FindClass(clazz, h_loader);
57 
58     const auto pointer_size = class_linker->GetImagePointerSize();
59     std::vector<ArtMethod*> methods;
60     for (auto& m : klass->GetVirtualMethods(pointer_size)) {
61       methods.push_back(&m);
62     }
63     return methods;
64   }
65 
GetFd(const ScratchFile & file)66   uint32_t GetFd(const ScratchFile& file) {
67     return static_cast<uint32_t>(file.GetFd());
68   }
69 
SaveProfilingInfo(const std::string & filename,const std::vector<ArtMethod * > & methods,Hotness::Flag flags)70   bool SaveProfilingInfo(
71       const std::string& filename,
72       const std::vector<ArtMethod*>& methods,
73       Hotness::Flag flags) {
74     ProfileCompilationInfo info;
75     std::vector<ProfileMethodInfo> profile_methods;
76     profile_methods.reserve(methods.size());
77     ScopedObjectAccess soa(Thread::Current());
78     for (ArtMethod* method : methods) {
79       profile_methods.emplace_back(
80           MethodReference(method->GetDexFile(), method->GetDexMethodIndex()));
81     }
82     if (!info.AddMethods(profile_methods, flags)) {
83       return false;
84     }
85     if (info.GetNumberOfMethods() != profile_methods.size()) {
86       return false;
87     }
88     ProfileCompilationInfo file_profile;
89     if (!file_profile.Load(filename, false)) {
90       return false;
91     }
92     if (!info.MergeWith(file_profile)) {
93       return false;
94     }
95 
96     return info.Save(filename, nullptr);
97   }
98 
99   // Saves the given art methods to a profile backed by 'filename' and adds
100   // some fake inline caches to it. The added inline caches are returned in
101   // the out map `profile_methods_map`.
SaveProfilingInfoWithFakeInlineCaches(const std::string & filename,const std::vector<ArtMethod * > & methods,Hotness::Flag flags,SafeMap<ArtMethod *,ProfileMethodInfo> * profile_methods_map)102   bool SaveProfilingInfoWithFakeInlineCaches(
103       const std::string& filename,
104       const std::vector<ArtMethod*>& methods,
105       Hotness::Flag flags,
106       /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) {
107     ProfileCompilationInfo info;
108     std::vector<ProfileMethodInfo> profile_methods;
109     ScopedObjectAccess soa(Thread::Current());
110     for (ArtMethod* method : methods) {
111       std::vector<ProfileMethodInfo::ProfileInlineCache> caches;
112       // Monomorphic
113       for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
114         std::vector<TypeReference> classes;
115         classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0));
116         caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
117       }
118       // Polymorphic
119       for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
120         std::vector<TypeReference> classes;
121         for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) {
122           classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
123         }
124         caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
125       }
126       // Megamorphic
127       for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
128         std::vector<TypeReference> classes;
129         for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) {
130           classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
131         }
132         caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
133       }
134       // Missing types
135       for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
136         std::vector<TypeReference> classes;
137         caches.emplace_back(dex_pc, /*is_missing_types*/true, classes);
138       }
139       ProfileMethodInfo pmi(MethodReference(method->GetDexFile(),
140                                             method->GetDexMethodIndex()),
141                             caches);
142       profile_methods.push_back(pmi);
143       profile_methods_map->Put(method, pmi);
144     }
145 
146     if (!info.AddMethods(profile_methods,
147                          flags,
148                          ProfileCompilationInfo::ProfileSampleAnnotation::kNone,
149                          /*is_test=*/ true)
150         || info.GetNumberOfMethods() != profile_methods.size()) {
151       return false;
152     }
153     return info.Save(filename, nullptr);
154   }
155 
156   // Creates an inline cache which will be destructed at the end of the test.
CreateInlineCacheMap()157   ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() {
158     used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap(
159         std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)));
160     return used_inline_caches.back().get();
161   }
162 
163   // Cannot sizeof the actual arrays so hard code the values here.
164   // They should not change anyway.
165   static constexpr int kProfileMagicSize = 4;
166   static constexpr int kProfileVersionSize = 4;
167 
168   std::unique_ptr<ArenaAllocator> allocator_;
169 
170   // Cache of inline caches generated during tests.
171   // This makes it easier to pass data between different utilities and ensure that
172   // caches are destructed at the end of the test.
173   std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
174 };
175 
TEST_F(ProfileCompilationInfoTest,SaveArtMethods)176 TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
177   ScratchFile profile;
178 
179   Thread* self = Thread::Current();
180   jobject class_loader;
181   {
182     ScopedObjectAccess soa(self);
183     class_loader = LoadDex("ProfileTestMultiDex");
184   }
185   ASSERT_NE(class_loader, nullptr);
186 
187   // Save virtual methods from Main.
188   std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
189   ASSERT_TRUE(SaveProfilingInfo(
190       profile.GetFilename(),
191       main_methods,
192       static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagPostStartup)));
193 
194   // Check that what we saved is in the profile.
195   ProfileCompilationInfo info1;
196   ASSERT_TRUE(info1.Load(profile.GetFilename(), /*clear_if_invalid=*/false));
197   ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size());
198   {
199     ScopedObjectAccess soa(self);
200     for (ArtMethod* m : main_methods) {
201       Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
202       ASSERT_TRUE(h.IsHot());
203       ASSERT_TRUE(h.IsPostStartup());
204     }
205   }
206 
207   // Save virtual methods from Second.
208   std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
209   ASSERT_TRUE(SaveProfilingInfo(
210     profile.GetFilename(),
211     second_methods,
212     static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup)));
213 
214   // Check that what we saved is in the profile (methods form Main and Second).
215   ProfileCompilationInfo info2;
216   ASSERT_TRUE(info2.Load(profile.GetFilename(), /*clear_if_invalid=*/false));
217   ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size());
218   {
219     ScopedObjectAccess soa(self);
220     for (ArtMethod* m : main_methods) {
221       Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
222       ASSERT_TRUE(h.IsHot());
223       ASSERT_TRUE(h.IsPostStartup());
224     }
225     for (ArtMethod* m : second_methods) {
226       Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
227       ASSERT_TRUE(h.IsHot());
228       ASSERT_TRUE(h.IsStartup());
229     }
230   }
231 }
232 
TEST_F(ProfileCompilationInfoTest,SaveArtMethodsWithInlineCaches)233 TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
234   ScratchFile profile;
235 
236   Thread* self = Thread::Current();
237   jobject class_loader;
238   {
239     ScopedObjectAccess soa(self);
240     class_loader = LoadDex("ProfileTestMultiDex");
241   }
242   ASSERT_NE(class_loader, nullptr);
243 
244   // Save virtual methods from Main.
245   std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
246 
247   SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map;
248   ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches(
249       profile.GetFilename(),
250       main_methods,
251       static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup),
252       &profile_methods_map));
253 
254   // Check that what we saved is in the profile.
255   ProfileCompilationInfo info;
256   ASSERT_TRUE(info.Load(profile.GetFilename(), /*clear_if_invalid=*/false));
257   ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size());
258   {
259     ScopedObjectAccess soa(self);
260     for (ArtMethod* m : main_methods) {
261       MethodReference method_ref(m->GetDexFile(), m->GetDexMethodIndex());
262       Hotness h = info.GetMethodHotness(method_ref);
263       ASSERT_TRUE(h.IsHot());
264       ASSERT_TRUE(h.IsStartup());
265       const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
266       ProfileCompilationInfo::MethodHotness offline_hotness = info.GetMethodHotness(method_ref);
267       ASSERT_TRUE(offline_hotness.IsHot());
268       ASSERT_TRUE(ProfileTestHelper::EqualInlineCaches(
269                       pmi.inline_caches, method_ref.dex_file, offline_hotness, info));
270     }
271   }
272 }
273 
274 }  // namespace art
275