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 <algorithm>
19 #include <stdio.h>
20
21 #include "base/arena_allocator.h"
22 #include "base/common_art_test.h"
23 #include "base/unix_file/fd_file.h"
24 #include "dex/compact_dex_file.h"
25 #include "dex/dex_file.h"
26 #include "dex/dex_file_loader.h"
27 #include "dex/method_reference.h"
28 #include "dex/type_reference.h"
29 #include "profile/profile_compilation_info.h"
30 #include "profile/profile_test_helper.h"
31 #include "ziparchive/zip_writer.h"
32
33 namespace art {
34
35 using ItemMetadata = FlattenProfileData::ItemMetadata;
36
37 class ProfileCompilationInfoTest : public CommonArtTest, public ProfileTestHelper {
38 public:
SetUp()39 void SetUp() override {
40 CommonArtTest::SetUp();
41 allocator_.reset(new ArenaAllocator(&pool_));
42
43 dex1 = BuildDex("location1", /*location_checksum=*/ 1, "LUnique1;", /*num_method_ids=*/ 101);
44 dex2 = BuildDex("location2", /*location_checksum=*/ 2, "LUnique2;", /*num_method_ids=*/ 102);
45 dex3 = BuildDex("location3", /*location_checksum=*/ 3, "LUnique3;", /*num_method_ids=*/ 103);
46 dex4 = BuildDex("location4", /*location_checksum=*/ 4, "LUnique4;", /*num_method_ids=*/ 104);
47
48 dex1_checksum_missmatch = BuildDex("location1",
49 /*location_checksum=*/ 12,
50 "LUnique1;",
51 /*num_method_ids=*/ 101);
52 dex1_renamed = BuildDex("location1-renamed",
53 /*location_checksum=*/ 1,
54 "LUnique1;",
55 /*num_method_ids=*/ 101);
56 dex2_renamed = BuildDex("location2-renamed",
57 /*location_checksum=*/ 2,
58 "LUnique2;",
59 /*num_method_ids=*/ 102);
60 }
61
62 protected:
GetFd(const ScratchFile & file)63 uint32_t GetFd(const ScratchFile& file) {
64 return static_cast<uint32_t>(file.GetFd());
65 }
66
GetMethod(const ProfileCompilationInfo & info,const DexFile * dex,uint16_t method_idx,const ProfileSampleAnnotation & annotation=ProfileSampleAnnotation::kNone)67 ProfileCompilationInfo::MethodHotness GetMethod(
68 const ProfileCompilationInfo& info,
69 const DexFile* dex,
70 uint16_t method_idx,
71 const ProfileSampleAnnotation& annotation = ProfileSampleAnnotation::kNone) {
72 return info.GetMethodHotness(MethodReference(dex, method_idx), annotation);
73 }
74
75 // Creates the default inline caches used in tests.
GetTestInlineCaches()76 std::vector<ProfileInlineCache> GetTestInlineCaches() {
77 std::vector<ProfileInlineCache> inline_caches;
78 // Monomorphic
79 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
80 std::vector<TypeReference> types = {TypeReference(dex1, dex::TypeIndex(0))};
81 inline_caches.push_back(ProfileInlineCache(dex_pc, /*missing_types=*/ false, types));
82 }
83 // Polymorphic
84 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
85 std::vector<TypeReference> types = {
86 TypeReference(dex1, dex::TypeIndex(0)),
87 TypeReference(dex2, dex::TypeIndex(1)),
88 TypeReference(dex3, dex::TypeIndex(2))};
89 inline_caches.push_back(ProfileInlineCache(dex_pc, /*missing_types=*/ false, types));
90 }
91 // Megamorphic
92 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
93 // We need 5 types to make the cache megamorphic.
94 // The `is_megamorphic` flag shall be `false`; it is not used for testing.
95 std::vector<TypeReference> types = {
96 TypeReference(dex1, dex::TypeIndex(0)),
97 TypeReference(dex1, dex::TypeIndex(1)),
98 TypeReference(dex1, dex::TypeIndex(2)),
99 TypeReference(dex1, dex::TypeIndex(3)),
100 TypeReference(dex1, dex::TypeIndex(4))};
101 inline_caches.push_back(ProfileInlineCache(dex_pc, /*missing_types=*/ false, types));
102 }
103 // Missing types
104 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
105 std::vector<TypeReference> types;
106 inline_caches.push_back(ProfileInlineCache(dex_pc, /*missing_types=*/ true, types));
107 }
108
109 return inline_caches;
110 }
111
MakeMegamorphic(std::vector<ProfileInlineCache> * inline_caches)112 void MakeMegamorphic(/*out*/std::vector<ProfileInlineCache>* inline_caches) {
113 for (ProfileInlineCache& cache : *inline_caches) {
114 uint16_t k = 5;
115 while (cache.classes.size() < ProfileCompilationInfo::kIndividualInlineCacheSize) {
116 TypeReference type_ref(dex1, dex::TypeIndex(k++));
117 if (std::find(cache.classes.begin(), cache.classes.end(), type_ref) ==
118 cache.classes.end()) {
119 const_cast<std::vector<TypeReference>*>(&cache.classes)->push_back(type_ref);
120 }
121 }
122 }
123 }
124
SetIsMissingTypes(std::vector<ProfileInlineCache> * inline_caches)125 void SetIsMissingTypes(/*out*/std::vector<ProfileInlineCache>* inline_caches) {
126 for (ProfileInlineCache& cache : *inline_caches) {
127 *(const_cast<bool*>(&(cache.is_missing_types))) = true;
128 }
129 }
130
TestProfileLoadFromZip(const char * zip_entry,size_t zip_flags,bool should_succeed,bool should_succeed_with_empty_profile=false)131 void TestProfileLoadFromZip(const char* zip_entry,
132 size_t zip_flags,
133 bool should_succeed,
134 bool should_succeed_with_empty_profile = false) {
135 // Create a valid profile.
136 ScratchFile profile;
137 ProfileCompilationInfo saved_info;
138 for (uint16_t i = 0; i < 10; i++) {
139 ASSERT_TRUE(AddMethod(&saved_info, dex1, /*method_idx=*/ i));
140 ASSERT_TRUE(AddMethod(&saved_info, dex2, /*method_idx=*/ i));
141 }
142 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
143 ASSERT_EQ(0, profile.GetFile()->Flush());
144
145 // Prepare the profile content for zipping.
146 std::vector<uint8_t> data(profile.GetFile()->GetLength());
147 ASSERT_TRUE(profile.GetFile()->PreadFully(data.data(), data.size(), /*offset=*/ 0));
148
149 // Zip the profile content.
150 ScratchFile zip;
151 FILE* file = fopen(zip.GetFile()->GetPath().c_str(), "wbe");
152 ZipWriter writer(file);
153 writer.StartEntry(zip_entry, zip_flags);
154 writer.WriteBytes(data.data(), data.size());
155 writer.FinishEntry();
156 writer.Finish();
157 fflush(file);
158 fclose(file);
159
160 // Verify loading from the zip archive.
161 ProfileCompilationInfo loaded_info;
162 ASSERT_EQ(should_succeed, loaded_info.Load(zip.GetFile()->GetPath(), false));
163 if (should_succeed) {
164 if (should_succeed_with_empty_profile) {
165 ASSERT_TRUE(loaded_info.IsEmpty());
166 } else {
167 ASSERT_TRUE(loaded_info.Equals(saved_info));
168 }
169 }
170 }
171
IsEmpty(const ProfileCompilationInfo & info)172 bool IsEmpty(const ProfileCompilationInfo& info) {
173 return info.IsEmpty();
174 }
175
SizeStressTest(bool random)176 void SizeStressTest(bool random) {
177 ProfileCompilationInfo boot_profile(/*for_boot_image=*/ true);
178 ProfileCompilationInfo reg_profile(/*for_boot_image=*/ false);
179
180 static constexpr size_t kNumDexFiles = 5;
181
182 std::vector<const DexFile*> dex_files;
183 for (uint32_t i = 0; i < kNumDexFiles; i++) {
184 dex_files.push_back(BuildDex(std::to_string(i), i, "LC;", kMaxMethodIds));
185 }
186
187 std::srand(0);
188 // Set a few flags on a 2 different methods in each of the profile.
189 for (const DexFile* dex_file : dex_files) {
190 for (uint32_t method_idx = 0; method_idx < kMaxMethodIds; method_idx++) {
191 for (uint32_t flag_index = 0; flag_index <= kMaxHotnessFlagBootIndex; flag_index++) {
192 if (!random || rand() % 2 == 0) {
193 ASSERT_TRUE(AddMethod(
194 &boot_profile,
195 dex_file,
196 method_idx,
197 static_cast<Hotness::Flag>(1 << flag_index)));
198 }
199 }
200 for (uint32_t flag_index = 0; flag_index <= kMaxHotnessFlagRegularIndex; flag_index++) {
201 if (!random || rand() % 2 == 0) {
202 ASSERT_TRUE(AddMethod(
203 ®_profile,
204 dex_file,
205 method_idx,
206 static_cast<Hotness::Flag>(1 << flag_index)));
207 }
208 }
209 }
210 }
211
212 ScratchFile boot_file;
213 ScratchFile reg_file;
214
215 ASSERT_TRUE(boot_profile.Save(GetFd(boot_file)));
216 ASSERT_TRUE(reg_profile.Save(GetFd(reg_file)));
217
218 ProfileCompilationInfo loaded_boot(/*for_boot_image=*/ true);
219 ProfileCompilationInfo loaded_reg;
220 ASSERT_TRUE(loaded_boot.Load(GetFd(boot_file)));
221 ASSERT_TRUE(loaded_reg.Load(GetFd(reg_file)));
222 }
223
224 static constexpr size_t kMaxMethodIds = 65535;
225 static constexpr size_t kMaxClassIds = 65535;
226 static constexpr uint32_t kMaxHotnessFlagBootIndex =
227 WhichPowerOf2(static_cast<uint32_t>(Hotness::kFlagLastBoot));
228 static constexpr uint32_t kMaxHotnessFlagRegularIndex =
229 WhichPowerOf2(static_cast<uint32_t>(Hotness::kFlagLastRegular));
230
231 // Cannot sizeof the actual arrays so hard code the values here.
232 // They should not change anyway.
233 static constexpr int kProfileMagicSize = 4;
234 static constexpr int kProfileVersionSize = 4;
235
236 MallocArenaPool pool_;
237 std::unique_ptr<ArenaAllocator> allocator_;
238
239 const DexFile* dex1;
240 const DexFile* dex2;
241 const DexFile* dex3;
242 const DexFile* dex4;
243 const DexFile* dex1_checksum_missmatch;
244 const DexFile* dex1_renamed;
245 const DexFile* dex2_renamed;
246
247 // Cache of inline caches generated during tests.
248 // This makes it easier to pass data between different utilities and ensure that
249 // caches are destructed at the end of the test.
250 std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
251 };
252
TEST_F(ProfileCompilationInfoTest,AddClasses)253 TEST_F(ProfileCompilationInfoTest, AddClasses) {
254 ProfileCompilationInfo info;
255
256 // Add all classes with a `TypeId` in `dex1`.
257 uint32_t num_type_ids1 = dex1->NumTypeIds();
258 for (uint32_t type_index = 0; type_index != num_type_ids1; ++type_index) {
259 ASSERT_TRUE(info.AddClass(*dex1, dex::TypeIndex(type_index)));
260 }
261 // Add classes without `TypeId` in `dex1`.
262 for (uint32_t type_index = num_type_ids1; type_index != DexFile::kDexNoIndex16; ++type_index) {
263 std::string descriptor = "LX" + std::to_string(type_index) + ";";
264 ASSERT_TRUE(info.AddClass(*dex1, descriptor));
265 }
266 // Fail to add another class without `TypeId` in `dex1` as we have
267 // run out of available artificial type indexes.
268 ASSERT_FALSE(info.AddClass(*dex1, "LCannotAddThis;"));
269
270 // Add all classes with a `TypeId` in `dex2`.
271 uint32_t num_type_ids2 = dex2->NumTypeIds();
272 for (uint32_t type_index = 0; type_index != num_type_ids2; ++type_index) {
273 ASSERT_TRUE(info.AddClass(*dex2, dex::TypeIndex(type_index)));
274 }
275 // Fail to add another class without `TypeId` in `dex2` as we have
276 // run out of available artificial type indexes when adding types for `dex1`.
277 ASSERT_FALSE(info.AddClass(*dex2, "LCannotAddThis;"));
278 // Add classes without `TypeId` in `dex2` for which we already have articial indexes.
279 ASSERT_EQ(num_type_ids1, num_type_ids2);
280 for (uint32_t type_index = num_type_ids2; type_index != DexFile::kDexNoIndex16; ++type_index) {
281 std::string descriptor = "LX" + std::to_string(type_index) + ";";
282 ASSERT_TRUE(info.AddClass(*dex2, descriptor));
283 }
284 }
285
TEST_F(ProfileCompilationInfoTest,SaveFd)286 TEST_F(ProfileCompilationInfoTest, SaveFd) {
287 ScratchFile profile;
288
289 ProfileCompilationInfo saved_info;
290 // Save a few methods.
291 for (uint16_t i = 0; i < 10; i++) {
292 ASSERT_TRUE(AddMethod(&saved_info, dex1, /*method_idx=*/ i));
293 ASSERT_TRUE(AddMethod(&saved_info, dex2, /*method_idx=*/ i));
294 }
295 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
296 ASSERT_EQ(0, profile.GetFile()->Flush());
297
298 // Check that we get back what we saved.
299 ProfileCompilationInfo loaded_info;
300 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
301 ASSERT_TRUE(loaded_info.Equals(saved_info));
302
303 // Save more methods.
304 for (uint16_t i = 0; i < 100; i++) {
305 ASSERT_TRUE(AddMethod(&saved_info, dex1, /*method_idx=*/ i));
306 ASSERT_TRUE(AddMethod(&saved_info, dex2, /*method_idx=*/ i));
307 ASSERT_TRUE(AddMethod(&saved_info, dex3, /*method_idx=*/ i));
308 }
309 ASSERT_TRUE(profile.GetFile()->ResetOffset());
310 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
311 ASSERT_EQ(0, profile.GetFile()->Flush());
312
313 // Check that we get back everything we saved.
314 ProfileCompilationInfo loaded_info2;
315 ASSERT_TRUE(loaded_info2.Load(GetFd(profile)));
316 ASSERT_TRUE(loaded_info2.Equals(saved_info));
317 }
318
TEST_F(ProfileCompilationInfoTest,AddMethodsAndClassesFail)319 TEST_F(ProfileCompilationInfoTest, AddMethodsAndClassesFail) {
320 ScratchFile profile;
321
322 ProfileCompilationInfo info;
323 ASSERT_TRUE(AddMethod(&info, dex1, /*method_idx=*/ 1));
324 // Trying to add info for an existing file but with a different checksum.
325 ASSERT_FALSE(AddMethod(&info, dex1_checksum_missmatch, /*method_idx=*/ 2));
326 }
327
TEST_F(ProfileCompilationInfoTest,MergeFail)328 TEST_F(ProfileCompilationInfoTest, MergeFail) {
329 ScratchFile profile;
330
331 ProfileCompilationInfo info1;
332 ASSERT_TRUE(AddMethod(&info1, dex1, /*method_idx=*/ 1));
333 // Use the same file, change the checksum.
334 ProfileCompilationInfo info2;
335 ASSERT_TRUE(AddMethod(&info2, dex1_checksum_missmatch, /*method_idx=*/ 2));
336
337 ASSERT_FALSE(info1.MergeWith(info2));
338 }
339
340
TEST_F(ProfileCompilationInfoTest,MergeFdFail)341 TEST_F(ProfileCompilationInfoTest, MergeFdFail) {
342 ScratchFile profile;
343
344 ProfileCompilationInfo info1;
345 ASSERT_TRUE(AddMethod(&info1, dex1, /*method_idx=*/ 1));
346 // Use the same file, change the checksum.
347 ProfileCompilationInfo info2;
348 ASSERT_TRUE(AddMethod(&info2, dex1_checksum_missmatch, /*method_idx=*/ 2));
349
350 ASSERT_TRUE(info1.Save(profile.GetFd()));
351 ASSERT_EQ(0, profile.GetFile()->Flush());
352
353 ASSERT_FALSE(info2.Load(profile.GetFd()));
354 }
355
TEST_F(ProfileCompilationInfoTest,SaveMaxMethods)356 TEST_F(ProfileCompilationInfoTest, SaveMaxMethods) {
357 ScratchFile profile;
358
359 const DexFile* dex_max1 = BuildDex("location-max1",
360 /*location_checksum=*/ 5,
361 "LUniqueMax1;",
362 kMaxMethodIds,
363 kMaxClassIds);
364 const DexFile* dex_max2 = BuildDex("location-max2",
365 /*location_checksum=*/ 6,
366 "LUniqueMax2;",
367 kMaxMethodIds,
368 kMaxClassIds);
369
370
371 ProfileCompilationInfo saved_info;
372 // Save the maximum number of methods
373 for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
374 ASSERT_TRUE(AddMethod(&saved_info, dex_max1, /*method_idx=*/ i));
375 ASSERT_TRUE(AddMethod(&saved_info, dex_max2, /*method_idx=*/ i));
376 }
377 // Save the maximum number of classes
378 for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
379 ASSERT_TRUE(AddClass(&saved_info, dex_max1, dex::TypeIndex(i)));
380 ASSERT_TRUE(AddClass(&saved_info, dex_max2, dex::TypeIndex(i)));
381 }
382
383 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
384 ASSERT_EQ(0, profile.GetFile()->Flush());
385
386 // Check that we get back what we saved.
387 ProfileCompilationInfo loaded_info;
388 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
389 ASSERT_TRUE(loaded_info.Equals(saved_info));
390 }
391
TEST_F(ProfileCompilationInfoTest,SaveEmpty)392 TEST_F(ProfileCompilationInfoTest, SaveEmpty) {
393 ScratchFile profile;
394
395 ProfileCompilationInfo saved_info;
396 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
397 ASSERT_EQ(0, profile.GetFile()->Flush());
398
399 // Check that we get back what we saved.
400 ProfileCompilationInfo loaded_info;
401 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
402 ASSERT_TRUE(loaded_info.Equals(saved_info));
403 }
404
TEST_F(ProfileCompilationInfoTest,LoadEmpty)405 TEST_F(ProfileCompilationInfoTest, LoadEmpty) {
406 ScratchFile profile;
407
408 ProfileCompilationInfo empty_info;
409
410 ProfileCompilationInfo loaded_info;
411 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
412 ASSERT_TRUE(loaded_info.Equals(empty_info));
413 }
414
TEST_F(ProfileCompilationInfoTest,BadMagic)415 TEST_F(ProfileCompilationInfoTest, BadMagic) {
416 ScratchFile profile;
417 uint8_t buffer[] = { 1, 2, 3, 4 };
418 ASSERT_TRUE(profile.GetFile()->WriteFully(buffer, sizeof(buffer)));
419 ProfileCompilationInfo loaded_info;
420 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
421 }
422
TEST_F(ProfileCompilationInfoTest,BadVersion)423 TEST_F(ProfileCompilationInfoTest, BadVersion) {
424 ScratchFile profile;
425
426 ASSERT_TRUE(profile.GetFile()->WriteFully(
427 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
428 uint8_t version[] = { 'v', 'e', 'r', 's', 'i', 'o', 'n' };
429 ASSERT_TRUE(profile.GetFile()->WriteFully(version, sizeof(version)));
430 ASSERT_EQ(0, profile.GetFile()->Flush());
431
432 ProfileCompilationInfo loaded_info;
433 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
434 }
435
TEST_F(ProfileCompilationInfoTest,Incomplete)436 TEST_F(ProfileCompilationInfoTest, Incomplete) {
437 ScratchFile profile;
438 ASSERT_TRUE(profile.GetFile()->WriteFully(
439 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
440 ASSERT_TRUE(profile.GetFile()->WriteFully(
441 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
442 // Write that we have one section info.
443 const uint32_t file_section_count = 1u;
444 ASSERT_TRUE(profile.GetFile()->WriteFully(&file_section_count, sizeof(file_section_count)));
445 ASSERT_EQ(0, profile.GetFile()->Flush());
446
447 ProfileCompilationInfo loaded_info;
448 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
449 }
450
TEST_F(ProfileCompilationInfoTest,TooLongDexLocation)451 TEST_F(ProfileCompilationInfoTest, TooLongDexLocation) {
452 ScratchFile profile;
453 ASSERT_TRUE(profile.GetFile()->WriteFully(
454 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
455 ASSERT_TRUE(profile.GetFile()->WriteFully(
456 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
457 // Write that we have one section info.
458 const uint32_t file_section_count = 1u;
459 ASSERT_TRUE(profile.GetFile()->WriteFully(&file_section_count, sizeof(file_section_count)));
460
461 constexpr size_t kInvalidDexFileLocationLength = 1025u;
462 constexpr uint32_t kDexFilesOffset =
463 kProfileMagicSize + kProfileVersionSize + sizeof(file_section_count) + 4u * sizeof(uint32_t);
464 constexpr uint32_t kDexFilesSize =
465 sizeof(ProfileIndexType) + // number of dex files
466 3u * sizeof(uint32_t) + // numeric data
467 kInvalidDexFileLocationLength + 1u; // null-terminated string
468 const uint32_t section_info[] = {
469 0u, // type = kDexFiles
470 kDexFilesOffset,
471 kDexFilesSize,
472 0u, // inflated size = 0
473 };
474 ASSERT_TRUE(profile.GetFile()->WriteFully(section_info, sizeof(section_info)));
475
476 ProfileIndexType num_dex_files = 1u;
477 ASSERT_TRUE(profile.GetFile()->WriteFully(&num_dex_files, sizeof(num_dex_files)));
478
479 uint32_t numeric_data[3] = {
480 1234u, // checksum
481 1u, // num_type_ids
482 2u, // num_method_ids
483 };
484 ASSERT_TRUE(profile.GetFile()->WriteFully(numeric_data, sizeof(numeric_data)));
485
486 std::string dex_location(kInvalidDexFileLocationLength, 'a');
487 ASSERT_TRUE(profile.GetFile()->WriteFully(dex_location.c_str(), dex_location.size() + 1u));
488
489 ASSERT_EQ(0, profile.GetFile()->Flush());
490
491 ProfileCompilationInfo loaded_info;
492 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
493 }
494
TEST_F(ProfileCompilationInfoTest,UnexpectedContent)495 TEST_F(ProfileCompilationInfoTest, UnexpectedContent) {
496 ScratchFile profile;
497
498 ProfileCompilationInfo saved_info;
499 for (uint16_t i = 0; i < 10; i++) {
500 ASSERT_TRUE(AddMethod(&saved_info, dex1, /*method_idx=*/ i));
501 }
502 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
503
504 uint8_t random_data[] = { 1, 2, 3};
505 int64_t file_length = profile.GetFile()->GetLength();
506 ASSERT_GT(file_length, 0);
507 ASSERT_TRUE(profile.GetFile()->PwriteFully(random_data, sizeof(random_data), file_length));
508
509 ASSERT_EQ(0, profile.GetFile()->Flush());
510 ASSERT_EQ(profile.GetFile()->GetLength(),
511 file_length + static_cast<int64_t>(sizeof(random_data)));
512
513 // Extra data at the end of the file is OK, loading the profile should succeed.
514 ProfileCompilationInfo loaded_info;
515 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
516 }
517
TEST_F(ProfileCompilationInfoTest,SaveInlineCaches)518 TEST_F(ProfileCompilationInfoTest, SaveInlineCaches) {
519 ScratchFile profile;
520
521 ProfileCompilationInfo saved_info;
522 std::vector<ProfileInlineCache> inline_caches = GetTestInlineCaches();
523
524 // Add methods with inline caches.
525 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
526 // Add a method which is part of the same dex file as one of the
527 // class from the inline caches.
528 ASSERT_TRUE(AddMethod(&saved_info, dex1, method_idx, inline_caches));
529 // Add a method which is outside the set of dex files.
530 ASSERT_TRUE(AddMethod(&saved_info, dex4, method_idx, inline_caches));
531 }
532
533 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
534 ASSERT_EQ(0, profile.GetFile()->Flush());
535
536 // Check that we get back what we saved.
537 ProfileCompilationInfo loaded_info;
538 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
539
540 ASSERT_TRUE(loaded_info.Equals(saved_info));
541
542 ProfileCompilationInfo::MethodHotness loaded_hotness1 =
543 GetMethod(loaded_info, dex1, /*method_idx=*/ 3);
544 ASSERT_TRUE(loaded_hotness1.IsHot());
545 ASSERT_TRUE(EqualInlineCaches(inline_caches, dex1, loaded_hotness1, loaded_info));
546 ProfileCompilationInfo::MethodHotness loaded_hotness2 =
547 GetMethod(loaded_info, dex4, /*method_idx=*/ 3);
548 ASSERT_TRUE(loaded_hotness2.IsHot());
549 ASSERT_TRUE(EqualInlineCaches(inline_caches, dex4, loaded_hotness2, loaded_info));
550 }
551
TEST_F(ProfileCompilationInfoTest,MegamorphicInlineCaches)552 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) {
553 ProfileCompilationInfo saved_info;
554 std::vector<ProfileInlineCache> inline_caches = GetTestInlineCaches();
555
556 // Add methods with inline caches.
557 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
558 ASSERT_TRUE(AddMethod(&saved_info, dex1, method_idx, inline_caches));
559 }
560
561 ScratchFile profile;
562 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
563 ASSERT_EQ(0, profile.GetFile()->Flush());
564
565 // Make the inline caches megamorphic and add them to the profile again.
566 ProfileCompilationInfo saved_info_extra;
567 std::vector<ProfileInlineCache> inline_caches_extra = GetTestInlineCaches();
568 MakeMegamorphic(&inline_caches_extra);
569 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
570 ASSERT_TRUE(AddMethod(&saved_info_extra, dex1, method_idx, inline_caches_extra));
571 }
572
573 ScratchFile extra_profile;
574 ASSERT_TRUE(saved_info_extra.Save(GetFd(extra_profile)));
575 ASSERT_EQ(0, extra_profile.GetFile()->Flush());
576
577 // Merge the profiles so that we have the same view as the file.
578 ASSERT_TRUE(saved_info.MergeWith(saved_info_extra));
579
580 // Check that we get back what we saved.
581 ProfileCompilationInfo loaded_info;
582 ASSERT_TRUE(loaded_info.Load(GetFd(extra_profile)));
583
584 ASSERT_TRUE(loaded_info.Equals(saved_info));
585
586 ProfileCompilationInfo::MethodHotness loaded_hotness1 =
587 GetMethod(loaded_info, dex1, /*method_idx=*/ 3);
588
589 ASSERT_TRUE(loaded_hotness1.IsHot());
590 ASSERT_TRUE(EqualInlineCaches(inline_caches_extra, dex1, loaded_hotness1, loaded_info));
591 }
592
TEST_F(ProfileCompilationInfoTest,MissingTypesInlineCaches)593 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCaches) {
594 ProfileCompilationInfo saved_info;
595 std::vector<ProfileInlineCache> inline_caches = GetTestInlineCaches();
596
597 // Add methods with inline caches.
598 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
599 ASSERT_TRUE(AddMethod(&saved_info, dex1, method_idx, inline_caches));
600 }
601
602 ScratchFile profile;
603 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
604 ASSERT_EQ(0, profile.GetFile()->Flush());
605
606 // Make some inline caches megamorphic and add them to the profile again.
607 ProfileCompilationInfo saved_info_extra;
608 std::vector<ProfileInlineCache> inline_caches_extra = GetTestInlineCaches();
609 MakeMegamorphic(&inline_caches_extra);
610 for (uint16_t method_idx = 5; method_idx < 10; method_idx++) {
611 ASSERT_TRUE(AddMethod(&saved_info_extra, dex1, method_idx, inline_caches));
612 }
613
614 // Mark all inline caches with missing types and add them to the profile again.
615 // This will verify that all inline caches (megamorphic or not) should be marked as missing types.
616 std::vector<ProfileInlineCache> missing_types = GetTestInlineCaches();
617 SetIsMissingTypes(&missing_types);
618 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
619 ASSERT_TRUE(AddMethod(&saved_info_extra, dex1, method_idx, missing_types));
620 }
621
622 ScratchFile extra_profile;
623 ASSERT_TRUE(saved_info_extra.Save(GetFd(extra_profile)));
624 ASSERT_EQ(0, extra_profile.GetFile()->Flush());
625
626 // Merge the profiles so that we have the same view as the file.
627 ASSERT_TRUE(saved_info.MergeWith(saved_info_extra));
628
629 // Check that we get back what we saved.
630 ProfileCompilationInfo loaded_info;
631 ASSERT_TRUE(loaded_info.Load(GetFd(extra_profile)));
632
633 ASSERT_TRUE(loaded_info.Equals(saved_info));
634
635 ProfileCompilationInfo::MethodHotness loaded_hotness1 =
636 GetMethod(loaded_info, dex1, /*method_idx=*/ 3);
637 ASSERT_TRUE(loaded_hotness1.IsHot());
638 ASSERT_TRUE(EqualInlineCaches(missing_types, dex1, loaded_hotness1, loaded_info));
639 }
640
TEST_F(ProfileCompilationInfoTest,InvalidChecksumInInlineCache)641 TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCache) {
642 ScratchFile profile;
643
644 ProfileCompilationInfo info;
645 std::vector<ProfileInlineCache> inline_caches1 = GetTestInlineCaches();
646 std::vector<ProfileInlineCache> inline_caches2 = GetTestInlineCaches();
647 // Modify the checksum to trigger a mismatch.
648 std::vector<TypeReference>* types = const_cast<std::vector<TypeReference>*>(
649 &inline_caches2[0].classes);
650 types->front().dex_file = dex1_checksum_missmatch;
651
652 ASSERT_TRUE(AddMethod(&info, dex1, /*method_idx=*/ 0, inline_caches1));
653
654 // The dex files referenced in inline infos do not matter. We are recoding class
655 // references across dex files by looking up the descriptor in the referencing
656 // method's dex file. If not found, we create an artificial type index.
657 ASSERT_TRUE(AddMethod(&info, dex2, /*method_idx=*/ 0, inline_caches2));
658 }
659
TEST_F(ProfileCompilationInfoTest,InlineCacheAcrossDexFiles)660 TEST_F(ProfileCompilationInfoTest, InlineCacheAcrossDexFiles) {
661 ScratchFile profile;
662
663 const char kDex1Class[] = "LUnique1;";
664 const dex::TypeId* dex1_tid = dex1->FindTypeId(kDex1Class);
665 ASSERT_TRUE(dex1_tid != nullptr);
666 dex::TypeIndex dex1_tidx = dex1->GetIndexForTypeId(*dex1_tid);
667 ASSERT_FALSE(dex2->FindTypeId(kDex1Class) != nullptr);
668
669 const uint16_t dex_pc = 33u;
670 std::vector<TypeReference> types = {TypeReference(dex1, dex1_tidx)};
671 std::vector<ProfileInlineCache> inline_caches {
672 ProfileInlineCache(dex_pc, /*missing_types=*/ false, types)
673 };
674
675 ProfileCompilationInfo info;
676 ASSERT_TRUE(AddMethod(&info, dex2, /*method_idx=*/ 0, inline_caches));
677 Hotness hotness = GetMethod(info, dex2, /*method_idx=*/ 0);
678 ASSERT_TRUE(hotness.IsHot());
679 ASSERT_TRUE(EqualInlineCaches(inline_caches, dex2, hotness, info));
680 const ProfileCompilationInfo::InlineCacheMap* inline_cache_map = hotness.GetInlineCacheMap();
681 ASSERT_TRUE(inline_cache_map != nullptr);
682 ASSERT_EQ(1u, inline_cache_map->size());
683 ASSERT_EQ(dex_pc, inline_cache_map->begin()->first);
684 const ProfileCompilationInfo::DexPcData& dex_pc_data = inline_cache_map->begin()->second;
685 ASSERT_FALSE(dex_pc_data.is_missing_types);
686 ASSERT_FALSE(dex_pc_data.is_megamorphic);
687 ASSERT_EQ(1u, dex_pc_data.classes.size());
688 dex::TypeIndex type_index = *dex_pc_data.classes.begin();
689 ASSERT_FALSE(dex2->IsTypeIndexValid(type_index));
690 ASSERT_STREQ(kDex1Class, info.GetTypeDescriptor(dex2, type_index));
691 }
692
693 // Verify that profiles behave correctly even if the methods are added in a different
694 // order and with a different dex profile indices for the dex files.
TEST_F(ProfileCompilationInfoTest,MergeInlineCacheTriggerReindex)695 TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) {
696 ScratchFile profile;
697
698 ProfileCompilationInfo info;
699 ProfileCompilationInfo info_reindexed;
700
701 std::vector<ProfileInlineCache> inline_caches;
702 for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
703 std::vector<TypeReference> types = {
704 TypeReference(dex1, dex::TypeIndex(0)),
705 TypeReference(dex2, dex::TypeIndex(1))};
706 inline_caches.push_back(ProfileInlineCache(dex_pc, /*missing_types=*/ false, types));
707 }
708
709 std::vector<ProfileInlineCache> inline_caches_reindexed;
710 for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) {
711 std::vector<TypeReference> types = {
712 TypeReference(dex2, dex::TypeIndex(1)),
713 TypeReference(dex1, dex::TypeIndex(0))};
714 inline_caches_reindexed.push_back(ProfileInlineCache(dex_pc, /*missing_types=*/ false, types));
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(&info, dex1, method_idx, inline_caches));
720 ASSERT_TRUE(AddMethod(&info, dex2, method_idx, inline_caches));
721 }
722
723 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
724 ASSERT_TRUE(AddMethod(&info_reindexed, dex2, method_idx, inline_caches_reindexed));
725 ASSERT_TRUE(AddMethod(&info_reindexed, dex1, method_idx, inline_caches_reindexed));
726 }
727
728 ProfileCompilationInfo info_backup;
729 info_backup.MergeWith(info);
730 ASSERT_TRUE(info.MergeWith(info_reindexed));
731 // Merging should have no effect as we're adding the exact same stuff.
732 ASSERT_TRUE(info.Equals(info_backup));
733 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
734 ProfileCompilationInfo::MethodHotness loaded_hotness1 = GetMethod(info, dex1, method_idx);
735 ASSERT_TRUE(loaded_hotness1.IsHot());
736 ASSERT_TRUE(EqualInlineCaches(inline_caches, dex1, loaded_hotness1, info));
737 ProfileCompilationInfo::MethodHotness loaded_hotness2 = GetMethod(info, dex2, method_idx);
738 ASSERT_TRUE(loaded_hotness2.IsHot());
739 ASSERT_TRUE(EqualInlineCaches(inline_caches, dex2, loaded_hotness2, info));
740 }
741 }
742
TEST_F(ProfileCompilationInfoTest,AddMoreDexFileThanLimitRegular)743 TEST_F(ProfileCompilationInfoTest, AddMoreDexFileThanLimitRegular) {
744 ProfileCompilationInfo info;
745 // Save a few methods.
746 for (uint16_t i = 0; i < std::numeric_limits<ProfileIndexType>::max(); i++) {
747 std::string location = std::to_string(i);
748 const DexFile* dex = BuildDex(location, /*location_checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
749 ASSERT_TRUE(AddMethod(&info, dex, /*method_idx=*/ 0));
750 }
751 // Add an extra dex file.
752 const DexFile* dex = BuildDex("-1", /*location_checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
753 ASSERT_FALSE(AddMethod(&info, dex, /*method_idx=*/ 0));
754 }
755
TEST_F(ProfileCompilationInfoTest,AddMoreDexFileThanLimitBoot)756 TEST_F(ProfileCompilationInfoTest, AddMoreDexFileThanLimitBoot) {
757 ProfileCompilationInfo info(/*for_boot_image=*/true);
758 // Save a few methods.
759 for (uint16_t i = 0; i < std::numeric_limits<ProfileIndexType>::max(); i++) {
760 std::string location = std::to_string(i);
761 const DexFile* dex = BuildDex(location, /*location_checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
762 ASSERT_TRUE(AddMethod(&info, dex, /*method_idx=*/ 0));
763 }
764 // Add an extra dex file.
765 const DexFile* dex = BuildDex("-1", /*location_checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
766 ASSERT_FALSE(AddMethod(&info, dex, /*method_idx=*/ 0));
767 }
768
TEST_F(ProfileCompilationInfoTest,MegamorphicInlineCachesMerge)769 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) {
770 // Create a megamorphic inline cache.
771 std::vector<ProfileInlineCache> inline_caches;
772 std::vector<TypeReference> types = {
773 TypeReference(dex1, dex::TypeIndex(0)),
774 TypeReference(dex1, dex::TypeIndex(1)),
775 TypeReference(dex1, dex::TypeIndex(2)),
776 TypeReference(dex1, dex::TypeIndex(3)),
777 TypeReference(dex1, dex::TypeIndex(4))};
778 inline_caches.push_back(ProfileInlineCache(0, /*missing_types=*/ false, types));
779
780 ProfileCompilationInfo info_megamorphic;
781 ASSERT_TRUE(AddMethod(&info_megamorphic, dex1, 0, inline_caches));
782
783 // Create a profile with no inline caches (for the same method).
784 ProfileCompilationInfo info_no_inline_cache;
785 ASSERT_TRUE(AddMethod(&info_no_inline_cache, dex1, 0));
786
787 // Merge the megamorphic cache into the empty one.
788 ASSERT_TRUE(info_no_inline_cache.MergeWith(info_megamorphic));
789 ScratchFile profile;
790 // Saving profile should work without crashing (b/35644850).
791 ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile)));
792 }
793
TEST_F(ProfileCompilationInfoTest,MissingTypesInlineCachesMerge)794 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCachesMerge) {
795 // Create an inline cache with missing types
796 std::vector<ProfileInlineCache> inline_caches;
797 std::vector<TypeReference> types = {};
798 inline_caches.push_back(ProfileInlineCache(0, /*missing_types=*/ true, types));
799
800 ProfileCompilationInfo info_missing_types;
801 ASSERT_TRUE(AddMethod(&info_missing_types, dex1, /*method_idx=*/ 0, inline_caches));
802
803 // Create a profile with no inline caches (for the same method).
804 ProfileCompilationInfo info_no_inline_cache;
805 ASSERT_TRUE(AddMethod(&info_no_inline_cache, dex1, /*method_idx=*/ 0));
806
807 // Merge the missing type cache into the empty one.
808 // Everything should be saved without errors.
809 ASSERT_TRUE(info_no_inline_cache.MergeWith(info_missing_types));
810 ScratchFile profile;
811 ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile)));
812 }
813
TEST_F(ProfileCompilationInfoTest,SampledMethodsTest)814 TEST_F(ProfileCompilationInfoTest, SampledMethodsTest) {
815 ProfileCompilationInfo test_info;
816 AddMethod(&test_info, dex1, 1, Hotness::kFlagStartup);
817 AddMethod(&test_info, dex1, 5, Hotness::kFlagPostStartup);
818 AddMethod(&test_info, dex2, 2, Hotness::kFlagStartup);
819 AddMethod(&test_info, dex2, 4, Hotness::kFlagPostStartup);
820 auto run_test = [&dex1 = dex1, &dex2 = dex2](const ProfileCompilationInfo& info) {
821 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, 2)).IsInProfile());
822 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, 4)).IsInProfile());
823 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, 1)).IsStartup());
824 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, 3)).IsStartup());
825 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, 5)).IsPostStartup());
826 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, 6)).IsStartup());
827 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex2, 2)).IsStartup());
828 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex2, 4)).IsPostStartup());
829 };
830 run_test(test_info);
831
832 // Save the profile.
833 ScratchFile profile;
834 ASSERT_TRUE(test_info.Save(GetFd(profile)));
835 ASSERT_EQ(0, profile.GetFile()->Flush());
836
837 // Load the profile and make sure we can read the data and it matches what we expect.
838 ProfileCompilationInfo loaded_info;
839 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
840 run_test(loaded_info);
841
842 // Test that the bitmap gets merged properly.
843 EXPECT_FALSE(test_info.GetMethodHotness(MethodReference(dex1, 11)).IsStartup());
844 {
845 ProfileCompilationInfo merge_info;
846 AddMethod(&merge_info, dex1, 11, Hotness::kFlagStartup);
847 test_info.MergeWith(merge_info);
848 }
849 EXPECT_TRUE(test_info.GetMethodHotness(MethodReference(dex1, 11)).IsStartup());
850
851 // Test bulk adding.
852 {
853 std::unique_ptr<const DexFile> dex(OpenTestDexFile("ManyMethods"));
854 ProfileCompilationInfo info;
855 std::vector<uint16_t> hot_methods = {1, 3, 5};
856 std::vector<uint16_t> startup_methods = {1, 2};
857 std::vector<uint16_t> post_methods = {0, 2, 6};
858 ASSERT_GE(dex->NumMethodIds(), 7u);
859 info.AddMethodsForDex(static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup),
860 dex.get(),
861 hot_methods.begin(),
862 hot_methods.end());
863 info.AddMethodsForDex(Hotness::kFlagStartup,
864 dex.get(),
865 startup_methods.begin(),
866 startup_methods.end());
867 info.AddMethodsForDex(Hotness::kFlagPostStartup,
868 dex.get(),
869 post_methods.begin(),
870 post_methods.end());
871 for (uint16_t id : hot_methods) {
872 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsHot());
873 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsStartup());
874 }
875 for (uint16_t id : startup_methods) {
876 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsStartup());
877 }
878 for (uint16_t id : post_methods) {
879 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsPostStartup());
880 }
881 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), 6)).IsPostStartup());
882 // Check that methods that shouldn't have been touched are OK.
883 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), 0)).IsInProfile());
884 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 4)).IsInProfile());
885 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 7)).IsInProfile());
886 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 1)).IsPostStartup());
887 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 4)).IsStartup());
888 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex.get(), 6)).IsStartup());
889 }
890 }
891
TEST_F(ProfileCompilationInfoTest,LoadFromZipCompress)892 TEST_F(ProfileCompilationInfoTest, LoadFromZipCompress) {
893 TestProfileLoadFromZip("primary.prof",
894 ZipWriter::kCompress | ZipWriter::kAlign32,
895 /*should_succeed=*/true);
896 }
897
TEST_F(ProfileCompilationInfoTest,LoadFromZipUnCompress)898 TEST_F(ProfileCompilationInfoTest, LoadFromZipUnCompress) {
899 TestProfileLoadFromZip("primary.prof",
900 ZipWriter::kAlign32,
901 /*should_succeed=*/true);
902 }
903
TEST_F(ProfileCompilationInfoTest,LoadFromZipUnAligned)904 TEST_F(ProfileCompilationInfoTest, LoadFromZipUnAligned) {
905 TestProfileLoadFromZip("primary.prof",
906 0,
907 /*should_succeed=*/true);
908 }
909
TEST_F(ProfileCompilationInfoTest,LoadFromZipFailBadZipEntry)910 TEST_F(ProfileCompilationInfoTest, LoadFromZipFailBadZipEntry) {
911 TestProfileLoadFromZip("invalid.profile.entry",
912 0,
913 /*should_succeed=*/true,
914 /*should_succeed_with_empty_profile=*/true);
915 }
916
TEST_F(ProfileCompilationInfoTest,LoadFromZipFailBadProfile)917 TEST_F(ProfileCompilationInfoTest, LoadFromZipFailBadProfile) {
918 // Create a bad profile.
919 ScratchFile profile;
920 ASSERT_TRUE(profile.GetFile()->WriteFully(
921 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
922 ASSERT_TRUE(profile.GetFile()->WriteFully(
923 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
924 // Write that we have one section info.
925 const uint32_t file_section_count = 1u;
926 ASSERT_TRUE(profile.GetFile()->WriteFully(&file_section_count, sizeof(file_section_count)));
927 ASSERT_EQ(0, profile.GetFile()->Flush());
928
929 // Prepare the profile content for zipping.
930 std::vector<uint8_t> data(profile.GetFile()->GetLength());
931 ASSERT_TRUE(profile.GetFile()->PreadFully(data.data(), data.size(), /*offset=*/ 0));
932
933 // Zip the profile content.
934 ScratchFile zip;
935 FILE* file = fopen(zip.GetFile()->GetPath().c_str(), "wbe");
936 ZipWriter writer(file);
937 writer.StartEntry("primary.prof", ZipWriter::kAlign32);
938 writer.WriteBytes(data.data(), data.size());
939 writer.FinishEntry();
940 writer.Finish();
941 fflush(file);
942 fclose(file);
943
944 // Check that we failed to load.
945 ProfileCompilationInfo loaded_info;
946 ASSERT_FALSE(loaded_info.Load(GetFd(zip)));
947 }
948
TEST_F(ProfileCompilationInfoTest,UpdateProfileKeyOk)949 TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOk) {
950 std::vector<std::unique_ptr<const DexFile>> dex_files;
951 dex_files.push_back(std::unique_ptr<const DexFile>(dex1_renamed));
952 dex_files.push_back(std::unique_ptr<const DexFile>(dex2_renamed));
953
954 ProfileCompilationInfo info;
955 AddMethod(&info, dex1, /*method_idx=*/ 0);
956 AddMethod(&info, dex2, /*method_idx=*/ 0);
957
958 // Update the profile keys based on the original dex files
959 bool matched = false;
960 ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &matched));
961 ASSERT_TRUE(matched);
962
963 // Verify that we find the methods when searched with the original dex files.
964 for (const std::unique_ptr<const DexFile>& dex : dex_files) {
965 ProfileCompilationInfo::MethodHotness loaded_hotness =
966 GetMethod(info, dex.get(), /*method_idx=*/ 0);
967 ASSERT_TRUE(loaded_hotness.IsHot());
968 }
969
970 // Release the ownership as this is held by the test class;
971 for (std::unique_ptr<const DexFile>& dex : dex_files) {
972 UNUSED(dex.release());
973 }
974 }
975
TEST_F(ProfileCompilationInfoTest,UpdateProfileKeyOkWithAnnotation)976 TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkWithAnnotation) {
977 std::vector<std::unique_ptr<const DexFile>> dex_files;
978 dex_files.push_back(std::unique_ptr<const DexFile>(dex1_renamed));
979 dex_files.push_back(std::unique_ptr<const DexFile>(dex2_renamed));
980
981 ProfileCompilationInfo info;
982 ProfileCompilationInfo::ProfileSampleAnnotation annotation("test.package");
983 AddMethod(&info, dex1, /*method_idx=*/ 0, Hotness::kFlagHot, annotation);
984 AddMethod(&info, dex2, /*method_idx=*/ 0, Hotness::kFlagHot, annotation);
985
986 // Update the profile keys based on the original dex files
987 bool matched = false;
988 ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &matched));
989 ASSERT_TRUE(matched);
990
991 // Verify that we find the methods when searched with the original dex files.
992 for (const std::unique_ptr<const DexFile>& dex : dex_files) {
993 ProfileCompilationInfo::MethodHotness loaded_hotness =
994 GetMethod(info, dex.get(), /*method_idx=*/ 0, annotation);
995 ASSERT_TRUE(loaded_hotness.IsHot());
996 }
997
998 // Release the ownership as this is held by the test class;
999 for (std::unique_ptr<const DexFile>& dex : dex_files) {
1000 UNUSED(dex.release());
1001 }
1002 }
1003
TEST_F(ProfileCompilationInfoTest,UpdateProfileKeyOkMatchedButNoUpdate)1004 TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkMatchedButNoUpdate) {
1005 std::vector<std::unique_ptr<const DexFile>> dex_files;
1006 dex_files.push_back(std::unique_ptr<const DexFile>(dex1));
1007
1008 // Both the checksum and the location match the original dex file.
1009 ProfileCompilationInfo info;
1010 AddMethod(&info, dex1, /*method_idx=*/0);
1011
1012 // No update should happen, but this should be considered as a happy case.
1013 bool matched = false;
1014 ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &matched));
1015 ASSERT_TRUE(matched);
1016
1017 // Verify that we find the methods when searched with the original dex files.
1018 for (const std::unique_ptr<const DexFile>& dex : dex_files) {
1019 ProfileCompilationInfo::MethodHotness loaded_hotness =
1020 GetMethod(info, dex.get(), /*method_idx=*/ 0);
1021 ASSERT_TRUE(loaded_hotness.IsHot());
1022 }
1023
1024 // Release the ownership as this is held by the test class;
1025 for (std::unique_ptr<const DexFile>& dex : dex_files) {
1026 UNUSED(dex.release());
1027 }
1028 }
1029
TEST_F(ProfileCompilationInfoTest,UpdateProfileKeyOkButNoMatch)1030 TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkButNoMatch) {
1031 std::vector<std::unique_ptr<const DexFile>> dex_files;
1032 dex_files.push_back(std::unique_ptr<const DexFile>(dex1_renamed));
1033 dex_files.push_back(std::unique_ptr<const DexFile>(dex2_renamed));
1034
1035 // This is a partial match: `dex1` matches `dex1_renamed`, but `dex3` matches nothing. It should
1036 // be treated as a match failure.
1037 ProfileCompilationInfo info;
1038 AddMethod(&info, dex1, /*method_idx=*/0);
1039 AddMethod(&info, dex3, /*method_idx=*/0);
1040
1041 // Update the profile keys based on the original dex files.
1042 bool matched = false;
1043 ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &matched));
1044 ASSERT_FALSE(matched);
1045
1046 // Verify that the unmatched entry is kept.
1047 ProfileCompilationInfo::MethodHotness loaded_hotness = GetMethod(info, dex3, /*method_idx=*/0);
1048 ASSERT_TRUE(loaded_hotness.IsHot());
1049
1050 // Verify that we can find the updated entry.
1051 ProfileCompilationInfo::MethodHotness loaded_hotness_2 =
1052 GetMethod(info, dex1_renamed, /*method_idx=*/0);
1053 ASSERT_TRUE(loaded_hotness_2.IsHot());
1054
1055 // Release the ownership as this is held by the test class;
1056 for (std::unique_ptr<const DexFile>& dex : dex_files) {
1057 UNUSED(dex.release());
1058 }
1059 }
1060
TEST_F(ProfileCompilationInfoTest,UpdateProfileKeyOkButEmpty)1061 TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyOkButEmpty) {
1062 std::vector<std::unique_ptr<const DexFile>> dex_files;
1063 dex_files.push_back(std::unique_ptr<const DexFile>(dex1_renamed));
1064 dex_files.push_back(std::unique_ptr<const DexFile>(dex2_renamed));
1065
1066 // Empty profile.
1067 ProfileCompilationInfo info;
1068
1069 // Update the profile keys based on the original dex files.
1070 bool matched = false;
1071 ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &matched));
1072 ASSERT_FALSE(matched);
1073
1074 // Verify that the updated profile is still empty.
1075 EXPECT_TRUE(info.IsEmpty());
1076
1077 // Release the ownership as this is held by the test class;
1078 for (std::unique_ptr<const DexFile>& dex : dex_files) {
1079 UNUSED(dex.release());
1080 }
1081 }
1082
TEST_F(ProfileCompilationInfoTest,UpdateProfileKeyFail)1083 TEST_F(ProfileCompilationInfoTest, UpdateProfileKeyFail) {
1084 std::vector<std::unique_ptr<const DexFile>> dex_files;
1085 dex_files.push_back(std::unique_ptr<const DexFile>(dex1_renamed));
1086
1087 ProfileCompilationInfo info;
1088 AddMethod(&info, dex1, /*method_idx=*/ 0);
1089
1090 // Add a method index using the location we want to rename to.
1091 // This will cause the rename to fail because an existing entry would already have that name.
1092 AddMethod(&info, dex1_renamed, /*method_idx=*/ 0);
1093
1094 bool matched = false;
1095 ASSERT_FALSE(info.UpdateProfileKeys(dex_files, &matched));
1096 ASSERT_TRUE(matched);
1097
1098 // Release the ownership as this is held by the test class;
1099 for (std::unique_ptr<const DexFile>& dex : dex_files) {
1100 UNUSED(dex.release());
1101 }
1102 }
1103
TEST_F(ProfileCompilationInfoTest,FilteredLoading)1104 TEST_F(ProfileCompilationInfoTest, FilteredLoading) {
1105 ScratchFile profile;
1106
1107 ProfileCompilationInfo saved_info;
1108 std::vector<ProfileInlineCache> inline_caches = GetTestInlineCaches();
1109
1110 // Add methods with inline caches.
1111 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1112 // Add a method which is part of the same dex file as one of the class from the inline caches.
1113 ASSERT_TRUE(AddMethod(&saved_info, dex1, method_idx, inline_caches));
1114 ASSERT_TRUE(AddMethod(&saved_info, dex2, method_idx, inline_caches));
1115 // Add a method which is outside the set of dex files.
1116 ASSERT_TRUE(AddMethod(&saved_info, dex4, method_idx, inline_caches));
1117 }
1118
1119 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
1120 ASSERT_EQ(0, profile.GetFile()->Flush());
1121
1122 // Check that we get back what we saved.
1123 ProfileCompilationInfo loaded_info;
1124
1125 // Filter out dex locations. Keep only dex_location1 and dex_location3.
1126 ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1127 [&dex1 = dex1, &dex3 = dex3](const std::string& dex_location, uint32_t checksum) -> bool {
1128 return (dex_location == dex1->GetLocation() && checksum == dex1->GetLocationChecksum())
1129 || (dex_location == dex3->GetLocation() && checksum == dex3->GetLocationChecksum());
1130 };
1131 ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
1132
1133 // Verify that we filtered out locations during load.
1134 // Note that `dex3` did not have any data recorded in the profile.
1135 ASSERT_EQ(1u, loaded_info.GetNumberOfDexFiles());
1136
1137 // Dex location 2 and 4 should have been filtered out
1138 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1139 ASSERT_FALSE(GetMethod(loaded_info, dex2, method_idx).IsHot());
1140 ASSERT_FALSE(GetMethod(loaded_info, dex4, method_idx).IsHot());
1141 }
1142
1143 // Dex location 1 should have all all the inline caches referencing dex location 2 set to
1144 // missing types.
1145 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1146 // The methods for dex location 1 should be in the profile data.
1147 ProfileCompilationInfo::MethodHotness loaded_hotness1 =
1148 GetMethod(loaded_info, dex1, method_idx);
1149 ASSERT_TRUE(loaded_hotness1.IsHot());
1150
1151 // Verify the inline cache. Note that references to other dex files are translated
1152 // to use type indexes within the referencing dex file and artificial type indexes
1153 // referencing "extra descriptors" are used when there is no `dex::TypeId` for
1154 // these types. `EqualInlineCaches()` compares descriptors when necessary.
1155 ASSERT_TRUE(EqualInlineCaches(inline_caches, dex1, loaded_hotness1, loaded_info));
1156 }
1157 }
1158
TEST_F(ProfileCompilationInfoTest,FilteredLoadingRemoveAll)1159 TEST_F(ProfileCompilationInfoTest, FilteredLoadingRemoveAll) {
1160 ScratchFile profile;
1161
1162 ProfileCompilationInfo saved_info;
1163 std::vector<ProfileInlineCache> inline_caches = GetTestInlineCaches();
1164
1165 // Add methods with inline caches.
1166 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1167 // Add a method which is part of the same dex file as one of the class from the inline caches.
1168 ASSERT_TRUE(AddMethod(&saved_info, dex1, method_idx, inline_caches));
1169 ASSERT_TRUE(AddMethod(&saved_info, dex2, method_idx, inline_caches));
1170 // Add a method which is outside the set of dex files.
1171 ASSERT_TRUE(AddMethod(&saved_info, dex4, method_idx, inline_caches));
1172 }
1173
1174 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
1175 ASSERT_EQ(0, profile.GetFile()->Flush());
1176
1177 // Check that we get back what we saved.
1178 ProfileCompilationInfo loaded_info;
1179
1180 // Remove all elements.
1181 ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1182 [](const std::string&, uint32_t) -> bool { return false; };
1183 ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
1184
1185 // Verify that we filtered out everything.
1186 ASSERT_TRUE(IsEmpty(loaded_info));
1187 }
1188
TEST_F(ProfileCompilationInfoTest,FilteredLoadingKeepAll)1189 TEST_F(ProfileCompilationInfoTest, FilteredLoadingKeepAll) {
1190 ScratchFile profile;
1191
1192 ProfileCompilationInfo saved_info;
1193 std::vector<ProfileInlineCache> inline_caches = GetTestInlineCaches();
1194
1195 // Add methods with inline caches.
1196 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1197 // Add a method which is part of the same dex file as one of the
1198 // class from the inline caches.
1199 ASSERT_TRUE(AddMethod(&saved_info, dex1, method_idx, inline_caches));
1200 // Add a method which is outside the set of dex files.
1201 ASSERT_TRUE(AddMethod(&saved_info, dex4, method_idx, inline_caches));
1202 }
1203
1204 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
1205 ASSERT_EQ(0, profile.GetFile()->Flush());
1206
1207 // Check that we get back what we saved.
1208 ProfileCompilationInfo loaded_info;
1209
1210 // Keep all elements.
1211 ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1212 [](const std::string&, uint32_t) -> bool { return true; };
1213 ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
1214
1215
1216 ASSERT_TRUE(loaded_info.Equals(saved_info));
1217
1218 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1219 ProfileCompilationInfo::MethodHotness loaded_hotness1 =
1220 GetMethod(loaded_info, dex1, method_idx);
1221 ASSERT_TRUE(loaded_hotness1.IsHot());
1222 ASSERT_TRUE(EqualInlineCaches(inline_caches, dex1, loaded_hotness1, loaded_info));
1223 }
1224 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) {
1225 ProfileCompilationInfo::MethodHotness loaded_hotness2 =
1226 GetMethod(loaded_info, dex4, method_idx);
1227 ASSERT_TRUE(loaded_hotness2.IsHot());
1228 ASSERT_TRUE(EqualInlineCaches(inline_caches, dex4, loaded_hotness2, loaded_info));
1229 }
1230 }
1231
1232 // Regression test: we were failing to do a filtering loading when the filtered dex file
1233 // contained profiled classes.
TEST_F(ProfileCompilationInfoTest,FilteredLoadingWithClasses)1234 TEST_F(ProfileCompilationInfoTest, FilteredLoadingWithClasses) {
1235 ScratchFile profile;
1236
1237 const DexFile* dex1_1000 = BuildDex("location1_1000",
1238 /*location_checksum=*/ 7,
1239 "LC1_1000;",
1240 /*num_method_ids=*/ 1u,
1241 /*num_class_ids=*/ 1000u);
1242 const DexFile* dex2_1000 = BuildDex("location2_1000",
1243 /*location_checksum=*/ 8,
1244 "LC2_1000;",
1245 /*num_method_ids=*/ 1u,
1246 /*num_class_ids=*/ 1000u);
1247
1248 // Save a profile with 2 dex files containing just classes.
1249 ProfileCompilationInfo saved_info;
1250 uint16_t item_count = 1000;
1251 for (uint16_t i = 0; i < item_count; i++) {
1252 ASSERT_TRUE(AddClass(&saved_info, dex1_1000, dex::TypeIndex(i)));
1253 ASSERT_TRUE(AddClass(&saved_info, dex2_1000, dex::TypeIndex(i)));
1254 }
1255
1256 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
1257 ASSERT_EQ(0, profile.GetFile()->Flush());
1258
1259
1260 // Filter out dex locations: keep only `dex2_1000->GetLocation()`.
1261 ProfileCompilationInfo loaded_info;
1262 ProfileCompilationInfo::ProfileLoadFilterFn filter_fn =
1263 [dex2_1000](const std::string& dex_location, uint32_t checksum) -> bool {
1264 return dex_location == dex2_1000->GetLocation() &&
1265 checksum == dex2_1000->GetLocationChecksum();
1266 };
1267 ASSERT_TRUE(loaded_info.Load(GetFd(profile), true, filter_fn));
1268
1269 // Compute the expectation.
1270 ProfileCompilationInfo expected_info;
1271 for (uint16_t i = 0; i < item_count; i++) {
1272 ASSERT_TRUE(AddClass(&expected_info, dex2_1000, dex::TypeIndex(i)));
1273 }
1274
1275 // Validate the expectation.
1276 ASSERT_TRUE(loaded_info.Equals(expected_info));
1277 }
1278
1279
TEST_F(ProfileCompilationInfoTest,ClearData)1280 TEST_F(ProfileCompilationInfoTest, ClearData) {
1281 ProfileCompilationInfo info;
1282 for (uint16_t i = 0; i < 10; i++) {
1283 ASSERT_TRUE(AddMethod(&info, dex1, /*method_idx=*/ i));
1284 }
1285 ASSERT_FALSE(IsEmpty(info));
1286 info.ClearData();
1287 ASSERT_TRUE(IsEmpty(info));
1288 }
1289
TEST_F(ProfileCompilationInfoTest,ClearDataAndSave)1290 TEST_F(ProfileCompilationInfoTest, ClearDataAndSave) {
1291 ProfileCompilationInfo info;
1292 for (uint16_t i = 0; i < 10; i++) {
1293 ASSERT_TRUE(AddMethod(&info, dex1, /*method_idx=*/ i));
1294 }
1295 info.ClearData();
1296
1297 ScratchFile profile;
1298 ASSERT_TRUE(info.Save(GetFd(profile)));
1299 ASSERT_EQ(0, profile.GetFile()->Flush());
1300
1301 // Check that we get back what we saved.
1302 ProfileCompilationInfo loaded_info;
1303 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
1304 ASSERT_TRUE(loaded_info.Equals(info));
1305 }
1306
TEST_F(ProfileCompilationInfoTest,InitProfiles)1307 TEST_F(ProfileCompilationInfoTest, InitProfiles) {
1308 ProfileCompilationInfo info;
1309 ASSERT_EQ(
1310 memcmp(info.GetVersion(),
1311 ProfileCompilationInfo::kProfileVersion,
1312 ProfileCompilationInfo::kProfileVersionSize),
1313 0);
1314 ASSERT_FALSE(info.IsForBootImage());
1315
1316 ProfileCompilationInfo info1(/*for_boot_image=*/ true);
1317 ASSERT_EQ(
1318 memcmp(info1.GetVersion(),
1319 ProfileCompilationInfo::kProfileVersionForBootImage,
1320 ProfileCompilationInfo::kProfileVersionSize),
1321 0);
1322 ASSERT_TRUE(info1.IsForBootImage());
1323 }
1324
TEST_F(ProfileCompilationInfoTest,VersionEquality)1325 TEST_F(ProfileCompilationInfoTest, VersionEquality) {
1326 ProfileCompilationInfo info(/*for_boot_image=*/ false);
1327 ProfileCompilationInfo info1(/*for_boot_image=*/ true);
1328 ASSERT_FALSE(info.Equals(info1));
1329 }
1330
TEST_F(ProfileCompilationInfoTest,AllMethodFlags)1331 TEST_F(ProfileCompilationInfoTest, AllMethodFlags) {
1332 ProfileCompilationInfo info(/*for_boot_image=*/ true);
1333
1334 for (uint32_t index = 0; index <= kMaxHotnessFlagBootIndex; index++) {
1335 AddMethod(&info, dex1, index, static_cast<Hotness::Flag>(1 << index));
1336 }
1337
1338 auto run_test = [&dex1 = dex1](const ProfileCompilationInfo& info) {
1339 for (uint32_t index = 0; index <= kMaxHotnessFlagBootIndex; index++) {
1340 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, index)).IsInProfile());
1341 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, index))
1342 .HasFlagSet(static_cast<Hotness::Flag>(1 << index))) << index << " "
1343 << info.GetMethodHotness(MethodReference(dex1, index)).GetFlags();
1344 }
1345 };
1346 run_test(info);
1347
1348 // Save the profile.
1349 ScratchFile profile;
1350 ASSERT_TRUE(info.Save(GetFd(profile)));
1351 ASSERT_EQ(0, profile.GetFile()->Flush());
1352
1353 // Load the profile and make sure we can read the data and it matches what we expect.
1354 ProfileCompilationInfo loaded_info(/*for_boot_image=*/ true);
1355 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
1356 run_test(loaded_info);
1357 }
1358
TEST_F(ProfileCompilationInfoTest,AllMethodFlagsOnOneMethod)1359 TEST_F(ProfileCompilationInfoTest, AllMethodFlagsOnOneMethod) {
1360 ProfileCompilationInfo info(/*for_boot_image=*/ true);
1361
1362 // Set all flags on a single method.
1363 for (uint32_t index = 0; index <= kMaxHotnessFlagBootIndex; index++) {
1364 AddMethod(&info, dex1, 0, static_cast<Hotness::Flag>(1 << index));
1365 }
1366
1367 auto run_test = [&dex1 = dex1](const ProfileCompilationInfo& info) {
1368 for (uint32_t index = 0; index <= kMaxHotnessFlagBootIndex; index++) {
1369 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, 0)).IsInProfile());
1370 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, 0))
1371 .HasFlagSet(static_cast<Hotness::Flag>(1 << index)));
1372 }
1373 };
1374 run_test(info);
1375
1376 // Save the profile.
1377 ScratchFile profile;
1378 ASSERT_TRUE(info.Save(GetFd(profile)));
1379 ASSERT_EQ(0, profile.GetFile()->Flush());
1380
1381 // Load the profile and make sure we can read the data and it matches what we expect.
1382 ProfileCompilationInfo loaded_info(/*for_boot_image=*/ true);
1383 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
1384 run_test(loaded_info);
1385 }
1386
1387
TEST_F(ProfileCompilationInfoTest,MethodFlagsMerge)1388 TEST_F(ProfileCompilationInfoTest, MethodFlagsMerge) {
1389 ProfileCompilationInfo info1(/*for_boot_image=*/ true);
1390 ProfileCompilationInfo info2(/*for_boot_image=*/ true);
1391
1392 // Set a few flags on a 2 different methods in each of the profile.
1393 for (uint32_t index = 0; index <= kMaxHotnessFlagBootIndex / 4; index++) {
1394 AddMethod(&info1, dex1, 0, static_cast<Hotness::Flag>(1 << index));
1395 AddMethod(&info2, dex1, 1, static_cast<Hotness::Flag>(1 << index));
1396 }
1397
1398 // Set a few more flags on the method 1.
1399 for (uint32_t index = kMaxHotnessFlagBootIndex / 4 + 1;
1400 index <= kMaxHotnessFlagBootIndex / 2;
1401 index++) {
1402 AddMethod(&info2, dex1, 1, static_cast<Hotness::Flag>(1 << index));
1403 }
1404
1405 ASSERT_TRUE(info1.MergeWith(info2));
1406
1407 auto run_test = [&dex1 = dex1](const ProfileCompilationInfo& info) {
1408 // Assert that the flags were merged correctly for both methods.
1409 for (uint32_t index = 0; index <= kMaxHotnessFlagBootIndex / 4; index++) {
1410 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, 0)).IsInProfile());
1411 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, 0))
1412 .HasFlagSet(static_cast<Hotness::Flag>(1 << index)));
1413
1414 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, 1)).IsInProfile());
1415 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, 1))
1416 .HasFlagSet(static_cast<Hotness::Flag>(1 << index)));
1417 }
1418
1419 // Assert that no flags were merged unnecessary.
1420 for (uint32_t index = kMaxHotnessFlagBootIndex / 4 + 1;
1421 index <= kMaxHotnessFlagBootIndex / 2;
1422 index++) {
1423 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, 0)).IsInProfile());
1424 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, 0))
1425 .HasFlagSet(static_cast<Hotness::Flag>(1 << index)));
1426
1427 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, 1)).IsInProfile());
1428 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, 1))
1429 .HasFlagSet(static_cast<Hotness::Flag>(1 << index)));
1430 }
1431
1432 // Assert that no extra flags were added.
1433 for (uint32_t index = kMaxHotnessFlagBootIndex / 2 + 1;
1434 index <= kMaxHotnessFlagBootIndex;
1435 index++) {
1436 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, 0))
1437 .HasFlagSet(static_cast<Hotness::Flag>(1 << index)));
1438 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, 1))
1439 .HasFlagSet(static_cast<Hotness::Flag>(1 << index)));
1440 }
1441 };
1442
1443 run_test(info1);
1444
1445 // Save the profile.
1446 ScratchFile profile;
1447 ASSERT_TRUE(info1.Save(GetFd(profile)));
1448 ASSERT_EQ(0, profile.GetFile()->Flush());
1449
1450 // Load the profile and make sure we can read the data and it matches what we expect.
1451 ProfileCompilationInfo loaded_info(/*for_boot_image=*/ true);
1452 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
1453 run_test(loaded_info);
1454 }
1455
TEST_F(ProfileCompilationInfoTest,SizeStressTestAllIn)1456 TEST_F(ProfileCompilationInfoTest, SizeStressTestAllIn) {
1457 SizeStressTest(/*random=*/ false);
1458 }
1459
TEST_F(ProfileCompilationInfoTest,SizeStressTestAllInRandom)1460 TEST_F(ProfileCompilationInfoTest, SizeStressTestAllInRandom) {
1461 SizeStressTest(/*random=*/ true);
1462 }
1463
1464 // Verifies that we correctly add methods to the profile according to their flags.
TEST_F(ProfileCompilationInfoTest,AddMethodsProfileMethodInfoBasic)1465 TEST_F(ProfileCompilationInfoTest, AddMethodsProfileMethodInfoBasic) {
1466 std::unique_ptr<const DexFile> dex(OpenTestDexFile("ManyMethods"));
1467
1468 ProfileCompilationInfo info;
1469
1470 MethodReference hot(dex.get(), 0);
1471 MethodReference hot_startup(dex.get(), 1);
1472 MethodReference startup(dex.get(), 2);
1473
1474 // Add methods
1475 ASSERT_TRUE(info.AddMethod(ProfileMethodInfo(hot), Hotness::kFlagHot));
1476 ASSERT_TRUE(info.AddMethod(
1477 ProfileMethodInfo(hot_startup),
1478 static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup)));
1479 ASSERT_TRUE(info.AddMethod(ProfileMethodInfo(startup), Hotness::kFlagStartup));
1480
1481 // Verify the profile recorded them correctly.
1482 EXPECT_TRUE(info.GetMethodHotness(hot).IsInProfile());
1483 EXPECT_EQ(info.GetMethodHotness(hot).GetFlags(), Hotness::kFlagHot);
1484
1485 EXPECT_TRUE(info.GetMethodHotness(hot_startup).IsInProfile());
1486 EXPECT_EQ(info.GetMethodHotness(hot_startup).GetFlags(),
1487 static_cast<uint32_t>(Hotness::kFlagHot | Hotness::kFlagStartup));
1488
1489 EXPECT_TRUE(info.GetMethodHotness(startup).IsInProfile());
1490 EXPECT_EQ(info.GetMethodHotness(startup).GetFlags(), Hotness::kFlagStartup);
1491 }
1492
1493 // Verifies that we correctly add inline caches to the profile only for hot methods.
TEST_F(ProfileCompilationInfoTest,AddMethodsProfileMethodInfoInlineCaches)1494 TEST_F(ProfileCompilationInfoTest, AddMethodsProfileMethodInfoInlineCaches) {
1495 ProfileCompilationInfo info;
1496 MethodReference hot(dex1, 0);
1497 MethodReference startup(dex1, 2);
1498
1499 // Add inline caches with the methods. The profile should record only the one for the hot method.
1500 std::vector<TypeReference> types = {};
1501 ProfileMethodInfo::ProfileInlineCache ic(/*pc=*/0, /*missing_types=*/true, types);
1502 std::vector<ProfileMethodInfo::ProfileInlineCache> inline_caches = {ic};
1503 info.AddMethod(ProfileMethodInfo(hot, inline_caches),
1504 Hotness::kFlagHot,
1505 ProfileSampleAnnotation::kNone,
1506 /*is_test=*/ true);
1507 info.AddMethod(ProfileMethodInfo(startup, inline_caches),
1508 Hotness::kFlagStartup,
1509 ProfileSampleAnnotation::kNone,
1510 /*is_test=*/ true);
1511
1512 // Check the hot method's inline cache.
1513 ProfileCompilationInfo::MethodHotness hot_hotness = GetMethod(info, dex1, hot.index);
1514 ASSERT_TRUE(hot_hotness.IsHot());
1515 ASSERT_EQ(hot_hotness.GetInlineCacheMap()->size(), 1u);
1516 ASSERT_TRUE(hot_hotness.GetInlineCacheMap()->Get(0).is_missing_types);
1517
1518 // Check there's no inline caches for the startup method.
1519 ASSERT_FALSE(GetMethod(info, dex1, startup.index).IsHot());
1520 }
1521
1522 // Verifies that we correctly add methods to the profile according to their flags.
TEST_F(ProfileCompilationInfoTest,AddMethodsProfileMethodInfoFail)1523 TEST_F(ProfileCompilationInfoTest, AddMethodsProfileMethodInfoFail) {
1524 ProfileCompilationInfo info;
1525
1526 MethodReference hot(dex1, 0);
1527 MethodReference bad_ref(dex1, kMaxMethodIds);
1528
1529 std::vector<ProfileMethodInfo> pmis = {ProfileMethodInfo(hot), ProfileMethodInfo(bad_ref)};
1530 ASSERT_FALSE(info.AddMethods(pmis, Hotness::kFlagHot));
1531 }
1532
1533 // Verify that we can add methods with annotations.
TEST_F(ProfileCompilationInfoTest,AddAnnotationsToMethods)1534 TEST_F(ProfileCompilationInfoTest, AddAnnotationsToMethods) {
1535 ProfileCompilationInfo info;
1536
1537 ProfileSampleAnnotation psa1("test1");
1538 ProfileSampleAnnotation psa2("test2");
1539 // Save a few methods using different annotations, some overlapping, some not.
1540 for (uint16_t i = 0; i < 10; i++) {
1541 ASSERT_TRUE(AddMethod(&info, dex1, /*method_idx=*/ i, Hotness::kFlagHot, psa1));
1542 }
1543 for (uint16_t i = 5; i < 15; i++) {
1544 ASSERT_TRUE(AddMethod(&info, dex1, /*method_idx=*/ i, Hotness::kFlagHot, psa2));
1545 }
1546
1547 auto run_test = [&dex1 = dex1, &psa1 = psa1, &psa2 = psa2](const ProfileCompilationInfo& info) {
1548 // Check that all methods are in.
1549 for (uint16_t i = 0; i < 10; i++) {
1550 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, i), psa1).IsInProfile());
1551 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, i), psa1).IsHot());
1552 }
1553 for (uint16_t i = 5; i < 15; i++) {
1554 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, i), psa2).IsInProfile());
1555 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, i), psa2).IsHot());
1556 }
1557 // Check that the non-overlapping methods are not added with a wrong annotation.
1558 for (uint16_t i = 10; i < 15; i++) {
1559 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, i), psa1).IsInProfile());
1560 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, i), psa1).IsHot());
1561 }
1562 for (uint16_t i = 0; i < 5; i++) {
1563 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, i), psa2).IsInProfile());
1564 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, i), psa2).IsHot());
1565 }
1566 // Check that when querying without an annotation only the first one is searched.
1567 for (uint16_t i = 0; i < 10; i++) {
1568 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, i)).IsInProfile());
1569 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, i)).IsHot());
1570 }
1571 // ... this should be false because they belong the second appearance of dex1.
1572 for (uint16_t i = 10; i < 15; i++) {
1573 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, i)).IsInProfile());
1574 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, i)).IsHot());
1575 }
1576
1577 // Check that the methods cannot be found with a non existing annotation.
1578 MethodReference ref(dex1, 0);
1579 ProfileSampleAnnotation not_existing("A");
1580 EXPECT_FALSE(info.GetMethodHotness(ref, not_existing).IsInProfile());
1581 EXPECT_FALSE(info.GetMethodHotness(ref, not_existing).IsHot());
1582 };
1583
1584 // Run the test before save.
1585 run_test(info);
1586
1587 ScratchFile profile;
1588 ASSERT_TRUE(info.Save(GetFd(profile)));
1589 ASSERT_EQ(0, profile.GetFile()->Flush());
1590
1591 // Check that we get back what we saved.
1592 ProfileCompilationInfo loaded_info;
1593 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
1594 ASSERT_TRUE(loaded_info.Equals(info));
1595
1596 // Run the test after save and load.
1597 run_test(loaded_info);
1598 }
1599
1600 // Verify that we can add classes with annotations.
TEST_F(ProfileCompilationInfoTest,AddAnnotationsToClasses)1601 TEST_F(ProfileCompilationInfoTest, AddAnnotationsToClasses) {
1602 ProfileCompilationInfo info;
1603
1604 ProfileSampleAnnotation psa1("test1");
1605 ProfileSampleAnnotation psa2("test2");
1606 // Save a few classes using different annotations, some overlapping, some not.
1607 for (uint16_t i = 0; i < 7; i++) {
1608 ASSERT_TRUE(AddClass(&info, dex1, dex::TypeIndex(i), psa1));
1609 }
1610 for (uint16_t i = 3; i < 10; i++) {
1611 ASSERT_TRUE(AddClass(&info, dex1, dex::TypeIndex(i), psa2));
1612 }
1613
1614 auto run_test = [&dex1 = dex1, &psa1 = psa1, &psa2 = psa2](const ProfileCompilationInfo& info) {
1615 // Check that all classes are in.
1616 for (uint16_t i = 0; i < 7; i++) {
1617 EXPECT_TRUE(info.ContainsClass(*dex1, dex::TypeIndex(i), psa1));
1618 }
1619 for (uint16_t i = 3; i < 10; i++) {
1620 EXPECT_TRUE(info.ContainsClass(*dex1, dex::TypeIndex(i), psa2));
1621 }
1622 // Check that the non-overlapping classes are not added with a wrong annotation.
1623 for (uint16_t i = 7; i < 10; i++) {
1624 EXPECT_FALSE(info.ContainsClass(*dex1, dex::TypeIndex(i), psa1));
1625 }
1626 for (uint16_t i = 0; i < 3; i++) {
1627 EXPECT_FALSE(info.ContainsClass(*dex1, dex::TypeIndex(i), psa2));
1628 }
1629 // Check that when querying without an annotation only the first one is searched.
1630 for (uint16_t i = 0; i < 7; i++) {
1631 EXPECT_TRUE(info.ContainsClass(*dex1, dex::TypeIndex(i)));
1632 }
1633 // ... this should be false because they belong the second appearance of dex1.
1634 for (uint16_t i = 7; i < 10; i++) {
1635 EXPECT_FALSE(info.ContainsClass(*dex1, dex::TypeIndex(i)));
1636 }
1637
1638 // Check that the classes cannot be found with a non existing annotation.
1639 EXPECT_FALSE(info.ContainsClass(*dex1, dex::TypeIndex(0), ProfileSampleAnnotation("new_test")));
1640 };
1641
1642 // Run the test before save.
1643 run_test(info);
1644
1645 ScratchFile profile;
1646 ASSERT_TRUE(info.Save(GetFd(profile)));
1647 ASSERT_EQ(0, profile.GetFile()->Flush());
1648
1649 // Check that we get back what we saved.
1650 ProfileCompilationInfo loaded_info;
1651 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
1652 ASSERT_TRUE(loaded_info.Equals(info));
1653
1654 // Run the test after save and load.
1655 run_test(loaded_info);
1656 }
1657
1658 // Verify we can merge samples with annotations.
TEST_F(ProfileCompilationInfoTest,MergeWithAnnotations)1659 TEST_F(ProfileCompilationInfoTest, MergeWithAnnotations) {
1660 ProfileCompilationInfo info1;
1661 ProfileCompilationInfo info2;
1662
1663 ProfileSampleAnnotation psa1("test1");
1664 ProfileSampleAnnotation psa2("test2");
1665
1666 for (uint16_t i = 0; i < 7; i++) {
1667 ASSERT_TRUE(AddMethod(&info1, dex1, /*method_idx=*/ i, Hotness::kFlagHot, psa1));
1668 ASSERT_TRUE(AddClass(&info1, dex1, dex::TypeIndex(i), psa1));
1669 }
1670 for (uint16_t i = 3; i < 10; i++) {
1671 ASSERT_TRUE(AddMethod(&info2, dex1, /*method_idx=*/ i, Hotness::kFlagHot, psa1));
1672 ASSERT_TRUE(AddMethod(&info2, dex1, /*method_idx=*/ i, Hotness::kFlagHot, psa2));
1673 ASSERT_TRUE(AddMethod(&info2, dex2, /*method_idx=*/ i, Hotness::kFlagHot, psa2));
1674 ASSERT_TRUE(AddClass(&info2, dex1, dex::TypeIndex(i), psa1));
1675 ASSERT_TRUE(AddClass(&info2, dex1, dex::TypeIndex(i), psa2));
1676 }
1677
1678 ProfileCompilationInfo info;
1679 ASSERT_TRUE(info.MergeWith(info1));
1680 ASSERT_TRUE(info.MergeWith(info2));
1681
1682 // Check that all items are in.
1683 for (uint16_t i = 0; i < 10; i++) {
1684 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, i), psa1).IsInProfile());
1685 EXPECT_TRUE(info.ContainsClass(*dex1, dex::TypeIndex(i), psa1));
1686 }
1687 for (uint16_t i = 3; i < 10; i++) {
1688 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex1, i), psa2).IsInProfile());
1689 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex2, i), psa2).IsInProfile());
1690 EXPECT_TRUE(info.ContainsClass(*dex1, dex::TypeIndex(i), psa2));
1691 }
1692
1693 // Check that the non-overlapping items are not added with a wrong annotation.
1694 for (uint16_t i = 0; i < 3; i++) {
1695 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex1, i), psa2).IsInProfile());
1696 EXPECT_FALSE(info.GetMethodHotness(MethodReference(dex2, i), psa2).IsInProfile());
1697 EXPECT_FALSE(info.ContainsClass(*dex1, dex::TypeIndex(i), psa2));
1698 }
1699 }
1700
1701 // Verify we can merge samples with annotations.
TEST_F(ProfileCompilationInfoTest,MergeWithInlineCaches)1702 TEST_F(ProfileCompilationInfoTest, MergeWithInlineCaches) {
1703 ProfileCompilationInfo info1(/*for_boot_image=*/ true);
1704 ProfileCompilationInfo info2(/*for_boot_image=*/ true);
1705 // TODO This should be something other than 'kNone'
1706 ProfileSampleAnnotation psa1(ProfileSampleAnnotation::kNone);
1707 std::vector<TypeReference> dex1_type_12 { TypeReference(dex1, dex::TypeIndex(1)),
1708 TypeReference(dex1, dex::TypeIndex(2)) };
1709 std::vector<TypeReference> dex1_type_48 { TypeReference(dex1, dex::TypeIndex(4)),
1710 TypeReference(dex1, dex::TypeIndex(8)) };
1711 std::vector<TypeReference> dex2_type_12 { TypeReference(dex2, dex::TypeIndex(1)),
1712 TypeReference(dex2, dex::TypeIndex(2)) };
1713 std::vector<TypeReference> dex2_type_48 { TypeReference(dex2, dex::TypeIndex(4)),
1714 TypeReference(dex2, dex::TypeIndex(8)) };
1715 std::vector<ProfileInlineCache> ic1 { ProfileInlineCache(
1716 /*pc=*/ 12,
1717 /*missing_types=*/ false,
1718 /*profile_classes=*/ dex1_type_12),
1719 ProfileInlineCache(
1720 /*pc=*/ 15,
1721 /*missing_types=*/ false,
1722 /*profile_classes=*/ dex1_type_48) };
1723 std::vector<ProfileInlineCache> ic2 { ProfileInlineCache(
1724 /*pc=*/ 12,
1725 /*missing_types=*/ false,
1726 /*profile_classes=*/ dex2_type_48),
1727 ProfileInlineCache(
1728 /*pc=*/ 15,
1729 /*missing_types=*/ false,
1730 /*profile_classes=*/ dex2_type_12) };
1731
1732 for (uint16_t i = 0; i < 10; i++) {
1733 ASSERT_TRUE(AddMethod(&info1, dex1, /*method_idx=*/ i, ic1, psa1));
1734 ASSERT_TRUE(AddClass(&info1, dex1, dex::TypeIndex(i), psa1));
1735 ASSERT_TRUE(AddClass(&info1, dex2, dex::TypeIndex(i), psa1));
1736 ASSERT_TRUE(AddMethod(&info2, dex1, /*method_idx=*/ i, ic2, psa1));
1737 ASSERT_TRUE(AddClass(&info2, dex1, dex::TypeIndex(i), psa1));
1738 ASSERT_TRUE(AddClass(&info2, dex2, dex::TypeIndex(i), psa1));
1739 }
1740
1741 ProfileCompilationInfo info_12(/*for_boot_image=*/ true);
1742 ASSERT_TRUE(info_12.MergeWith(info1));
1743 ASSERT_TRUE(info_12.MergeWith(info2));
1744
1745 // Check that all items are in.
1746 for (uint16_t i = 0; i < 10; i++) {
1747 EXPECT_TRUE(info_12.GetMethodHotness(MethodReference(dex1, i), psa1).IsInProfile());
1748 EXPECT_TRUE(info_12.ContainsClass(*dex1, dex::TypeIndex(i), psa1));
1749 ProfileCompilationInfo::MethodHotness loaded_ic_12 =
1750 GetMethod(info_12, dex1, /*method_idx=*/ i);
1751 ASSERT_TRUE(loaded_ic_12.IsHot());
1752 std::vector<TypeReference> cls_pc12;
1753 cls_pc12.resize(dex1_type_12.size() + dex2_type_48.size(),
1754 TypeReference(nullptr, dex::TypeIndex(-1)));
1755 auto copy_end_12 = std::copy(dex1_type_12.begin(), dex1_type_12.end(), cls_pc12.begin());
1756 std::copy(dex2_type_48.begin(), dex2_type_48.end(), copy_end_12);
1757 std::vector<TypeReference> cls_pc15;
1758 cls_pc15.resize(dex2_type_12.size() + dex1_type_48.size(),
1759 TypeReference(nullptr, dex::TypeIndex(-1)));
1760 auto copy_end_15 = std::copy(dex2_type_12.begin(), dex2_type_12.end(), cls_pc15.begin());
1761 std::copy(dex1_type_48.begin(), dex1_type_48.end(), copy_end_15);
1762 std::vector<ProfileInlineCache> expected{ ProfileInlineCache(
1763 /*pc=*/ 12,
1764 /*missing_types=*/ false,
1765 /*profile_classes=*/ cls_pc12),
1766 ProfileInlineCache(
1767 /*pc=*/ 15,
1768 /*missing_types=*/ false,
1769 /*profile_classes=*/ cls_pc15) };
1770 EXPECT_EQ(loaded_ic_12.GetInlineCacheMap()->size(), expected.size());
1771 EXPECT_TRUE(EqualInlineCaches(expected, dex1, loaded_ic_12, info_12)) << i;
1772 }
1773 }
1774
1775 // Verify the bulk extraction API.
TEST_F(ProfileCompilationInfoTest,ExtractInfoWithAnnations)1776 TEST_F(ProfileCompilationInfoTest, ExtractInfoWithAnnations) {
1777 ProfileCompilationInfo info;
1778
1779 ProfileSampleAnnotation psa1("test1");
1780 ProfileSampleAnnotation psa2("test2");
1781
1782 std::set<dex::TypeIndex> expected_classes;
1783 std::set<uint16_t> expected_hot_methods;
1784 std::set<uint16_t> expected_startup_methods;
1785 std::set<uint16_t> expected_post_startup_methods;
1786
1787 for (uint16_t i = 0; i < 10; i++) {
1788 ASSERT_TRUE(AddMethod(&info, dex1, /*method_idx=*/ i, Hotness::kFlagHot, psa1));
1789 ASSERT_TRUE(AddClass(&info, dex1, dex::TypeIndex(i), psa1));
1790 expected_hot_methods.insert(i);
1791 expected_classes.insert(dex::TypeIndex(i));
1792 }
1793 for (uint16_t i = 5; i < 15; i++) {
1794 ASSERT_TRUE(AddMethod(&info, dex1, /*method_idx=*/ i, Hotness::kFlagHot, psa2));
1795 ASSERT_TRUE(AddMethod(&info, dex1, /*method_idx=*/ i, Hotness::kFlagStartup, psa1));
1796 expected_startup_methods.insert(i);
1797 }
1798
1799 std::set<dex::TypeIndex> classes;
1800 std::set<uint16_t> hot_methods;
1801 std::set<uint16_t> startup_methods;
1802 std::set<uint16_t> post_startup_methods;
1803
1804 EXPECT_TRUE(info.GetClassesAndMethods(
1805 *dex1, &classes, &hot_methods, &startup_methods, &post_startup_methods, psa1));
1806 EXPECT_EQ(expected_classes, classes);
1807 EXPECT_EQ(expected_hot_methods, hot_methods);
1808 EXPECT_EQ(expected_startup_methods, startup_methods);
1809 EXPECT_EQ(expected_post_startup_methods, post_startup_methods);
1810
1811 EXPECT_FALSE(info.GetClassesAndMethods(
1812 *dex1,
1813 &classes,
1814 &hot_methods,
1815 &startup_methods,
1816 &post_startup_methods,
1817 ProfileSampleAnnotation("new_test")));
1818 }
1819
1820 // Verify the behavior for adding methods with annotations and different dex checksums.
TEST_F(ProfileCompilationInfoTest,AddMethodsWithAnnotationAndDifferentChecksum)1821 TEST_F(ProfileCompilationInfoTest, AddMethodsWithAnnotationAndDifferentChecksum) {
1822 ProfileCompilationInfo info;
1823
1824 ProfileSampleAnnotation psa1("test1");
1825 ProfileSampleAnnotation psa2("test2");
1826
1827 MethodReference ref(dex1, 0);
1828 MethodReference ref_checksum_missmatch(dex1_checksum_missmatch, 1);
1829
1830 ASSERT_TRUE(info.AddMethod(ProfileMethodInfo(ref), Hotness::kFlagHot, psa1));
1831 // Adding a method with a different dex checksum and the same annotation should fail.
1832 ASSERT_FALSE(info.AddMethod(ProfileMethodInfo(ref_checksum_missmatch), Hotness::kFlagHot, psa1));
1833 // However, a method with a different dex checksum and a different annotation should be ok.
1834 ASSERT_TRUE(info.AddMethod(ProfileMethodInfo(ref_checksum_missmatch), Hotness::kFlagHot, psa2));
1835 }
1836
1837 // Verify the behavior for searching method with annotations and different dex checksums.
TEST_F(ProfileCompilationInfoTest,FindMethodsWithAnnotationAndDifferentChecksum)1838 TEST_F(ProfileCompilationInfoTest, FindMethodsWithAnnotationAndDifferentChecksum) {
1839 ProfileCompilationInfo info;
1840
1841 ProfileSampleAnnotation psa1("test1");
1842
1843 MethodReference ref(dex1, 0);
1844 MethodReference ref_checksum_missmatch(dex1_checksum_missmatch, 0);
1845
1846 ASSERT_TRUE(info.AddMethod(ProfileMethodInfo(ref), Hotness::kFlagHot, psa1));
1847
1848 // The method should be in the profile when searched with the correct data.
1849 EXPECT_TRUE(info.GetMethodHotness(ref, psa1).IsInProfile());
1850 // We should get a negative result if the dex checksum does not match.
1851 EXPECT_FALSE(info.GetMethodHotness(ref_checksum_missmatch, psa1).IsInProfile());
1852
1853 // If we search without annotation we should have the same behaviour.
1854 EXPECT_TRUE(info.GetMethodHotness(ref).IsInProfile());
1855 EXPECT_FALSE(info.GetMethodHotness(ref_checksum_missmatch).IsInProfile());
1856 }
1857
TEST_F(ProfileCompilationInfoTest,ClearDataAndAdjustVersionRegularToBoot)1858 TEST_F(ProfileCompilationInfoTest, ClearDataAndAdjustVersionRegularToBoot) {
1859 ProfileCompilationInfo info;
1860
1861 AddMethod(&info, dex1, /*method_idx=*/ 0, Hotness::kFlagHot);
1862
1863 info.ClearDataAndAdjustVersion(/*for_boot_image=*/true);
1864 ASSERT_TRUE(info.IsEmpty());
1865 ASSERT_TRUE(info.IsForBootImage());
1866 }
1867
TEST_F(ProfileCompilationInfoTest,ClearDataAndAdjustVersionBootToRegular)1868 TEST_F(ProfileCompilationInfoTest, ClearDataAndAdjustVersionBootToRegular) {
1869 ProfileCompilationInfo info(/*for_boot_image=*/true);
1870
1871 AddMethod(&info, dex1, /*method_idx=*/ 0, Hotness::kFlagHot);
1872
1873 info.ClearDataAndAdjustVersion(/*for_boot_image=*/false);
1874 ASSERT_TRUE(info.IsEmpty());
1875 ASSERT_FALSE(info.IsForBootImage());
1876 }
1877
1878 template<class T>
sort(const std::list<T> & list)1879 static std::list<T> sort(const std::list<T>& list) {
1880 std::list<T> copy(list);
1881 copy.sort();
1882 return copy;
1883 }
1884
1885 // Verify we can extract profile data
TEST_F(ProfileCompilationInfoTest,ExtractProfileData)1886 TEST_F(ProfileCompilationInfoTest, ExtractProfileData) {
1887 // Setup test data
1888 ProfileCompilationInfo info;
1889
1890 ProfileSampleAnnotation psa1("test1");
1891 ProfileSampleAnnotation psa2("test2");
1892
1893 for (uint16_t i = 0; i < 10; i++) {
1894 // Add dex1 data with different annotations so that we can check the annotation count.
1895 ASSERT_TRUE(AddMethod(&info, dex1, /*method_idx=*/ i, Hotness::kFlagHot, psa1));
1896 ASSERT_TRUE(AddClass(&info, dex1, dex::TypeIndex(i), psa1));
1897 ASSERT_TRUE(AddMethod(&info, dex1, /*method_idx=*/ i, Hotness::kFlagStartup, psa2));
1898 ASSERT_TRUE(AddClass(&info, dex1, dex::TypeIndex(i), psa2));
1899 ASSERT_TRUE(AddMethod(&info, dex2, /*method_idx=*/ i, Hotness::kFlagHot, psa2));
1900 // dex3 will not be used in the data extraction
1901 ASSERT_TRUE(AddMethod(&info, dex3, /*method_idx=*/ i, Hotness::kFlagHot, psa2));
1902 }
1903
1904 std::vector<std::unique_ptr<const DexFile>> dex_files;
1905 dex_files.push_back(std::unique_ptr<const DexFile>(dex1));
1906 dex_files.push_back(std::unique_ptr<const DexFile>(dex2));
1907
1908 // Run the test: extract the data for dex1 and dex2
1909 std::unique_ptr<FlattenProfileData> flattenProfileData = info.ExtractProfileData(dex_files);
1910
1911 // Check the results
1912 ASSERT_TRUE(flattenProfileData != nullptr);
1913 ASSERT_EQ(flattenProfileData->GetMaxAggregationForMethods(), 2u);
1914 ASSERT_EQ(flattenProfileData->GetMaxAggregationForClasses(), 2u);
1915
1916 const SafeMap<MethodReference, ItemMetadata>& methods = flattenProfileData->GetMethodData();
1917 const SafeMap<TypeReference, ItemMetadata>& classes = flattenProfileData->GetClassData();
1918 ASSERT_EQ(methods.size(), 20u); // 10 methods in dex1, 10 in dex2
1919 ASSERT_EQ(classes.size(), 10u); // 10 methods in dex1
1920
1921 std::list<ProfileSampleAnnotation> expectedAnnotations1({psa1, psa2});
1922 std::list<ProfileSampleAnnotation> expectedAnnotations2({psa2});
1923 for (uint16_t i = 0; i < 10; i++) {
1924 // Check dex1 methods.
1925 auto mIt1 = methods.find(MethodReference(dex1, i));
1926 ASSERT_TRUE(mIt1 != methods.end());
1927 ASSERT_EQ(mIt1->second.GetFlags(), Hotness::kFlagHot | Hotness::kFlagStartup);
1928 ASSERT_EQ(sort(mIt1->second.GetAnnotations()), expectedAnnotations1);
1929 // Check dex1 classes
1930 auto cIt1 = classes.find(TypeReference(dex1, dex::TypeIndex(i)));
1931 ASSERT_TRUE(cIt1 != classes.end());
1932 ASSERT_EQ(cIt1->second.GetFlags(), 0);
1933 ASSERT_EQ(sort(cIt1->second.GetAnnotations()), expectedAnnotations1);
1934 // Check dex2 methods.
1935 auto mIt2 = methods.find(MethodReference(dex2, i));
1936 ASSERT_TRUE(mIt2 != methods.end());
1937 ASSERT_EQ(mIt2->second.GetFlags(), Hotness::kFlagHot);
1938 ASSERT_EQ(sort(mIt2->second.GetAnnotations()), expectedAnnotations2);
1939 }
1940
1941 // Release the ownership as this is held by the test class;
1942 for (std::unique_ptr<const DexFile>& dex : dex_files) {
1943 UNUSED(dex.release());
1944 }
1945 }
1946
1947 // Verify we can merge 2 previously flatten data.
TEST_F(ProfileCompilationInfoTest,MergeFlattenData)1948 TEST_F(ProfileCompilationInfoTest, MergeFlattenData) {
1949 // Setup test data: two profiles with different content which will be used
1950 // to extract FlattenProfileData, later to be merged.
1951 ProfileCompilationInfo info1;
1952 ProfileCompilationInfo info2;
1953
1954 ProfileSampleAnnotation psa1("test1");
1955 ProfileSampleAnnotation psa2("test2");
1956
1957 for (uint16_t i = 0; i < 10; i++) {
1958 // Add dex1 data with different annotations so that we can check the annotation count.
1959 ASSERT_TRUE(AddMethod(&info1, dex1, /*method_idx=*/ i, Hotness::kFlagHot, psa1));
1960 ASSERT_TRUE(AddClass(&info2, dex1, dex::TypeIndex(i), psa1));
1961 ASSERT_TRUE(AddMethod(&info1, dex1, /*method_idx=*/ i, Hotness::kFlagStartup, psa2));
1962 ASSERT_TRUE(AddClass(&info1, dex1, dex::TypeIndex(i), psa2));
1963 ASSERT_TRUE(AddMethod(i % 2 == 0 ? &info1 : &info2, dex2,
1964 /*method_idx=*/ i,
1965 Hotness::kFlagHot,
1966 psa2));
1967 }
1968
1969 std::vector<std::unique_ptr<const DexFile>> dex_files;
1970 dex_files.push_back(std::unique_ptr<const DexFile>(dex1));
1971 dex_files.push_back(std::unique_ptr<const DexFile>(dex2));
1972
1973 // Run the test: extract the data for dex1 and dex2 and then merge it into
1974 std::unique_ptr<FlattenProfileData> flattenProfileData1 = info1.ExtractProfileData(dex_files);
1975 std::unique_ptr<FlattenProfileData> flattenProfileData2 = info2.ExtractProfileData(dex_files);
1976
1977 flattenProfileData1->MergeData(*flattenProfileData2);
1978 // Check the results
1979 ASSERT_EQ(flattenProfileData1->GetMaxAggregationForMethods(), 2u);
1980 ASSERT_EQ(flattenProfileData1->GetMaxAggregationForClasses(), 2u);
1981
1982 const SafeMap<MethodReference, ItemMetadata>& methods = flattenProfileData1->GetMethodData();
1983 const SafeMap<TypeReference, ItemMetadata>& classes = flattenProfileData1->GetClassData();
1984 ASSERT_EQ(methods.size(), 20u); // 10 methods in dex1, 10 in dex2
1985 ASSERT_EQ(classes.size(), 10u); // 10 methods in dex1
1986
1987 std::list<ProfileSampleAnnotation> expectedAnnotations1({psa1, psa2});
1988 std::list<ProfileSampleAnnotation> expectedAnnotations2({psa2});
1989 for (uint16_t i = 0; i < 10; i++) {
1990 // Check dex1 methods.
1991 auto mIt1 = methods.find(MethodReference(dex1, i));
1992 ASSERT_TRUE(mIt1 != methods.end());
1993 ASSERT_EQ(mIt1->second.GetFlags(), Hotness::kFlagHot | Hotness::kFlagStartup);
1994 ASSERT_EQ(sort(mIt1->second.GetAnnotations()), expectedAnnotations1);
1995 // Check dex1 classes
1996 auto cIt1 = classes.find(TypeReference(dex1, dex::TypeIndex(i)));
1997 ASSERT_TRUE(cIt1 != classes.end());
1998 ASSERT_EQ(cIt1->second.GetFlags(), 0);
1999 ASSERT_EQ(sort(cIt1->second.GetAnnotations()).size(), expectedAnnotations1.size());
2000 ASSERT_EQ(sort(cIt1->second.GetAnnotations()), expectedAnnotations1);
2001 // Check dex2 methods.
2002 auto mIt2 = methods.find(MethodReference(dex2, i));
2003 ASSERT_TRUE(mIt2 != methods.end());
2004 ASSERT_EQ(mIt2->second.GetFlags(), Hotness::kFlagHot);
2005 ASSERT_EQ(sort(mIt2->second.GetAnnotations()), expectedAnnotations2);
2006 }
2007
2008 // Release the ownership as this is held by the test class;
2009 for (std::unique_ptr<const DexFile>& dex : dex_files) {
2010 UNUSED(dex.release());
2011 }
2012 }
2013
2014 } // namespace art
2015