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
19 #include "base/unix_file/fd_file.h"
20 #include "art_method-inl.h"
21 #include "class_linker-inl.h"
22 #include "common_runtime_test.h"
23 #include "dex_file.h"
24 #include "method_reference.h"
25 #include "mirror/class-inl.h"
26 #include "mirror/class_loader.h"
27 #include "handle_scope-inl.h"
28 #include "linear_alloc.h"
29 #include "jit/profile_compilation_info.h"
30 #include "scoped_thread_state_change-inl.h"
31
32 namespace art {
33
34 class ProfileCompilationInfoTest : public CommonRuntimeTest {
35 public:
PostRuntimeCreate()36 void PostRuntimeCreate() OVERRIDE {
37 arena_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
38 }
39
40 protected:
GetVirtualMethods(jobject class_loader,const std::string & clazz)41 std::vector<ArtMethod*> GetVirtualMethods(jobject class_loader,
42 const std::string& clazz) {
43 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
44 Thread* self = Thread::Current();
45 ScopedObjectAccess soa(self);
46 StackHandleScope<1> hs(self);
47 Handle<mirror::ClassLoader> h_loader(
48 hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader()));
49 mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader);
50
51 const auto pointer_size = class_linker->GetImagePointerSize();
52 std::vector<ArtMethod*> methods;
53 for (auto& m : klass->GetVirtualMethods(pointer_size)) {
54 methods.push_back(&m);
55 }
56 return methods;
57 }
58
AddMethod(const std::string & dex_location,uint32_t checksum,uint16_t method_index,ProfileCompilationInfo * info)59 bool AddMethod(const std::string& dex_location,
60 uint32_t checksum,
61 uint16_t method_index,
62 ProfileCompilationInfo* info) {
63 return info->AddMethodIndex(dex_location, checksum, method_index);
64 }
65
AddMethod(const std::string & dex_location,uint32_t checksum,uint16_t method_index,const ProfileCompilationInfo::OfflineProfileMethodInfo & pmi,ProfileCompilationInfo * info)66 bool AddMethod(const std::string& dex_location,
67 uint32_t checksum,
68 uint16_t method_index,
69 const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi,
70 ProfileCompilationInfo* info) {
71 return info->AddMethod(dex_location, checksum, method_index, pmi);
72 }
73
AddClass(const std::string & dex_location,uint32_t checksum,uint16_t class_index,ProfileCompilationInfo * info)74 bool AddClass(const std::string& dex_location,
75 uint32_t checksum,
76 uint16_t class_index,
77 ProfileCompilationInfo* info) {
78 return info->AddMethodIndex(dex_location, checksum, class_index);
79 }
80
GetFd(const ScratchFile & file)81 uint32_t GetFd(const ScratchFile& file) {
82 return static_cast<uint32_t>(file.GetFd());
83 }
84
SaveProfilingInfo(const std::string & filename,const std::vector<ArtMethod * > & methods,const std::set<DexCacheResolvedClasses> & resolved_classes)85 bool SaveProfilingInfo(
86 const std::string& filename,
87 const std::vector<ArtMethod*>& methods,
88 const std::set<DexCacheResolvedClasses>& resolved_classes) {
89 ProfileCompilationInfo info;
90 std::vector<ProfileMethodInfo> profile_methods;
91 ScopedObjectAccess soa(Thread::Current());
92 for (ArtMethod* method : methods) {
93 profile_methods.emplace_back(method->GetDexFile(), method->GetDexMethodIndex());
94 }
95 if (!info.AddMethodsAndClasses(profile_methods, resolved_classes)) {
96 return false;
97 }
98 if (info.GetNumberOfMethods() != profile_methods.size()) {
99 return false;
100 }
101 ProfileCompilationInfo file_profile;
102 if (!file_profile.Load(filename, false)) {
103 return false;
104 }
105 if (!info.MergeWith(file_profile)) {
106 return false;
107 }
108
109 return info.Save(filename, nullptr);
110 }
111
112 // Saves the given art methods to a profile backed by 'filename' and adds
113 // some fake inline caches to it. The added inline caches are returned in
114 // the out map `profile_methods_map`.
SaveProfilingInfoWithFakeInlineCaches(const std::string & filename,const std::vector<ArtMethod * > & methods,SafeMap<ArtMethod *,ProfileMethodInfo> * profile_methods_map)115 bool SaveProfilingInfoWithFakeInlineCaches(
116 const std::string& filename,
117 const std::vector<ArtMethod*>& methods,
118 /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) {
119 ProfileCompilationInfo info;
120 std::vector<ProfileMethodInfo> profile_methods;
121 ScopedObjectAccess soa(Thread::Current());
122 for (ArtMethod* method : methods) {
123 std::vector<ProfileMethodInfo::ProfileInlineCache> caches;
124 // Monomorphic
125 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
126 std::vector<ProfileMethodInfo::ProfileClassReference> classes;
127 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0));
128 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
129 }
130 // Polymorphic
131 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
132 std::vector<ProfileMethodInfo::ProfileClassReference> classes;
133 for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) {
134 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
135 }
136 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
137 }
138 // Megamorphic
139 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
140 std::vector<ProfileMethodInfo::ProfileClassReference> classes;
141 for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) {
142 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
143 }
144 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
145 }
146 // Missing types
147 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
148 std::vector<ProfileMethodInfo::ProfileClassReference> classes;
149 caches.emplace_back(dex_pc, /*is_missing_types*/true, classes);
150 }
151 ProfileMethodInfo pmi(method->GetDexFile(), method->GetDexMethodIndex(), caches);
152 profile_methods.push_back(pmi);
153 profile_methods_map->Put(method, pmi);
154 }
155
156 if (!info.AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>())) {
157 return false;
158 }
159 if (info.GetNumberOfMethods() != profile_methods.size()) {
160 return false;
161 }
162 return info.Save(filename, nullptr);
163 }
164
165 // Creates an inline cache which will be destructed at the end of the test.
CreateInlineCacheMap()166 ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() {
167 used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap(
168 std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)));
169 return used_inline_caches.back().get();
170 }
171
ConvertProfileMethodInfo(const ProfileMethodInfo & pmi)172 ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo(
173 const ProfileMethodInfo& pmi) {
174 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
175 ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(ic_map);
176 SafeMap<DexFile*, uint8_t> dex_map; // dex files to profile index
177 for (const auto& inline_cache : pmi.inline_caches) {
178 ProfileCompilationInfo::DexPcData& dex_pc_data =
179 ic_map->FindOrAdd(
180 inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(arena_.get()))->second;
181 if (inline_cache.is_missing_types) {
182 dex_pc_data.SetIsMissingTypes();
183 }
184 for (const auto& class_ref : inline_cache.classes) {
185 uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file),
186 static_cast<uint8_t>(dex_map.size()))->second;
187 dex_pc_data.AddClass(dex_profile_index, class_ref.type_index);
188 if (dex_profile_index >= offline_pmi.dex_references.size()) {
189 // This is a new dex.
190 const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey(
191 class_ref.dex_file->GetLocation());
192 offline_pmi.dex_references.emplace_back(dex_key,
193 class_ref.dex_file->GetLocationChecksum());
194 }
195 }
196 }
197 return offline_pmi;
198 }
199
200 // Creates an offline profile used for testing inline caches.
GetOfflineProfileMethodInfo()201 ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() {
202 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
203 // Monomorphic
204 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
205 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
206 dex_pc_data.AddClass(0, dex::TypeIndex(0));
207 ic_map->Put(dex_pc, dex_pc_data);
208 }
209 // Polymorphic
210 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
211 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
212 dex_pc_data.AddClass(0, dex::TypeIndex(0));
213 dex_pc_data.AddClass(1, dex::TypeIndex(1));
214 dex_pc_data.AddClass(2, dex::TypeIndex(2));
215
216 ic_map->Put(dex_pc, dex_pc_data);
217 }
218 // Megamorphic
219 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
220 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
221 dex_pc_data.SetIsMegamorphic();
222 ic_map->Put(dex_pc, dex_pc_data);
223 }
224 // Missing types
225 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
226 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
227 dex_pc_data.SetIsMissingTypes();
228 ic_map->Put(dex_pc, dex_pc_data);
229 }
230
231 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
232
233 pmi.dex_references.emplace_back("dex_location1", /* checksum */1);
234 pmi.dex_references.emplace_back("dex_location2", /* checksum */2);
235 pmi.dex_references.emplace_back("dex_location3", /* checksum */3);
236
237 return pmi;
238 }
239
MakeMegamorphic(ProfileCompilationInfo::OfflineProfileMethodInfo * pmi)240 void MakeMegamorphic(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) {
241 ProfileCompilationInfo::InlineCacheMap* ic_map =
242 const_cast<ProfileCompilationInfo::InlineCacheMap*>(pmi->inline_caches);
243 for (auto it : *ic_map) {
244 for (uint16_t k = 0; k <= 2 * InlineCache::kIndividualCacheSize; k++) {
245 it.second.AddClass(0, dex::TypeIndex(k));
246 }
247 }
248 }
249
SetIsMissingTypes(ProfileCompilationInfo::OfflineProfileMethodInfo * pmi)250 void SetIsMissingTypes(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) {
251 ProfileCompilationInfo::InlineCacheMap* ic_map =
252 const_cast<ProfileCompilationInfo::InlineCacheMap*>(pmi->inline_caches);
253 for (auto it : *ic_map) {
254 it.second.SetIsMissingTypes();
255 }
256 }
257
258 // Cannot sizeof the actual arrays so hard code the values here.
259 // They should not change anyway.
260 static constexpr int kProfileMagicSize = 4;
261 static constexpr int kProfileVersionSize = 4;
262
263 std::unique_ptr<ArenaAllocator> arena_;
264
265 // Cache of inline caches generated during tests.
266 // This makes it easier to pass data between different utilities and ensure that
267 // caches are destructed at the end of the test.
268 std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
269 };
270
TEST_F(ProfileCompilationInfoTest,SaveArtMethods)271 TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
272 ScratchFile profile;
273
274 Thread* self = Thread::Current();
275 jobject class_loader;
276 {
277 ScopedObjectAccess soa(self);
278 class_loader = LoadDex("ProfileTestMultiDex");
279 }
280 ASSERT_NE(class_loader, nullptr);
281
282 // Save virtual methods from Main.
283 std::set<DexCacheResolvedClasses> resolved_classes;
284 std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
285 ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), main_methods, resolved_classes));
286
287 // Check that what we saved is in the profile.
288 ProfileCompilationInfo info1;
289 ASSERT_TRUE(info1.Load(GetFd(profile)));
290 ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size());
291 {
292 ScopedObjectAccess soa(self);
293 for (ArtMethod* m : main_methods) {
294 ASSERT_TRUE(info1.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
295 }
296 }
297
298 // Save virtual methods from Second.
299 std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
300 ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), second_methods, resolved_classes));
301
302 // Check that what we saved is in the profile (methods form Main and Second).
303 ProfileCompilationInfo info2;
304 ASSERT_TRUE(profile.GetFile()->ResetOffset());
305 ASSERT_TRUE(info2.Load(GetFd(profile)));
306 ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size());
307 {
308 ScopedObjectAccess soa(self);
309 for (ArtMethod* m : main_methods) {
310 ASSERT_TRUE(info2.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
311 }
312 for (ArtMethod* m : second_methods) {
313 ASSERT_TRUE(info2.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
314 }
315 }
316 }
317
TEST_F(ProfileCompilationInfoTest,SaveFd)318 TEST_F(ProfileCompilationInfoTest, SaveFd) {
319 ScratchFile profile;
320
321 ProfileCompilationInfo saved_info;
322 // Save a few methods.
323 for (uint16_t i = 0; i < 10; i++) {
324 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
325 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
326 }
327 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
328 ASSERT_EQ(0, profile.GetFile()->Flush());
329
330 // Check that we get back what we saved.
331 ProfileCompilationInfo loaded_info;
332 ASSERT_TRUE(profile.GetFile()->ResetOffset());
333 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
334 ASSERT_TRUE(loaded_info.Equals(saved_info));
335
336 // Save more methods.
337 for (uint16_t i = 0; i < 100; i++) {
338 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
339 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
340 ASSERT_TRUE(AddMethod("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
341 }
342 ASSERT_TRUE(profile.GetFile()->ResetOffset());
343 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
344 ASSERT_EQ(0, profile.GetFile()->Flush());
345
346 // Check that we get back everything we saved.
347 ProfileCompilationInfo loaded_info2;
348 ASSERT_TRUE(profile.GetFile()->ResetOffset());
349 ASSERT_TRUE(loaded_info2.Load(GetFd(profile)));
350 ASSERT_TRUE(loaded_info2.Equals(saved_info));
351 }
352
TEST_F(ProfileCompilationInfoTest,AddMethodsAndClassesFail)353 TEST_F(ProfileCompilationInfoTest, AddMethodsAndClassesFail) {
354 ScratchFile profile;
355
356 ProfileCompilationInfo info;
357 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
358 // Trying to add info for an existing file but with a different checksum.
359 ASSERT_FALSE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
360 }
361
TEST_F(ProfileCompilationInfoTest,MergeFail)362 TEST_F(ProfileCompilationInfoTest, MergeFail) {
363 ScratchFile profile;
364
365 ProfileCompilationInfo info1;
366 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
367 // Use the same file, change the checksum.
368 ProfileCompilationInfo info2;
369 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
370
371 ASSERT_FALSE(info1.MergeWith(info2));
372 }
373
TEST_F(ProfileCompilationInfoTest,SaveMaxMethods)374 TEST_F(ProfileCompilationInfoTest, SaveMaxMethods) {
375 ScratchFile profile;
376
377 ProfileCompilationInfo saved_info;
378 // Save the maximum number of methods
379 for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
380 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
381 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
382 }
383 // Save the maximum number of classes
384 for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
385 ASSERT_TRUE(AddClass("dex_location1", /* checksum */ 1, /* class_idx */ i, &saved_info));
386 ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, /* class_idx */ i, &saved_info));
387 }
388
389 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
390 ASSERT_EQ(0, profile.GetFile()->Flush());
391
392 // Check that we get back what we saved.
393 ProfileCompilationInfo loaded_info;
394 ASSERT_TRUE(profile.GetFile()->ResetOffset());
395 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
396 ASSERT_TRUE(loaded_info.Equals(saved_info));
397 }
398
TEST_F(ProfileCompilationInfoTest,SaveEmpty)399 TEST_F(ProfileCompilationInfoTest, SaveEmpty) {
400 ScratchFile profile;
401
402 ProfileCompilationInfo saved_info;
403 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
404 ASSERT_EQ(0, profile.GetFile()->Flush());
405
406 // Check that we get back what we saved.
407 ProfileCompilationInfo loaded_info;
408 ASSERT_TRUE(profile.GetFile()->ResetOffset());
409 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
410 ASSERT_TRUE(loaded_info.Equals(saved_info));
411 }
412
TEST_F(ProfileCompilationInfoTest,LoadEmpty)413 TEST_F(ProfileCompilationInfoTest, LoadEmpty) {
414 ScratchFile profile;
415
416 ProfileCompilationInfo empty_info;
417
418 ProfileCompilationInfo loaded_info;
419 ASSERT_TRUE(profile.GetFile()->ResetOffset());
420 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
421 ASSERT_TRUE(loaded_info.Equals(empty_info));
422 }
423
TEST_F(ProfileCompilationInfoTest,BadMagic)424 TEST_F(ProfileCompilationInfoTest, BadMagic) {
425 ScratchFile profile;
426 uint8_t buffer[] = { 1, 2, 3, 4 };
427 ASSERT_TRUE(profile.GetFile()->WriteFully(buffer, sizeof(buffer)));
428 ProfileCompilationInfo loaded_info;
429 ASSERT_TRUE(profile.GetFile()->ResetOffset());
430 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
431 }
432
TEST_F(ProfileCompilationInfoTest,BadVersion)433 TEST_F(ProfileCompilationInfoTest, BadVersion) {
434 ScratchFile profile;
435
436 ASSERT_TRUE(profile.GetFile()->WriteFully(
437 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
438 uint8_t version[] = { 'v', 'e', 'r', 's', 'i', 'o', 'n' };
439 ASSERT_TRUE(profile.GetFile()->WriteFully(version, sizeof(version)));
440 ASSERT_EQ(0, profile.GetFile()->Flush());
441
442 ProfileCompilationInfo loaded_info;
443 ASSERT_TRUE(profile.GetFile()->ResetOffset());
444 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
445 }
446
TEST_F(ProfileCompilationInfoTest,Incomplete)447 TEST_F(ProfileCompilationInfoTest, Incomplete) {
448 ScratchFile profile;
449 ASSERT_TRUE(profile.GetFile()->WriteFully(
450 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
451 ASSERT_TRUE(profile.GetFile()->WriteFully(
452 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
453 // Write that we have at least one line.
454 uint8_t line_number[] = { 0, 1 };
455 ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
456 ASSERT_EQ(0, profile.GetFile()->Flush());
457
458 ProfileCompilationInfo loaded_info;
459 ASSERT_TRUE(profile.GetFile()->ResetOffset());
460 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
461 }
462
TEST_F(ProfileCompilationInfoTest,TooLongDexLocation)463 TEST_F(ProfileCompilationInfoTest, TooLongDexLocation) {
464 ScratchFile profile;
465 ASSERT_TRUE(profile.GetFile()->WriteFully(
466 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
467 ASSERT_TRUE(profile.GetFile()->WriteFully(
468 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
469 // Write that we have at least one line.
470 uint8_t line_number[] = { 0, 1 };
471 ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
472
473 // dex_location_size, methods_size, classes_size, checksum.
474 // Dex location size is too big and should be rejected.
475 uint8_t line[] = { 255, 255, 0, 1, 0, 1, 0, 0, 0, 0 };
476 ASSERT_TRUE(profile.GetFile()->WriteFully(line, sizeof(line)));
477 ASSERT_EQ(0, profile.GetFile()->Flush());
478
479 ProfileCompilationInfo loaded_info;
480 ASSERT_TRUE(profile.GetFile()->ResetOffset());
481 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
482 }
483
TEST_F(ProfileCompilationInfoTest,UnexpectedContent)484 TEST_F(ProfileCompilationInfoTest, UnexpectedContent) {
485 ScratchFile profile;
486
487 ProfileCompilationInfo saved_info;
488 // Save the maximum number of methods
489 for (uint16_t i = 0; i < 10; i++) {
490 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
491 }
492 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
493
494 uint8_t random_data[] = { 1, 2, 3};
495 ASSERT_TRUE(profile.GetFile()->WriteFully(random_data, sizeof(random_data)));
496
497 ASSERT_EQ(0, profile.GetFile()->Flush());
498
499 // Check that we fail because of unexpected data at the end of the file.
500 ProfileCompilationInfo loaded_info;
501 ASSERT_TRUE(profile.GetFile()->ResetOffset());
502 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
503 }
504
TEST_F(ProfileCompilationInfoTest,SaveInlineCaches)505 TEST_F(ProfileCompilationInfoTest, SaveInlineCaches) {
506 ScratchFile profile;
507
508 ProfileCompilationInfo saved_info;
509 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
510
511 // Add methods with inline caches.
512 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
513 // Add a method which is part of the same dex file as one of the
514 // class from the inline caches.
515 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
516 // Add a method which is outside the set of dex files.
517 ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
518 }
519
520 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
521 ASSERT_EQ(0, profile.GetFile()->Flush());
522
523 // Check that we get back what we saved.
524 ProfileCompilationInfo loaded_info;
525 ASSERT_TRUE(profile.GetFile()->ResetOffset());
526 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
527
528 ASSERT_TRUE(loaded_info.Equals(saved_info));
529
530 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
531 loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3);
532 ASSERT_TRUE(loaded_pmi1 != nullptr);
533 ASSERT_TRUE(*loaded_pmi1 == pmi);
534 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 =
535 loaded_info.GetMethod("dex_location4", /* checksum */ 4, /* method_idx */ 3);
536 ASSERT_TRUE(loaded_pmi2 != nullptr);
537 ASSERT_TRUE(*loaded_pmi2 == pmi);
538 }
539
TEST_F(ProfileCompilationInfoTest,MegamorphicInlineCaches)540 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) {
541 ScratchFile profile;
542
543 ProfileCompilationInfo saved_info;
544 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
545
546 // Add methods with inline caches.
547 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
548 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
549 }
550
551 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
552 ASSERT_EQ(0, profile.GetFile()->Flush());
553
554 // Make the inline caches megamorphic and add them to the profile again.
555 ProfileCompilationInfo saved_info_extra;
556 ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo();
557 MakeMegamorphic(&pmi_extra);
558 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
559 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
560 }
561
562 ASSERT_TRUE(profile.GetFile()->ResetOffset());
563 ASSERT_TRUE(saved_info_extra.Save(GetFd(profile)));
564 ASSERT_EQ(0, profile.GetFile()->Flush());
565
566 // Merge the profiles so that we have the same view as the file.
567 ASSERT_TRUE(saved_info.MergeWith(saved_info_extra));
568
569 // Check that we get back what we saved.
570 ProfileCompilationInfo loaded_info;
571 ASSERT_TRUE(profile.GetFile()->ResetOffset());
572 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
573
574 ASSERT_TRUE(loaded_info.Equals(saved_info));
575
576 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
577 loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3);
578
579 ASSERT_TRUE(loaded_pmi1 != nullptr);
580 ASSERT_TRUE(*loaded_pmi1 == pmi_extra);
581 }
582
TEST_F(ProfileCompilationInfoTest,MissingTypesInlineCaches)583 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCaches) {
584 ScratchFile profile;
585
586 ProfileCompilationInfo saved_info;
587 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
588
589 // Add methods with inline caches.
590 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
591 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
592 }
593
594 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
595 ASSERT_EQ(0, profile.GetFile()->Flush());
596
597 // Make some inline caches megamorphic and add them to the profile again.
598 ProfileCompilationInfo saved_info_extra;
599 ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo();
600 MakeMegamorphic(&pmi_extra);
601 for (uint16_t method_idx = 5; method_idx < 10; method_idx++) {
602 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
603 }
604
605 // Mark all inline caches with missing types and add them to the profile again.
606 // This will verify that all inline caches (megamorphic or not) should be marked as missing types.
607 ProfileCompilationInfo::OfflineProfileMethodInfo missing_types = GetOfflineProfileMethodInfo();
608 SetIsMissingTypes(&missing_types);
609 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
610 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
611 }
612
613 ASSERT_TRUE(profile.GetFile()->ResetOffset());
614 ASSERT_TRUE(saved_info_extra.Save(GetFd(profile)));
615 ASSERT_EQ(0, profile.GetFile()->Flush());
616
617 // Merge the profiles so that we have the same view as the file.
618 ASSERT_TRUE(saved_info.MergeWith(saved_info_extra));
619
620 // Check that we get back what we saved.
621 ProfileCompilationInfo loaded_info;
622 ASSERT_TRUE(profile.GetFile()->ResetOffset());
623 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
624
625 ASSERT_TRUE(loaded_info.Equals(saved_info));
626
627 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
628 loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3);
629 ASSERT_TRUE(loaded_pmi1 != nullptr);
630 ASSERT_TRUE(*loaded_pmi1 == pmi_extra);
631 }
632
TEST_F(ProfileCompilationInfoTest,SaveArtMethodsWithInlineCaches)633 TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
634 ScratchFile profile;
635
636 Thread* self = Thread::Current();
637 jobject class_loader;
638 {
639 ScopedObjectAccess soa(self);
640 class_loader = LoadDex("ProfileTestMultiDex");
641 }
642 ASSERT_NE(class_loader, nullptr);
643
644 // Save virtual methods from Main.
645 std::set<DexCacheResolvedClasses> resolved_classes;
646 std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
647
648 SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map;
649 ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches(
650 profile.GetFilename(), main_methods, &profile_methods_map));
651
652 // Check that what we saved is in the profile.
653 ProfileCompilationInfo info;
654 ASSERT_TRUE(info.Load(GetFd(profile)));
655 ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size());
656 {
657 ScopedObjectAccess soa(self);
658 for (ArtMethod* m : main_methods) {
659 ASSERT_TRUE(info.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
660 const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
661 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_pmi =
662 info.GetMethod(m->GetDexFile()->GetLocation(),
663 m->GetDexFile()->GetLocationChecksum(),
664 m->GetDexMethodIndex());
665 ASSERT_TRUE(offline_pmi != nullptr);
666 ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi =
667 ConvertProfileMethodInfo(pmi);
668 ASSERT_EQ(converted_pmi, *offline_pmi);
669 }
670 }
671 }
672
TEST_F(ProfileCompilationInfoTest,InvalidChecksumInInlineCache)673 TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCache) {
674 ScratchFile profile;
675
676 ProfileCompilationInfo info;
677 ProfileCompilationInfo::OfflineProfileMethodInfo pmi1 = GetOfflineProfileMethodInfo();
678 ProfileCompilationInfo::OfflineProfileMethodInfo pmi2 = GetOfflineProfileMethodInfo();
679 // Modify the checksum to trigger a mismatch.
680 pmi2.dex_references[0].dex_checksum++;
681
682 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /*method_idx*/ 0, pmi1, &info));
683 ASSERT_FALSE(AddMethod("dex_location2", /* checksum */ 2, /*method_idx*/ 0, pmi2, &info));
684 }
685
686 // Verify that profiles behave correctly even if the methods are added in a different
687 // order and with a different dex profile indices for the dex files.
TEST_F(ProfileCompilationInfoTest,MergeInlineCacheTriggerReindex)688 TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) {
689 ScratchFile profile;
690
691 ProfileCompilationInfo info;
692 ProfileCompilationInfo info_reindexed;
693
694 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
695 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
696 pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
697 pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2);
698 for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
699 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
700 dex_pc_data.AddClass(0, dex::TypeIndex(0));
701 dex_pc_data.AddClass(1, dex::TypeIndex(1));
702 ic_map->Put(dex_pc, dex_pc_data);
703 }
704
705 ProfileCompilationInfo::InlineCacheMap* ic_map_reindexed = CreateInlineCacheMap();
706 ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed(ic_map_reindexed);
707 pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2);
708 pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1);
709 for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
710 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
711 dex_pc_data.AddClass(1, dex::TypeIndex(0));
712 dex_pc_data.AddClass(0, dex::TypeIndex(1));
713 ic_map_reindexed->Put(dex_pc, dex_pc_data);
714 }
715
716 // Profile 1 and Profile 2 get the same methods but in different order.
717 // This will trigger a different dex numbers.
718 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
719 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &info));
720 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &info));
721 }
722
723 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
724 ASSERT_TRUE(AddMethod(
725 "dex_location2", /* checksum */ 2, method_idx, pmi_reindexed, &info_reindexed));
726 ASSERT_TRUE(AddMethod(
727 "dex_location1", /* checksum */ 1, method_idx, pmi_reindexed, &info_reindexed));
728 }
729
730 ProfileCompilationInfo info_backup;
731 info_backup.MergeWith(info);
732 ASSERT_TRUE(info.MergeWith(info_reindexed));
733 // Merging should have no effect as we're adding the exact same stuff.
734 ASSERT_TRUE(info.Equals(info_backup));
735 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
736 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
737 info.GetMethod("dex_location1", /* checksum */ 1, method_idx);
738 ASSERT_TRUE(loaded_pmi1 != nullptr);
739 ASSERT_TRUE(*loaded_pmi1 == pmi);
740 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 =
741 info.GetMethod("dex_location2", /* checksum */ 2, method_idx);
742 ASSERT_TRUE(loaded_pmi2 != nullptr);
743 ASSERT_TRUE(*loaded_pmi2 == pmi);
744 }
745 }
746
TEST_F(ProfileCompilationInfoTest,AddMoreDexFileThanLimit)747 TEST_F(ProfileCompilationInfoTest, AddMoreDexFileThanLimit) {
748 ProfileCompilationInfo info;
749 // Save a few methods.
750 for (uint16_t i = 0; i < std::numeric_limits<uint8_t>::max(); i++) {
751 std::string dex_location = std::to_string(i);
752 ASSERT_TRUE(AddMethod(dex_location, /* checksum */ 1, /* method_idx */ i, &info));
753 }
754 // We only support at most 255 dex files.
755 ASSERT_FALSE(AddMethod(
756 /*dex_location*/ "256", /* checksum */ 1, /* method_idx */ 0, &info));
757 }
758
TEST_F(ProfileCompilationInfoTest,MegamorphicInlineCachesMerge)759 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) {
760 // Create a megamorphic inline cache.
761 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
762 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
763 pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
764 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
765 dex_pc_data.SetIsMegamorphic();
766 ic_map->Put(/*dex_pc*/ 0, dex_pc_data);
767
768 ProfileCompilationInfo info_megamorphic;
769 ASSERT_TRUE(AddMethod("dex_location1",
770 /*checksum*/ 1,
771 /*method_idx*/ 0,
772 pmi,
773 &info_megamorphic));
774
775 // Create a profile with no inline caches (for the same method).
776 ProfileCompilationInfo info_no_inline_cache;
777 ASSERT_TRUE(AddMethod("dex_location1",
778 /*checksum*/ 1,
779 /*method_idx*/ 0,
780 &info_no_inline_cache));
781
782 // Merge the megamorphic cache into the empty one.
783 ASSERT_TRUE(info_no_inline_cache.MergeWith(info_megamorphic));
784 ScratchFile profile;
785 // Saving profile should work without crashing (b/35644850).
786 ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile)));
787 }
788
TEST_F(ProfileCompilationInfoTest,MissingTypesInlineCachesMerge)789 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCachesMerge) {
790 // Create an inline cache with missing types
791 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
792 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
793 pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1);
794 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
795 dex_pc_data.SetIsMissingTypes();
796 ic_map->Put(/*dex_pc*/ 0, dex_pc_data);
797
798 ProfileCompilationInfo info_megamorphic;
799 ASSERT_TRUE(AddMethod("dex_location1",
800 /*checksum*/ 1,
801 /*method_idx*/ 0,
802 pmi,
803 &info_megamorphic));
804
805 // Create a profile with no inline caches (for the same method).
806 ProfileCompilationInfo info_no_inline_cache;
807 ASSERT_TRUE(AddMethod("dex_location1",
808 /*checksum*/ 1,
809 /*method_idx*/ 0,
810 &info_no_inline_cache));
811
812 // Merge the missing type cache into the empty one.
813 // Everything should be saved without errors.
814 ASSERT_TRUE(info_no_inline_cache.MergeWith(info_megamorphic));
815 ScratchFile profile;
816 ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile)));
817 }
818
TEST_F(ProfileCompilationInfoTest,LoadShouldClearExistingDataFromProfiles)819 TEST_F(ProfileCompilationInfoTest, LoadShouldClearExistingDataFromProfiles) {
820 ScratchFile profile;
821
822 ProfileCompilationInfo saved_info;
823 // Save a few methods.
824 for (uint16_t i = 0; i < 10; i++) {
825 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
826 }
827 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
828 ASSERT_EQ(0, profile.GetFile()->Flush());
829 ASSERT_TRUE(profile.GetFile()->ResetOffset());
830
831 // Add a bunch of methods to test_info.
832 ProfileCompilationInfo test_info;
833 for (uint16_t i = 0; i < 10; i++) {
834 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &test_info));
835 }
836
837 // Attempt to load the saved profile into test_info.
838 // This should fail since the test_info already contains data and the load would overwrite it.
839 ASSERT_FALSE(test_info.Load(GetFd(profile)));
840 }
841 } // namespace art
842