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