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