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 <regex> 18 #include <sstream> 19 #include <string> 20 #include <vector> 21 22 #include <sys/wait.h> 23 #include <unistd.h> 24 25 #include "android-base/stringprintf.h" 26 27 #include "common_runtime_test.h" 28 29 #include "base/logging.h" 30 #include "base/macros.h" 31 #include "base/mutex-inl.h" 32 #include "bytecode_utils.h" 33 #include "dex_file-inl.h" 34 #include "dex2oat_environment_test.h" 35 #include "dex2oat_return_codes.h" 36 #include "jit/profile_compilation_info.h" 37 #include "oat.h" 38 #include "oat_file.h" 39 #include "utils.h" 40 41 namespace art { 42 43 static constexpr size_t kMaxMethodIds = 65535; 44 static constexpr bool kDebugArgs = false; 45 46 using android::base::StringPrintf; 47 48 class Dex2oatTest : public Dex2oatEnvironmentTest { 49 public: 50 virtual void TearDown() OVERRIDE { 51 Dex2oatEnvironmentTest::TearDown(); 52 53 output_ = ""; 54 error_msg_ = ""; 55 success_ = false; 56 } 57 58 protected: 59 int GenerateOdexForTestWithStatus(const std::vector<std::string>& dex_locations, 60 const std::string& odex_location, 61 CompilerFilter::Filter filter, 62 std::string* error_msg, 63 const std::vector<std::string>& extra_args = {}, 64 bool use_fd = false) { 65 std::unique_ptr<File> oat_file; 66 std::vector<std::string> args; 67 // Add dex file args. 68 for (const std::string& dex_location : dex_locations) { 69 args.push_back("--dex-file=" + dex_location); 70 } 71 if (use_fd) { 72 oat_file.reset(OS::CreateEmptyFile(odex_location.c_str())); 73 CHECK(oat_file != nullptr) << odex_location; 74 args.push_back("--oat-fd=" + std::to_string(oat_file->Fd())); 75 args.push_back("--oat-location=" + odex_location); 76 } else { 77 args.push_back("--oat-file=" + odex_location); 78 } 79 args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter)); 80 args.push_back("--runtime-arg"); 81 args.push_back("-Xnorelocate"); 82 83 args.insert(args.end(), extra_args.begin(), extra_args.end()); 84 85 int status = Dex2Oat(args, error_msg); 86 if (oat_file != nullptr) { 87 CHECK_EQ(oat_file->FlushClose(), 0) << "Could not flush and close oat file"; 88 } 89 return status; 90 } 91 92 void GenerateOdexForTest(const std::string& dex_location, 93 const std::string& odex_location, 94 CompilerFilter::Filter filter, 95 const std::vector<std::string>& extra_args = {}, 96 bool expect_success = true, 97 bool use_fd = false, 98 std::function<void(const OatFile&)> check_oat = [](const OatFile&) {}) { 99 std::string error_msg; 100 int status = GenerateOdexForTestWithStatus({dex_location}, 101 odex_location, 102 filter, 103 &error_msg, 104 extra_args, 105 use_fd); 106 bool success = (status == 0); 107 if (expect_success) { 108 ASSERT_TRUE(success) << error_msg << std::endl << output_; 109 110 // Verify the odex file was generated as expected. 111 std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), 112 odex_location.c_str(), 113 nullptr, 114 nullptr, 115 false, 116 /*low_4gb*/false, 117 dex_location.c_str(), 118 &error_msg)); 119 ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; 120 121 CheckFilter(filter, odex_file->GetCompilerFilter()); 122 check_oat(*(odex_file.get())); 123 } else { 124 ASSERT_FALSE(success) << output_; 125 126 error_msg_ = error_msg; 127 128 // Verify there's no loadable odex file. 129 std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), 130 odex_location.c_str(), 131 nullptr, 132 nullptr, 133 false, 134 /*low_4gb*/false, 135 dex_location.c_str(), 136 &error_msg)); 137 ASSERT_TRUE(odex_file.get() == nullptr); 138 } 139 } 140 141 // Check the input compiler filter against the generated oat file's filter. May be overridden 142 // in subclasses when equality is not expected. 143 virtual void CheckFilter(CompilerFilter::Filter expected, CompilerFilter::Filter actual) { 144 EXPECT_EQ(expected, actual); 145 } 146 147 int Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) { 148 Runtime* runtime = Runtime::Current(); 149 150 const std::vector<gc::space::ImageSpace*>& image_spaces = 151 runtime->GetHeap()->GetBootImageSpaces(); 152 if (image_spaces.empty()) { 153 *error_msg = "No image location found for Dex2Oat."; 154 return false; 155 } 156 std::string image_location = image_spaces[0]->GetImageLocation(); 157 158 std::vector<std::string> argv; 159 argv.push_back(runtime->GetCompilerExecutable()); 160 161 if (runtime->IsJavaDebuggable()) { 162 argv.push_back("--debuggable"); 163 } 164 runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); 165 166 if (!runtime->IsVerificationEnabled()) { 167 argv.push_back("--compiler-filter=assume-verified"); 168 } 169 170 if (runtime->MustRelocateIfPossible()) { 171 argv.push_back("--runtime-arg"); 172 argv.push_back("-Xrelocate"); 173 } else { 174 argv.push_back("--runtime-arg"); 175 argv.push_back("-Xnorelocate"); 176 } 177 178 if (!kIsTargetBuild) { 179 argv.push_back("--host"); 180 } 181 182 argv.push_back("--boot-image=" + image_location); 183 184 std::vector<std::string> compiler_options = runtime->GetCompilerOptions(); 185 argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); 186 187 argv.insert(argv.end(), dex2oat_args.begin(), dex2oat_args.end()); 188 189 // We must set --android-root. 190 const char* android_root = getenv("ANDROID_ROOT"); 191 CHECK(android_root != nullptr); 192 argv.push_back("--android-root=" + std::string(android_root)); 193 194 if (kDebugArgs) { 195 std::string all_args; 196 for (const std::string& arg : argv) { 197 all_args += arg + " "; 198 } 199 LOG(ERROR) << all_args; 200 } 201 202 int link[2]; 203 204 if (pipe(link) == -1) { 205 return false; 206 } 207 208 pid_t pid = fork(); 209 if (pid == -1) { 210 return false; 211 } 212 213 if (pid == 0) { 214 // We need dex2oat to actually log things. 215 setenv("ANDROID_LOG_TAGS", "*:d", 1); 216 dup2(link[1], STDERR_FILENO); 217 close(link[0]); 218 close(link[1]); 219 std::vector<const char*> c_args; 220 for (const std::string& str : argv) { 221 c_args.push_back(str.c_str()); 222 } 223 c_args.push_back(nullptr); 224 execv(c_args[0], const_cast<char* const*>(c_args.data())); 225 exit(1); 226 UNREACHABLE(); 227 } else { 228 close(link[1]); 229 char buffer[128]; 230 memset(buffer, 0, 128); 231 ssize_t bytes_read = 0; 232 233 while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { 234 output_ += std::string(buffer, bytes_read); 235 } 236 close(link[0]); 237 int status = -1; 238 if (waitpid(pid, &status, 0) != -1) { 239 success_ = (status == 0); 240 } 241 return status; 242 } 243 } 244 245 std::string output_ = ""; 246 std::string error_msg_ = ""; 247 bool success_ = false; 248 }; 249 250 class Dex2oatSwapTest : public Dex2oatTest { 251 protected: 252 void RunTest(bool use_fd, bool expect_use, const std::vector<std::string>& extra_args = {}) { 253 std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar"; 254 std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex"; 255 256 Copy(GetTestDexFileName(), dex_location); 257 258 std::vector<std::string> copy(extra_args); 259 260 std::unique_ptr<ScratchFile> sf; 261 if (use_fd) { 262 sf.reset(new ScratchFile()); 263 copy.push_back(android::base::StringPrintf("--swap-fd=%d", sf->GetFd())); 264 } else { 265 std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap"; 266 copy.push_back("--swap-file=" + swap_location); 267 } 268 GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy); 269 270 CheckValidity(); 271 ASSERT_TRUE(success_); 272 CheckResult(expect_use); 273 } 274 275 virtual std::string GetTestDexFileName() { 276 return Dex2oatEnvironmentTest::GetTestDexFileName("VerifierDeps"); 277 } 278 279 virtual void CheckResult(bool expect_use) { 280 if (kIsTargetBuild) { 281 CheckTargetResult(expect_use); 282 } else { 283 CheckHostResult(expect_use); 284 } 285 } 286 287 virtual void CheckTargetResult(bool expect_use ATTRIBUTE_UNUSED) { 288 // TODO: Ignore for now, as we won't capture any output (it goes to the logcat). We may do 289 // something for variants with file descriptor where we can control the lifetime of 290 // the swap file and thus take a look at it. 291 } 292 293 virtual void CheckHostResult(bool expect_use) { 294 if (!kIsTargetBuild) { 295 if (expect_use) { 296 EXPECT_NE(output_.find("Large app, accepted running with swap."), std::string::npos) 297 << output_; 298 } else { 299 EXPECT_EQ(output_.find("Large app, accepted running with swap."), std::string::npos) 300 << output_; 301 } 302 } 303 } 304 305 // Check whether the dex2oat run was really successful. 306 virtual void CheckValidity() { 307 if (kIsTargetBuild) { 308 CheckTargetValidity(); 309 } else { 310 CheckHostValidity(); 311 } 312 } 313 314 virtual void CheckTargetValidity() { 315 // TODO: Ignore for now, as we won't capture any output (it goes to the logcat). We may do 316 // something for variants with file descriptor where we can control the lifetime of 317 // the swap file and thus take a look at it. 318 } 319 320 // On the host, we can get the dex2oat output. Here, look for "dex2oat took." 321 virtual void CheckHostValidity() { 322 EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_; 323 } 324 }; 325 326 TEST_F(Dex2oatSwapTest, DoNotUseSwapDefaultSingleSmall) { 327 RunTest(false /* use_fd */, false /* expect_use */); 328 RunTest(true /* use_fd */, false /* expect_use */); 329 } 330 331 TEST_F(Dex2oatSwapTest, DoNotUseSwapSingle) { 332 RunTest(false /* use_fd */, false /* expect_use */, { "--swap-dex-size-threshold=0" }); 333 RunTest(true /* use_fd */, false /* expect_use */, { "--swap-dex-size-threshold=0" }); 334 } 335 336 TEST_F(Dex2oatSwapTest, DoNotUseSwapSmall) { 337 RunTest(false /* use_fd */, false /* expect_use */, { "--swap-dex-count-threshold=0" }); 338 RunTest(true /* use_fd */, false /* expect_use */, { "--swap-dex-count-threshold=0" }); 339 } 340 341 TEST_F(Dex2oatSwapTest, DoUseSwapSingleSmall) { 342 RunTest(false /* use_fd */, 343 true /* expect_use */, 344 { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" }); 345 RunTest(true /* use_fd */, 346 true /* expect_use */, 347 { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" }); 348 } 349 350 class Dex2oatSwapUseTest : public Dex2oatSwapTest { 351 protected: 352 void CheckHostResult(bool expect_use) OVERRIDE { 353 if (!kIsTargetBuild) { 354 if (expect_use) { 355 EXPECT_NE(output_.find("Large app, accepted running with swap."), std::string::npos) 356 << output_; 357 } else { 358 EXPECT_EQ(output_.find("Large app, accepted running with swap."), std::string::npos) 359 << output_; 360 } 361 } 362 } 363 364 std::string GetTestDexFileName() OVERRIDE { 365 // Use Statics as it has a handful of functions. 366 return CommonRuntimeTest::GetTestDexFileName("Statics"); 367 } 368 369 void GrabResult1() { 370 if (!kIsTargetBuild) { 371 native_alloc_1_ = ParseNativeAlloc(); 372 swap_1_ = ParseSwap(false /* expected */); 373 } else { 374 native_alloc_1_ = std::numeric_limits<size_t>::max(); 375 swap_1_ = 0; 376 } 377 } 378 379 void GrabResult2() { 380 if (!kIsTargetBuild) { 381 native_alloc_2_ = ParseNativeAlloc(); 382 swap_2_ = ParseSwap(true /* expected */); 383 } else { 384 native_alloc_2_ = 0; 385 swap_2_ = std::numeric_limits<size_t>::max(); 386 } 387 } 388 389 private: 390 size_t ParseNativeAlloc() { 391 std::regex native_alloc_regex("dex2oat took.*native alloc=[^ ]+ \\(([0-9]+)B\\)"); 392 std::smatch native_alloc_match; 393 bool found = std::regex_search(output_, native_alloc_match, native_alloc_regex); 394 if (!found) { 395 EXPECT_TRUE(found); 396 return 0; 397 } 398 if (native_alloc_match.size() != 2U) { 399 EXPECT_EQ(native_alloc_match.size(), 2U); 400 return 0; 401 } 402 403 std::istringstream stream(native_alloc_match[1].str()); 404 size_t value; 405 stream >> value; 406 407 return value; 408 } 409 410 size_t ParseSwap(bool expected) { 411 std::regex swap_regex("dex2oat took[^\\n]+swap=[^ ]+ \\(([0-9]+)B\\)"); 412 std::smatch swap_match; 413 bool found = std::regex_search(output_, swap_match, swap_regex); 414 if (found != expected) { 415 EXPECT_EQ(expected, found); 416 return 0; 417 } 418 419 if (!found) { 420 return 0; 421 } 422 423 if (swap_match.size() != 2U) { 424 EXPECT_EQ(swap_match.size(), 2U); 425 return 0; 426 } 427 428 std::istringstream stream(swap_match[1].str()); 429 size_t value; 430 stream >> value; 431 432 return value; 433 } 434 435 protected: 436 size_t native_alloc_1_; 437 size_t native_alloc_2_; 438 439 size_t swap_1_; 440 size_t swap_2_; 441 }; 442 443 TEST_F(Dex2oatSwapUseTest, CheckSwapUsage) { 444 // Native memory usage isn't correctly tracked under sanitization. 445 TEST_DISABLED_FOR_MEMORY_TOOL_ASAN(); 446 447 // The `native_alloc_2_ >= native_alloc_1_` assertion below may not 448 // hold true on some x86 systems; disable this test while we 449 // investigate (b/29259363). 450 TEST_DISABLED_FOR_X86(); 451 452 RunTest(false /* use_fd */, 453 false /* expect_use */); 454 GrabResult1(); 455 std::string output_1 = output_; 456 457 output_ = ""; 458 459 RunTest(false /* use_fd */, 460 true /* expect_use */, 461 { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" }); 462 GrabResult2(); 463 std::string output_2 = output_; 464 465 if (native_alloc_2_ >= native_alloc_1_ || swap_1_ >= swap_2_) { 466 EXPECT_LT(native_alloc_2_, native_alloc_1_); 467 EXPECT_LT(swap_1_, swap_2_); 468 469 LOG(ERROR) << output_1; 470 LOG(ERROR) << output_2; 471 } 472 } 473 474 class Dex2oatVeryLargeTest : public Dex2oatTest { 475 protected: 476 void CheckFilter(CompilerFilter::Filter input ATTRIBUTE_UNUSED, 477 CompilerFilter::Filter result ATTRIBUTE_UNUSED) OVERRIDE { 478 // Ignore, we'll do our own checks. 479 } 480 481 void RunTest(CompilerFilter::Filter filter, 482 bool expect_large, 483 bool expect_downgrade, 484 const std::vector<std::string>& extra_args = {}) { 485 std::string dex_location = GetScratchDir() + "/DexNoOat.jar"; 486 std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex"; 487 std::string app_image_file = GetScratchDir() + "/Test.art"; 488 489 Copy(GetDexSrc1(), dex_location); 490 491 std::vector<std::string> new_args(extra_args); 492 new_args.push_back("--app-image-file=" + app_image_file); 493 GenerateOdexForTest(dex_location, odex_location, filter, new_args); 494 495 CheckValidity(); 496 ASSERT_TRUE(success_); 497 CheckResult(dex_location, 498 odex_location, 499 app_image_file, 500 filter, 501 expect_large, 502 expect_downgrade); 503 } 504 505 void CheckResult(const std::string& dex_location, 506 const std::string& odex_location, 507 const std::string& app_image_file, 508 CompilerFilter::Filter filter, 509 bool expect_large, 510 bool expect_downgrade) { 511 if (expect_downgrade) { 512 EXPECT_TRUE(expect_large); 513 } 514 // Host/target independent checks. 515 std::string error_msg; 516 std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), 517 odex_location.c_str(), 518 nullptr, 519 nullptr, 520 false, 521 /*low_4gb*/false, 522 dex_location.c_str(), 523 &error_msg)); 524 ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; 525 EXPECT_GT(app_image_file.length(), 0u); 526 std::unique_ptr<File> file(OS::OpenFileForReading(app_image_file.c_str())); 527 if (expect_large) { 528 // Note: we cannot check the following 529 // EXPECT_FALSE(CompilerFilter::IsAotCompilationEnabled(odex_file->GetCompilerFilter())); 530 // The reason is that the filter override currently happens when the dex files are 531 // loaded in dex2oat, which is after the oat file has been started. Thus, the header 532 // store cannot be changed, and the original filter is set in stone. 533 534 for (const OatDexFile* oat_dex_file : odex_file->GetOatDexFiles()) { 535 std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg); 536 ASSERT_TRUE(dex_file != nullptr); 537 uint32_t class_def_count = dex_file->NumClassDefs(); 538 ASSERT_LT(class_def_count, std::numeric_limits<uint16_t>::max()); 539 for (uint16_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { 540 OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index); 541 EXPECT_EQ(oat_class.GetType(), OatClassType::kOatClassNoneCompiled); 542 } 543 } 544 545 // If the input filter was "below," it should have been used. 546 if (!CompilerFilter::IsAsGoodAs(CompilerFilter::kExtract, filter)) { 547 EXPECT_EQ(odex_file->GetCompilerFilter(), filter); 548 } 549 550 // If expect large, make sure the app image isn't generated or is empty. 551 if (file != nullptr) { 552 EXPECT_EQ(file->GetLength(), 0u); 553 } 554 } else { 555 EXPECT_EQ(odex_file->GetCompilerFilter(), filter); 556 ASSERT_TRUE(file != nullptr) << app_image_file; 557 EXPECT_GT(file->GetLength(), 0u); 558 } 559 560 // Host/target dependent checks. 561 if (kIsTargetBuild) { 562 CheckTargetResult(expect_downgrade); 563 } else { 564 CheckHostResult(expect_downgrade); 565 } 566 } 567 568 void CheckTargetResult(bool expect_downgrade ATTRIBUTE_UNUSED) { 569 // TODO: Ignore for now. May do something for fd things. 570 } 571 572 void CheckHostResult(bool expect_downgrade) { 573 if (!kIsTargetBuild) { 574 if (expect_downgrade) { 575 EXPECT_NE(output_.find("Very large app, downgrading to"), std::string::npos) << output_; 576 } else { 577 EXPECT_EQ(output_.find("Very large app, downgrading to"), std::string::npos) << output_; 578 } 579 } 580 } 581 582 // Check whether the dex2oat run was really successful. 583 void CheckValidity() { 584 if (kIsTargetBuild) { 585 CheckTargetValidity(); 586 } else { 587 CheckHostValidity(); 588 } 589 } 590 591 void CheckTargetValidity() { 592 // TODO: Ignore for now. 593 } 594 595 // On the host, we can get the dex2oat output. Here, look for "dex2oat took." 596 void CheckHostValidity() { 597 EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_; 598 } 599 }; 600 601 TEST_F(Dex2oatVeryLargeTest, DontUseVeryLarge) { 602 RunTest(CompilerFilter::kAssumeVerified, false, false); 603 RunTest(CompilerFilter::kExtract, false, false); 604 RunTest(CompilerFilter::kQuicken, false, false); 605 RunTest(CompilerFilter::kSpeed, false, false); 606 607 RunTest(CompilerFilter::kAssumeVerified, false, false, { "--very-large-app-threshold=10000000" }); 608 RunTest(CompilerFilter::kExtract, false, false, { "--very-large-app-threshold=10000000" }); 609 RunTest(CompilerFilter::kQuicken, false, false, { "--very-large-app-threshold=10000000" }); 610 RunTest(CompilerFilter::kSpeed, false, false, { "--very-large-app-threshold=10000000" }); 611 } 612 613 TEST_F(Dex2oatVeryLargeTest, UseVeryLarge) { 614 RunTest(CompilerFilter::kAssumeVerified, true, false, { "--very-large-app-threshold=100" }); 615 RunTest(CompilerFilter::kExtract, true, false, { "--very-large-app-threshold=100" }); 616 RunTest(CompilerFilter::kQuicken, true, true, { "--very-large-app-threshold=100" }); 617 RunTest(CompilerFilter::kSpeed, true, true, { "--very-large-app-threshold=100" }); 618 } 619 620 // Regressin test for b/35665292. 621 TEST_F(Dex2oatVeryLargeTest, SpeedProfileNoProfile) { 622 // Test that dex2oat doesn't crash with speed-profile but no input profile. 623 RunTest(CompilerFilter::kSpeedProfile, false, false); 624 } 625 626 class Dex2oatLayoutTest : public Dex2oatTest { 627 protected: 628 void CheckFilter(CompilerFilter::Filter input ATTRIBUTE_UNUSED, 629 CompilerFilter::Filter result ATTRIBUTE_UNUSED) OVERRIDE { 630 // Ignore, we'll do our own checks. 631 } 632 633 // Emits a profile with a single dex file with the given location and a single class index of 1. 634 void GenerateProfile(const std::string& test_profile, 635 const std::string& dex_location, 636 size_t num_classes, 637 uint32_t checksum) { 638 int profile_test_fd = open(test_profile.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644); 639 CHECK_GE(profile_test_fd, 0); 640 641 ProfileCompilationInfo info; 642 std::string profile_key = ProfileCompilationInfo::GetProfileDexFileKey(dex_location); 643 for (size_t i = 0; i < num_classes; ++i) { 644 info.AddClassIndex(profile_key, checksum, dex::TypeIndex(1 + i), kMaxMethodIds); 645 } 646 bool result = info.Save(profile_test_fd); 647 close(profile_test_fd); 648 ASSERT_TRUE(result); 649 } 650 651 void CompileProfileOdex(const std::string& dex_location, 652 const std::string& odex_location, 653 const std::string& app_image_file_name, 654 bool use_fd, 655 size_t num_profile_classes, 656 const std::vector<std::string>& extra_args = {}, 657 bool expect_success = true) { 658 const std::string profile_location = GetScratchDir() + "/primary.prof"; 659 const char* location = dex_location.c_str(); 660 std::string error_msg; 661 std::vector<std::unique_ptr<const DexFile>> dex_files; 662 ASSERT_TRUE(DexFile::Open(location, location, true, &error_msg, &dex_files)); 663 EXPECT_EQ(dex_files.size(), 1U); 664 std::unique_ptr<const DexFile>& dex_file = dex_files[0]; 665 GenerateProfile(profile_location, 666 dex_location, 667 num_profile_classes, 668 dex_file->GetLocationChecksum()); 669 std::vector<std::string> copy(extra_args); 670 copy.push_back("--profile-file=" + profile_location); 671 std::unique_ptr<File> app_image_file; 672 if (!app_image_file_name.empty()) { 673 if (use_fd) { 674 app_image_file.reset(OS::CreateEmptyFile(app_image_file_name.c_str())); 675 copy.push_back("--app-image-fd=" + std::to_string(app_image_file->Fd())); 676 } else { 677 copy.push_back("--app-image-file=" + app_image_file_name); 678 } 679 } 680 GenerateOdexForTest(dex_location, 681 odex_location, 682 CompilerFilter::kSpeedProfile, 683 copy, 684 expect_success, 685 use_fd); 686 if (app_image_file != nullptr) { 687 ASSERT_EQ(app_image_file->FlushCloseOrErase(), 0) << "Could not flush and close art file"; 688 } 689 } 690 691 uint64_t GetImageSize(const std::string& image_file_name) { 692 EXPECT_FALSE(image_file_name.empty()); 693 std::unique_ptr<File> file(OS::OpenFileForReading(image_file_name.c_str())); 694 CHECK(file != nullptr); 695 ImageHeader image_header; 696 const bool success = file->ReadFully(&image_header, sizeof(image_header)); 697 CHECK(success); 698 CHECK(image_header.IsValid()); 699 ReaderMutexLock mu(Thread::Current(), *Locks::mutator_lock_); 700 return image_header.GetImageSize(); 701 } 702 703 void RunTest(bool app_image) { 704 std::string dex_location = GetScratchDir() + "/DexNoOat.jar"; 705 std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex"; 706 std::string app_image_file = app_image ? (GetOdexDir() + "/DexOdexNoOat.art"): ""; 707 Copy(GetDexSrc2(), dex_location); 708 709 uint64_t image_file_empty_profile = 0; 710 if (app_image) { 711 CompileProfileOdex(dex_location, 712 odex_location, 713 app_image_file, 714 /* use_fd */ false, 715 /* num_profile_classes */ 0); 716 CheckValidity(); 717 ASSERT_TRUE(success_); 718 // Don't check the result since CheckResult relies on the class being in the profile. 719 image_file_empty_profile = GetImageSize(app_image_file); 720 EXPECT_GT(image_file_empty_profile, 0u); 721 } 722 723 // Small profile. 724 CompileProfileOdex(dex_location, 725 odex_location, 726 app_image_file, 727 /* use_fd */ false, 728 /* num_profile_classes */ 1); 729 CheckValidity(); 730 ASSERT_TRUE(success_); 731 CheckResult(dex_location, odex_location, app_image_file); 732 733 if (app_image) { 734 // Test that the profile made a difference by adding more classes. 735 const uint64_t image_file_small_profile = GetImageSize(app_image_file); 736 CHECK_LT(image_file_empty_profile, image_file_small_profile); 737 } 738 } 739 740 void RunTestVDex() { 741 std::string dex_location = GetScratchDir() + "/DexNoOat.jar"; 742 std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex"; 743 std::string vdex_location = GetOdexDir() + "/DexOdexNoOat.vdex"; 744 std::string app_image_file_name = GetOdexDir() + "/DexOdexNoOat.art"; 745 Copy(GetDexSrc2(), dex_location); 746 747 std::unique_ptr<File> vdex_file1(OS::CreateEmptyFile(vdex_location.c_str())); 748 CHECK(vdex_file1 != nullptr) << vdex_location; 749 ScratchFile vdex_file2; 750 { 751 std::string input_vdex = "--input-vdex-fd=-1"; 752 std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd()); 753 CompileProfileOdex(dex_location, 754 odex_location, 755 app_image_file_name, 756 /* use_fd */ true, 757 /* num_profile_classes */ 1, 758 { input_vdex, output_vdex }); 759 EXPECT_GT(vdex_file1->GetLength(), 0u); 760 } 761 { 762 // Test that vdex and dexlayout fail gracefully. 763 std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_file1->Fd()); 764 std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file2.GetFd()); 765 CompileProfileOdex(dex_location, 766 odex_location, 767 app_image_file_name, 768 /* use_fd */ true, 769 /* num_profile_classes */ 1, 770 { input_vdex, output_vdex }, 771 /* expect_success */ true); 772 EXPECT_GT(vdex_file2.GetFile()->GetLength(), 0u); 773 } 774 ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file"; 775 CheckValidity(); 776 ASSERT_TRUE(success_); 777 } 778 779 void CheckResult(const std::string& dex_location, 780 const std::string& odex_location, 781 const std::string& app_image_file_name) { 782 // Host/target independent checks. 783 std::string error_msg; 784 std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), 785 odex_location.c_str(), 786 nullptr, 787 nullptr, 788 false, 789 /*low_4gb*/false, 790 dex_location.c_str(), 791 &error_msg)); 792 ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; 793 794 const char* location = dex_location.c_str(); 795 std::vector<std::unique_ptr<const DexFile>> dex_files; 796 ASSERT_TRUE(DexFile::Open(location, location, true, &error_msg, &dex_files)); 797 EXPECT_EQ(dex_files.size(), 1U); 798 std::unique_ptr<const DexFile>& old_dex_file = dex_files[0]; 799 800 for (const OatDexFile* oat_dex_file : odex_file->GetOatDexFiles()) { 801 std::unique_ptr<const DexFile> new_dex_file = oat_dex_file->OpenDexFile(&error_msg); 802 ASSERT_TRUE(new_dex_file != nullptr); 803 uint32_t class_def_count = new_dex_file->NumClassDefs(); 804 ASSERT_LT(class_def_count, std::numeric_limits<uint16_t>::max()); 805 ASSERT_GE(class_def_count, 2U); 806 807 // The new layout swaps the classes at indexes 0 and 1. 808 std::string old_class0 = old_dex_file->PrettyType(old_dex_file->GetClassDef(0).class_idx_); 809 std::string old_class1 = old_dex_file->PrettyType(old_dex_file->GetClassDef(1).class_idx_); 810 std::string new_class0 = new_dex_file->PrettyType(new_dex_file->GetClassDef(0).class_idx_); 811 std::string new_class1 = new_dex_file->PrettyType(new_dex_file->GetClassDef(1).class_idx_); 812 EXPECT_EQ(old_class0, new_class1); 813 EXPECT_EQ(old_class1, new_class0); 814 } 815 816 EXPECT_EQ(odex_file->GetCompilerFilter(), CompilerFilter::kSpeedProfile); 817 818 if (!app_image_file_name.empty()) { 819 // Go peek at the image header to make sure it was large enough to contain the class. 820 std::unique_ptr<File> file(OS::OpenFileForReading(app_image_file_name.c_str())); 821 ImageHeader image_header; 822 bool success = file->ReadFully(&image_header, sizeof(image_header)); 823 ASSERT_TRUE(success); 824 ASSERT_TRUE(image_header.IsValid()); 825 EXPECT_GT(image_header.GetImageSection(ImageHeader::kSectionObjects).Size(), 0u); 826 } 827 } 828 829 // Check whether the dex2oat run was really successful. 830 void CheckValidity() { 831 if (kIsTargetBuild) { 832 CheckTargetValidity(); 833 } else { 834 CheckHostValidity(); 835 } 836 } 837 838 void CheckTargetValidity() { 839 // TODO: Ignore for now. 840 } 841 842 // On the host, we can get the dex2oat output. Here, look for "dex2oat took." 843 void CheckHostValidity() { 844 EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_; 845 } 846 }; 847 848 TEST_F(Dex2oatLayoutTest, TestLayout) { 849 RunTest(/* app-image */ false); 850 } 851 852 TEST_F(Dex2oatLayoutTest, TestLayoutAppImage) { 853 RunTest(/* app-image */ true); 854 } 855 856 TEST_F(Dex2oatLayoutTest, TestVdexLayout) { 857 RunTestVDex(); 858 } 859 860 class Dex2oatUnquickenTest : public Dex2oatTest { 861 protected: 862 void RunUnquickenMultiDex() { 863 std::string dex_location = GetScratchDir() + "/UnquickenMultiDex.jar"; 864 std::string odex_location = GetOdexDir() + "/UnquickenMultiDex.odex"; 865 std::string vdex_location = GetOdexDir() + "/UnquickenMultiDex.vdex"; 866 Copy(GetTestDexFileName("MultiDex"), dex_location); 867 868 std::unique_ptr<File> vdex_file1(OS::CreateEmptyFile(vdex_location.c_str())); 869 CHECK(vdex_file1 != nullptr) << vdex_location; 870 // Quicken the dex file into a vdex file. 871 { 872 std::string input_vdex = "--input-vdex-fd=-1"; 873 std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd()); 874 GenerateOdexForTest(dex_location, 875 odex_location, 876 CompilerFilter::kQuicken, 877 { input_vdex, output_vdex }, 878 /* expect_success */ true, 879 /* use_fd */ true); 880 EXPECT_GT(vdex_file1->GetLength(), 0u); 881 } 882 // Unquicken by running the verify compiler filter on the vdex file. 883 { 884 std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_file1->Fd()); 885 std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_file1->Fd()); 886 GenerateOdexForTest(dex_location, 887 odex_location, 888 CompilerFilter::kVerify, 889 { input_vdex, output_vdex }, 890 /* expect_success */ true, 891 /* use_fd */ true); 892 } 893 ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file"; 894 CheckResult(dex_location, odex_location); 895 ASSERT_TRUE(success_); 896 } 897 898 void CheckResult(const std::string& dex_location, const std::string& odex_location) { 899 std::string error_msg; 900 std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), 901 odex_location.c_str(), 902 nullptr, 903 nullptr, 904 false, 905 /*low_4gb*/false, 906 dex_location.c_str(), 907 &error_msg)); 908 ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; 909 ASSERT_GE(odex_file->GetOatDexFiles().size(), 1u); 910 911 // Iterate over the dex files and ensure there is no quickened instruction. 912 for (const OatDexFile* oat_dex_file : odex_file->GetOatDexFiles()) { 913 std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg); 914 for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { 915 const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); 916 const uint8_t* class_data = dex_file->GetClassData(class_def); 917 if (class_data != nullptr) { 918 for (ClassDataItemIterator class_it(*dex_file, class_data); 919 class_it.HasNext(); 920 class_it.Next()) { 921 if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) { 922 for (CodeItemIterator it(*class_it.GetMethodCodeItem()); !it.Done(); it.Advance()) { 923 Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction()); 924 ASSERT_FALSE(inst->IsQuickened()); 925 } 926 } 927 } 928 } 929 } 930 } 931 } 932 }; 933 934 TEST_F(Dex2oatUnquickenTest, UnquickenMultiDex) { 935 RunUnquickenMultiDex(); 936 } 937 938 class Dex2oatWatchdogTest : public Dex2oatTest { 939 protected: 940 void RunTest(bool expect_success, const std::vector<std::string>& extra_args = {}) { 941 std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar"; 942 std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex"; 943 944 Copy(GetTestDexFileName(), dex_location); 945 946 std::vector<std::string> copy(extra_args); 947 948 std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap"; 949 copy.push_back("--swap-file=" + swap_location); 950 GenerateOdexForTest(dex_location, 951 odex_location, 952 CompilerFilter::kSpeed, 953 copy, 954 expect_success); 955 } 956 957 std::string GetTestDexFileName() { 958 return GetDexSrc1(); 959 } 960 }; 961 962 TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) { 963 // Check with default. 964 RunTest(true); 965 966 // Check with ten minutes. 967 RunTest(true, { "--watchdog-timeout=600000" }); 968 } 969 970 TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) { 971 // Check with ten milliseconds. 972 RunTest(false, { "--watchdog-timeout=10" }); 973 } 974 975 class Dex2oatReturnCodeTest : public Dex2oatTest { 976 protected: 977 int RunTest(const std::vector<std::string>& extra_args = {}) { 978 std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar"; 979 std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex"; 980 981 Copy(GetTestDexFileName(), dex_location); 982 983 std::string error_msg; 984 return GenerateOdexForTestWithStatus({dex_location}, 985 odex_location, 986 CompilerFilter::kSpeed, 987 &error_msg, 988 extra_args); 989 } 990 991 std::string GetTestDexFileName() { 992 return GetDexSrc1(); 993 } 994 }; 995 996 TEST_F(Dex2oatReturnCodeTest, TestCreateRuntime) { 997 TEST_DISABLED_FOR_MEMORY_TOOL(); // b/19100793 998 int status = RunTest({ "--boot-image=/this/does/not/exist/yolo.oat" }); 999 EXPECT_EQ(static_cast<int>(dex2oat::ReturnCode::kCreateRuntime), WEXITSTATUS(status)) << output_; 1000 } 1001 1002 class Dex2oatClassLoaderContextTest : public Dex2oatTest { 1003 protected: 1004 void RunTest(const char* class_loader_context, 1005 const char* expected_classpath_key, 1006 bool expected_success, 1007 bool use_second_source = false) { 1008 std::string dex_location = GetUsedDexLocation(); 1009 std::string odex_location = GetUsedOatLocation(); 1010 1011 Copy(use_second_source ? GetDexSrc2() : GetDexSrc1(), dex_location); 1012 1013 std::string error_msg; 1014 std::vector<std::string> extra_args; 1015 if (class_loader_context != nullptr) { 1016 extra_args.push_back(std::string("--class-loader-context=") + class_loader_context); 1017 } 1018 auto check_oat = [expected_classpath_key](const OatFile& oat_file) { 1019 ASSERT_TRUE(expected_classpath_key != nullptr); 1020 const char* classpath = oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey); 1021 ASSERT_TRUE(classpath != nullptr); 1022 ASSERT_STREQ(expected_classpath_key, classpath); 1023 }; 1024 1025 GenerateOdexForTest(dex_location, 1026 odex_location, 1027 CompilerFilter::kQuicken, 1028 extra_args, 1029 expected_success, 1030 /*use_fd*/ false, 1031 check_oat); 1032 } 1033 1034 std::string GetUsedDexLocation() { 1035 return GetScratchDir() + "/Context.jar"; 1036 } 1037 1038 std::string GetUsedOatLocation() { 1039 return GetOdexDir() + "/Context.odex"; 1040 } 1041 1042 const char* kEmptyClassPathKey = "PCL[]"; 1043 }; 1044 1045 TEST_F(Dex2oatClassLoaderContextTest, InvalidContext) { 1046 RunTest("Invalid[]", /*expected_classpath_key*/ nullptr, /*expected_success*/ false); 1047 } 1048 1049 TEST_F(Dex2oatClassLoaderContextTest, EmptyContext) { 1050 RunTest("PCL[]", kEmptyClassPathKey, /*expected_success*/ true); 1051 } 1052 1053 TEST_F(Dex2oatClassLoaderContextTest, SpecialContext) { 1054 RunTest(OatFile::kSpecialSharedLibrary, 1055 OatFile::kSpecialSharedLibrary, 1056 /*expected_success*/ true); 1057 } 1058 1059 TEST_F(Dex2oatClassLoaderContextTest, ContextWithTheSourceDexFiles) { 1060 std::string context = "PCL[" + GetUsedDexLocation() + "]"; 1061 RunTest(context.c_str(), kEmptyClassPathKey, /*expected_success*/ true); 1062 } 1063 1064 TEST_F(Dex2oatClassLoaderContextTest, ContextWithOtherDexFiles) { 1065 std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Nested"); 1066 1067 std::string context = "PCL[" + dex_files[0]->GetLocation() + "]"; 1068 std::string expected_classpath_key = "PCL[" + 1069 dex_files[0]->GetLocation() + "*" + std::to_string(dex_files[0]->GetLocationChecksum()) + "]"; 1070 RunTest(context.c_str(), expected_classpath_key.c_str(), true); 1071 } 1072 1073 TEST_F(Dex2oatClassLoaderContextTest, ContextWithStrippedDexFiles) { 1074 std::string stripped_classpath = GetScratchDir() + "/stripped_classpath.jar"; 1075 Copy(GetStrippedDexSrc1(), stripped_classpath); 1076 1077 std::string context = "PCL[" + stripped_classpath + "]"; 1078 // Expect an empty context because stripped dex files cannot be open. 1079 RunTest(context.c_str(), kEmptyClassPathKey , /*expected_success*/ true); 1080 } 1081 1082 TEST_F(Dex2oatClassLoaderContextTest, ContextWithStrippedDexFilesBackedByOdex) { 1083 std::string stripped_classpath = GetScratchDir() + "/stripped_classpath.jar"; 1084 std::string odex_for_classpath = GetOdexDir() + "/stripped_classpath.odex"; 1085 1086 Copy(GetDexSrc1(), stripped_classpath); 1087 1088 GenerateOdexForTest(stripped_classpath, 1089 odex_for_classpath, 1090 CompilerFilter::kQuicken, 1091 {}, 1092 true); 1093 1094 // Strip the dex file 1095 Copy(GetStrippedDexSrc1(), stripped_classpath); 1096 1097 std::string context = "PCL[" + stripped_classpath + "]"; 1098 std::string expected_classpath_key; 1099 { 1100 // Open the oat file to get the expected classpath. 1101 OatFileAssistant oat_file_assistant(stripped_classpath.c_str(), kRuntimeISA, false); 1102 std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile()); 1103 std::vector<std::unique_ptr<const DexFile>> oat_dex_files = 1104 OatFileAssistant::LoadDexFiles(*oat_file, stripped_classpath.c_str()); 1105 expected_classpath_key = "PCL["; 1106 for (size_t i = 0; i < oat_dex_files.size(); i++) { 1107 if (i > 0) { 1108 expected_classpath_key + ":"; 1109 } 1110 expected_classpath_key += oat_dex_files[i]->GetLocation() + "*" + 1111 std::to_string(oat_dex_files[i]->GetLocationChecksum()); 1112 } 1113 expected_classpath_key += "]"; 1114 } 1115 1116 RunTest(context.c_str(), 1117 expected_classpath_key.c_str(), 1118 /*expected_success*/ true, 1119 /*use_second_source*/ true); 1120 } 1121 1122 TEST_F(Dex2oatClassLoaderContextTest, ContextWithNotExistentDexFiles) { 1123 std::string context = "PCL[does_not_exists.dex]"; 1124 // Expect an empty context because stripped dex files cannot be open. 1125 RunTest(context.c_str(), kEmptyClassPathKey, /*expected_success*/ true); 1126 } 1127 1128 TEST_F(Dex2oatClassLoaderContextTest, ChainContext) { 1129 std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); 1130 std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); 1131 1132 std::string context = "PCL[" + GetTestDexFileName("Nested") + "];" + 1133 "DLC[" + GetTestDexFileName("MultiDex") + "]"; 1134 std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "];" + 1135 "DLC[" + CreateClassPathWithChecksums(dex_files2) + "]"; 1136 1137 RunTest(context.c_str(), expected_classpath_key.c_str(), true); 1138 } 1139 1140 class Dex2oatDeterminism : public Dex2oatTest {}; 1141 1142 TEST_F(Dex2oatDeterminism, UnloadCompile) { 1143 if (!kUseReadBarrier && 1144 gc::kCollectorTypeDefault != gc::kCollectorTypeCMS && 1145 gc::kCollectorTypeDefault != gc::kCollectorTypeMS) { 1146 LOG(INFO) << "Test requires determinism support."; 1147 return; 1148 } 1149 Runtime* const runtime = Runtime::Current(); 1150 std::string out_dir = GetScratchDir(); 1151 const std::string base_oat_name = out_dir + "/base.oat"; 1152 const std::string base_vdex_name = out_dir + "/base.vdex"; 1153 const std::string unload_oat_name = out_dir + "/unload.oat"; 1154 const std::string unload_vdex_name = out_dir + "/unload.vdex"; 1155 const std::string no_unload_oat_name = out_dir + "/nounload.oat"; 1156 const std::string no_unload_vdex_name = out_dir + "/nounload.vdex"; 1157 const std::string app_image_name = out_dir + "/unload.art"; 1158 std::string error_msg; 1159 const std::vector<gc::space::ImageSpace*>& spaces = runtime->GetHeap()->GetBootImageSpaces(); 1160 ASSERT_GT(spaces.size(), 0u); 1161 const std::string image_location = spaces[0]->GetImageLocation(); 1162 // Without passing in an app image, it will unload in between compilations. 1163 const int res = GenerateOdexForTestWithStatus( 1164 GetLibCoreDexFileNames(), 1165 base_oat_name, 1166 CompilerFilter::Filter::kQuicken, 1167 &error_msg, 1168 {"--force-determinism", "--avoid-storing-invocation"}); 1169 EXPECT_EQ(res, 0); 1170 Copy(base_oat_name, unload_oat_name); 1171 Copy(base_vdex_name, unload_vdex_name); 1172 std::unique_ptr<File> unload_oat(OS::OpenFileForReading(unload_oat_name.c_str())); 1173 std::unique_ptr<File> unload_vdex(OS::OpenFileForReading(unload_vdex_name.c_str())); 1174 ASSERT_TRUE(unload_oat != nullptr); 1175 ASSERT_TRUE(unload_vdex != nullptr); 1176 EXPECT_GT(unload_oat->GetLength(), 0u); 1177 EXPECT_GT(unload_vdex->GetLength(), 0u); 1178 // Regenerate with an app image to disable the dex2oat unloading and verify that the output is 1179 // the same. 1180 const int res2 = GenerateOdexForTestWithStatus( 1181 GetLibCoreDexFileNames(), 1182 base_oat_name, 1183 CompilerFilter::Filter::kQuicken, 1184 &error_msg, 1185 {"--force-determinism", "--avoid-storing-invocation", "--app-image-file=" + app_image_name}); 1186 EXPECT_EQ(res2, 0); 1187 Copy(base_oat_name, no_unload_oat_name); 1188 Copy(base_vdex_name, no_unload_vdex_name); 1189 std::unique_ptr<File> no_unload_oat(OS::OpenFileForReading(no_unload_oat_name.c_str())); 1190 std::unique_ptr<File> no_unload_vdex(OS::OpenFileForReading(no_unload_vdex_name.c_str())); 1191 ASSERT_TRUE(no_unload_oat != nullptr); 1192 ASSERT_TRUE(no_unload_vdex != nullptr); 1193 EXPECT_GT(no_unload_oat->GetLength(), 0u); 1194 EXPECT_GT(no_unload_vdex->GetLength(), 0u); 1195 // Verify that both of the files are the same (odex and vdex). 1196 EXPECT_EQ(unload_oat->GetLength(), no_unload_oat->GetLength()); 1197 EXPECT_EQ(unload_vdex->GetLength(), no_unload_vdex->GetLength()); 1198 EXPECT_EQ(unload_oat->Compare(no_unload_oat.get()), 0) 1199 << unload_oat_name << " " << no_unload_oat_name; 1200 EXPECT_EQ(unload_vdex->Compare(no_unload_vdex.get()), 0) 1201 << unload_vdex_name << " " << no_unload_vdex_name; 1202 // App image file. 1203 std::unique_ptr<File> app_image_file(OS::OpenFileForReading(app_image_name.c_str())); 1204 ASSERT_TRUE(app_image_file != nullptr); 1205 EXPECT_GT(app_image_file->GetLength(), 0u); 1206 } 1207 1208 // Test that dexlayout section info is correctly written to the oat file for profile based 1209 // compilation. 1210 TEST_F(Dex2oatTest, LayoutSections) { 1211 using Hotness = ProfileCompilationInfo::MethodHotness; 1212 std::unique_ptr<const DexFile> dex(OpenTestDexFile("ManyMethods")); 1213 ScratchFile profile_file; 1214 // We can only layout method indices with code items, figure out which ones have this property 1215 // first. 1216 std::vector<uint16_t> methods; 1217 { 1218 const DexFile::TypeId* type_id = dex->FindTypeId("LManyMethods;"); 1219 dex::TypeIndex type_idx = dex->GetIndexForTypeId(*type_id); 1220 const DexFile::ClassDef* class_def = dex->FindClassDef(type_idx); 1221 ClassDataItemIterator it(*dex, dex->GetClassData(*class_def)); 1222 it.SkipAllFields(); 1223 std::set<size_t> code_item_offsets; 1224 for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { 1225 const uint16_t method_idx = it.GetMemberIndex(); 1226 const size_t code_item_offset = it.GetMethodCodeItemOffset(); 1227 if (code_item_offsets.insert(code_item_offset).second) { 1228 // Unique code item, add the method index. 1229 methods.push_back(method_idx); 1230 } 1231 } 1232 DCHECK(!it.HasNext()); 1233 } 1234 ASSERT_GE(methods.size(), 8u); 1235 std::vector<uint16_t> hot_methods = {methods[1], methods[3], methods[5]}; 1236 std::vector<uint16_t> startup_methods = {methods[1], methods[2], methods[7]}; 1237 std::vector<uint16_t> post_methods = {methods[0], methods[2], methods[6]}; 1238 // Here, we build the profile from the method lists. 1239 ProfileCompilationInfo info; 1240 info.AddMethodsForDex( 1241 static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup), 1242 dex.get(), 1243 hot_methods.begin(), 1244 hot_methods.end()); 1245 info.AddMethodsForDex( 1246 Hotness::kFlagStartup, 1247 dex.get(), 1248 startup_methods.begin(), 1249 startup_methods.end()); 1250 info.AddMethodsForDex( 1251 Hotness::kFlagPostStartup, 1252 dex.get(), 1253 post_methods.begin(), 1254 post_methods.end()); 1255 for (uint16_t id : hot_methods) { 1256 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsHot()); 1257 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsStartup()); 1258 } 1259 for (uint16_t id : startup_methods) { 1260 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsStartup()); 1261 } 1262 for (uint16_t id : post_methods) { 1263 EXPECT_TRUE(info.GetMethodHotness(MethodReference(dex.get(), id)).IsPostStartup()); 1264 } 1265 // Save the profile since we want to use it with dex2oat to produce an oat file. 1266 ASSERT_TRUE(info.Save(profile_file.GetFd())); 1267 // Generate a profile based odex. 1268 const std::string dir = GetScratchDir(); 1269 const std::string oat_filename = dir + "/base.oat"; 1270 const std::string vdex_filename = dir + "/base.vdex"; 1271 std::string error_msg; 1272 const int res = GenerateOdexForTestWithStatus( 1273 {dex->GetLocation()}, 1274 oat_filename, 1275 CompilerFilter::Filter::kQuicken, 1276 &error_msg, 1277 {"--profile-file=" + profile_file.GetFilename()}); 1278 EXPECT_EQ(res, 0); 1279 1280 // Open our generated oat file. 1281 std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_filename.c_str(), 1282 oat_filename.c_str(), 1283 nullptr, 1284 nullptr, 1285 false, 1286 /*low_4gb*/false, 1287 dex->GetLocation().c_str(), 1288 &error_msg)); 1289 ASSERT_TRUE(odex_file != nullptr); 1290 std::vector<const OatDexFile*> oat_dex_files = odex_file->GetOatDexFiles(); 1291 ASSERT_EQ(oat_dex_files.size(), 1u); 1292 // Check that the code sections match what we expect. 1293 for (const OatDexFile* oat_dex : oat_dex_files) { 1294 const DexLayoutSections* const sections = oat_dex->GetDexLayoutSections(); 1295 // Testing of logging the sections. 1296 ASSERT_TRUE(sections != nullptr); 1297 LOG(INFO) << *sections; 1298 1299 // Load the sections into temporary variables for convenience. 1300 const DexLayoutSection& code_section = 1301 sections->sections_[static_cast<size_t>(DexLayoutSections::SectionType::kSectionTypeCode)]; 1302 const DexLayoutSection::Subsection& section_hot_code = 1303 code_section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeHot)]; 1304 const DexLayoutSection::Subsection& section_sometimes_used = 1305 code_section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeSometimesUsed)]; 1306 const DexLayoutSection::Subsection& section_startup_only = 1307 code_section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeStartupOnly)]; 1308 const DexLayoutSection::Subsection& section_unused = 1309 code_section.parts_[static_cast<size_t>(LayoutType::kLayoutTypeUnused)]; 1310 1311 // All the sections should be non-empty. 1312 EXPECT_GT(section_hot_code.size_, 0u); 1313 EXPECT_GT(section_sometimes_used.size_, 0u); 1314 EXPECT_GT(section_startup_only.size_, 0u); 1315 EXPECT_GT(section_unused.size_, 0u); 1316 1317 // Open the dex file since we need to peek at the code items to verify the layout matches what 1318 // we expect. 1319 std::unique_ptr<const DexFile> dex_file(oat_dex->OpenDexFile(&error_msg)); 1320 ASSERT_TRUE(dex_file != nullptr) << error_msg; 1321 const DexFile::TypeId* type_id = dex_file->FindTypeId("LManyMethods;"); 1322 ASSERT_TRUE(type_id != nullptr); 1323 dex::TypeIndex type_idx = dex_file->GetIndexForTypeId(*type_id); 1324 const DexFile::ClassDef* class_def = dex_file->FindClassDef(type_idx); 1325 ASSERT_TRUE(class_def != nullptr); 1326 1327 // Count how many code items are for each category, there should be at least one per category. 1328 size_t hot_count = 0; 1329 size_t post_startup_count = 0; 1330 size_t startup_count = 0; 1331 size_t unused_count = 0; 1332 // Visit all of the methdos of the main class and cross reference the method indices to their 1333 // corresponding code item offsets to verify the layout. 1334 ClassDataItemIterator it(*dex_file, dex_file->GetClassData(*class_def)); 1335 it.SkipAllFields(); 1336 for (; it.HasNextDirectMethod() || it.HasNextVirtualMethod(); it.Next()) { 1337 const size_t method_idx = it.GetMemberIndex(); 1338 const size_t code_item_offset = it.GetMethodCodeItemOffset(); 1339 const bool is_hot = ContainsElement(hot_methods, method_idx); 1340 const bool is_startup = ContainsElement(startup_methods, method_idx); 1341 const bool is_post_startup = ContainsElement(post_methods, method_idx); 1342 if (is_hot) { 1343 // Hot is highest precedence, check that the hot methods are in the hot section. 1344 EXPECT_LT(code_item_offset - section_hot_code.offset_, section_hot_code.size_); 1345 ++hot_count; 1346 } else if (is_post_startup) { 1347 // Post startup is sometimes used section. 1348 EXPECT_LT(code_item_offset - section_sometimes_used.offset_, section_sometimes_used.size_); 1349 ++post_startup_count; 1350 } else if (is_startup) { 1351 // Startup at this point means not hot or post startup, these must be startup only then. 1352 EXPECT_LT(code_item_offset - section_startup_only.offset_, section_startup_only.size_); 1353 ++startup_count; 1354 } else { 1355 // If no flags are set, the method should be unused. 1356 EXPECT_LT(code_item_offset - section_unused.offset_, section_unused.size_); 1357 ++unused_count; 1358 } 1359 } 1360 DCHECK(!it.HasNext()); 1361 EXPECT_GT(hot_count, 0u); 1362 EXPECT_GT(post_startup_count, 0u); 1363 EXPECT_GT(startup_count, 0u); 1364 EXPECT_GT(unused_count, 0u); 1365 } 1366 } 1367 1368 } // namespace art 1369