• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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