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     ScopedObjectAccess soa(Thread::Current());
76     for (ArtMethod* method : methods) {
77       profile_methods.emplace_back(
78           MethodReference(method->GetDexFile(), method->GetDexMethodIndex()));
79     }
80     if (!info.AddMethods(profile_methods, flags)) {
81       return false;
82     }
83     if (info.GetNumberOfMethods() != profile_methods.size()) {
84       return false;
85     }
86     ProfileCompilationInfo file_profile;
87     if (!file_profile.Load(filename, false)) {
88       return false;
89     }
90     if (!info.MergeWith(file_profile)) {
91       return false;
92     }
93 
94     return info.Save(filename, nullptr);
95   }
96 
97   // Saves the given art methods to a profile backed by 'filename' and adds
98   // some fake inline caches to it. The added inline caches are returned in
99   // 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)100   bool SaveProfilingInfoWithFakeInlineCaches(
101       const std::string& filename,
102       const std::vector<ArtMethod*>& methods,
103       Hotness::Flag flags,
104       /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) {
105     ProfileCompilationInfo info;
106     std::vector<ProfileMethodInfo> profile_methods;
107     ScopedObjectAccess soa(Thread::Current());
108     for (ArtMethod* method : methods) {
109       std::vector<ProfileMethodInfo::ProfileInlineCache> caches;
110       // Monomorphic
111       for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
112         std::vector<TypeReference> classes;
113         classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0));
114         caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
115       }
116       // Polymorphic
117       for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
118         std::vector<TypeReference> classes;
119         for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) {
120           classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
121         }
122         caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
123       }
124       // Megamorphic
125       for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
126         std::vector<TypeReference> classes;
127         for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) {
128           classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
129         }
130         caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
131       }
132       // Missing types
133       for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
134         std::vector<TypeReference> classes;
135         caches.emplace_back(dex_pc, /*is_missing_types*/true, classes);
136       }
137       ProfileMethodInfo pmi(MethodReference(method->GetDexFile(),
138                                             method->GetDexMethodIndex()),
139                             caches);
140       profile_methods.push_back(pmi);
141       profile_methods_map->Put(method, pmi);
142     }
143 
144     if (!info.AddMethods(profile_methods, flags)
145         || info.GetNumberOfMethods() != profile_methods.size()) {
146       return false;
147     }
148     return info.Save(filename, nullptr);
149   }
150 
151   // Creates an inline cache which will be destructed at the end of the test.
CreateInlineCacheMap()152   ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() {
153     used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap(
154         std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)));
155     return used_inline_caches.back().get();
156   }
157 
158   // Cannot sizeof the actual arrays so hard code the values here.
159   // They should not change anyway.
160   static constexpr int kProfileMagicSize = 4;
161   static constexpr int kProfileVersionSize = 4;
162 
163   std::unique_ptr<ArenaAllocator> allocator_;
164 
165   // Cache of inline caches generated during tests.
166   // This makes it easier to pass data between different utilities and ensure that
167   // caches are destructed at the end of the test.
168   std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
169 };
170 
TEST_F(ProfileCompilationInfoTest,SaveArtMethods)171 TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
172   ScratchFile profile;
173 
174   Thread* self = Thread::Current();
175   jobject class_loader;
176   {
177     ScopedObjectAccess soa(self);
178     class_loader = LoadDex("ProfileTestMultiDex");
179   }
180   ASSERT_NE(class_loader, nullptr);
181 
182   // Save virtual methods from Main.
183   std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
184   ASSERT_TRUE(SaveProfilingInfo(
185       profile.GetFilename(),
186       main_methods,
187       static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagPostStartup)));
188 
189   // Check that what we saved is in the profile.
190   ProfileCompilationInfo info1;
191   ASSERT_TRUE(info1.Load(GetFd(profile)));
192   ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size());
193   {
194     ScopedObjectAccess soa(self);
195     for (ArtMethod* m : main_methods) {
196       Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
197       ASSERT_TRUE(h.IsHot());
198       ASSERT_TRUE(h.IsPostStartup());
199     }
200   }
201 
202   // Save virtual methods from Second.
203   std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
204   ASSERT_TRUE(SaveProfilingInfo(
205     profile.GetFilename(),
206     second_methods,
207     static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup)));
208 
209   // Check that what we saved is in the profile (methods form Main and Second).
210   ProfileCompilationInfo info2;
211   ASSERT_TRUE(info2.Load(GetFd(profile)));
212   ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size());
213   {
214     ScopedObjectAccess soa(self);
215     for (ArtMethod* m : main_methods) {
216       Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
217       ASSERT_TRUE(h.IsHot());
218       ASSERT_TRUE(h.IsPostStartup());
219     }
220     for (ArtMethod* m : second_methods) {
221       Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
222       ASSERT_TRUE(h.IsHot());
223       ASSERT_TRUE(h.IsStartup());
224     }
225   }
226 }
227 
TEST_F(ProfileCompilationInfoTest,SaveArtMethodsWithInlineCaches)228 TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
229   ScratchFile profile;
230 
231   Thread* self = Thread::Current();
232   jobject class_loader;
233   {
234     ScopedObjectAccess soa(self);
235     class_loader = LoadDex("ProfileTestMultiDex");
236   }
237   ASSERT_NE(class_loader, nullptr);
238 
239   // Save virtual methods from Main.
240   std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
241 
242   SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map;
243   ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches(
244       profile.GetFilename(),
245       main_methods,
246       static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup),
247       &profile_methods_map));
248 
249   // Check that what we saved is in the profile.
250   ProfileCompilationInfo info;
251   ASSERT_TRUE(info.Load(GetFd(profile)));
252   ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size());
253   {
254     ScopedObjectAccess soa(self);
255     for (ArtMethod* m : main_methods) {
256       MethodReference method_ref(m->GetDexFile(), m->GetDexMethodIndex());
257       Hotness h = info.GetMethodHotness(method_ref);
258       ASSERT_TRUE(h.IsHot());
259       ASSERT_TRUE(h.IsStartup());
260       const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
261       ProfileCompilationInfo::MethodHotness offline_hotness = info.GetMethodHotness(method_ref);
262       ASSERT_TRUE(offline_hotness.IsHot());
263       ASSERT_TRUE(ProfileTestHelper::EqualInlineCaches(
264                       pmi.inline_caches, method_ref.dex_file, offline_hotness, info));
265     }
266   }
267 }
268 
269 }  // namespace art
270