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