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