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