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