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