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 #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 "jit/profile_compilation_info.h"
30 #include "linear_alloc.h"
31 #include "mirror/class-inl.h"
32 #include "mirror/class_loader.h"
33 #include "scoped_thread_state_change-inl.h"
34 #include "ziparchive/zip_writer.h"
35 
36 namespace art {
37 
38 using Hotness = ProfileCompilationInfo::MethodHotness;
39 
40 static constexpr size_t kMaxMethodIds = 65535;
41 
42 class ProfileCompilationInfoTest : public CommonRuntimeTest {
43  public:
PostRuntimeCreate()44   void PostRuntimeCreate() OVERRIDE {
45     allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
46   }
47 
48  protected:
GetVirtualMethods(jobject class_loader,const std::string & clazz)49   std::vector<ArtMethod*> GetVirtualMethods(jobject class_loader,
50                                             const std::string& clazz) {
51     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
52     Thread* self = Thread::Current();
53     ScopedObjectAccess soa(self);
54     StackHandleScope<1> hs(self);
55     Handle<mirror::ClassLoader> h_loader(
56         hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader()));
57     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 
AddMethod(const std::string & dex_location,uint32_t checksum,uint16_t method_index,ProfileCompilationInfo * info)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 
AddMethod(const std::string & dex_location,uint32_t checksum,uint16_t method_index,const ProfileCompilationInfo::OfflineProfileMethodInfo & pmi,ProfileCompilationInfo * info)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 
AddClass(const std::string & dex_location,uint32_t checksum,dex::TypeIndex type_index,ProfileCompilationInfo * info)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 
GetFd(const ScratchFile & file)96   uint32_t GetFd(const ScratchFile& file) {
97     return static_cast<uint32_t>(file.GetFd());
98   }
99 
SaveProfilingInfo(const std::string & filename,const std::vector<ArtMethod * > & methods,const std::set<DexCacheResolvedClasses> & resolved_classes,Hotness::Flag flags)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`.
SaveProfilingInfoWithFakeInlineCaches(const std::string & filename,const std::vector<ArtMethod * > & methods,Hotness::Flag flags,SafeMap<ArtMethod *,ProfileMethodInfo> * 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.
CreateInlineCacheMap()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 
ConvertProfileMethodInfo(const ProfileMethodInfo & pmi)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   // Creates an offline profile used for testing inline caches.
GetOfflineProfileMethodInfo()220   ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() {
221     ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
222 
223     // Monomorphic
224     for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
225       ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
226       dex_pc_data.AddClass(0, dex::TypeIndex(0));
227       ic_map->Put(dex_pc, dex_pc_data);
228     }
229     // Polymorphic
230     for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
231       ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
232       dex_pc_data.AddClass(0, dex::TypeIndex(0));
233       dex_pc_data.AddClass(1, dex::TypeIndex(1));
234       dex_pc_data.AddClass(2, dex::TypeIndex(2));
235 
236       ic_map->Put(dex_pc, dex_pc_data);
237     }
238     // Megamorphic
239     for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
240       ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
241       dex_pc_data.SetIsMegamorphic();
242       ic_map->Put(dex_pc, dex_pc_data);
243     }
244     // Missing types
245     for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
246       ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
247       dex_pc_data.SetIsMissingTypes();
248       ic_map->Put(dex_pc, dex_pc_data);
249     }
250 
251     ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
252 
253     pmi.dex_references.emplace_back("dex_location1", /* checksum */1, kMaxMethodIds);
254     pmi.dex_references.emplace_back("dex_location2", /* checksum */2, kMaxMethodIds);
255     pmi.dex_references.emplace_back("dex_location3", /* checksum */3, kMaxMethodIds);
256 
257     return pmi;
258   }
259 
MakeMegamorphic(ProfileCompilationInfo::OfflineProfileMethodInfo * pmi)260   void MakeMegamorphic(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) {
261     ProfileCompilationInfo::InlineCacheMap* ic_map =
262         const_cast<ProfileCompilationInfo::InlineCacheMap*>(pmi->inline_caches);
263     for (auto it : *ic_map) {
264       for (uint16_t k = 0; k <= 2 * InlineCache::kIndividualCacheSize; k++) {
265         it.second.AddClass(0, dex::TypeIndex(k));
266       }
267     }
268   }
269 
SetIsMissingTypes(ProfileCompilationInfo::OfflineProfileMethodInfo * pmi)270   void SetIsMissingTypes(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) {
271     ProfileCompilationInfo::InlineCacheMap* ic_map =
272         const_cast<ProfileCompilationInfo::InlineCacheMap*>(pmi->inline_caches);
273     for (auto it : *ic_map) {
274       it.second.SetIsMissingTypes();
275     }
276   }
277 
TestProfileLoadFromZip(const char * zip_entry,size_t zip_flags,bool should_succeed,bool should_succeed_with_empty_profile=false)278   void TestProfileLoadFromZip(const char* zip_entry,
279                               size_t zip_flags,
280                               bool should_succeed,
281                               bool should_succeed_with_empty_profile = false) {
282     // Create a valid profile.
283     ScratchFile profile;
284     ProfileCompilationInfo saved_info;
285     for (uint16_t i = 0; i < 10; i++) {
286       ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
287       ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
288     }
289     ASSERT_TRUE(saved_info.Save(GetFd(profile)));
290     ASSERT_EQ(0, profile.GetFile()->Flush());
291 
292     // Prepare the profile content for zipping.
293     ASSERT_TRUE(profile.GetFile()->ResetOffset());
294     std::vector<uint8_t> data(profile.GetFile()->GetLength());
295     ASSERT_TRUE(profile.GetFile()->ReadFully(data.data(), data.size()));
296 
297     // Zip the profile content.
298     ScratchFile zip;
299     FILE* file = fopen(zip.GetFile()->GetPath().c_str(), "wb");
300     ZipWriter writer(file);
301     writer.StartEntry(zip_entry, zip_flags);
302     writer.WriteBytes(data.data(), data.size());
303     writer.FinishEntry();
304     writer.Finish();
305     fflush(file);
306     fclose(file);
307 
308     // Verify loading from the zip archive.
309     ProfileCompilationInfo loaded_info;
310     ASSERT_TRUE(zip.GetFile()->ResetOffset());
311     ASSERT_EQ(should_succeed, loaded_info.Load(zip.GetFile()->GetPath(), false));
312     if (should_succeed) {
313       if (should_succeed_with_empty_profile) {
314         ASSERT_TRUE(loaded_info.IsEmpty());
315       } else {
316         ASSERT_TRUE(loaded_info.Equals(saved_info));
317       }
318     }
319   }
320 
IsEmpty(const ProfileCompilationInfo & info)321   bool IsEmpty(const ProfileCompilationInfo& info) {
322     return info.IsEmpty();
323   }
324 
325   // Cannot sizeof the actual arrays so hard code the values here.
326   // They should not change anyway.
327   static constexpr int kProfileMagicSize = 4;
328   static constexpr int kProfileVersionSize = 4;
329 
330   std::unique_ptr<ArenaAllocator> allocator_;
331 
332   // Cache of inline caches generated during tests.
333   // This makes it easier to pass data between different utilities and ensure that
334   // caches are destructed at the end of the test.
335   std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
336 };
337 
TEST_F(ProfileCompilationInfoTest,SaveArtMethods)338 TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
339   ScratchFile profile;
340 
341   Thread* self = Thread::Current();
342   jobject class_loader;
343   {
344     ScopedObjectAccess soa(self);
345     class_loader = LoadDex("ProfileTestMultiDex");
346   }
347   ASSERT_NE(class_loader, nullptr);
348 
349   // Save virtual methods from Main.
350   std::set<DexCacheResolvedClasses> resolved_classes;
351   std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
352   ASSERT_TRUE(SaveProfilingInfo(
353       profile.GetFilename(), main_methods, resolved_classes, Hotness::kFlagPostStartup));
354 
355   // Check that what we saved is in the profile.
356   ProfileCompilationInfo info1;
357   ASSERT_TRUE(info1.Load(GetFd(profile)));
358   ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size());
359   {
360     ScopedObjectAccess soa(self);
361     for (ArtMethod* m : main_methods) {
362       Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
363       ASSERT_TRUE(h.IsHot());
364       ASSERT_TRUE(h.IsPostStartup());
365     }
366   }
367 
368   // Save virtual methods from Second.
369   std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
370   ASSERT_TRUE(SaveProfilingInfo(
371     profile.GetFilename(), second_methods, resolved_classes, Hotness::kFlagStartup));
372 
373   // Check that what we saved is in the profile (methods form Main and Second).
374   ProfileCompilationInfo info2;
375   ASSERT_TRUE(profile.GetFile()->ResetOffset());
376   ASSERT_TRUE(info2.Load(GetFd(profile)));
377   ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size());
378   {
379     ScopedObjectAccess soa(self);
380     for (ArtMethod* m : main_methods) {
381       Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
382       ASSERT_TRUE(h.IsHot());
383       ASSERT_TRUE(h.IsPostStartup());
384     }
385     for (ArtMethod* m : second_methods) {
386       Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
387       ASSERT_TRUE(h.IsHot());
388       ASSERT_TRUE(h.IsStartup());
389     }
390   }
391 }
392 
TEST_F(ProfileCompilationInfoTest,SaveFd)393 TEST_F(ProfileCompilationInfoTest, SaveFd) {
394   ScratchFile profile;
395 
396   ProfileCompilationInfo saved_info;
397   // Save a few methods.
398   for (uint16_t i = 0; i < 10; i++) {
399     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
400     ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
401   }
402   ASSERT_TRUE(saved_info.Save(GetFd(profile)));
403   ASSERT_EQ(0, profile.GetFile()->Flush());
404 
405   // Check that we get back what we saved.
406   ProfileCompilationInfo loaded_info;
407   ASSERT_TRUE(profile.GetFile()->ResetOffset());
408   ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
409   ASSERT_TRUE(loaded_info.Equals(saved_info));
410 
411   // Save more methods.
412   for (uint16_t i = 0; i < 100; i++) {
413     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
414     ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
415     ASSERT_TRUE(AddMethod("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
416   }
417   ASSERT_TRUE(profile.GetFile()->ResetOffset());
418   ASSERT_TRUE(saved_info.Save(GetFd(profile)));
419   ASSERT_EQ(0, profile.GetFile()->Flush());
420 
421   // Check that we get back everything we saved.
422   ProfileCompilationInfo loaded_info2;
423   ASSERT_TRUE(profile.GetFile()->ResetOffset());
424   ASSERT_TRUE(loaded_info2.Load(GetFd(profile)));
425   ASSERT_TRUE(loaded_info2.Equals(saved_info));
426 }
427 
TEST_F(ProfileCompilationInfoTest,AddMethodsAndClassesFail)428 TEST_F(ProfileCompilationInfoTest, AddMethodsAndClassesFail) {
429   ScratchFile profile;
430 
431   ProfileCompilationInfo info;
432   ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
433   // Trying to add info for an existing file but with a different checksum.
434   ASSERT_FALSE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
435 }
436 
TEST_F(ProfileCompilationInfoTest,MergeFail)437 TEST_F(ProfileCompilationInfoTest, MergeFail) {
438   ScratchFile profile;
439 
440   ProfileCompilationInfo info1;
441   ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
442   // Use the same file, change the checksum.
443   ProfileCompilationInfo info2;
444   ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
445 
446   ASSERT_FALSE(info1.MergeWith(info2));
447 }
448 
449 
TEST_F(ProfileCompilationInfoTest,MergeFdFail)450 TEST_F(ProfileCompilationInfoTest, MergeFdFail) {
451   ScratchFile profile;
452 
453   ProfileCompilationInfo info1;
454   ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
455   // Use the same file, change the checksum.
456   ProfileCompilationInfo info2;
457   ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
458 
459   ASSERT_TRUE(info1.Save(profile.GetFd()));
460   ASSERT_EQ(0, profile.GetFile()->Flush());
461   ASSERT_TRUE(profile.GetFile()->ResetOffset());
462 
463   ASSERT_FALSE(info2.Load(profile.GetFd()));
464 }
465 
TEST_F(ProfileCompilationInfoTest,SaveMaxMethods)466 TEST_F(ProfileCompilationInfoTest, SaveMaxMethods) {
467   ScratchFile profile;
468 
469   ProfileCompilationInfo saved_info;
470   // Save the maximum number of methods
471   for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
472     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
473     ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
474   }
475   // Save the maximum number of classes
476   for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
477     ASSERT_TRUE(AddClass("dex_location1", /* checksum */ 1, dex::TypeIndex(i), &saved_info));
478     ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, dex::TypeIndex(i), &saved_info));
479   }
480 
481   ASSERT_TRUE(saved_info.Save(GetFd(profile)));
482   ASSERT_EQ(0, profile.GetFile()->Flush());
483 
484   // Check that we get back what we saved.
485   ProfileCompilationInfo loaded_info;
486   ASSERT_TRUE(profile.GetFile()->ResetOffset());
487   ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
488   ASSERT_TRUE(loaded_info.Equals(saved_info));
489 }
490 
TEST_F(ProfileCompilationInfoTest,SaveEmpty)491 TEST_F(ProfileCompilationInfoTest, SaveEmpty) {
492   ScratchFile profile;
493 
494   ProfileCompilationInfo saved_info;
495   ASSERT_TRUE(saved_info.Save(GetFd(profile)));
496   ASSERT_EQ(0, profile.GetFile()->Flush());
497 
498   // Check that we get back what we saved.
499   ProfileCompilationInfo loaded_info;
500   ASSERT_TRUE(profile.GetFile()->ResetOffset());
501   ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
502   ASSERT_TRUE(loaded_info.Equals(saved_info));
503 }
504 
TEST_F(ProfileCompilationInfoTest,LoadEmpty)505 TEST_F(ProfileCompilationInfoTest, LoadEmpty) {
506   ScratchFile profile;
507 
508   ProfileCompilationInfo empty_info;
509 
510   ProfileCompilationInfo loaded_info;
511   ASSERT_TRUE(profile.GetFile()->ResetOffset());
512   ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
513   ASSERT_TRUE(loaded_info.Equals(empty_info));
514 }
515 
TEST_F(ProfileCompilationInfoTest,BadMagic)516 TEST_F(ProfileCompilationInfoTest, BadMagic) {
517   ScratchFile profile;
518   uint8_t buffer[] = { 1, 2, 3, 4 };
519   ASSERT_TRUE(profile.GetFile()->WriteFully(buffer, sizeof(buffer)));
520   ProfileCompilationInfo loaded_info;
521   ASSERT_TRUE(profile.GetFile()->ResetOffset());
522   ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
523 }
524 
TEST_F(ProfileCompilationInfoTest,BadVersion)525 TEST_F(ProfileCompilationInfoTest, BadVersion) {
526   ScratchFile profile;
527 
528   ASSERT_TRUE(profile.GetFile()->WriteFully(
529       ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
530   uint8_t version[] = { 'v', 'e', 'r', 's', 'i', 'o', 'n' };
531   ASSERT_TRUE(profile.GetFile()->WriteFully(version, sizeof(version)));
532   ASSERT_EQ(0, profile.GetFile()->Flush());
533 
534   ProfileCompilationInfo loaded_info;
535   ASSERT_TRUE(profile.GetFile()->ResetOffset());
536   ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
537 }
538 
TEST_F(ProfileCompilationInfoTest,Incomplete)539 TEST_F(ProfileCompilationInfoTest, Incomplete) {
540   ScratchFile profile;
541   ASSERT_TRUE(profile.GetFile()->WriteFully(
542       ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
543   ASSERT_TRUE(profile.GetFile()->WriteFully(
544       ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
545   // Write that we have at least one line.
546   uint8_t line_number[] = { 0, 1 };
547   ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
548   ASSERT_EQ(0, profile.GetFile()->Flush());
549 
550   ProfileCompilationInfo loaded_info;
551   ASSERT_TRUE(profile.GetFile()->ResetOffset());
552   ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
553 }
554 
TEST_F(ProfileCompilationInfoTest,TooLongDexLocation)555 TEST_F(ProfileCompilationInfoTest, TooLongDexLocation) {
556   ScratchFile profile;
557   ASSERT_TRUE(profile.GetFile()->WriteFully(
558       ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
559   ASSERT_TRUE(profile.GetFile()->WriteFully(
560       ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
561   // Write that we have at least one line.
562   uint8_t line_number[] = { 0, 1 };
563   ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
564 
565   // dex_location_size, methods_size, classes_size, checksum.
566   // Dex location size is too big and should be rejected.
567   uint8_t line[] = { 255, 255, 0, 1, 0, 1, 0, 0, 0, 0 };
568   ASSERT_TRUE(profile.GetFile()->WriteFully(line, sizeof(line)));
569   ASSERT_EQ(0, profile.GetFile()->Flush());
570 
571   ProfileCompilationInfo loaded_info;
572   ASSERT_TRUE(profile.GetFile()->ResetOffset());
573   ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
574 }
575 
TEST_F(ProfileCompilationInfoTest,UnexpectedContent)576 TEST_F(ProfileCompilationInfoTest, UnexpectedContent) {
577   ScratchFile profile;
578 
579   ProfileCompilationInfo saved_info;
580   // Save the maximum number of methods
581   for (uint16_t i = 0; i < 10; i++) {
582     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
583   }
584   ASSERT_TRUE(saved_info.Save(GetFd(profile)));
585 
586   uint8_t random_data[] = { 1, 2, 3};
587   ASSERT_TRUE(profile.GetFile()->WriteFully(random_data, sizeof(random_data)));
588 
589   ASSERT_EQ(0, profile.GetFile()->Flush());
590 
591   // Check that we fail because of unexpected data at the end of the file.
592   ProfileCompilationInfo loaded_info;
593   ASSERT_TRUE(profile.GetFile()->ResetOffset());
594   ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
595 }
596 
TEST_F(ProfileCompilationInfoTest,SaveInlineCaches)597 TEST_F(ProfileCompilationInfoTest, SaveInlineCaches) {
598   ScratchFile profile;
599 
600   ProfileCompilationInfo saved_info;
601   ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
602 
603   // Add methods with inline caches.
604   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
605     // Add a method which is part of the same dex file as one of the
606     // class from the inline caches.
607     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
608     // Add a method which is outside the set of dex files.
609     ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
610   }
611 
612   ASSERT_TRUE(saved_info.Save(GetFd(profile)));
613   ASSERT_EQ(0, profile.GetFile()->Flush());
614 
615   // Check that we get back what we saved.
616   ProfileCompilationInfo loaded_info;
617   ASSERT_TRUE(profile.GetFile()->ResetOffset());
618   ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
619 
620   ASSERT_TRUE(loaded_info.Equals(saved_info));
621 
622   std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
623       loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3);
624   ASSERT_TRUE(loaded_pmi1 != nullptr);
625   ASSERT_TRUE(*loaded_pmi1 == pmi);
626   std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 =
627       loaded_info.GetMethod("dex_location4", /* checksum */ 4, /* method_idx */ 3);
628   ASSERT_TRUE(loaded_pmi2 != nullptr);
629   ASSERT_TRUE(*loaded_pmi2 == pmi);
630 }
631 
TEST_F(ProfileCompilationInfoTest,MegamorphicInlineCaches)632 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) {
633   ScratchFile profile;
634 
635   ProfileCompilationInfo saved_info;
636   ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
637 
638   // Add methods with inline caches.
639   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
640     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
641   }
642 
643   ASSERT_TRUE(saved_info.Save(GetFd(profile)));
644   ASSERT_EQ(0, profile.GetFile()->Flush());
645 
646   // Make the inline caches megamorphic and add them to the profile again.
647   ProfileCompilationInfo saved_info_extra;
648   ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo();
649   MakeMegamorphic(&pmi_extra);
650   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
651     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
652   }
653 
654   ASSERT_TRUE(profile.GetFile()->ResetOffset());
655   ASSERT_TRUE(saved_info_extra.Save(GetFd(profile)));
656   ASSERT_EQ(0, profile.GetFile()->Flush());
657 
658   // Merge the profiles so that we have the same view as the file.
659   ASSERT_TRUE(saved_info.MergeWith(saved_info_extra));
660 
661   // Check that we get back what we saved.
662   ProfileCompilationInfo loaded_info;
663   ASSERT_TRUE(profile.GetFile()->ResetOffset());
664   ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
665 
666   ASSERT_TRUE(loaded_info.Equals(saved_info));
667 
668   std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
669       loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3);
670 
671   ASSERT_TRUE(loaded_pmi1 != nullptr);
672   ASSERT_TRUE(*loaded_pmi1 == pmi_extra);
673 }
674 
TEST_F(ProfileCompilationInfoTest,MissingTypesInlineCaches)675 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCaches) {
676   ScratchFile profile;
677 
678   ProfileCompilationInfo saved_info;
679   ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
680 
681   // Add methods with inline caches.
682   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
683     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
684   }
685 
686   ASSERT_TRUE(saved_info.Save(GetFd(profile)));
687   ASSERT_EQ(0, profile.GetFile()->Flush());
688 
689   // Make some inline caches megamorphic and add them to the profile again.
690   ProfileCompilationInfo saved_info_extra;
691   ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo();
692   MakeMegamorphic(&pmi_extra);
693   for (uint16_t method_idx = 5; method_idx < 10; method_idx++) {
694     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
695   }
696 
697   // Mark all inline caches with missing types and add them to the profile again.
698   // This will verify that all inline caches (megamorphic or not) should be marked as missing types.
699   ProfileCompilationInfo::OfflineProfileMethodInfo missing_types = GetOfflineProfileMethodInfo();
700   SetIsMissingTypes(&missing_types);
701   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
702     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra));
703   }
704 
705   ASSERT_TRUE(profile.GetFile()->ResetOffset());
706   ASSERT_TRUE(saved_info_extra.Save(GetFd(profile)));
707   ASSERT_EQ(0, profile.GetFile()->Flush());
708 
709   // Merge the profiles so that we have the same view as the file.
710   ASSERT_TRUE(saved_info.MergeWith(saved_info_extra));
711 
712   // Check that we get back what we saved.
713   ProfileCompilationInfo loaded_info;
714   ASSERT_TRUE(profile.GetFile()->ResetOffset());
715   ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
716 
717   ASSERT_TRUE(loaded_info.Equals(saved_info));
718 
719   std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
720       loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3);
721   ASSERT_TRUE(loaded_pmi1 != nullptr);
722   ASSERT_TRUE(*loaded_pmi1 == pmi_extra);
723 }
724 
TEST_F(ProfileCompilationInfoTest,SaveArtMethodsWithInlineCaches)725 TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
726   ScratchFile profile;
727 
728   Thread* self = Thread::Current();
729   jobject class_loader;
730   {
731     ScopedObjectAccess soa(self);
732     class_loader = LoadDex("ProfileTestMultiDex");
733   }
734   ASSERT_NE(class_loader, nullptr);
735 
736   // Save virtual methods from Main.
737   std::set<DexCacheResolvedClasses> resolved_classes;
738   std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
739 
740   SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map;
741   ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches(
742       profile.GetFilename(), main_methods, Hotness::kFlagStartup, &profile_methods_map));
743 
744   // Check that what we saved is in the profile.
745   ProfileCompilationInfo info;
746   ASSERT_TRUE(info.Load(GetFd(profile)));
747   ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size());
748   {
749     ScopedObjectAccess soa(self);
750     for (ArtMethod* m : main_methods) {
751       Hotness h = info.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
752       ASSERT_TRUE(h.IsHot());
753       ASSERT_TRUE(h.IsStartup());
754       const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
755       std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_pmi =
756           info.GetMethod(m->GetDexFile()->GetLocation(),
757                          m->GetDexFile()->GetLocationChecksum(),
758                          m->GetDexMethodIndex());
759       ASSERT_TRUE(offline_pmi != nullptr);
760       ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi =
761           ConvertProfileMethodInfo(pmi);
762       ASSERT_EQ(converted_pmi, *offline_pmi);
763     }
764   }
765 }
766 
TEST_F(ProfileCompilationInfoTest,InvalidChecksumInInlineCache)767 TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCache) {
768   ScratchFile profile;
769 
770   ProfileCompilationInfo info;
771   ProfileCompilationInfo::OfflineProfileMethodInfo pmi1 = GetOfflineProfileMethodInfo();
772   ProfileCompilationInfo::OfflineProfileMethodInfo pmi2 = GetOfflineProfileMethodInfo();
773   // Modify the checksum to trigger a mismatch.
774   pmi2.dex_references[0].dex_checksum++;
775 
776   ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /*method_idx*/ 0, pmi1, &info));
777   ASSERT_FALSE(AddMethod("dex_location2", /* checksum */ 2, /*method_idx*/ 0, pmi2, &info));
778 }
779 
780 // Verify that profiles behave correctly even if the methods are added in a different
781 // order and with a different dex profile indices for the dex files.
TEST_F(ProfileCompilationInfoTest,MergeInlineCacheTriggerReindex)782 TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) {
783   ScratchFile profile;
784 
785   ProfileCompilationInfo info;
786   ProfileCompilationInfo info_reindexed;
787 
788   ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
789   ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
790   pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds);
791   pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2, kMaxMethodIds);
792   for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
793     ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
794     dex_pc_data.AddClass(0, dex::TypeIndex(0));
795     dex_pc_data.AddClass(1, dex::TypeIndex(1));
796     ic_map->Put(dex_pc, dex_pc_data);
797   }
798 
799   ProfileCompilationInfo::InlineCacheMap* ic_map_reindexed = CreateInlineCacheMap();
800   ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed(ic_map_reindexed);
801   pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2, kMaxMethodIds);
802   pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds);
803   for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
804     ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
805     dex_pc_data.AddClass(1, dex::TypeIndex(0));
806     dex_pc_data.AddClass(0, dex::TypeIndex(1));
807     ic_map_reindexed->Put(dex_pc, dex_pc_data);
808   }
809 
810   // Profile 1 and Profile 2 get the same methods but in different order.
811   // This will trigger a different dex numbers.
812   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
813     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &info));
814     ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &info));
815   }
816 
817   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
818     ASSERT_TRUE(AddMethod(
819       "dex_location2", /* checksum */ 2, method_idx, pmi_reindexed, &info_reindexed));
820     ASSERT_TRUE(AddMethod(
821       "dex_location1", /* checksum */ 1, method_idx, pmi_reindexed, &info_reindexed));
822   }
823 
824   ProfileCompilationInfo info_backup;
825   info_backup.MergeWith(info);
826   ASSERT_TRUE(info.MergeWith(info_reindexed));
827   // Merging should have no effect as we're adding the exact same stuff.
828   ASSERT_TRUE(info.Equals(info_backup));
829   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
830     std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
831         info.GetMethod("dex_location1", /* checksum */ 1, method_idx);
832     ASSERT_TRUE(loaded_pmi1 != nullptr);
833     ASSERT_TRUE(*loaded_pmi1 == pmi);
834     std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 =
835         info.GetMethod("dex_location2", /* checksum */ 2, method_idx);
836     ASSERT_TRUE(loaded_pmi2 != nullptr);
837     ASSERT_TRUE(*loaded_pmi2 == pmi);
838   }
839 }
840 
TEST_F(ProfileCompilationInfoTest,AddMoreDexFileThanLimit)841 TEST_F(ProfileCompilationInfoTest, AddMoreDexFileThanLimit) {
842   ProfileCompilationInfo info;
843   // Save a few methods.
844   for (uint16_t i = 0; i < std::numeric_limits<uint8_t>::max(); i++) {
845     std::string dex_location = std::to_string(i);
846     ASSERT_TRUE(AddMethod(dex_location, /* checksum */ 1, /* method_idx */ i, &info));
847   }
848   // We only support at most 255 dex files.
849   ASSERT_FALSE(AddMethod(
850       /*dex_location*/ "256", /* checksum */ 1, /* method_idx */ 0, &info));
851 }
852 
TEST_F(ProfileCompilationInfoTest,MegamorphicInlineCachesMerge)853 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) {
854   // Create a megamorphic inline cache.
855   ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
856   ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
857   pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds);
858   ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
859   dex_pc_data.SetIsMegamorphic();
860   ic_map->Put(/*dex_pc*/ 0, dex_pc_data);
861 
862   ProfileCompilationInfo info_megamorphic;
863   ASSERT_TRUE(AddMethod("dex_location1",
864                         /*checksum*/ 1,
865                         /*method_idx*/ 0,
866                         pmi,
867                         &info_megamorphic));
868 
869   // Create a profile with no inline caches (for the same method).
870   ProfileCompilationInfo info_no_inline_cache;
871   ASSERT_TRUE(AddMethod("dex_location1",
872                         /*checksum*/ 1,
873                         /*method_idx*/ 0,
874                         &info_no_inline_cache));
875 
876   // Merge the megamorphic cache into the empty one.
877   ASSERT_TRUE(info_no_inline_cache.MergeWith(info_megamorphic));
878   ScratchFile profile;
879   // Saving profile should work without crashing (b/35644850).
880   ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile)));
881 }
882 
TEST_F(ProfileCompilationInfoTest,MissingTypesInlineCachesMerge)883 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCachesMerge) {
884   // Create an inline cache with missing types
885   ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
886   ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
887   pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1, kMaxMethodIds);
888   ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
889   dex_pc_data.SetIsMissingTypes();
890   ic_map->Put(/*dex_pc*/ 0, dex_pc_data);
891 
892   ProfileCompilationInfo info_megamorphic;
893   ASSERT_TRUE(AddMethod("dex_location1",
894                         /*checksum*/ 1,
895                         /*method_idx*/ 0,
896                         pmi,
897                         &info_megamorphic));
898 
899   // Create a profile with no inline caches (for the same method).
900   ProfileCompilationInfo info_no_inline_cache;
901   ASSERT_TRUE(AddMethod("dex_location1",
902                         /*checksum*/ 1,
903                         /*method_idx*/ 0,
904                         &info_no_inline_cache));
905 
906   // Merge the missing type cache into the empty one.
907   // Everything should be saved without errors.
908   ASSERT_TRUE(info_no_inline_cache.MergeWith(info_megamorphic));
909   ScratchFile profile;
910   ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile)));
911 }
912 
TEST_F(ProfileCompilationInfoTest,SampledMethodsTest)913 TEST_F(ProfileCompilationInfoTest, SampledMethodsTest) {
914   ProfileCompilationInfo test_info;
915   static constexpr size_t kNumMethods = 1000;
916   static constexpr size_t kChecksum1 = 1234;
917   static constexpr size_t kChecksum2 = 4321;
918   static const std::string kDex1 = "dex1";
919   static const std::string kDex2 = "dex2";
920   test_info.AddMethodIndex(Hotness::kFlagStartup, kDex1, kChecksum1, 1, kNumMethods);
921   test_info.AddMethodIndex(Hotness::kFlagPostStartup, kDex1, kChecksum1, 5, kNumMethods);
922   test_info.AddMethodIndex(Hotness::kFlagStartup, kDex2, kChecksum2, 2, kNumMethods);
923   test_info.AddMethodIndex(Hotness::kFlagPostStartup, kDex2, kChecksum2, 4, kNumMethods);
924   auto run_test = [](const ProfileCompilationInfo& info) {
925     EXPECT_FALSE(info.GetMethodHotness(kDex1, kChecksum1, 2).IsInProfile());
926     EXPECT_FALSE(info.GetMethodHotness(kDex1, kChecksum1, 4).IsInProfile());
927     EXPECT_TRUE(info.GetMethodHotness(kDex1, kChecksum1, 1).IsStartup());
928     EXPECT_FALSE(info.GetMethodHotness(kDex1, kChecksum1, 3).IsStartup());
929     EXPECT_TRUE(info.GetMethodHotness(kDex1, kChecksum1, 5).IsPostStartup());
930     EXPECT_FALSE(info.GetMethodHotness(kDex1, kChecksum1, 6).IsStartup());
931     EXPECT_TRUE(info.GetMethodHotness(kDex2, kChecksum2, 2).IsStartup());
932     EXPECT_TRUE(info.GetMethodHotness(kDex2, kChecksum2, 4).IsPostStartup());
933   };
934   run_test(test_info);
935 
936   // Save the profile.
937   ScratchFile profile;
938   ASSERT_TRUE(test_info.Save(GetFd(profile)));
939   ASSERT_EQ(0, profile.GetFile()->Flush());
940   ASSERT_TRUE(profile.GetFile()->ResetOffset());
941 
942   // Load the profile and make sure we can read the data and it matches what we expect.
943   ProfileCompilationInfo loaded_info;
944   ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
945   run_test(loaded_info);
946 
947   // Test that the bitmap gets merged properly.
948   EXPECT_FALSE(test_info.GetMethodHotness(kDex1, kChecksum1, 11).IsStartup());
949   {
950     ProfileCompilationInfo merge_info;
951     merge_info.AddMethodIndex(Hotness::kFlagStartup, kDex1, kChecksum1, 11, kNumMethods);
952     test_info.MergeWith(merge_info);
953   }
954   EXPECT_TRUE(test_info.GetMethodHotness(kDex1, kChecksum1, 11).IsStartup());
955 
956   // Test bulk adding.
957   {
958     std::unique_ptr<const DexFile> dex(OpenTestDexFile("ManyMethods"));
959     ProfileCompilationInfo info;
960     std::vector<uint16_t> hot_methods = {1, 3, 5};
961     std::vector<uint16_t> startup_methods = {1, 2};
962     std::vector<uint16_t> post_methods = {0, 2, 6};
963     ASSERT_GE(dex->NumMethodIds(), 7u);
964     info.AddMethodsForDex(static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup),
965                           dex.get(),
966                           hot_methods.begin(),
967                           hot_methods.end());
968     info.AddMethodsForDex(Hotness::kFlagStartup,
969                           dex.get(),
970                           startup_methods.begin(),
971                           startup_methods.end());
972     info.AddMethodsForDex(Hotness::kFlagPostStartup,
973                           dex.get(),
974                           post_methods.begin(),
975                           post_methods.end());
976     for (uint16_t id : hot_methods) {
977       EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsHot());
978       EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsStartup());
979     }
980     for (uint16_t id : startup_methods) {
981       EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsStartup());
982     }
983     for (uint16_t id : post_methods) {
984       EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsPostStartup());
985     }
986     EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), 6)).IsPostStartup());
987     // Check that methods that shouldn't have been touched are OK.
988     EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), 0)).IsInProfile());
989     EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 4)).IsInProfile());
990     EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 7)).IsInProfile());
991     EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 1)).IsPostStartup());
992     EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 4)).IsStartup());
993     EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 6)).IsStartup());
994   }
995 }
996 
TEST_F(ProfileCompilationInfoTest,LoadFromZipCompress)997 TEST_F(ProfileCompilationInfoTest, LoadFromZipCompress) {
998   TestProfileLoadFromZip("primary.prof",
999                          ZipWriter::kCompress | ZipWriter::kAlign32,
1000                          /*should_succeed*/true);
1001 }
1002 
TEST_F(ProfileCompilationInfoTest,LoadFromZipUnCompress)1003 TEST_F(ProfileCompilationInfoTest, LoadFromZipUnCompress) {
1004   TestProfileLoadFromZip("primary.prof",
1005                          ZipWriter::kAlign32,
1006                          /*should_succeed*/true);
1007 }
1008 
TEST_F(ProfileCompilationInfoTest,LoadFromZipUnAligned)1009 TEST_F(ProfileCompilationInfoTest, LoadFromZipUnAligned) {
1010   TestProfileLoadFromZip("primary.prof",
1011                          0,
1012                          /*should_succeed*/true);
1013 }
1014 
TEST_F(ProfileCompilationInfoTest,LoadFromZipFailBadZipEntry)1015 TEST_F(ProfileCompilationInfoTest, LoadFromZipFailBadZipEntry) {
1016   TestProfileLoadFromZip("invalid.profile.entry",
1017                          0,
1018                          /*should_succeed*/true,
1019                          /*should_succeed_with_empty_profile*/true);
1020 }
1021 
TEST_F(ProfileCompilationInfoTest,LoadFromZipFailBadProfile)1022 TEST_F(ProfileCompilationInfoTest, LoadFromZipFailBadProfile) {
1023   // Create a bad profile.
1024   ScratchFile profile;
1025   ASSERT_TRUE(profile.GetFile()->WriteFully(
1026       ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
1027   ASSERT_TRUE(profile.GetFile()->WriteFully(
1028       ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
1029   // Write that we have at least one line.
1030   uint8_t line_number[] = { 0, 1 };
1031   ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
1032   ASSERT_EQ(0, profile.GetFile()->Flush());
1033 
1034   // Prepare the profile content for zipping.
1035   ASSERT_TRUE(profile.GetFile()->ResetOffset());
1036   std::vector<uint8_t> data(profile.GetFile()->GetLength());
1037   ASSERT_TRUE(profile.GetFile()->ReadFully(data.data(), data.size()));
1038 
1039   // Zip the profile content.
1040   ScratchFile zip;
1041   FILE* file = fopen(zip.GetFile()->GetPath().c_str(), "wb");
1042   ZipWriter writer(file);
1043   writer.StartEntry("primary.prof", ZipWriter::kAlign32);
1044   writer.WriteBytes(data.data(), data.size());
1045   writer.FinishEntry();
1046   writer.Finish();
1047   fflush(file);
1048   fclose(file);
1049 
1050   // Check that we failed to load.
1051   ProfileCompilationInfo loaded_info;
1052   ASSERT_TRUE(zip.GetFile()->ResetOffset());
1053   ASSERT_FALSE(loaded_info.Load(GetFd(zip)));
1054 }
1055 
TEST_F(ProfileCompilationInfoTest,UpdateProfileKeyOk)1056 TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOk) {
1057   std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
1058 
1059   ProfileCompilationInfo info;
1060   for (const std::unique_ptr<const DexFile>& dex : dex_files) {
1061     // Create the profile with a different location so that we can update it to the
1062     // real dex location later.
1063     std::string base_location = DexFileLoader::GetBaseLocation(dex->GetLocation());
1064     std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(dex->GetLocation());
1065     std::string old_name = base_location + "-old" + multidex_suffix;
1066     info.AddMethodIndex(Hotness::kFlagHot,
1067                         old_name,
1068                         dex->GetLocationChecksum(),
1069                         /* method_idx */ 0,
1070                         dex->NumMethodIds());
1071   }
1072 
1073   // Update the profile keys based on the original dex files
1074   ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
1075 
1076   // Verify that we find the methods when searched with the original dex files.
1077   for (const std::unique_ptr<const DexFile>& dex : dex_files) {
1078     std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi =
1079         info.GetMethod(dex->GetLocation(), dex->GetLocationChecksum(), /* method_idx */ 0);
1080     ASSERT_TRUE(loaded_pmi != nullptr);
1081   }
1082 }
1083 
TEST_F(ProfileCompilationInfoTest,UpdateProfileKeyOkButNoUpdate)1084 TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkButNoUpdate) {
1085   std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
1086 
1087   ProfileCompilationInfo info;
1088   info.AddMethodIndex(Hotness::kFlagHot,
1089                       "my.app",
1090                       /* checksum */ 123,
1091                       /* method_idx */ 0,
1092                       /* num_method_ids */ 10);
1093 
1094   // Update the profile keys based on the original dex files
1095   ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
1096 
1097   // Verify that we did not perform any update and that we cannot find anything with the new
1098   // location.
1099   for (const std::unique_ptr<const DexFile>& dex : dex_files) {
1100     std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi =
1101         info.GetMethod(dex->GetLocation(), dex->GetLocationChecksum(), /* method_idx */ 0);
1102     ASSERT_TRUE(loaded_pmi == nullptr);
1103   }
1104 
1105   // Verify that we can find the original entry.
1106   std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi =
1107         info.GetMethod("my.app", /* checksum */ 123, /* method_idx */ 0);
1108   ASSERT_TRUE(loaded_pmi != nullptr);
1109 }
1110 
TEST_F(ProfileCompilationInfoTest,UpdateProfileKeyFail)1111 TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyFail) {
1112   std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("MultiDex");
1113 
1114 
1115   ProfileCompilationInfo info;
1116   // Add all dex
1117   for (const std::unique_ptr<const DexFile>& dex : dex_files) {
1118     // Create the profile with a different location so that we can update it to the
1119     // real dex location later.
1120     std::string base_location = DexFileLoader::GetBaseLocation(dex->GetLocation());
1121     std::string multidex_suffix = DexFileLoader::GetMultiDexSuffix(dex->GetLocation());
1122     std::string old_name = base_location + "-old" + multidex_suffix;
1123     info.AddMethodIndex(Hotness::kFlagHot,
1124                         old_name,
1125                         dex->GetLocationChecksum(),
1126                         /* method_idx */ 0,
1127                         dex->NumMethodIds());
1128   }
1129 
1130   // Add a method index using the location we want to rename to.
1131   // This will cause the rename to fail because an existing entry would already have that name.
1132   info.AddMethodIndex(Hotness::kFlagHot,
1133                       dex_files[0]->GetLocation(),
1134                       /* checksum */ 123,
1135                       /* method_idx */ 0,
1136                       dex_files[0]->NumMethodIds());
1137 
1138   ASSERT_FALSE(info.UpdateProfileKeys(dex_files));
1139 }
1140 
TEST_F(ProfileCompilationInfoTest,FilteredLoading)1141 TEST_F(ProfileCompilationInfoTest, FilteredLoading) {
1142   ScratchFile profile;
1143 
1144   ProfileCompilationInfo saved_info;
1145   ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
1146 
1147   // Add methods with inline caches.
1148   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1149     // Add a method which is part of the same dex file as one of the class from the inline caches.
1150     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
1151     ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &saved_info));
1152     // Add a method which is outside the set of dex files.
1153     ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
1154   }
1155 
1156   ASSERT_TRUE(saved_info.Save(GetFd(profile)));
1157   ASSERT_EQ(0, profile.GetFile()->Flush());
1158 
1159   // Check that we get back what we saved.
1160   ProfileCompilationInfo loaded_info;
1161   ASSERT_TRUE(profile.GetFile()->ResetOffset());
1162 
1163   // Filter out dex locations. Keep only dex_location1 and dex_location3.
1164   ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1165       [](const std::string& dex_location, uint32_t checksum) -> bool {
1166           return (dex_location == "dex_location1" && checksum == 1)
1167               || (dex_location == "dex_location3" && checksum == 3);
1168         };
1169   ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
1170 
1171   // Verify that we filtered out locations during load.
1172 
1173   // Dex location 2 and 4 should have been filtered out
1174   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1175     ASSERT_TRUE(nullptr == loaded_info.GetMethod("dex_location2", /* checksum */ 2, method_idx));
1176     ASSERT_TRUE(nullptr == loaded_info.GetMethod("dex_location4", /* checksum */ 4, method_idx));
1177   }
1178 
1179   // Dex location 1 should have all all the inline caches referencing dex location 2 set to
1180   // missing types.
1181   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1182     // The methods for dex location 1 should be in the profile data.
1183     std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
1184       loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ method_idx);
1185     ASSERT_TRUE(loaded_pmi1 != nullptr);
1186 
1187     // Verify the inline cache.
1188     // Everything should be as constructed by GetOfflineProfileMethodInfo with the exception
1189     // of the inline caches referring types from dex_location2.
1190     // These should be set to IsMissingType.
1191     ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
1192 
1193     // Monomorphic types should remain the same as dex_location1 was kept.
1194     for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
1195       ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
1196       dex_pc_data.AddClass(0, dex::TypeIndex(0));
1197       ic_map->Put(dex_pc, dex_pc_data);
1198     }
1199     // Polymorphic inline cache should have been transformed to IsMissingType due to
1200     // the removal of dex_location2.
1201     for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
1202       ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
1203       dex_pc_data.SetIsMissingTypes();
1204       ic_map->Put(dex_pc, dex_pc_data);
1205     }
1206 
1207     // Megamorphic are not affected by removal of dex files.
1208     for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
1209       ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
1210       dex_pc_data.SetIsMegamorphic();
1211       ic_map->Put(dex_pc, dex_pc_data);
1212     }
1213     // Missing types are not affected be removal of dex files.
1214     for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
1215       ProfileCompilationInfo::DexPcData dex_pc_data(allocator_.get());
1216       dex_pc_data.SetIsMissingTypes();
1217       ic_map->Put(dex_pc, dex_pc_data);
1218     }
1219 
1220     ProfileCompilationInfo::OfflineProfileMethodInfo expected_pmi(ic_map);
1221 
1222     // The dex references should not have  dex_location2 in the list.
1223     expected_pmi.dex_references.emplace_back("dex_location1", /* checksum */1, kMaxMethodIds);
1224     expected_pmi.dex_references.emplace_back("dex_location3", /* checksum */3, kMaxMethodIds);
1225 
1226     // Now check that we get back what we expect.
1227     ASSERT_TRUE(*loaded_pmi1 == expected_pmi);
1228   }
1229 }
1230 
TEST_F(ProfileCompilationInfoTest,FilteredLoadingRemoveAll)1231 TEST_F(ProfileCompilationInfoTest, FilteredLoadingRemoveAll) {
1232   ScratchFile profile;
1233 
1234   ProfileCompilationInfo saved_info;
1235   ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
1236 
1237   // Add methods with inline caches.
1238   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1239     // Add a method which is part of the same dex file as one of the class from the inline caches.
1240     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
1241     ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &saved_info));
1242     // Add a method which is outside the set of dex files.
1243     ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
1244   }
1245 
1246   ASSERT_TRUE(saved_info.Save(GetFd(profile)));
1247   ASSERT_EQ(0, profile.GetFile()->Flush());
1248 
1249   // Check that we get back what we saved.
1250   ProfileCompilationInfo loaded_info;
1251   ASSERT_TRUE(profile.GetFile()->ResetOffset());
1252 
1253   // Remove all elements.
1254   ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1255       [](const std::string&, uint32_t) -> bool { return false; };
1256   ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
1257 
1258   // Verify that we filtered out everything.
1259   ASSERT_TRUE(IsEmpty(loaded_info));
1260 }
1261 
TEST_F(ProfileCompilationInfoTest,FilteredLoadingKeepAll)1262 TEST_F(ProfileCompilationInfoTest, FilteredLoadingKeepAll) {
1263   ScratchFile profile;
1264 
1265   ProfileCompilationInfo saved_info;
1266   ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo();
1267 
1268   // Add methods with inline caches.
1269   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1270     // Add a method which is part of the same dex file as one of the
1271     // class from the inline caches.
1272     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info));
1273     // Add a method which is outside the set of dex files.
1274     ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info));
1275   }
1276 
1277   ASSERT_TRUE(saved_info.Save(GetFd(profile)));
1278   ASSERT_EQ(0, profile.GetFile()->Flush());
1279 
1280   // Check that we get back what we saved.
1281   ProfileCompilationInfo loaded_info;
1282   ASSERT_TRUE(profile.GetFile()->ResetOffset());
1283 
1284   // Keep all elements.
1285   ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1286       [](const std::string&, uint32_t) -> bool { return true; };
1287   ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
1288 
1289 
1290   ASSERT_TRUE(loaded_info.Equals(saved_info));
1291 
1292   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1293     std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 =
1294         loaded_info.GetMethod("dex_location1", /* checksum */ 1, method_idx);
1295     ASSERT_TRUE(loaded_pmi1 != nullptr);
1296     ASSERT_TRUE(*loaded_pmi1 == pmi);
1297   }
1298   for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1299     std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 =
1300         loaded_info.GetMethod("dex_location4", /* checksum */ 4, method_idx);
1301     ASSERT_TRUE(loaded_pmi2 != nullptr);
1302     ASSERT_TRUE(*loaded_pmi2 == pmi);
1303   }
1304 }
1305 
1306 // Regression test: we were failing to do a filtering loading when the filtered dex file
1307 // contained profiled classes.
TEST_F(ProfileCompilationInfoTest,FilteredLoadingWithClasses)1308 TEST_F(ProfileCompilationInfoTest, FilteredLoadingWithClasses) {
1309   ScratchFile profile;
1310 
1311   // Save a profile with 2 dex files containing just classes.
1312   ProfileCompilationInfo saved_info;
1313   uint16_t item_count = 1000;
1314   for (uint16_t i = 0; i < item_count; i++) {
1315     ASSERT_TRUE(AddClass("dex_location1", /* checksum */ 1, dex::TypeIndex(i), &saved_info));
1316     ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, dex::TypeIndex(i), &saved_info));
1317   }
1318 
1319   ASSERT_TRUE(saved_info.Save(GetFd(profile)));
1320   ASSERT_EQ(0, profile.GetFile()->Flush());
1321 
1322 
1323   // Filter out dex locations: kepp only dex_location2.
1324   ProfileCompilationInfo loaded_info;
1325   ASSERT_TRUE(profile.GetFile()->ResetOffset());
1326   ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1327       [](const std::string& dex_location, uint32_t checksum) -> bool {
1328           return (dex_location == "dex_location2" && checksum == 2);
1329         };
1330   ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
1331 
1332   // Compute the expectation.
1333   ProfileCompilationInfo expected_info;
1334   for (uint16_t i = 0; i < item_count; i++) {
1335     ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, dex::TypeIndex(i), &expected_info));
1336   }
1337 
1338   // Validate the expectation.
1339   ASSERT_TRUE(loaded_info.Equals(expected_info));
1340 }
1341 
1342 
TEST_F(ProfileCompilationInfoTest,ClearData)1343 TEST_F(ProfileCompilationInfoTest, ClearData) {
1344   ProfileCompilationInfo info;
1345   for (uint16_t i = 0; i < 10; i++) {
1346     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info));
1347   }
1348   ASSERT_FALSE(IsEmpty(info));
1349   info.ClearData();
1350   ASSERT_TRUE(IsEmpty(info));
1351 }
1352 
TEST_F(ProfileCompilationInfoTest,ClearDataAndSave)1353 TEST_F(ProfileCompilationInfoTest, ClearDataAndSave) {
1354   ProfileCompilationInfo info;
1355   for (uint16_t i = 0; i < 10; i++) {
1356     ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &info));
1357   }
1358   info.ClearData();
1359 
1360   ScratchFile profile;
1361   ASSERT_TRUE(info.Save(GetFd(profile)));
1362   ASSERT_EQ(0, profile.GetFile()->Flush());
1363 
1364   // Check that we get back what we saved.
1365   ProfileCompilationInfo loaded_info;
1366   ASSERT_TRUE(profile.GetFile()->ResetOffset());
1367   ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
1368   ASSERT_TRUE(loaded_info.Equals(info));
1369 }
1370 
1371 }  // namespace art
1372