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