Home | History | Annotate | Download | only in jit
      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:
     36   void PostRuntimeCreate() OVERRIDE {
     37     arena_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
     38   }
     39 
     40  protected:
     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 
     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 
     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 
     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 
     81   uint32_t GetFd(const ScratchFile& file) {
     82     return static_cast<uint32_t>(file.GetFd());
     83   }
     84 
     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`.
    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.
    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 
    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.
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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.
    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 
    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 
    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 
    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 
    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