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