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