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 19 #include "base/unix_file/fd_file.h" 20 #include "art_method-inl.h" 21 #include "class_linker-inl.h" 22 #include "common_runtime_test.h" 23 #include "dex_file.h" 24 #include "method_reference.h" 25 #include "mirror/class-inl.h" 26 #include "mirror/class_loader.h" 27 #include "handle_scope-inl.h" 28 #include "linear_alloc.h" 29 #include "jit/profile_compilation_info.h" 30 #include "scoped_thread_state_change-inl.h" 31 32 namespace art { 33 34 class ProfileCompilationInfoTest : public CommonRuntimeTest { 35 public: 36 void PostRuntimeCreate() OVERRIDE { 37 arena_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool())); 38 } 39 40 protected: 41 std::vector<ArtMethod*> GetVirtualMethods(jobject class_loader, 42 const std::string& clazz) { 43 ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); 44 Thread* self = Thread::Current(); 45 ScopedObjectAccess soa(self); 46 StackHandleScope<1> hs(self); 47 Handle<mirror::ClassLoader> h_loader( 48 hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader())); 49 mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader); 50 51 const auto pointer_size = class_linker->GetImagePointerSize(); 52 std::vector<ArtMethod*> methods; 53 for (auto& m : klass->GetVirtualMethods(pointer_size)) { 54 methods.push_back(&m); 55 } 56 return methods; 57 } 58 59 bool AddMethod(const std::string& dex_location, 60 uint32_t checksum, 61 uint16_t method_index, 62 ProfileCompilationInfo* info) { 63 return info->AddMethodIndex(dex_location, checksum, method_index); 64 } 65 66 bool AddMethod(const std::string& dex_location, 67 uint32_t checksum, 68 uint16_t method_index, 69 const ProfileCompilationInfo::OfflineProfileMethodInfo& pmi, 70 ProfileCompilationInfo* info) { 71 return info->AddMethod(dex_location, checksum, method_index, pmi); 72 } 73 74 bool AddClass(const std::string& dex_location, 75 uint32_t checksum, 76 uint16_t class_index, 77 ProfileCompilationInfo* info) { 78 return info->AddMethodIndex(dex_location, checksum, class_index); 79 } 80 81 uint32_t GetFd(const ScratchFile& file) { 82 return static_cast<uint32_t>(file.GetFd()); 83 } 84 85 bool SaveProfilingInfo( 86 const std::string& filename, 87 const std::vector<ArtMethod*>& methods, 88 const std::set<DexCacheResolvedClasses>& resolved_classes) { 89 ProfileCompilationInfo info; 90 std::vector<ProfileMethodInfo> profile_methods; 91 ScopedObjectAccess soa(Thread::Current()); 92 for (ArtMethod* method : methods) { 93 profile_methods.emplace_back(method->GetDexFile(), method->GetDexMethodIndex()); 94 } 95 if (!info.AddMethodsAndClasses(profile_methods, resolved_classes)) { 96 return false; 97 } 98 if (info.GetNumberOfMethods() != profile_methods.size()) { 99 return false; 100 } 101 ProfileCompilationInfo file_profile; 102 if (!file_profile.Load(filename, false)) { 103 return false; 104 } 105 if (!info.MergeWith(file_profile)) { 106 return false; 107 } 108 109 return info.Save(filename, nullptr); 110 } 111 112 // Saves the given art methods to a profile backed by 'filename' and adds 113 // some fake inline caches to it. The added inline caches are returned in 114 // the out map `profile_methods_map`. 115 bool SaveProfilingInfoWithFakeInlineCaches( 116 const std::string& filename, 117 const std::vector<ArtMethod*>& methods, 118 /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) { 119 ProfileCompilationInfo info; 120 std::vector<ProfileMethodInfo> profile_methods; 121 ScopedObjectAccess soa(Thread::Current()); 122 for (ArtMethod* method : methods) { 123 std::vector<ProfileMethodInfo::ProfileInlineCache> caches; 124 // Monomorphic 125 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) { 126 std::vector<ProfileMethodInfo::ProfileClassReference> classes; 127 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0)); 128 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); 129 } 130 // Polymorphic 131 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) { 132 std::vector<ProfileMethodInfo::ProfileClassReference> classes; 133 for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) { 134 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); 135 } 136 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); 137 } 138 // Megamorphic 139 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) { 140 std::vector<ProfileMethodInfo::ProfileClassReference> classes; 141 for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) { 142 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k)); 143 } 144 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes); 145 } 146 // Missing types 147 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) { 148 std::vector<ProfileMethodInfo::ProfileClassReference> classes; 149 caches.emplace_back(dex_pc, /*is_missing_types*/true, classes); 150 } 151 ProfileMethodInfo pmi(method->GetDexFile(), method->GetDexMethodIndex(), caches); 152 profile_methods.push_back(pmi); 153 profile_methods_map->Put(method, pmi); 154 } 155 156 if (!info.AddMethodsAndClasses(profile_methods, std::set<DexCacheResolvedClasses>())) { 157 return false; 158 } 159 if (info.GetNumberOfMethods() != profile_methods.size()) { 160 return false; 161 } 162 return info.Save(filename, nullptr); 163 } 164 165 // Creates an inline cache which will be destructed at the end of the test. 166 ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() { 167 used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap( 168 std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile))); 169 return used_inline_caches.back().get(); 170 } 171 172 ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo( 173 const ProfileMethodInfo& pmi) { 174 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); 175 ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(ic_map); 176 SafeMap<DexFile*, uint8_t> dex_map; // dex files to profile index 177 for (const auto& inline_cache : pmi.inline_caches) { 178 ProfileCompilationInfo::DexPcData& dex_pc_data = 179 ic_map->FindOrAdd( 180 inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(arena_.get()))->second; 181 if (inline_cache.is_missing_types) { 182 dex_pc_data.SetIsMissingTypes(); 183 } 184 for (const auto& class_ref : inline_cache.classes) { 185 uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file), 186 static_cast<uint8_t>(dex_map.size()))->second; 187 dex_pc_data.AddClass(dex_profile_index, class_ref.type_index); 188 if (dex_profile_index >= offline_pmi.dex_references.size()) { 189 // This is a new dex. 190 const std::string& dex_key = ProfileCompilationInfo::GetProfileDexFileKey( 191 class_ref.dex_file->GetLocation()); 192 offline_pmi.dex_references.emplace_back(dex_key, 193 class_ref.dex_file->GetLocationChecksum()); 194 } 195 } 196 } 197 return offline_pmi; 198 } 199 200 // Creates an offline profile used for testing inline caches. 201 ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo() { 202 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); 203 // Monomorphic 204 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) { 205 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); 206 dex_pc_data.AddClass(0, dex::TypeIndex(0)); 207 ic_map->Put(dex_pc, dex_pc_data); 208 } 209 // Polymorphic 210 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) { 211 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); 212 dex_pc_data.AddClass(0, dex::TypeIndex(0)); 213 dex_pc_data.AddClass(1, dex::TypeIndex(1)); 214 dex_pc_data.AddClass(2, dex::TypeIndex(2)); 215 216 ic_map->Put(dex_pc, dex_pc_data); 217 } 218 // Megamorphic 219 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) { 220 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); 221 dex_pc_data.SetIsMegamorphic(); 222 ic_map->Put(dex_pc, dex_pc_data); 223 } 224 // Missing types 225 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) { 226 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); 227 dex_pc_data.SetIsMissingTypes(); 228 ic_map->Put(dex_pc, dex_pc_data); 229 } 230 231 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map); 232 233 pmi.dex_references.emplace_back("dex_location1", /* checksum */1); 234 pmi.dex_references.emplace_back("dex_location2", /* checksum */2); 235 pmi.dex_references.emplace_back("dex_location3", /* checksum */3); 236 237 return pmi; 238 } 239 240 void MakeMegamorphic(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) { 241 ProfileCompilationInfo::InlineCacheMap* ic_map = 242 const_cast<ProfileCompilationInfo::InlineCacheMap*>(pmi->inline_caches); 243 for (auto it : *ic_map) { 244 for (uint16_t k = 0; k <= 2 * InlineCache::kIndividualCacheSize; k++) { 245 it.second.AddClass(0, dex::TypeIndex(k)); 246 } 247 } 248 } 249 250 void SetIsMissingTypes(/*out*/ProfileCompilationInfo::OfflineProfileMethodInfo* pmi) { 251 ProfileCompilationInfo::InlineCacheMap* ic_map = 252 const_cast<ProfileCompilationInfo::InlineCacheMap*>(pmi->inline_caches); 253 for (auto it : *ic_map) { 254 it.second.SetIsMissingTypes(); 255 } 256 } 257 258 // Cannot sizeof the actual arrays so hard code the values here. 259 // They should not change anyway. 260 static constexpr int kProfileMagicSize = 4; 261 static constexpr int kProfileVersionSize = 4; 262 263 std::unique_ptr<ArenaAllocator> arena_; 264 265 // Cache of inline caches generated during tests. 266 // This makes it easier to pass data between different utilities and ensure that 267 // caches are destructed at the end of the test. 268 std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches; 269 }; 270 271 TEST_F(ProfileCompilationInfoTest, SaveArtMethods) { 272 ScratchFile profile; 273 274 Thread* self = Thread::Current(); 275 jobject class_loader; 276 { 277 ScopedObjectAccess soa(self); 278 class_loader = LoadDex("ProfileTestMultiDex"); 279 } 280 ASSERT_NE(class_loader, nullptr); 281 282 // Save virtual methods from Main. 283 std::set<DexCacheResolvedClasses> resolved_classes; 284 std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;"); 285 ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), main_methods, resolved_classes)); 286 287 // Check that what we saved is in the profile. 288 ProfileCompilationInfo info1; 289 ASSERT_TRUE(info1.Load(GetFd(profile))); 290 ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size()); 291 { 292 ScopedObjectAccess soa(self); 293 for (ArtMethod* m : main_methods) { 294 ASSERT_TRUE(info1.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); 295 } 296 } 297 298 // Save virtual methods from Second. 299 std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;"); 300 ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), second_methods, resolved_classes)); 301 302 // Check that what we saved is in the profile (methods form Main and Second). 303 ProfileCompilationInfo info2; 304 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 305 ASSERT_TRUE(info2.Load(GetFd(profile))); 306 ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size()); 307 { 308 ScopedObjectAccess soa(self); 309 for (ArtMethod* m : main_methods) { 310 ASSERT_TRUE(info2.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); 311 } 312 for (ArtMethod* m : second_methods) { 313 ASSERT_TRUE(info2.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); 314 } 315 } 316 } 317 318 TEST_F(ProfileCompilationInfoTest, SaveFd) { 319 ScratchFile profile; 320 321 ProfileCompilationInfo saved_info; 322 // Save a few methods. 323 for (uint16_t i = 0; i < 10; i++) { 324 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info)); 325 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info)); 326 } 327 ASSERT_TRUE(saved_info.Save(GetFd(profile))); 328 ASSERT_EQ(0, profile.GetFile()->Flush()); 329 330 // Check that we get back what we saved. 331 ProfileCompilationInfo loaded_info; 332 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 333 ASSERT_TRUE(loaded_info.Load(GetFd(profile))); 334 ASSERT_TRUE(loaded_info.Equals(saved_info)); 335 336 // Save more methods. 337 for (uint16_t i = 0; i < 100; i++) { 338 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info)); 339 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info)); 340 ASSERT_TRUE(AddMethod("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info)); 341 } 342 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 343 ASSERT_TRUE(saved_info.Save(GetFd(profile))); 344 ASSERT_EQ(0, profile.GetFile()->Flush()); 345 346 // Check that we get back everything we saved. 347 ProfileCompilationInfo loaded_info2; 348 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 349 ASSERT_TRUE(loaded_info2.Load(GetFd(profile))); 350 ASSERT_TRUE(loaded_info2.Equals(saved_info)); 351 } 352 353 TEST_F(ProfileCompilationInfoTest, AddMethodsAndClassesFail) { 354 ScratchFile profile; 355 356 ProfileCompilationInfo info; 357 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info)); 358 // Trying to add info for an existing file but with a different checksum. 359 ASSERT_FALSE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info)); 360 } 361 362 TEST_F(ProfileCompilationInfoTest, MergeFail) { 363 ScratchFile profile; 364 365 ProfileCompilationInfo info1; 366 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1)); 367 // Use the same file, change the checksum. 368 ProfileCompilationInfo info2; 369 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2)); 370 371 ASSERT_FALSE(info1.MergeWith(info2)); 372 } 373 374 TEST_F(ProfileCompilationInfoTest, SaveMaxMethods) { 375 ScratchFile profile; 376 377 ProfileCompilationInfo saved_info; 378 // Save the maximum number of methods 379 for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) { 380 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info)); 381 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info)); 382 } 383 // Save the maximum number of classes 384 for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) { 385 ASSERT_TRUE(AddClass("dex_location1", /* checksum */ 1, /* class_idx */ i, &saved_info)); 386 ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, /* class_idx */ i, &saved_info)); 387 } 388 389 ASSERT_TRUE(saved_info.Save(GetFd(profile))); 390 ASSERT_EQ(0, profile.GetFile()->Flush()); 391 392 // Check that we get back what we saved. 393 ProfileCompilationInfo loaded_info; 394 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 395 ASSERT_TRUE(loaded_info.Load(GetFd(profile))); 396 ASSERT_TRUE(loaded_info.Equals(saved_info)); 397 } 398 399 TEST_F(ProfileCompilationInfoTest, SaveEmpty) { 400 ScratchFile profile; 401 402 ProfileCompilationInfo saved_info; 403 ASSERT_TRUE(saved_info.Save(GetFd(profile))); 404 ASSERT_EQ(0, profile.GetFile()->Flush()); 405 406 // Check that we get back what we saved. 407 ProfileCompilationInfo loaded_info; 408 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 409 ASSERT_TRUE(loaded_info.Load(GetFd(profile))); 410 ASSERT_TRUE(loaded_info.Equals(saved_info)); 411 } 412 413 TEST_F(ProfileCompilationInfoTest, LoadEmpty) { 414 ScratchFile profile; 415 416 ProfileCompilationInfo empty_info; 417 418 ProfileCompilationInfo loaded_info; 419 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 420 ASSERT_TRUE(loaded_info.Load(GetFd(profile))); 421 ASSERT_TRUE(loaded_info.Equals(empty_info)); 422 } 423 424 TEST_F(ProfileCompilationInfoTest, BadMagic) { 425 ScratchFile profile; 426 uint8_t buffer[] = { 1, 2, 3, 4 }; 427 ASSERT_TRUE(profile.GetFile()->WriteFully(buffer, sizeof(buffer))); 428 ProfileCompilationInfo loaded_info; 429 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 430 ASSERT_FALSE(loaded_info.Load(GetFd(profile))); 431 } 432 433 TEST_F(ProfileCompilationInfoTest, BadVersion) { 434 ScratchFile profile; 435 436 ASSERT_TRUE(profile.GetFile()->WriteFully( 437 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize)); 438 uint8_t version[] = { 'v', 'e', 'r', 's', 'i', 'o', 'n' }; 439 ASSERT_TRUE(profile.GetFile()->WriteFully(version, sizeof(version))); 440 ASSERT_EQ(0, profile.GetFile()->Flush()); 441 442 ProfileCompilationInfo loaded_info; 443 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 444 ASSERT_FALSE(loaded_info.Load(GetFd(profile))); 445 } 446 447 TEST_F(ProfileCompilationInfoTest, Incomplete) { 448 ScratchFile profile; 449 ASSERT_TRUE(profile.GetFile()->WriteFully( 450 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize)); 451 ASSERT_TRUE(profile.GetFile()->WriteFully( 452 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize)); 453 // Write that we have at least one line. 454 uint8_t line_number[] = { 0, 1 }; 455 ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number))); 456 ASSERT_EQ(0, profile.GetFile()->Flush()); 457 458 ProfileCompilationInfo loaded_info; 459 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 460 ASSERT_FALSE(loaded_info.Load(GetFd(profile))); 461 } 462 463 TEST_F(ProfileCompilationInfoTest, TooLongDexLocation) { 464 ScratchFile profile; 465 ASSERT_TRUE(profile.GetFile()->WriteFully( 466 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize)); 467 ASSERT_TRUE(profile.GetFile()->WriteFully( 468 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize)); 469 // Write that we have at least one line. 470 uint8_t line_number[] = { 0, 1 }; 471 ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number))); 472 473 // dex_location_size, methods_size, classes_size, checksum. 474 // Dex location size is too big and should be rejected. 475 uint8_t line[] = { 255, 255, 0, 1, 0, 1, 0, 0, 0, 0 }; 476 ASSERT_TRUE(profile.GetFile()->WriteFully(line, sizeof(line))); 477 ASSERT_EQ(0, profile.GetFile()->Flush()); 478 479 ProfileCompilationInfo loaded_info; 480 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 481 ASSERT_FALSE(loaded_info.Load(GetFd(profile))); 482 } 483 484 TEST_F(ProfileCompilationInfoTest, UnexpectedContent) { 485 ScratchFile profile; 486 487 ProfileCompilationInfo saved_info; 488 // Save the maximum number of methods 489 for (uint16_t i = 0; i < 10; i++) { 490 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info)); 491 } 492 ASSERT_TRUE(saved_info.Save(GetFd(profile))); 493 494 uint8_t random_data[] = { 1, 2, 3}; 495 ASSERT_TRUE(profile.GetFile()->WriteFully(random_data, sizeof(random_data))); 496 497 ASSERT_EQ(0, profile.GetFile()->Flush()); 498 499 // Check that we fail because of unexpected data at the end of the file. 500 ProfileCompilationInfo loaded_info; 501 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 502 ASSERT_FALSE(loaded_info.Load(GetFd(profile))); 503 } 504 505 TEST_F(ProfileCompilationInfoTest, SaveInlineCaches) { 506 ScratchFile profile; 507 508 ProfileCompilationInfo saved_info; 509 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo(); 510 511 // Add methods with inline caches. 512 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { 513 // Add a method which is part of the same dex file as one of the 514 // class from the inline caches. 515 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info)); 516 // Add a method which is outside the set of dex files. 517 ASSERT_TRUE(AddMethod("dex_location4", /* checksum */ 4, method_idx, pmi, &saved_info)); 518 } 519 520 ASSERT_TRUE(saved_info.Save(GetFd(profile))); 521 ASSERT_EQ(0, profile.GetFile()->Flush()); 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", /* checksum */ 1, /* method_idx */ 3); 532 ASSERT_TRUE(loaded_pmi1 != nullptr); 533 ASSERT_TRUE(*loaded_pmi1 == pmi); 534 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 = 535 loaded_info.GetMethod("dex_location4", /* checksum */ 4, /* method_idx */ 3); 536 ASSERT_TRUE(loaded_pmi2 != nullptr); 537 ASSERT_TRUE(*loaded_pmi2 == pmi); 538 } 539 540 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCaches) { 541 ScratchFile profile; 542 543 ProfileCompilationInfo saved_info; 544 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo(); 545 546 // Add methods with inline caches. 547 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { 548 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info)); 549 } 550 551 ASSERT_TRUE(saved_info.Save(GetFd(profile))); 552 ASSERT_EQ(0, profile.GetFile()->Flush()); 553 554 // Make the inline caches megamorphic and add them to the profile again. 555 ProfileCompilationInfo saved_info_extra; 556 ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo(); 557 MakeMegamorphic(&pmi_extra); 558 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { 559 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra)); 560 } 561 562 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 563 ASSERT_TRUE(saved_info_extra.Save(GetFd(profile))); 564 ASSERT_EQ(0, profile.GetFile()->Flush()); 565 566 // Merge the profiles so that we have the same view as the file. 567 ASSERT_TRUE(saved_info.MergeWith(saved_info_extra)); 568 569 // Check that we get back what we saved. 570 ProfileCompilationInfo loaded_info; 571 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 572 ASSERT_TRUE(loaded_info.Load(GetFd(profile))); 573 574 ASSERT_TRUE(loaded_info.Equals(saved_info)); 575 576 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 = 577 loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3); 578 579 ASSERT_TRUE(loaded_pmi1 != nullptr); 580 ASSERT_TRUE(*loaded_pmi1 == pmi_extra); 581 } 582 583 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCaches) { 584 ScratchFile profile; 585 586 ProfileCompilationInfo saved_info; 587 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = GetOfflineProfileMethodInfo(); 588 589 // Add methods with inline caches. 590 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { 591 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info)); 592 } 593 594 ASSERT_TRUE(saved_info.Save(GetFd(profile))); 595 ASSERT_EQ(0, profile.GetFile()->Flush()); 596 597 // Make some inline caches megamorphic and add them to the profile again. 598 ProfileCompilationInfo saved_info_extra; 599 ProfileCompilationInfo::OfflineProfileMethodInfo pmi_extra = GetOfflineProfileMethodInfo(); 600 MakeMegamorphic(&pmi_extra); 601 for (uint16_t method_idx = 5; method_idx < 10; method_idx++) { 602 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra)); 603 } 604 605 // Mark all inline caches with missing types and add them to the profile again. 606 // This will verify that all inline caches (megamorphic or not) should be marked as missing types. 607 ProfileCompilationInfo::OfflineProfileMethodInfo missing_types = GetOfflineProfileMethodInfo(); 608 SetIsMissingTypes(&missing_types); 609 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { 610 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &saved_info_extra)); 611 } 612 613 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 614 ASSERT_TRUE(saved_info_extra.Save(GetFd(profile))); 615 ASSERT_EQ(0, profile.GetFile()->Flush()); 616 617 // Merge the profiles so that we have the same view as the file. 618 ASSERT_TRUE(saved_info.MergeWith(saved_info_extra)); 619 620 // Check that we get back what we saved. 621 ProfileCompilationInfo loaded_info; 622 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 623 ASSERT_TRUE(loaded_info.Load(GetFd(profile))); 624 625 ASSERT_TRUE(loaded_info.Equals(saved_info)); 626 627 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 = 628 loaded_info.GetMethod("dex_location1", /* checksum */ 1, /* method_idx */ 3); 629 ASSERT_TRUE(loaded_pmi1 != nullptr); 630 ASSERT_TRUE(*loaded_pmi1 == pmi_extra); 631 } 632 633 TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) { 634 ScratchFile profile; 635 636 Thread* self = Thread::Current(); 637 jobject class_loader; 638 { 639 ScopedObjectAccess soa(self); 640 class_loader = LoadDex("ProfileTestMultiDex"); 641 } 642 ASSERT_NE(class_loader, nullptr); 643 644 // Save virtual methods from Main. 645 std::set<DexCacheResolvedClasses> resolved_classes; 646 std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;"); 647 648 SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map; 649 ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches( 650 profile.GetFilename(), main_methods, &profile_methods_map)); 651 652 // Check that what we saved is in the profile. 653 ProfileCompilationInfo info; 654 ASSERT_TRUE(info.Load(GetFd(profile))); 655 ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size()); 656 { 657 ScopedObjectAccess soa(self); 658 for (ArtMethod* m : main_methods) { 659 ASSERT_TRUE(info.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()))); 660 const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second; 661 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_pmi = 662 info.GetMethod(m->GetDexFile()->GetLocation(), 663 m->GetDexFile()->GetLocationChecksum(), 664 m->GetDexMethodIndex()); 665 ASSERT_TRUE(offline_pmi != nullptr); 666 ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi = 667 ConvertProfileMethodInfo(pmi); 668 ASSERT_EQ(converted_pmi, *offline_pmi); 669 } 670 } 671 } 672 673 TEST_F(ProfileCompilationInfoTest, InvalidChecksumInInlineCache) { 674 ScratchFile profile; 675 676 ProfileCompilationInfo info; 677 ProfileCompilationInfo::OfflineProfileMethodInfo pmi1 = GetOfflineProfileMethodInfo(); 678 ProfileCompilationInfo::OfflineProfileMethodInfo pmi2 = GetOfflineProfileMethodInfo(); 679 // Modify the checksum to trigger a mismatch. 680 pmi2.dex_references[0].dex_checksum++; 681 682 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /*method_idx*/ 0, pmi1, &info)); 683 ASSERT_FALSE(AddMethod("dex_location2", /* checksum */ 2, /*method_idx*/ 0, pmi2, &info)); 684 } 685 686 // Verify that profiles behave correctly even if the methods are added in a different 687 // order and with a different dex profile indices for the dex files. 688 TEST_F(ProfileCompilationInfoTest, MergeInlineCacheTriggerReindex) { 689 ScratchFile profile; 690 691 ProfileCompilationInfo info; 692 ProfileCompilationInfo info_reindexed; 693 694 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); 695 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map); 696 pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); 697 pmi.dex_references.emplace_back("dex_location2", /* checksum */ 2); 698 for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) { 699 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); 700 dex_pc_data.AddClass(0, dex::TypeIndex(0)); 701 dex_pc_data.AddClass(1, dex::TypeIndex(1)); 702 ic_map->Put(dex_pc, dex_pc_data); 703 } 704 705 ProfileCompilationInfo::InlineCacheMap* ic_map_reindexed = CreateInlineCacheMap(); 706 ProfileCompilationInfo::OfflineProfileMethodInfo pmi_reindexed(ic_map_reindexed); 707 pmi_reindexed.dex_references.emplace_back("dex_location2", /* checksum */ 2); 708 pmi_reindexed.dex_references.emplace_back("dex_location1", /* checksum */ 1); 709 for (uint16_t dex_pc = 1; dex_pc < 5; dex_pc++) { 710 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); 711 dex_pc_data.AddClass(1, dex::TypeIndex(0)); 712 dex_pc_data.AddClass(0, dex::TypeIndex(1)); 713 ic_map_reindexed->Put(dex_pc, dex_pc_data); 714 } 715 716 // Profile 1 and Profile 2 get the same methods but in different order. 717 // This will trigger a different dex numbers. 718 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { 719 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, method_idx, pmi, &info)); 720 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, method_idx, pmi, &info)); 721 } 722 723 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { 724 ASSERT_TRUE(AddMethod( 725 "dex_location2", /* checksum */ 2, method_idx, pmi_reindexed, &info_reindexed)); 726 ASSERT_TRUE(AddMethod( 727 "dex_location1", /* checksum */ 1, method_idx, pmi_reindexed, &info_reindexed)); 728 } 729 730 ProfileCompilationInfo info_backup; 731 info_backup.MergeWith(info); 732 ASSERT_TRUE(info.MergeWith(info_reindexed)); 733 // Merging should have no effect as we're adding the exact same stuff. 734 ASSERT_TRUE(info.Equals(info_backup)); 735 for (uint16_t method_idx = 0; method_idx < 10; method_idx++) { 736 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi1 = 737 info.GetMethod("dex_location1", /* checksum */ 1, method_idx); 738 ASSERT_TRUE(loaded_pmi1 != nullptr); 739 ASSERT_TRUE(*loaded_pmi1 == pmi); 740 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> loaded_pmi2 = 741 info.GetMethod("dex_location2", /* checksum */ 2, method_idx); 742 ASSERT_TRUE(loaded_pmi2 != nullptr); 743 ASSERT_TRUE(*loaded_pmi2 == pmi); 744 } 745 } 746 747 TEST_F(ProfileCompilationInfoTest, AddMoreDexFileThanLimit) { 748 ProfileCompilationInfo info; 749 // Save a few methods. 750 for (uint16_t i = 0; i < std::numeric_limits<uint8_t>::max(); i++) { 751 std::string dex_location = std::to_string(i); 752 ASSERT_TRUE(AddMethod(dex_location, /* checksum */ 1, /* method_idx */ i, &info)); 753 } 754 // We only support at most 255 dex files. 755 ASSERT_FALSE(AddMethod( 756 /*dex_location*/ "256", /* checksum */ 1, /* method_idx */ 0, &info)); 757 } 758 759 TEST_F(ProfileCompilationInfoTest, MegamorphicInlineCachesMerge) { 760 // Create a megamorphic inline cache. 761 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); 762 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map); 763 pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); 764 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); 765 dex_pc_data.SetIsMegamorphic(); 766 ic_map->Put(/*dex_pc*/ 0, dex_pc_data); 767 768 ProfileCompilationInfo info_megamorphic; 769 ASSERT_TRUE(AddMethod("dex_location1", 770 /*checksum*/ 1, 771 /*method_idx*/ 0, 772 pmi, 773 &info_megamorphic)); 774 775 // Create a profile with no inline caches (for the same method). 776 ProfileCompilationInfo info_no_inline_cache; 777 ASSERT_TRUE(AddMethod("dex_location1", 778 /*checksum*/ 1, 779 /*method_idx*/ 0, 780 &info_no_inline_cache)); 781 782 // Merge the megamorphic cache into the empty one. 783 ASSERT_TRUE(info_no_inline_cache.MergeWith(info_megamorphic)); 784 ScratchFile profile; 785 // Saving profile should work without crashing (b/35644850). 786 ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile))); 787 } 788 789 TEST_F(ProfileCompilationInfoTest, MissingTypesInlineCachesMerge) { 790 // Create an inline cache with missing types 791 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); 792 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map); 793 pmi.dex_references.emplace_back("dex_location1", /* checksum */ 1); 794 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); 795 dex_pc_data.SetIsMissingTypes(); 796 ic_map->Put(/*dex_pc*/ 0, dex_pc_data); 797 798 ProfileCompilationInfo info_megamorphic; 799 ASSERT_TRUE(AddMethod("dex_location1", 800 /*checksum*/ 1, 801 /*method_idx*/ 0, 802 pmi, 803 &info_megamorphic)); 804 805 // Create a profile with no inline caches (for the same method). 806 ProfileCompilationInfo info_no_inline_cache; 807 ASSERT_TRUE(AddMethod("dex_location1", 808 /*checksum*/ 1, 809 /*method_idx*/ 0, 810 &info_no_inline_cache)); 811 812 // Merge the missing type cache into the empty one. 813 // Everything should be saved without errors. 814 ASSERT_TRUE(info_no_inline_cache.MergeWith(info_megamorphic)); 815 ScratchFile profile; 816 ASSERT_TRUE(info_no_inline_cache.Save(GetFd(profile))); 817 } 818 819 TEST_F(ProfileCompilationInfoTest, LoadShouldClearExistingDataFromProfiles) { 820 ScratchFile profile; 821 822 ProfileCompilationInfo saved_info; 823 // Save a few methods. 824 for (uint16_t i = 0; i < 10; i++) { 825 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info)); 826 } 827 ASSERT_TRUE(saved_info.Save(GetFd(profile))); 828 ASSERT_EQ(0, profile.GetFile()->Flush()); 829 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 830 831 // Add a bunch of methods to test_info. 832 ProfileCompilationInfo test_info; 833 for (uint16_t i = 0; i < 10; i++) { 834 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &test_info)); 835 } 836 837 // Attempt to load the saved profile into test_info. 838 // This should fail since the test_info already contains data and the load would overwrite it. 839 ASSERT_FALSE(test_info.Load(GetFd(profile))); 840 } 841 } // namespace art 842