From 2f27abd6685f9be3a0923552afa1147751b438f9 Mon Sep 17 00:00:00 2001 From: Richard Uhler Date: Tue, 31 Jan 2017 14:02:34 +0000 Subject: [PATCH] Revert^2 "OatFileAssistant: look at vdex file for IsDexOptNeeded" This reverts commit ba037b121edc7259b3fa5607e6730402ce2ca61e. Test: m test-art-host, with new oat file assistant test added. Bug: 34339100 Change-Id: I1ab9ec9f6ec299be7b8144612b71e6054d84412c --- runtime/oat_file_assistant.cc | 144 ++++++++++++++++++++++++++++++------- runtime/oat_file_assistant.h | 10 +++ runtime/oat_file_assistant_test.cc | 139 ++++++++++++++++++++++++++++++----- 3 files changed, 252 insertions(+), 41 deletions(-) diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index b19ace546..07d7b5a0f 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -33,6 +33,7 @@ #include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "utils.h" +#include "vdex_file.h" namespace art { @@ -216,28 +217,38 @@ std::string OatFileAssistant::GetStatusDump() { bool oat_file_exists = false; bool odex_file_exists = false; if (oat_.Status() != kOatCannotOpen) { - // If we can open the file, neither Filename nor GetFile should return null. + // If we can open the file, Filename should not return null. CHECK(oat_.Filename() != nullptr); - CHECK(oat_.GetFile() != nullptr); oat_file_exists = true; - status << *oat_.Filename() << " [compilation_filter="; - status << CompilerFilter::NameOfFilter(oat_.GetFile()->GetCompilerFilter()); - status << ", status=" << oat_.Status(); + status << *oat_.Filename() << "[status=" << oat_.Status() << ", "; + const OatFile* file = oat_.GetFile(); + if (file == nullptr) { + // If the file is null even though the status is not kOatCannotOpen, it + // means we must have a vdex file with no corresponding oat file. In + // this case we cannot determine the compilation filter. Indicate that + // we have only the vdex file instead. + status << "vdex-only"; + } else { + status << "compilation_filter=" << CompilerFilter::NameOfFilter(file->GetCompilerFilter()); + } } if (odex_.Status() != kOatCannotOpen) { - // If we can open the file, neither Filename nor GetFile should return null. + // If we can open the file, Filename should not return null. CHECK(odex_.Filename() != nullptr); - CHECK(odex_.GetFile() != nullptr); odex_file_exists = true; if (oat_file_exists) { status << "] "; } - status << *odex_.Filename() << " [compilation_filter="; - status << CompilerFilter::NameOfFilter(odex_.GetFile()->GetCompilerFilter()); - status << ", status=" << odex_.Status(); + status << *odex_.Filename() << "[status=" << odex_.Status() << ", "; + const OatFile* file = odex_.GetFile(); + if (file == nullptr) { + status << "vdex-only"; + } else { + status << "compilation_filter=" << CompilerFilter::NameOfFilter(file->GetCompilerFilter()); + } } if (!oat_file_exists && !odex_file_exists) { @@ -303,24 +314,60 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() { return oat_.Status(); } -OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) { - // Verify the ART_USE_READ_BARRIER state. - const bool is_cc = file.GetOatHeader().IsConcurrentCopying(); - constexpr bool kRuntimeIsCC = kUseReadBarrier; - if (is_cc != kRuntimeIsCC) { - return kOatCannotOpen; +bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* error_msg) { + if (file.GetHeader().GetNumberOfDexFiles() <= 0) { + VLOG(oat) << "Vdex does not contain any dex files"; + return false; } - // Verify the dex checksum. + // TODO: Use GetRequiredDexChecksum to get secondary checksums as well, not + // just the primary. Because otherwise we may fail to see a secondary + // checksum failure in the case when the original (multidex) files are + // stripped but we have a newer odex file. + const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum(); + if (dex_checksum_pointer != nullptr) { + uint32_t actual_checksum = file.GetLocationChecksum(0); + if (*dex_checksum_pointer != actual_checksum) { + VLOG(oat) << "Dex checksum does not match for primary dex: " << dex_location_ + << ". Expected: " << *dex_checksum_pointer + << ", Actual: " << actual_checksum; + return false; + } + } + + // Verify the dex checksums for any secondary multidex files + for (uint32_t i = 1; i < file.GetHeader().GetNumberOfDexFiles(); i++) { + std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str()); + uint32_t expected_secondary_checksum = 0; + if (DexFile::GetChecksum(secondary_dex_location.c_str(), + &expected_secondary_checksum, + error_msg)) { + uint32_t actual_secondary_checksum = file.GetLocationChecksum(i); + if (expected_secondary_checksum != actual_secondary_checksum) { + VLOG(oat) << "Dex checksum does not match for secondary dex: " + << secondary_dex_location + << ". Expected: " << expected_secondary_checksum + << ", Actual: " << actual_secondary_checksum; + return false; + } + } else { + // If we can't get the checksum for the secondary location, we assume + // the dex checksum is up to date for this and all other secondary dex + // files. + break; + } + } + return true; +} + +bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) { // Note: GetOatDexFile will return null if the dex checksum doesn't match // what we provide, which verifies the primary dex checksum for us. - std::string error_msg; const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum(); const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile( - dex_location_.c_str(), dex_checksum_pointer, &error_msg); + dex_location_.c_str(), dex_checksum_pointer, error_msg); if (oat_dex_file == nullptr) { - LOG(ERROR) << error_msg; - return kOatDexOutOfDate; + return false; } // Verify the dex checksums for any secondary multidex files @@ -335,7 +382,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& uint32_t expected_secondary_checksum = 0; if (DexFile::GetChecksum(secondary_dex_location.c_str(), - &expected_secondary_checksum, &error_msg)) { + &expected_secondary_checksum, error_msg)) { uint32_t actual_secondary_checksum = secondary_oat_dex_file->GetDexFileLocationChecksum(); if (expected_secondary_checksum != actual_secondary_checksum) { @@ -343,7 +390,7 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& << secondary_dex_location << ". Expected: " << expected_secondary_checksum << ", Actual: " << actual_secondary_checksum; - return kOatDexOutOfDate; + return false; } } else { // If we can't get the checksum for the secondary location, we assume @@ -352,6 +399,35 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& break; } } + return true; +} + +OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile& file) { + // Verify the ART_USE_READ_BARRIER state. + // TODO: Don't fully reject files due to read barrier state. If they contain + // compiled code and are otherwise okay, we should return something like + // kOatRelocationOutOfDate. If they don't contain compiled code, the read + // barrier state doesn't matter. + const bool is_cc = file.GetOatHeader().IsConcurrentCopying(); + constexpr bool kRuntimeIsCC = kUseReadBarrier; + if (is_cc != kRuntimeIsCC) { + return kOatCannotOpen; + } + + // Verify the dex checksum. + std::string error_msg; + if (kIsVdexEnabled) { + VdexFile* vdex = file.GetVdexFile(); + if (!DexChecksumUpToDate(*vdex, &error_msg)) { + LOG(ERROR) << error_msg; + return kOatDexOutOfDate; + } + } else { + if (!DexChecksumUpToDate(file, &error_msg)) { + LOG(ERROR) << error_msg; + return kOatDexOutOfDate; + } + } CompilerFilter::Filter current_compiler_filter = file.GetCompilerFilter(); @@ -777,7 +853,27 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileInfo::Status() { status_attempted_ = true; const OatFile* file = GetFile(); if (file == nullptr) { - status_ = kOatCannotOpen; + // Check to see if there is a vdex file we can make use of. + std::string error_msg; + std::string vdex_filename = ReplaceFileExtension(filename_, "vdex"); + std::unique_ptr vdex = VdexFile::Open(vdex_filename, + /*writeable*/false, + /*low_4gb*/false, + &error_msg); + if (vdex == nullptr) { + status_ = kOatCannotOpen; + VLOG(oat) << "unable to open vdex file " << vdex_filename << ": " << error_msg; + } else { + if (oat_file_assistant_->DexChecksumUpToDate(*vdex, &error_msg)) { + // The vdex file does not contain enough information to determine + // whether it is up to date with respect to the boot image, so we + // assume it is out of date. + VLOG(oat) << error_msg; + status_ = kOatBootImageOutOfDate; + } else { + status_ = kOatDexOutOfDate; + } + } } else { status_ = oat_file_assistant_->GivenOatFileStatus(*file); VLOG(oat) << file->GetLocation() << " is " << status_ diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 588a698be..6d47ad222 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -379,6 +379,16 @@ class OatFileAssistant { // Return info for the best oat file. OatFileInfo& GetBestInfo(); + // Returns true if the dex checksums in the given vdex file are up to date + // with respect to the dex location. If the dex checksums are not up to + // date, error_msg is updated with a message describing the problem. + bool DexChecksumUpToDate(const VdexFile& file, std::string* error_msg); + + // Returns true if the dex checksums in the given oat file are up to date + // with respect to the dex location. If the dex checksums are not up to + // date, error_msg is updated with a message describing the problem. + bool DexChecksumUpToDate(const OatFile& file, std::string* error_msg); + // Return the status for a given opened oat file with respect to the dex // location. OatStatus GivenOatFileStatus(const OatFile& file); diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 577200847..102285a4d 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -111,27 +111,84 @@ TEST_F(OatFileAssistantTest, OatUpToDate) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } -// Case: We have a DEX file and ODEX file for a different dex location. -// Expect: The status is kDex2OatNeeded. -TEST_F(OatFileAssistantTest, OatForDifferentDex) { - // Generate an odex file for OatForDifferentDex_A.jar - std::string dex_location_a = GetScratchDir() + "/OatForDifferentDex_A.jar"; - std::string odex_location = GetOdexDir() + "/OatForDifferentDex.odex"; - Copy(GetDexSrc1(), dex_location_a); - GenerateOdexForTest(dex_location_a, odex_location, CompilerFilter::kSpeed); - - // Try to use that odex file for OatForDifferentDex.jar - std::string dex_location = GetScratchDir() + "/OatForDifferentDex.jar"; +// Case: We have a DEX file and up-to-date (ODEX) VDEX file for it, but no +// ODEX file. +TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) { + // This test case is only meaningful if vdex is enabled. + if (!kIsVdexEnabled) { + return; + } + + std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOdex.jar"; + std::string oat_location = GetOdexDir() + "/VdexUpToDateNoOdex.oat"; + Copy(GetDexSrc1(), dex_location); - OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + // Generating and deleting the oat file should have the side effect of + // creating an up-to-date vdex file. + GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(oat_location.c_str())); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), + oat_location.c_str(), + kRuntimeISA, + false); + + // Even though the vdex file is up to date, because we don't have the oat + // file, we can't know that the vdex depends on the boot image and is up to + // date with respect to the boot image. Instead we must assume the vdex file + // depends on the boot image and is out of date with respect to the boot + // image. + EXPECT_EQ(-OatFileAssistant::kDex2OatForBootImage, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); + + // Make sure we don't crash in this case when we dump the status. We don't + // care what the actual dumped value is. + oat_file_assistant.GetStatusDump(); +} + +// Case: We have a DEX file and empty VDEX and ODEX files. +TEST_F(OatFileAssistantTest, EmptyVdexOdex) { + std::string dex_location = GetScratchDir() + "/EmptyVdexOdex.jar"; + std::string odex_location = GetOdexDir() + "/EmptyVdexOdex.oat"; + std::string vdex_location = GetOdexDir() + "/EmptyVdexOdex.vdex"; + + Copy(GetDexSrc1(), dex_location); + OS::CreateEmptyFile(vdex_location.c_str()); + OS::CreateEmptyFile(odex_location.c_str()); + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); +} - EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); - EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OdexFileStatus()); - EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus()); +// Case: We have a DEX file and up-to-date (OAT) VDEX file for it, but no OAT +// file. +TEST_F(OatFileAssistantTest, VdexUpToDateNoOat) { + // This test case is only meaningful if vdex is enabled. + if (!kIsVdexEnabled) { + return; + } + + std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOat.jar"; + std::string oat_location; + std::string error_msg; + ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename( + dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg; + + Copy(GetDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(oat_location.c_str())); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + + // Even though the vdex file is up to date, because we don't have the oat + // file, we can't know that the vdex depends on the boot image and is up to + // date with respect to the boot image. Instead we must assume the vdex file + // depends on the boot image and is out of date with respect to the boot + // image. + EXPECT_EQ(OatFileAssistant::kDex2OatForBootImage, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); } // Case: We have a DEX file and speed-profile OAT file for it. @@ -254,6 +311,56 @@ TEST_F(OatFileAssistantTest, OatDexOutOfDate) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } +// Case: We have a DEX file and an (ODEX) VDEX file out of date with respect +// to the dex checksum, but no ODEX file. +TEST_F(OatFileAssistantTest, VdexDexOutOfDate) { + // This test case is only meaningful if vdex is enabled. + if (!kIsVdexEnabled) { + return; + } + + std::string dex_location = GetScratchDir() + "/VdexDexOutOfDate.jar"; + std::string oat_location = GetOdexDir() + "/VdexDexOutOfDate.oat"; + + Copy(GetDexSrc1(), dex_location); + GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(oat_location.c_str())); + Copy(GetDexSrc2(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), + oat_location.c_str(), + kRuntimeISA, + false); + + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); +} + +// Case: We have a MultiDEX (ODEX) VDEX file where the secondary dex file is +// out of date and there is no corresponding ODEX file. +TEST_F(OatFileAssistantTest, VdexMultiDexSecondaryOutOfDate) { + // This test case is only meaningful if vdex is enabled. + if (!kIsVdexEnabled) { + return; + } + + std::string dex_location = GetScratchDir() + "/VdexMultiDexSecondaryOutOfDate.jar"; + std::string oat_location = GetOdexDir() + "/VdexMultiDexSecondaryOutOfDate.oat"; + + Copy(GetMultiDexSrc1(), dex_location); + GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed); + ASSERT_EQ(0, unlink(oat_location.c_str())); + Copy(GetMultiDexSrc2(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), + oat_location.c_str(), + kRuntimeISA, + false); + + EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch, + oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); +} + // Case: We have a DEX file and an OAT file out of date with respect to the // boot image. TEST_F(OatFileAssistantTest, OatImageOutOfDate) { @@ -945,6 +1052,4 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { // - Dex is stripped, don't have odex. // - Oat file corrupted after status check, before reload unexecutable // because it's unrelocated and no dex2oat -// * Test unrelocated specific target compilation type can be relocated to -// make it up to date. } // namespace art -- 2.11.0