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