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 "art_method-inl.h" 20 #include "base/unix_file/fd_file.h" 21 #include "common_runtime_test.h" 22 #include "exec_utils.h" 23 #include "jit/profile_compilation_info.h" 24 #include "linear_alloc.h" 25 #include "mirror/class-inl.h" 26 #include "obj_ptr-inl.h" 27 #include "profile_assistant.h" 28 #include "scoped_thread_state_change-inl.h" 29 #include "utils.h" 30 31 namespace art { 32 33 static constexpr size_t kMaxMethodIds = 65535; 34 35 class ProfileAssistantTest : public CommonRuntimeTest { 36 public: 37 void PostRuntimeCreate() OVERRIDE { 38 arena_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool())); 39 } 40 41 protected: 42 void SetupProfile(const std::string& id, 43 uint32_t checksum, 44 uint16_t number_of_methods, 45 uint16_t number_of_classes, 46 const ScratchFile& profile, 47 ProfileCompilationInfo* info, 48 uint16_t start_method_index = 0, 49 bool reverse_dex_write_order = false) { 50 std::string dex_location1 = "location1" + id; 51 uint32_t dex_location_checksum1 = checksum; 52 std::string dex_location2 = "location2" + id; 53 uint32_t dex_location_checksum2 = 10 * checksum; 54 for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) { 55 // reverse_dex_write_order controls the order in which the dex files will be added to 56 // the profile and thus written to disk. 57 ProfileCompilationInfo::OfflineProfileMethodInfo pmi = 58 GetOfflineProfileMethodInfo(dex_location1, dex_location_checksum1, 59 dex_location2, dex_location_checksum2); 60 if (reverse_dex_write_order) { 61 ASSERT_TRUE(info->AddMethod(dex_location2, dex_location_checksum2, i, kMaxMethodIds, pmi)); 62 ASSERT_TRUE(info->AddMethod(dex_location1, dex_location_checksum1, i, kMaxMethodIds, pmi)); 63 } else { 64 ASSERT_TRUE(info->AddMethod(dex_location1, dex_location_checksum1, i, kMaxMethodIds, pmi)); 65 ASSERT_TRUE(info->AddMethod(dex_location2, dex_location_checksum2, i, kMaxMethodIds, pmi)); 66 } 67 } 68 for (uint16_t i = 0; i < number_of_classes; i++) { 69 ASSERT_TRUE(info->AddClassIndex(dex_location1, 70 dex_location_checksum1, 71 dex::TypeIndex(i), 72 kMaxMethodIds)); 73 } 74 75 ASSERT_TRUE(info->Save(GetFd(profile))); 76 ASSERT_EQ(0, profile.GetFile()->Flush()); 77 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 78 } 79 80 void SetupBasicProfile(const std::string& id, 81 uint32_t checksum, 82 uint16_t number_of_methods, 83 const std::vector<uint32_t> hot_methods, 84 const std::vector<uint32_t> startup_methods, 85 const std::vector<uint32_t> post_startup_methods, 86 const ScratchFile& profile, 87 ProfileCompilationInfo* info) { 88 std::string dex_location = "location1" + id; 89 using Hotness = ProfileCompilationInfo::MethodHotness; 90 for (uint32_t idx : hot_methods) { 91 info->AddMethodIndex(Hotness::kFlagHot, dex_location, checksum, idx, number_of_methods); 92 } 93 for (uint32_t idx : startup_methods) { 94 info->AddMethodIndex(Hotness::kFlagStartup, dex_location, checksum, idx, number_of_methods); 95 } 96 for (uint32_t idx : post_startup_methods) { 97 info->AddMethodIndex(Hotness::kFlagPostStartup, 98 dex_location, 99 checksum, 100 idx, 101 number_of_methods); 102 } 103 ASSERT_TRUE(info->Save(GetFd(profile))); 104 ASSERT_EQ(0, profile.GetFile()->Flush()); 105 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 106 } 107 108 // Creates an inline cache which will be destructed at the end of the test. 109 ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() { 110 used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap( 111 std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile))); 112 return used_inline_caches.back().get(); 113 } 114 115 ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo( 116 const std::string& dex_location1, uint32_t dex_checksum1, 117 const std::string& dex_location2, uint32_t dex_checksum2) { 118 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap(); 119 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map); 120 pmi.dex_references.emplace_back(dex_location1, dex_checksum1, kMaxMethodIds); 121 pmi.dex_references.emplace_back(dex_location2, dex_checksum2, kMaxMethodIds); 122 123 // Monomorphic 124 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) { 125 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); 126 dex_pc_data.AddClass(0, dex::TypeIndex(0)); 127 ic_map->Put(dex_pc, dex_pc_data); 128 } 129 // Polymorphic 130 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) { 131 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); 132 dex_pc_data.AddClass(0, dex::TypeIndex(0)); 133 dex_pc_data.AddClass(1, dex::TypeIndex(1)); 134 135 ic_map->Put(dex_pc, dex_pc_data); 136 } 137 // Megamorphic 138 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) { 139 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); 140 dex_pc_data.SetIsMegamorphic(); 141 ic_map->Put(dex_pc, dex_pc_data); 142 } 143 // Missing types 144 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) { 145 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get()); 146 dex_pc_data.SetIsMissingTypes(); 147 ic_map->Put(dex_pc, dex_pc_data); 148 } 149 150 return pmi; 151 } 152 153 int GetFd(const ScratchFile& file) const { 154 return static_cast<int>(file.GetFd()); 155 } 156 157 void CheckProfileInfo(ScratchFile& file, const ProfileCompilationInfo& info) { 158 ProfileCompilationInfo file_info; 159 ASSERT_TRUE(file.GetFile()->ResetOffset()); 160 ASSERT_TRUE(file_info.Load(GetFd(file))); 161 ASSERT_TRUE(file_info.Equals(info)); 162 } 163 164 std::string GetProfmanCmd() { 165 std::string file_path = GetTestAndroidRoot(); 166 file_path += "/bin/profman"; 167 if (kIsDebugBuild) { 168 file_path += "d"; 169 } 170 EXPECT_TRUE(OS::FileExists(file_path.c_str())) 171 << file_path << " should be a valid file path"; 172 return file_path; 173 } 174 175 // Runs test with given arguments. 176 int ProcessProfiles(const std::vector<int>& profiles_fd, int reference_profile_fd) { 177 std::string profman_cmd = GetProfmanCmd(); 178 std::vector<std::string> argv_str; 179 argv_str.push_back(profman_cmd); 180 for (size_t k = 0; k < profiles_fd.size(); k++) { 181 argv_str.push_back("--profile-file-fd=" + std::to_string(profiles_fd[k])); 182 } 183 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile_fd)); 184 185 std::string error; 186 return ExecAndReturnCode(argv_str, &error); 187 } 188 189 bool GenerateTestProfile(const std::string& filename) { 190 std::string profman_cmd = GetProfmanCmd(); 191 std::vector<std::string> argv_str; 192 argv_str.push_back(profman_cmd); 193 argv_str.push_back("--generate-test-profile=" + filename); 194 std::string error; 195 return ExecAndReturnCode(argv_str, &error); 196 } 197 198 bool GenerateTestProfileWithInputDex(const std::string& filename) { 199 std::string profman_cmd = GetProfmanCmd(); 200 std::vector<std::string> argv_str; 201 argv_str.push_back(profman_cmd); 202 argv_str.push_back("--generate-test-profile=" + filename); 203 argv_str.push_back("--generate-test-profile-seed=0"); 204 argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]); 205 argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]); 206 std::string error; 207 return ExecAndReturnCode(argv_str, &error); 208 } 209 210 bool CreateProfile(std::string profile_file_contents, 211 const std::string& filename, 212 const std::string& dex_location) { 213 ScratchFile class_names_file; 214 File* file = class_names_file.GetFile(); 215 EXPECT_TRUE(file->WriteFully(profile_file_contents.c_str(), profile_file_contents.length())); 216 EXPECT_EQ(0, file->Flush()); 217 EXPECT_TRUE(file->ResetOffset()); 218 std::string profman_cmd = GetProfmanCmd(); 219 std::vector<std::string> argv_str; 220 argv_str.push_back(profman_cmd); 221 argv_str.push_back("--create-profile-from=" + class_names_file.GetFilename()); 222 argv_str.push_back("--reference-profile-file=" + filename); 223 argv_str.push_back("--apk=" + dex_location); 224 argv_str.push_back("--dex-location=" + dex_location); 225 std::string error; 226 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0); 227 return true; 228 } 229 230 bool RunProfman(const std::string& filename, 231 std::vector<std::string>& extra_args, 232 std::string* output) { 233 ScratchFile output_file; 234 std::string profman_cmd = GetProfmanCmd(); 235 std::vector<std::string> argv_str; 236 argv_str.push_back(profman_cmd); 237 argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end()); 238 argv_str.push_back("--profile-file=" + filename); 239 argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]); 240 argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]); 241 argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(output_file))); 242 std::string error; 243 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0); 244 File* file = output_file.GetFile(); 245 EXPECT_EQ(0, file->Flush()); 246 EXPECT_TRUE(file->ResetOffset()); 247 int64_t length = file->GetLength(); 248 std::unique_ptr<char[]> buf(new char[length]); 249 EXPECT_EQ(file->Read(buf.get(), length, 0), length); 250 *output = std::string(buf.get(), length); 251 return true; 252 } 253 254 bool DumpClassesAndMethods(const std::string& filename, std::string* file_contents) { 255 std::vector<std::string> extra_args; 256 extra_args.push_back("--dump-classes-and-methods"); 257 return RunProfman(filename, extra_args, file_contents); 258 } 259 260 bool DumpOnly(const std::string& filename, std::string* file_contents) { 261 std::vector<std::string> extra_args; 262 extra_args.push_back("--dump-only"); 263 return RunProfman(filename, extra_args, file_contents); 264 } 265 266 bool CreateAndDump(const std::string& input_file_contents, 267 std::string* output_file_contents) { 268 ScratchFile profile_file; 269 EXPECT_TRUE(CreateProfile(input_file_contents, 270 profile_file.GetFilename(), 271 GetLibCoreDexFileNames()[0])); 272 profile_file.GetFile()->ResetOffset(); 273 EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents)); 274 return true; 275 } 276 277 mirror::Class* GetClass(jobject class_loader, const std::string& clazz) { 278 ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); 279 Thread* self = Thread::Current(); 280 ScopedObjectAccess soa(self); 281 StackHandleScope<1> hs(self); 282 Handle<mirror::ClassLoader> h_loader( 283 hs.NewHandle(ObjPtr<mirror::ClassLoader>::DownCast(self->DecodeJObject(class_loader)))); 284 return class_linker->FindClass(self, clazz.c_str(), h_loader); 285 } 286 287 ArtMethod* GetVirtualMethod(jobject class_loader, 288 const std::string& clazz, 289 const std::string& name) { 290 mirror::Class* klass = GetClass(class_loader, clazz); 291 ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); 292 const auto pointer_size = class_linker->GetImagePointerSize(); 293 ArtMethod* method = nullptr; 294 Thread* self = Thread::Current(); 295 ScopedObjectAccess soa(self); 296 for (auto& m : klass->GetVirtualMethods(pointer_size)) { 297 if (name == m.GetName()) { 298 EXPECT_TRUE(method == nullptr); 299 method = &m; 300 } 301 } 302 return method; 303 } 304 305 // Verify that given method has the expected inline caches and nothing else. 306 void AssertInlineCaches(ArtMethod* method, 307 const std::set<mirror::Class*>& expected_clases, 308 const ProfileCompilationInfo& info, 309 bool is_megamorphic, 310 bool is_missing_types) 311 REQUIRES_SHARED(Locks::mutator_lock_) { 312 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi = 313 info.GetMethod(method->GetDexFile()->GetLocation(), 314 method->GetDexFile()->GetLocationChecksum(), 315 method->GetDexMethodIndex()); 316 ASSERT_TRUE(pmi != nullptr); 317 ASSERT_EQ(pmi->inline_caches->size(), 1u); 318 const ProfileCompilationInfo::DexPcData& dex_pc_data = pmi->inline_caches->begin()->second; 319 320 ASSERT_EQ(dex_pc_data.is_megamorphic, is_megamorphic); 321 ASSERT_EQ(dex_pc_data.is_missing_types, is_missing_types); 322 ASSERT_EQ(expected_clases.size(), dex_pc_data.classes.size()); 323 size_t found = 0; 324 for (mirror::Class* it : expected_clases) { 325 for (const auto& class_ref : dex_pc_data.classes) { 326 ProfileCompilationInfo::DexReference dex_ref = 327 pmi->dex_references[class_ref.dex_profile_index]; 328 if (dex_ref.MatchesDex(&(it->GetDexFile())) && 329 class_ref.type_index == it->GetDexTypeIndex()) { 330 found++; 331 } 332 } 333 } 334 335 ASSERT_EQ(expected_clases.size(), found); 336 } 337 338 int CheckCompilationMethodPercentChange(uint16_t methods_in_cur_profile, 339 uint16_t methods_in_ref_profile) { 340 ScratchFile profile; 341 ScratchFile reference_profile; 342 std::vector<int> profile_fds({ GetFd(profile)}); 343 int reference_profile_fd = GetFd(reference_profile); 344 std::vector<uint32_t> hot_methods_cur; 345 std::vector<uint32_t> hot_methods_ref; 346 std::vector<uint32_t> empty_vector; 347 for (size_t i = 0; i < methods_in_cur_profile; ++i) { 348 hot_methods_cur.push_back(i); 349 } 350 for (size_t i = 0; i < methods_in_ref_profile; ++i) { 351 hot_methods_ref.push_back(i); 352 } 353 ProfileCompilationInfo info1; 354 uint16_t methods_in_profile = std::max(methods_in_cur_profile, methods_in_ref_profile); 355 SetupBasicProfile("p1", 1, methods_in_profile, hot_methods_cur, empty_vector, empty_vector, 356 profile, &info1); 357 ProfileCompilationInfo info2; 358 SetupBasicProfile("p1", 1, methods_in_profile, hot_methods_ref, empty_vector, empty_vector, 359 reference_profile, &info2); 360 return ProcessProfiles(profile_fds, reference_profile_fd); 361 } 362 363 int CheckCompilationClassPercentChange(uint16_t classes_in_cur_profile, 364 uint16_t classes_in_ref_profile) { 365 ScratchFile profile; 366 ScratchFile reference_profile; 367 368 std::vector<int> profile_fds({ GetFd(profile)}); 369 int reference_profile_fd = GetFd(reference_profile); 370 371 ProfileCompilationInfo info1; 372 SetupProfile("p1", 1, 0, classes_in_cur_profile, profile, &info1); 373 ProfileCompilationInfo info2; 374 SetupProfile("p1", 1, 0, classes_in_ref_profile, reference_profile, &info2); 375 return ProcessProfiles(profile_fds, reference_profile_fd); 376 } 377 378 std::unique_ptr<ArenaAllocator> arena_; 379 380 // Cache of inline caches generated during tests. 381 // This makes it easier to pass data between different utilities and ensure that 382 // caches are destructed at the end of the test. 383 std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches; 384 }; 385 386 TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { 387 ScratchFile profile1; 388 ScratchFile profile2; 389 ScratchFile reference_profile; 390 391 std::vector<int> profile_fds({ 392 GetFd(profile1), 393 GetFd(profile2)}); 394 int reference_profile_fd = GetFd(reference_profile); 395 396 const uint16_t kNumberOfMethodsToEnableCompilation = 100; 397 ProfileCompilationInfo info1; 398 SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1); 399 ProfileCompilationInfo info2; 400 SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2); 401 402 // We should advise compilation. 403 ASSERT_EQ(ProfileAssistant::kCompile, 404 ProcessProfiles(profile_fds, reference_profile_fd)); 405 // The resulting compilation info must be equal to the merge of the inputs. 406 ProfileCompilationInfo result; 407 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); 408 ASSERT_TRUE(result.Load(reference_profile_fd)); 409 410 ProfileCompilationInfo expected; 411 ASSERT_TRUE(expected.MergeWith(info1)); 412 ASSERT_TRUE(expected.MergeWith(info2)); 413 ASSERT_TRUE(expected.Equals(result)); 414 415 // The information from profiles must remain the same. 416 CheckProfileInfo(profile1, info1); 417 CheckProfileInfo(profile2, info2); 418 } 419 420 // TODO(calin): Add more tests for classes. 421 TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferencesBecauseOfClasses) { 422 ScratchFile profile1; 423 ScratchFile reference_profile; 424 425 std::vector<int> profile_fds({ 426 GetFd(profile1)}); 427 int reference_profile_fd = GetFd(reference_profile); 428 429 const uint16_t kNumberOfClassesToEnableCompilation = 100; 430 ProfileCompilationInfo info1; 431 SetupProfile("p1", 1, 0, kNumberOfClassesToEnableCompilation, profile1, &info1); 432 433 // We should advise compilation. 434 ASSERT_EQ(ProfileAssistant::kCompile, 435 ProcessProfiles(profile_fds, reference_profile_fd)); 436 // The resulting compilation info must be equal to the merge of the inputs. 437 ProfileCompilationInfo result; 438 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); 439 ASSERT_TRUE(result.Load(reference_profile_fd)); 440 441 ProfileCompilationInfo expected; 442 ASSERT_TRUE(expected.MergeWith(info1)); 443 ASSERT_TRUE(expected.Equals(result)); 444 445 // The information from profiles must remain the same. 446 CheckProfileInfo(profile1, info1); 447 } 448 449 TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) { 450 ScratchFile profile1; 451 ScratchFile profile2; 452 ScratchFile reference_profile; 453 454 std::vector<int> profile_fds({ 455 GetFd(profile1), 456 GetFd(profile2)}); 457 int reference_profile_fd = GetFd(reference_profile); 458 459 // The new profile info will contain the methods with indices 0-100. 460 const uint16_t kNumberOfMethodsToEnableCompilation = 100; 461 ProfileCompilationInfo info1; 462 SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1); 463 ProfileCompilationInfo info2; 464 SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2); 465 466 467 // The reference profile info will contain the methods with indices 50-150. 468 const uint16_t kNumberOfMethodsAlreadyCompiled = 100; 469 ProfileCompilationInfo reference_info; 470 SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, 0, reference_profile, 471 &reference_info, kNumberOfMethodsToEnableCompilation / 2); 472 473 // We should advise compilation. 474 ASSERT_EQ(ProfileAssistant::kCompile, 475 ProcessProfiles(profile_fds, reference_profile_fd)); 476 477 // The resulting compilation info must be equal to the merge of the inputs 478 ProfileCompilationInfo result; 479 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); 480 ASSERT_TRUE(result.Load(reference_profile_fd)); 481 482 ProfileCompilationInfo expected; 483 ASSERT_TRUE(expected.MergeWith(info1)); 484 ASSERT_TRUE(expected.MergeWith(info2)); 485 ASSERT_TRUE(expected.MergeWith(reference_info)); 486 ASSERT_TRUE(expected.Equals(result)); 487 488 // The information from profiles must remain the same. 489 CheckProfileInfo(profile1, info1); 490 CheckProfileInfo(profile2, info2); 491 } 492 493 TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) { 494 ScratchFile profile1; 495 ScratchFile profile2; 496 ScratchFile reference_profile; 497 498 std::vector<int> profile_fds({ 499 GetFd(profile1), 500 GetFd(profile2)}); 501 int reference_profile_fd = GetFd(reference_profile); 502 503 const uint16_t kNumberOfMethodsToSkipCompilation = 24; // Threshold is 100. 504 ProfileCompilationInfo info1; 505 SetupProfile("p1", 1, kNumberOfMethodsToSkipCompilation, 0, profile1, &info1); 506 ProfileCompilationInfo info2; 507 SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, 0, profile2, &info2); 508 509 // We should not advise compilation. 510 ASSERT_EQ(ProfileAssistant::kSkipCompilation, 511 ProcessProfiles(profile_fds, reference_profile_fd)); 512 513 // The information from profiles must remain the same. 514 ProfileCompilationInfo file_info1; 515 ASSERT_TRUE(profile1.GetFile()->ResetOffset()); 516 ASSERT_TRUE(file_info1.Load(GetFd(profile1))); 517 ASSERT_TRUE(file_info1.Equals(info1)); 518 519 ProfileCompilationInfo file_info2; 520 ASSERT_TRUE(profile2.GetFile()->ResetOffset()); 521 ASSERT_TRUE(file_info2.Load(GetFd(profile2))); 522 ASSERT_TRUE(file_info2.Equals(info2)); 523 524 // Reference profile files must remain empty. 525 ASSERT_EQ(0, reference_profile.GetFile()->GetLength()); 526 527 // The information from profiles must remain the same. 528 CheckProfileInfo(profile1, info1); 529 CheckProfileInfo(profile2, info2); 530 } 531 532 TEST_F(ProfileAssistantTest, DoNotAdviseCompilationMethodPercentage) { 533 const uint16_t kNumberOfMethodsInRefProfile = 6000; 534 const uint16_t kNumberOfMethodsInCurProfile = 6100; // Threshold is 2%. 535 // We should not advise compilation. 536 ASSERT_EQ(ProfileAssistant::kSkipCompilation, 537 CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile, 538 kNumberOfMethodsInRefProfile)); 539 } 540 541 TEST_F(ProfileAssistantTest, ShouldAdviseCompilationMethodPercentage) { 542 const uint16_t kNumberOfMethodsInRefProfile = 6000; 543 const uint16_t kNumberOfMethodsInCurProfile = 6200; // Threshold is 2%. 544 // We should advise compilation. 545 ASSERT_EQ(ProfileAssistant::kCompile, 546 CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile, 547 kNumberOfMethodsInRefProfile)); 548 } 549 550 TEST_F(ProfileAssistantTest, DoNotdviseCompilationClassPercentage) { 551 const uint16_t kNumberOfClassesInRefProfile = 6000; 552 const uint16_t kNumberOfClassesInCurProfile = 6110; // Threshold is 2%. 553 // We should not advise compilation. 554 ASSERT_EQ(ProfileAssistant::kSkipCompilation, 555 CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile, 556 kNumberOfClassesInRefProfile)); 557 } 558 559 TEST_F(ProfileAssistantTest, ShouldAdviseCompilationClassPercentage) { 560 const uint16_t kNumberOfClassesInRefProfile = 6000; 561 const uint16_t kNumberOfClassesInCurProfile = 6120; // Threshold is 2%. 562 // We should advise compilation. 563 ASSERT_EQ(ProfileAssistant::kCompile, 564 CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile, 565 kNumberOfClassesInRefProfile)); 566 } 567 568 TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) { 569 ScratchFile profile1; 570 ScratchFile profile2; 571 ScratchFile reference_profile; 572 573 std::vector<int> profile_fds({ 574 GetFd(profile1), 575 GetFd(profile2)}); 576 int reference_profile_fd = GetFd(reference_profile); 577 578 const uint16_t kNumberOfMethodsToEnableCompilation = 100; 579 // Assign different hashes for the same dex file. This will make merging of information to fail. 580 ProfileCompilationInfo info1; 581 SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1); 582 ProfileCompilationInfo info2; 583 SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2); 584 585 // We should fail processing. 586 ASSERT_EQ(ProfileAssistant::kErrorBadProfiles, 587 ProcessProfiles(profile_fds, reference_profile_fd)); 588 589 // The information from profiles must remain the same. 590 CheckProfileInfo(profile1, info1); 591 CheckProfileInfo(profile2, info2); 592 593 // Reference profile files must still remain empty. 594 ASSERT_EQ(0, reference_profile.GetFile()->GetLength()); 595 } 596 597 TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) { 598 ScratchFile profile1; 599 ScratchFile reference_profile; 600 601 std::vector<int> profile_fds({ 602 GetFd(profile1)}); 603 int reference_profile_fd = GetFd(reference_profile); 604 605 const uint16_t kNumberOfMethodsToEnableCompilation = 100; 606 // Assign different hashes for the same dex file. This will make merging of information to fail. 607 ProfileCompilationInfo info1; 608 SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1); 609 ProfileCompilationInfo reference_info; 610 SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, 0, reference_profile, &reference_info); 611 612 // We should not advise compilation. 613 ASSERT_TRUE(profile1.GetFile()->ResetOffset()); 614 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); 615 ASSERT_EQ(ProfileAssistant::kErrorBadProfiles, 616 ProcessProfiles(profile_fds, reference_profile_fd)); 617 618 // The information from profiles must remain the same. 619 CheckProfileInfo(profile1, info1); 620 } 621 622 TEST_F(ProfileAssistantTest, TestProfileGeneration) { 623 ScratchFile profile; 624 // Generate a test profile. 625 GenerateTestProfile(profile.GetFilename()); 626 627 // Verify that the generated profile is valid and can be loaded. 628 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 629 ProfileCompilationInfo info; 630 ASSERT_TRUE(info.Load(GetFd(profile))); 631 } 632 633 TEST_F(ProfileAssistantTest, TestProfileGenerationWithIndexDex) { 634 ScratchFile profile; 635 // Generate a test profile passing in a dex file as reference. 636 GenerateTestProfileWithInputDex(profile.GetFilename()); 637 638 // Verify that the generated profile is valid and can be loaded. 639 ASSERT_TRUE(profile.GetFile()->ResetOffset()); 640 ProfileCompilationInfo info; 641 ASSERT_TRUE(info.Load(GetFd(profile))); 642 } 643 644 TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) { 645 // Class names put here need to be in sorted order. 646 std::vector<std::string> class_names = { 647 "HLjava/lang/Object;-><init>()V", 648 "Ljava/lang/Comparable;", 649 "Ljava/lang/Math;", 650 "Ljava/lang/Object;", 651 "SPLjava/lang/Comparable;->compareTo(Ljava/lang/Object;)I", 652 }; 653 std::string file_contents; 654 for (std::string& class_name : class_names) { 655 file_contents += class_name + std::string("\n"); 656 } 657 std::string output_file_contents; 658 ASSERT_TRUE(CreateAndDump(file_contents, &output_file_contents)); 659 ASSERT_EQ(output_file_contents, file_contents); 660 } 661 662 TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) { 663 // Class names put here need to be in sorted order. 664 std::vector<std::string> class_names = { 665 "Ljava/lang/Math;->*", 666 }; 667 std::string input_file_contents; 668 std::string expected_contents; 669 for (std::string& class_name : class_names) { 670 input_file_contents += class_name + std::string("\n"); 671 expected_contents += DescriptorToDot(class_name.c_str()) + 672 std::string("\n"); 673 } 674 std::string output_file_contents; 675 ScratchFile profile_file; 676 EXPECT_TRUE(CreateProfile(input_file_contents, 677 profile_file.GetFilename(), 678 GetLibCoreDexFileNames()[0])); 679 ProfileCompilationInfo info; 680 profile_file.GetFile()->ResetOffset(); 681 ASSERT_TRUE(info.Load(GetFd(profile_file))); 682 // Verify that the profile has matching methods. 683 ScopedObjectAccess soa(Thread::Current()); 684 ObjPtr<mirror::Class> klass = GetClass(nullptr, "Ljava/lang/Math;"); 685 ASSERT_TRUE(klass != nullptr); 686 size_t method_count = 0; 687 for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) { 688 if (!method.IsCopied() && method.GetCodeItem() != nullptr) { 689 ++method_count; 690 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi = 691 info.GetMethod(method.GetDexFile()->GetLocation(), 692 method.GetDexFile()->GetLocationChecksum(), 693 method.GetDexMethodIndex()); 694 ASSERT_TRUE(pmi != nullptr) << method.PrettyMethod(); 695 } 696 } 697 EXPECT_GT(method_count, 0u); 698 } 699 700 TEST_F(ProfileAssistantTest, TestBootImageProfile) { 701 const std::string core_dex = GetLibCoreDexFileNames()[0]; 702 703 std::vector<ScratchFile> profiles; 704 705 // In image with enough clean occurrences. 706 const std::string kCleanClass = "Ljava/lang/CharSequence;"; 707 // In image with enough dirty occurrences. 708 const std::string kDirtyClass = "Ljava/lang/Object;"; 709 // Not in image becauseof not enough occurrences. 710 const std::string kUncommonCleanClass = "Ljava/lang/Process;"; 711 const std::string kUncommonDirtyClass = "Ljava/lang/Package;"; 712 // Method that is hot. 713 // Also adds the class through inference since it is in each dex. 714 const std::string kHotMethod = "Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I"; 715 // Method that doesn't add the class since its only in one profile. Should still show up in the 716 // boot profile. 717 const std::string kOtherMethod = "Ljava/util/HashMap;-><init>()V"; 718 // Method that gets marked as hot since it's in multiple profiles. 719 const std::string kMultiMethod = "Ljava/util/ArrayList;->clear()V"; 720 721 // Thresholds for this test. 722 static const size_t kDirtyThreshold = 3; 723 static const size_t kCleanThreshold = 2; 724 static const size_t kMethodThreshold = 2; 725 726 // Create a bunch of boot profiles. 727 std::string dex1 = 728 kCleanClass + "\n" + 729 kDirtyClass + "\n" + 730 kUncommonCleanClass + "\n" + 731 "H" + kHotMethod + "\n" + 732 kUncommonDirtyClass; 733 profiles.emplace_back(ScratchFile()); 734 EXPECT_TRUE(CreateProfile(dex1, profiles.back().GetFilename(), core_dex)); 735 736 // Create a bunch of boot profiles. 737 std::string dex2 = 738 kCleanClass + "\n" + 739 kDirtyClass + "\n" + 740 "P" + kHotMethod + "\n" + 741 "P" + kMultiMethod + "\n" + 742 kUncommonDirtyClass; 743 profiles.emplace_back(ScratchFile()); 744 EXPECT_TRUE(CreateProfile(dex2, profiles.back().GetFilename(), core_dex)); 745 746 // Create a bunch of boot profiles. 747 std::string dex3 = 748 "S" + kHotMethod + "\n" + 749 "P" + kOtherMethod + "\n" + 750 "P" + kMultiMethod + "\n" + 751 kDirtyClass + "\n"; 752 profiles.emplace_back(ScratchFile()); 753 EXPECT_TRUE(CreateProfile(dex3, profiles.back().GetFilename(), core_dex)); 754 755 // Generate the boot profile. 756 ScratchFile out_profile; 757 std::vector<std::string> args; 758 args.push_back(GetProfmanCmd()); 759 args.push_back("--generate-boot-image-profile"); 760 args.push_back("--boot-image-class-threshold=" + std::to_string(kDirtyThreshold)); 761 args.push_back("--boot-image-clean-class-threshold=" + std::to_string(kCleanThreshold)); 762 args.push_back("--boot-image-sampled-method-threshold=" + std::to_string(kMethodThreshold)); 763 args.push_back("--reference-profile-file=" + out_profile.GetFilename()); 764 args.push_back("--apk=" + core_dex); 765 args.push_back("--dex-location=" + core_dex); 766 for (const ScratchFile& profile : profiles) { 767 args.push_back("--profile-file=" + profile.GetFilename()); 768 } 769 std::string error; 770 EXPECT_EQ(ExecAndReturnCode(args, &error), 0) << error; 771 ASSERT_EQ(0, out_profile.GetFile()->Flush()); 772 ASSERT_TRUE(out_profile.GetFile()->ResetOffset()); 773 774 // Verify the boot profile contents. 775 std::string output_file_contents; 776 EXPECT_TRUE(DumpClassesAndMethods(out_profile.GetFilename(), &output_file_contents)); 777 // Common classes, should be in the classes of the profile. 778 EXPECT_NE(output_file_contents.find(kCleanClass + "\n"), std::string::npos) 779 << output_file_contents; 780 EXPECT_NE(output_file_contents.find(kDirtyClass + "\n"), std::string::npos) 781 << output_file_contents; 782 // Uncommon classes, should not fit preloaded class criteria and should not be in the profile. 783 EXPECT_EQ(output_file_contents.find(kUncommonCleanClass + "\n"), std::string::npos) 784 << output_file_contents; 785 EXPECT_EQ(output_file_contents.find(kUncommonDirtyClass + "\n"), std::string::npos) 786 << output_file_contents; 787 // Inferred class from a method common to all three profiles. 788 EXPECT_NE(output_file_contents.find("Ljava/lang/Comparable;\n"), std::string::npos) 789 << output_file_contents; 790 // Aggregated methods hotness information. 791 EXPECT_NE(output_file_contents.find("HSP" + kHotMethod), std::string::npos) 792 << output_file_contents; 793 EXPECT_NE(output_file_contents.find("P" + kOtherMethod), std::string::npos) 794 << output_file_contents; 795 // Not inferred class, method is only in one profile. 796 EXPECT_EQ(output_file_contents.find("Ljava/util/HashMap;\n"), std::string::npos) 797 << output_file_contents; 798 // Test the sampled methods that became hot. 799 // Other method is in only one profile, it should not become hot. 800 EXPECT_EQ(output_file_contents.find("HP" + kOtherMethod), std::string::npos) 801 << output_file_contents; 802 // Multi method is in at least two profiles, it should become hot. 803 EXPECT_NE(output_file_contents.find("HP" + kMultiMethod), std::string::npos) 804 << output_file_contents; 805 } 806 807 TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) { 808 // Class names put here need to be in sorted order. 809 std::vector<std::string> class_names = { 810 "Ldoesnt/match/this/one;", 811 "Ljava/lang/Comparable;", 812 "Ljava/lang/Object;" 813 }; 814 std::string input_file_contents; 815 for (std::string& class_name : class_names) { 816 input_file_contents += class_name + std::string("\n"); 817 } 818 std::string output_file_contents; 819 ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents)); 820 std::string expected_contents = 821 class_names[1] + std::string("\n") + 822 class_names[2] + std::string("\n"); 823 ASSERT_EQ(output_file_contents, expected_contents); 824 } 825 826 TEST_F(ProfileAssistantTest, TestProfileCreationNoneMatched) { 827 // Class names put here need to be in sorted order. 828 std::vector<std::string> class_names = { 829 "Ldoesnt/match/this/one;", 830 "Ldoesnt/match/this/one/either;", 831 "Lnor/this/one;" 832 }; 833 std::string input_file_contents; 834 for (std::string& class_name : class_names) { 835 input_file_contents += class_name + std::string("\n"); 836 } 837 std::string output_file_contents; 838 ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents)); 839 std::string expected_contents(""); 840 ASSERT_EQ(output_file_contents, expected_contents); 841 } 842 843 TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) { 844 // Create the profile content. 845 std::vector<std::string> methods = { 846 "LTestInline;->inlineMonomorphic(LSuper;)I+LSubA;", 847 "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;", 848 "LTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;", 849 "LTestInline;->inlineMissingTypes(LSuper;)I+missing_types", 850 "LTestInline;->noInlineCache(LSuper;)I" 851 }; 852 std::string input_file_contents; 853 for (std::string& m : methods) { 854 input_file_contents += m + std::string("\n"); 855 } 856 857 // Create the profile and save it to disk. 858 ScratchFile profile_file; 859 ASSERT_TRUE(CreateProfile(input_file_contents, 860 profile_file.GetFilename(), 861 GetTestDexFileName("ProfileTestMultiDex"))); 862 863 // Load the profile from disk. 864 ProfileCompilationInfo info; 865 profile_file.GetFile()->ResetOffset(); 866 ASSERT_TRUE(info.Load(GetFd(profile_file))); 867 868 // Load the dex files and verify that the profile contains the expected methods info. 869 ScopedObjectAccess soa(Thread::Current()); 870 jobject class_loader = LoadDex("ProfileTestMultiDex"); 871 ASSERT_NE(class_loader, nullptr); 872 873 mirror::Class* sub_a = GetClass(class_loader, "LSubA;"); 874 mirror::Class* sub_b = GetClass(class_loader, "LSubB;"); 875 mirror::Class* sub_c = GetClass(class_loader, "LSubC;"); 876 877 ASSERT_TRUE(sub_a != nullptr); 878 ASSERT_TRUE(sub_b != nullptr); 879 ASSERT_TRUE(sub_c != nullptr); 880 881 { 882 // Verify that method inlineMonomorphic has the expected inline caches and nothing else. 883 ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader, 884 "LTestInline;", 885 "inlineMonomorphic"); 886 ASSERT_TRUE(inline_monomorphic != nullptr); 887 std::set<mirror::Class*> expected_monomorphic; 888 expected_monomorphic.insert(sub_a); 889 AssertInlineCaches(inline_monomorphic, 890 expected_monomorphic, 891 info, 892 /*megamorphic*/false, 893 /*missing_types*/false); 894 } 895 896 { 897 // Verify that method inlinePolymorphic has the expected inline caches and nothing else. 898 ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader, 899 "LTestInline;", 900 "inlinePolymorphic"); 901 ASSERT_TRUE(inline_polymorhic != nullptr); 902 std::set<mirror::Class*> expected_polymorphic; 903 expected_polymorphic.insert(sub_a); 904 expected_polymorphic.insert(sub_b); 905 expected_polymorphic.insert(sub_c); 906 AssertInlineCaches(inline_polymorhic, 907 expected_polymorphic, 908 info, 909 /*megamorphic*/false, 910 /*missing_types*/false); 911 } 912 913 { 914 // Verify that method inlineMegamorphic has the expected inline caches and nothing else. 915 ArtMethod* inline_megamorphic = GetVirtualMethod(class_loader, 916 "LTestInline;", 917 "inlineMegamorphic"); 918 ASSERT_TRUE(inline_megamorphic != nullptr); 919 std::set<mirror::Class*> expected_megamorphic; 920 AssertInlineCaches(inline_megamorphic, 921 expected_megamorphic, 922 info, 923 /*megamorphic*/true, 924 /*missing_types*/false); 925 } 926 927 { 928 // Verify that method inlineMegamorphic has the expected inline caches and nothing else. 929 ArtMethod* inline_missing_types = GetVirtualMethod(class_loader, 930 "LTestInline;", 931 "inlineMissingTypes"); 932 ASSERT_TRUE(inline_missing_types != nullptr); 933 std::set<mirror::Class*> expected_missing_Types; 934 AssertInlineCaches(inline_missing_types, 935 expected_missing_Types, 936 info, 937 /*megamorphic*/false, 938 /*missing_types*/true); 939 } 940 941 { 942 // Verify that method noInlineCache has no inline caches in the profile. 943 ArtMethod* no_inline_cache = GetVirtualMethod(class_loader, "LTestInline;", "noInlineCache"); 944 ASSERT_TRUE(no_inline_cache != nullptr); 945 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi_no_inline_cache = 946 info.GetMethod(no_inline_cache->GetDexFile()->GetLocation(), 947 no_inline_cache->GetDexFile()->GetLocationChecksum(), 948 no_inline_cache->GetDexMethodIndex()); 949 ASSERT_TRUE(pmi_no_inline_cache != nullptr); 950 ASSERT_TRUE(pmi_no_inline_cache->inline_caches->empty()); 951 } 952 } 953 954 TEST_F(ProfileAssistantTest, MergeProfilesWithDifferentDexOrder) { 955 ScratchFile profile1; 956 ScratchFile reference_profile; 957 958 std::vector<int> profile_fds({GetFd(profile1)}); 959 int reference_profile_fd = GetFd(reference_profile); 960 961 // The new profile info will contain the methods with indices 0-100. 962 const uint16_t kNumberOfMethodsToEnableCompilation = 100; 963 ProfileCompilationInfo info1; 964 SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1, 965 /*start_method_index*/0, /*reverse_dex_write_order*/false); 966 967 // The reference profile info will contain the methods with indices 50-150. 968 // When setting up the profile reverse the order in which the dex files 969 // are added to the profile. This will verify that profman merges profiles 970 // with a different dex order correctly. 971 const uint16_t kNumberOfMethodsAlreadyCompiled = 100; 972 ProfileCompilationInfo reference_info; 973 SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, 0, reference_profile, 974 &reference_info, kNumberOfMethodsToEnableCompilation / 2, /*reverse_dex_write_order*/true); 975 976 // We should advise compilation. 977 ASSERT_EQ(ProfileAssistant::kCompile, 978 ProcessProfiles(profile_fds, reference_profile_fd)); 979 980 // The resulting compilation info must be equal to the merge of the inputs. 981 ProfileCompilationInfo result; 982 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset()); 983 ASSERT_TRUE(result.Load(reference_profile_fd)); 984 985 ProfileCompilationInfo expected; 986 ASSERT_TRUE(expected.MergeWith(reference_info)); 987 ASSERT_TRUE(expected.MergeWith(info1)); 988 ASSERT_TRUE(expected.Equals(result)); 989 990 // The information from profile must remain the same. 991 CheckProfileInfo(profile1, info1); 992 } 993 994 TEST_F(ProfileAssistantTest, TestProfileCreateWithInvalidData) { 995 // Create the profile content. 996 std::vector<std::string> profile_methods = { 997 "LTestInline;->inlineMonomorphic(LSuper;)I+invalid_class", 998 "LTestInline;->invalid_method", 999 "invalid_class" 1000 }; 1001 std::string input_file_contents; 1002 for (std::string& m : profile_methods) { 1003 input_file_contents += m + std::string("\n"); 1004 } 1005 1006 // Create the profile and save it to disk. 1007 ScratchFile profile_file; 1008 std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex"); 1009 ASSERT_TRUE(CreateProfile(input_file_contents, 1010 profile_file.GetFilename(), 1011 dex_filename)); 1012 1013 // Load the profile from disk. 1014 ProfileCompilationInfo info; 1015 profile_file.GetFile()->ResetOffset(); 1016 ASSERT_TRUE(info.Load(GetFd(profile_file))); 1017 1018 // Load the dex files and verify that the profile contains the expected methods info. 1019 ScopedObjectAccess soa(Thread::Current()); 1020 jobject class_loader = LoadDex("ProfileTestMultiDex"); 1021 ASSERT_NE(class_loader, nullptr); 1022 1023 ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader, 1024 "LTestInline;", 1025 "inlineMonomorphic"); 1026 const DexFile* dex_file = inline_monomorphic->GetDexFile(); 1027 1028 // Verify that the inline cache contains the invalid type. 1029 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi = 1030 info.GetMethod(dex_file->GetLocation(), 1031 dex_file->GetLocationChecksum(), 1032 inline_monomorphic->GetDexMethodIndex()); 1033 ASSERT_TRUE(pmi != nullptr); 1034 ASSERT_EQ(pmi->inline_caches->size(), 1u); 1035 const ProfileCompilationInfo::DexPcData& dex_pc_data = pmi->inline_caches->begin()->second; 1036 dex::TypeIndex invalid_class_index(std::numeric_limits<uint16_t>::max() - 1); 1037 ASSERT_EQ(1u, dex_pc_data.classes.size()); 1038 ASSERT_EQ(invalid_class_index, dex_pc_data.classes.begin()->type_index); 1039 1040 // Verify that the start-up classes contain the invalid class. 1041 std::set<dex::TypeIndex> classes; 1042 std::set<uint16_t> hot_methods; 1043 std::set<uint16_t> startup_methods; 1044 std::set<uint16_t> post_start_methods; 1045 ASSERT_TRUE(info.GetClassesAndMethods(*dex_file, 1046 &classes, 1047 &hot_methods, 1048 &startup_methods, 1049 &post_start_methods)); 1050 ASSERT_EQ(1u, classes.size()); 1051 ASSERT_TRUE(classes.find(invalid_class_index) != classes.end()); 1052 1053 // Verify that the invalid method is in the profile. 1054 ASSERT_EQ(2u, hot_methods.size()); 1055 uint16_t invalid_method_index = std::numeric_limits<uint16_t>::max() - 1; 1056 ASSERT_TRUE(hot_methods.find(invalid_method_index) != hot_methods.end()); 1057 } 1058 1059 TEST_F(ProfileAssistantTest, DumpOnly) { 1060 ScratchFile profile; 1061 1062 const uint32_t kNumberOfMethods = 64; 1063 std::vector<uint32_t> hot_methods; 1064 std::vector<uint32_t> startup_methods; 1065 std::vector<uint32_t> post_startup_methods; 1066 for (size_t i = 0; i < kNumberOfMethods; ++i) { 1067 if (i % 2 == 0) { 1068 hot_methods.push_back(i); 1069 } 1070 if (i % 3 == 1) { 1071 startup_methods.push_back(i); 1072 } 1073 if (i % 4 == 2) { 1074 post_startup_methods.push_back(i); 1075 } 1076 } 1077 EXPECT_GT(hot_methods.size(), 0u); 1078 EXPECT_GT(startup_methods.size(), 0u); 1079 EXPECT_GT(post_startup_methods.size(), 0u); 1080 ProfileCompilationInfo info1; 1081 SetupBasicProfile("p1", 1082 1, 1083 kNumberOfMethods, 1084 hot_methods, 1085 startup_methods, 1086 post_startup_methods, 1087 profile, 1088 &info1); 1089 std::string output; 1090 DumpOnly(profile.GetFilename(), &output); 1091 const size_t hot_offset = output.find("hot methods:"); 1092 const size_t startup_offset = output.find("startup methods:"); 1093 const size_t post_startup_offset = output.find("post startup methods:"); 1094 const size_t classes_offset = output.find("classes:"); 1095 ASSERT_NE(hot_offset, std::string::npos); 1096 ASSERT_NE(startup_offset, std::string::npos); 1097 ASSERT_NE(post_startup_offset, std::string::npos); 1098 ASSERT_LT(hot_offset, startup_offset); 1099 ASSERT_LT(startup_offset, post_startup_offset); 1100 // Check the actual contents of the dump by looking at the offsets of the methods. 1101 for (uint32_t m : hot_methods) { 1102 const size_t pos = output.find(std::to_string(m) + "[],", hot_offset); 1103 ASSERT_NE(pos, std::string::npos); 1104 EXPECT_LT(pos, startup_offset); 1105 } 1106 for (uint32_t m : startup_methods) { 1107 const size_t pos = output.find(std::to_string(m) + ",", startup_offset); 1108 ASSERT_NE(pos, std::string::npos); 1109 EXPECT_LT(pos, post_startup_offset); 1110 } 1111 for (uint32_t m : post_startup_methods) { 1112 const size_t pos = output.find(std::to_string(m) + ",", post_startup_offset); 1113 ASSERT_NE(pos, std::string::npos); 1114 EXPECT_LT(pos, classes_offset); 1115 } 1116 } 1117 1118 } // namespace art 1119