diff options
| -rw-r--r-- | compiler/driver/compiler_options.h | 4 | ||||
| -rw-r--r-- | dex2oat/dex2oat.cc | 11 | ||||
| -rw-r--r-- | runtime/oat.cc | 4 | ||||
| -rw-r--r-- | runtime/oat.h | 2 | ||||
| -rw-r--r-- | runtime/oat_file.cc | 4 | ||||
| -rw-r--r-- | runtime/oat_file.h | 2 | ||||
| -rw-r--r-- | runtime/oat_file_assistant.cc | 22 | ||||
| -rw-r--r-- | runtime/oat_file_assistant_test.cc | 71 |
8 files changed, 118 insertions, 2 deletions
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 39372b36b8..a220959288 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -116,6 +116,10 @@ class CompilerOptions FINAL { return compiler_filter_ == CompilerOptions::kVerifyNone; } + bool IsExtractOnly() const { + return compiler_filter_ == CompilerOptions::kVerifyAtRuntime; + } + size_t GetHugeMethodThreshold() const { return huge_method_threshold_; } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 48716481cc..8e80961e43 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1055,6 +1055,9 @@ class Dex2Oat FINAL { key_value_store_->Put( OatHeader::kDebuggableKey, compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue); + key_value_store_->Put( + OatHeader::kExtractOnlyKey, + compiler_options_->IsExtractOnly() ? OatHeader::kTrueValue : OatHeader::kFalseValue); } // Parse the arguments from the command line. In case of an unrecognized option or impossible @@ -1332,7 +1335,13 @@ class Dex2Oat FINAL { return false; } - { + if (compiler_options_->IsExtractOnly()) { + // ExtractOnly oat files only contain non-quickened DEX code and are + // therefore independent of the image file. + image_file_location_oat_checksum_ = 0u; + image_file_location_oat_data_begin_ = 0u; + image_patch_delta_ = 0; + } else { TimingLogger::ScopedTiming t3("Loading image checksum", timings_); std::vector<gc::space::ImageSpace*> image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces(); diff --git a/runtime/oat.cc b/runtime/oat.cc index c787b9adb1..4948558f84 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -466,6 +466,10 @@ bool OatHeader::IsDebuggable() const { return IsKeyEnabled(OatHeader::kDebuggableKey); } +bool OatHeader::IsExtractOnly() const { + return IsKeyEnabled(OatHeader::kExtractOnlyKey); +} + bool OatHeader::IsKeyEnabled(const char* key) const { const char* key_value = GetStoreValueByKey(key); return (key_value != nullptr && strncmp(key_value, kTrueValue, sizeof(kTrueValue)) == 0); diff --git a/runtime/oat.h b/runtime/oat.h index 989e3f9d89..fde386f37e 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -38,6 +38,7 @@ class PACKED(4) OatHeader { static constexpr const char* kDex2OatHostKey = "dex2oat-host"; static constexpr const char* kPicKey = "pic"; static constexpr const char* kDebuggableKey = "debuggable"; + static constexpr const char* kExtractOnlyKey = "extract-only"; static constexpr const char* kClassPathKey = "classpath"; static constexpr const char* kBootClassPath = "bootclasspath"; @@ -106,6 +107,7 @@ class PACKED(4) OatHeader { size_t GetHeaderSize() const; bool IsPic() const; bool IsDebuggable() const; + bool IsExtractOnly() const; private: OatHeader(InstructionSet instruction_set, diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 8f321a092c..f912598eef 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1221,6 +1221,10 @@ bool OatFile::IsDebuggable() const { return GetOatHeader().IsDebuggable(); } +bool OatFile::IsExtractOnly() const { + return GetOatHeader().IsExtractOnly(); +} + static constexpr char kDexClassPathEncodingSeparator = '*'; std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index dbd75415a4..bcc2d33333 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -85,6 +85,8 @@ class OatFile { // Indicates whether the oat file was compiled with full debugging capability. bool IsDebuggable() const; + bool IsExtractOnly() const; + const std::string& GetLocation() const { return location_; } diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index a8f84a2bfa..6daade0648 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -461,6 +461,23 @@ bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) { } } + if (file.IsExtractOnly()) { + VLOG(oat) << "Oat file is extract-only. Image checksum test skipped."; + if (kIsDebugBuild) { + // Sanity check that no classes have compiled code. Does not test that + // the DEX code has not been quickened. + std::string error_msg; + for (const OatFile::OatDexFile* current : file.GetOatDexFiles()) { + const DexFile* const dex_file = current->OpenDexFile(&error_msg).release(); + DCHECK(dex_file != nullptr); + for (size_t i = 0, e = dex_file->NumClassDefs(); i < e; ++i) { + DCHECK_EQ(current->GetOatClass(i).GetType(), kOatClassNoneCompiled); + } + } + } + return false; + } + // Verify the image checksum const ImageInfo* image_info = GetImageInfo(); if (image_info == nullptr) { @@ -486,7 +503,10 @@ bool OatFileAssistant::GivenOatFileIsUpToDate(const OatFile& file) { return false; } - if (file.IsPic()) { + if (file.IsPic() || file.IsExtractOnly()) { + // Oat files compiled in PIC mode do not require relocation and extract-only + // oat files do not contain any compiled code. Skip the relocation test. + VLOG(oat) << "Oat relocation test skipped."; return true; } diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 25dcbe4c63..83d4457a1c 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -259,6 +259,26 @@ class OatFileAssistantTest : public CommonRuntimeTest { EXPECT_TRUE(odex_file->IsPic()); } + void GenerateExtractOnlyOdexForTest(const std::string& dex_location, + const std::string& odex_location) { + std::vector<std::string> args; + args.push_back("--dex-file=" + dex_location); + args.push_back("--oat-file=" + odex_location); + args.push_back("--compiler-filter=verify-at-runtime"); + std::string error_msg; + ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; + + // Verify the odex file was generated as expected. + std::unique_ptr<OatFile> odex_file(OatFile::Open( + odex_location.c_str(), odex_location.c_str(), nullptr, nullptr, + false, dex_location.c_str(), &error_msg)); + ASSERT_TRUE(odex_file.get() != nullptr) << error_msg; + EXPECT_TRUE(odex_file->IsExtractOnly()); + EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0u); + EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0u); + EXPECT_EQ(odex_file->GetOatHeader().GetImagePatchDelta(), 0); +} + private: // Reserve memory around where the image will be loaded so other memory // won't conflict when it comes time to load the image. @@ -488,6 +508,32 @@ TEST_F(OatFileAssistantTest, OatOutOfDate) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } +// Case: We have a DEX file and an extract-only ODEX file out of date relative +// to the DEX file. +// Expect: The status is kDex2OatNeeded. +TEST_F(OatFileAssistantTest, ExtractOnlyOdexOutOfDate) { + std::string dex_location = GetScratchDir() + "/ExtractOnlyOdexOutOfDate.jar"; + std::string odex_location = GetOdexDir() + "/ExtractOnlyOdexOutOfDate.odex"; + + // We create a dex, generate an oat for it, then overwrite the dex with a + // different dex to make the oat out of date. + Copy(GetDexSrc1(), dex_location); + GenerateExtractOnlyOdexForTest(dex_location.c_str(), odex_location.c_str()); + Copy(GetDexSrc2(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded()); + + EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); + EXPECT_TRUE(oat_file_assistant.OdexFileExists()); + EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate()); + EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate()); + EXPECT_FALSE(oat_file_assistant.OatFileExists()); + EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); + EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); +} + // Case: We have a DEX file and an ODEX file, but no OAT file. // Expect: The status is kPatchOatNeeded. TEST_F(OatFileAssistantTest, DexOdexNoOat) { @@ -784,6 +830,31 @@ TEST_F(OatFileAssistantTest, DexPicOdexNoOat) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } +// Case: We have a DEX file and a ExtractOnly ODEX file, but no OAT file. +// Expect: The status is kNoDexOptNeeded, because ExtractOnly contains no code. +TEST_F(OatFileAssistantTest, DexExtractOnlyOdexNoOat) { + std::string dex_location = GetScratchDir() + "/DexExtractOnlyOdexNoOat.jar"; + std::string odex_location = GetOdexDir() + "/DexExtractOnlyOdexNoOat.odex"; + + // Create the dex and odex files + Copy(GetDexSrc1(), dex_location); + GenerateExtractOnlyOdexForTest(dex_location, odex_location); + + // Verify the status. + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false); + + EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded()); + + EXPECT_FALSE(oat_file_assistant.IsInBootClassPath()); + EXPECT_TRUE(oat_file_assistant.OdexFileExists()); + EXPECT_FALSE(oat_file_assistant.OdexFileIsOutOfDate()); + EXPECT_TRUE(oat_file_assistant.OdexFileIsUpToDate()); + EXPECT_FALSE(oat_file_assistant.OatFileExists()); + EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate()); + EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate()); + EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); +} + // Case: We have a DEX file and up-to-date OAT file for it. // Expect: We should load an executable dex file. TEST_F(OatFileAssistantTest, LoadOatUpToDate) { |