blob: 106a80a568ce8205a6bc271bd68e23b60c74317d [file] [log] [blame]
David Sehr1fed3432018-05-29 13:19:47 -07001/*
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 "scoped_thread_state_change-inl.h"
34#include "ziparchive/zip_writer.h"
35
36namespace art {
37
38using Hotness = ProfileCompilationInfo::MethodHotness;
39
40static constexpr size_t kMaxMethodIds = 65535;
41
42class ProfileCompilationInfoTest : public CommonRuntimeTest {
43 public:
44 void PostRuntimeCreate() OVERRIDE {
45 allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
46 }
47
48 protected:
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
67 bool AddMethod(const std::string& dex_location,
68 uint32_t checksum,
69 uint16_t method_index,
70 ProfileCompilationInfo* info) {
71 return info->AddMethodIndex(Hotness::kFlagHot,
72 dex_location,
73 checksum,
74 method_index,
75 kMaxMethodIds);
76 }
77
78 bool AddMethod(const std::string& dex_location,
79 uint32_t checksum,
80 uint16_t method_index,
81 const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi,
82 ProfileCompilationInfo* info) {
83 return info->AddMethod(
84 dex_location, checksum, method_index, kMaxMethodIds, pmi, Hotness::kFlagPostStartup);
85 }
86
87 bool AddClass(const std::string& dex_location,
88 uint32_t checksum,
89 dex::TypeIndex type_index,
90 ProfileCompilationInfo* info) {
91 DexCacheResolvedClasses classes(dex_location, dex_location, checksum, kMaxMethodIds);
92 classes.AddClass(type_index);
93 return info->AddClasses({classes});
94 }
95
96 uint32_t GetFd(const ScratchFile& file) {
97 return static_cast<uint32_t>(file.GetFd());
98 }
99
100 bool SaveProfilingInfo(
101 const std::string& filename,
102 const std::vector<ArtMethod*>& methods,
103 const std::set<DexCacheResolvedClasses>& resolved_classes,
104 Hotness::Flag flags) {
105 ProfileCompilationInfo info;
106 std::vector<ProfileMethodInfo> profile_methods;
107 ScopedObjectAccess soa(Thread::Current());
108 for (ArtMethod* method : methods) {
109 profile_methods.emplace_back(
110 MethodReference(method->GetDexFile(), method->GetDexMethodIndex()));
111 }
112 if (!info.AddMethods(profile_methods, flags) || !info.AddClasses(resolved_classes)) {
113 return false;
114 }
115 if (info.GetNumberOfMethods() != profile_methods.size()) {
116 return false;
117 }
118 ProfileCompilationInfo file_profile;
119 if (!file_profile.Load(filename, false)) {
120 return false;
121 }
122 if (!info.MergeWith(file_profile)) {
123 return false;
124 }
125
126 return info.Save(filename, nullptr);
127 }
128
129 // Saves the given art methods to a profile backed by 'filename' and adds
130 // some fake inline caches to it. The added inline caches are returned in
131 // the out map `profile_methods_map`.
132 bool SaveProfilingInfoWithFakeInlineCaches(
133 const std::string& filename,
134 const std::vector<ArtMethod*>& methods,
135 Hotness::Flag flags,
136 /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) {
137 ProfileCompilationInfo info;
138 std::vector<ProfileMethodInfo> profile_methods;
139 ScopedObjectAccess soa(Thread::Current());
140 for (ArtMethod* method : methods) {
141 std::vector<ProfileMethodInfo::ProfileInlineCache> caches;
142 // Monomorphic
143 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
144 std::vector<TypeReference> classes;
145 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0));
146 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
147 }
148 // Polymorphic
149 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
150 std::vector<TypeReference> classes;
151 for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) {
152 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
153 }
154 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
155 }
156 // Megamorphic
157 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
158 std::vector<TypeReference> classes;
159 for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) {
160 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
161 }
162 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
163 }
164 // Missing types
165 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
166 std::vector<TypeReference> classes;
167 caches.emplace_back(dex_pc, /*is_missing_types*/true, classes);
168 }
169 ProfileMethodInfo pmi(MethodReference(method->GetDexFile(),
170 method->GetDexMethodIndex()),
171 caches);
172 profile_methods.push_back(pmi);
173 profile_methods_map->Put(method, pmi);
174 }
175
176 if (!info.AddMethods(profile_methods, flags)
177 || info.GetNumberOfMethods() != profile_methods.size()) {
178 return false;
179 }
180 return info.Save(filename, nullptr);
181 }
182
183 // Creates an inline cache which will be destructed at the end of the test.
184 ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() {
185 used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap(
186 std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)));
187 return used_inline_caches.back().get();
188 }
189
190 ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo(
191 const ProfileMethodInfo& pmi) {
192 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
193 ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(ic_map);
194 SafeMap<DexFile*, uint8_t> dex_map; // dex files to profile index
195 for (const auto& inline_cache : pmi.inline_caches) {
196 ProfileCompilationInfo::DexPcData& dex_pc_data =
197 ic_map->FindOrAdd(
198 inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(allocator_.get()))->second;
199 if (inline_cache.is_missing_types) {
200 dex_pc_data.SetIsMissingTypes();
201 }
202 for (const auto& class_ref : inline_cache.classes) {
203 uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file),
204 static_cast<uint8_t>(dex_map.size()))->second;
205 dex_pc_data.AddClass(dex_profile_index, class_ref.TypeIndex());
206 if (dex_profile_index >= offline_pmi.dex_references.size()) {
207 // This is a new dex.
208 const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey(
209 class_ref.dex_file->GetLocation());
210 offline_pmi.dex_references.emplace_back(dex_key,
211 class_ref.dex_file->GetLocationChecksum(),
212 class_ref.dex_file->NumMethodIds());
213 }
214 }
215 }
216 return offline_pmi;
217 }
218
219 // Cannot sizeof the actual arrays so hard code the values here.
220 // They should not change anyway.
221 static constexpr int kProfileMagicSize = 4;
222 static constexpr int kProfileVersionSize = 4;
223
224 std::unique_ptr<ArenaAllocator> allocator_;
225
226 // Cache of inline caches generated during tests.
227 // This makes it easier to pass data between different utilities and ensure that
228 // caches are destructed at the end of the test.
229 std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
230};
231
232TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
233 ScratchFile profile;
234
235 Thread* self = Thread::Current();
236 jobject class_loader;
237 {
238 ScopedObjectAccess soa(self);
239 class_loader = LoadDex("ProfileTestMultiDex");
240 }
241 ASSERT_NE(class_loader, nullptr);
242
243 // Save virtual methods from Main.
244 std::set<DexCacheResolvedClasses> resolved_classes;
245 std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
246 ASSERT_TRUE(SaveProfilingInfo(
247 profile.GetFilename(), main_methods, resolved_classes, Hotness::kFlagPostStartup));
248
249 // Check that what we saved is in the profile.
250 ProfileCompilationInfo info1;
251 ASSERT_TRUE(info1.Load(GetFd(profile)));
252 ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size());
253 {
254 ScopedObjectAccess soa(self);
255 for (ArtMethod* m : main_methods) {
256 Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
257 ASSERT_TRUE(h.IsHot());
258 ASSERT_TRUE(h.IsPostStartup());
259 }
260 }
261
262 // Save virtual methods from Second.
263 std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
264 ASSERT_TRUE(SaveProfilingInfo(
265 profile.GetFilename(), second_methods, resolved_classes, Hotness::kFlagStartup));
266
267 // Check that what we saved is in the profile (methods form Main and Second).
268 ProfileCompilationInfo info2;
269 ASSERT_TRUE(profile.GetFile()->ResetOffset());
270 ASSERT_TRUE(info2.Load(GetFd(profile)));
271 ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size());
272 {
273 ScopedObjectAccess soa(self);
274 for (ArtMethod* m : main_methods) {
275 Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
276 ASSERT_TRUE(h.IsHot());
277 ASSERT_TRUE(h.IsPostStartup());
278 }
279 for (ArtMethod* m : second_methods) {
280 Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
281 ASSERT_TRUE(h.IsHot());
282 ASSERT_TRUE(h.IsStartup());
283 }
284 }
285}
286
287TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
288 ScratchFile profile;
289
290 Thread* self = Thread::Current();
291 jobject class_loader;
292 {
293 ScopedObjectAccess soa(self);
294 class_loader = LoadDex("ProfileTestMultiDex");
295 }
296 ASSERT_NE(class_loader, nullptr);
297
298 // Save virtual methods from Main.
299 std::set<DexCacheResolvedClasses> resolved_classes;
300 std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
301
302 SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map;
303 ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches(
304 profile.GetFilename(), main_methods, Hotness::kFlagStartup, &profile_methods_map));
305
306 // Check that what we saved is in the profile.
307 ProfileCompilationInfo info;
308 ASSERT_TRUE(info.Load(GetFd(profile)));
309 ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size());
310 {
311 ScopedObjectAccess soa(self);
312 for (ArtMethod* m : main_methods) {
313 Hotness h = info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
314 ASSERT_TRUE(h.IsHot());
315 ASSERT_TRUE(h.IsStartup());
316 const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
317 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_pmi =
318 info.GetMethod(m->GetDexFile()->GetLocation(),
319 m->GetDexFile()->GetLocationChecksum(),
320 m->GetDexMethodIndex());
321 ASSERT_TRUE(offline_pmi != nullptr);
322 ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi =
323 ConvertProfileMethodInfo(pmi);
324 ASSERT_EQ(converted_pmi, *offline_pmi);
325 }
326 }
327}
328
329} // namespace art