From b0bbe8e5c28c061bf4ef4086d3bbb2c5c5266979 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Sat, 19 Nov 2016 10:42:37 +0000 Subject: [PATCH] Use input-vdex-fd, or input-vdex in dex2oat. input-vdex-fd is used by installd input-vdex is used by run-tests, and (will be used by) go/lem This change copies the contents of the passed vdex to the new one, unquicken the new vdex, and run the fast verification on the new vdex. bug:30937355 Test: device boots, apps get updated faster with vdex Test: set TEST_VDEX to true in run-test-jar, run all tests Test: 628-vdex Change-Id: Idfbac4de411cebcf8ea7a6af7a417d7c7908dd72 --- compiler/driver/compiler_driver.cc | 95 ++++++++++++++++++++++++++++++++------ compiler/driver/compiler_driver.h | 11 +++-- compiler/oat_writer.cc | 29 ++++++++++++ compiler/oat_writer.h | 9 +++- compiler/verifier_deps_test.cc | 9 ++-- dex2oat/dex2oat.cc | 67 +++++++++++++++++++++++++-- runtime/vdex_file.cc | 24 +++++++++- runtime/vdex_file.h | 35 ++++++++++++++ test/628-vdex/expected.txt | 2 + test/628-vdex/info.txt | 0 test/628-vdex/run | 17 +++++++ test/628-vdex/src/Main.java | 37 +++++++++++++++ test/etc/run-test-jar | 13 +++++- test/run-test | 4 ++ 14 files changed, 322 insertions(+), 30 deletions(-) create mode 100644 test/628-vdex/expected.txt create mode 100644 test/628-vdex/info.txt create mode 100644 test/628-vdex/run create mode 100644 test/628-vdex/src/Main.java diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index e155e106f..ad75ec460 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -45,6 +45,7 @@ #include "dex_file-inl.h" #include "dex_instruction-inl.h" #include "dex/dex_to_dex_compiler.h" +#include "dex/dex_to_dex_decompiler.h" #include "dex/verification_results.h" #include "dex/verified_method.h" #include "driver/compiler_options.h" @@ -72,6 +73,7 @@ #include "transaction.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "utils/swap_space.h" +#include "vdex_file.h" #include "verifier/method_verifier.h" #include "verifier/method_verifier-inl.h" #include "verifier/verifier_log_mode.h" @@ -394,7 +396,6 @@ static void SetupIntrinsic(Thread* self, void CompilerDriver::CompileAll(jobject class_loader, const std::vector& dex_files, - verifier::VerifierDeps* verifier_deps, TimingLogger* timings) { DCHECK(!Runtime::Current()->IsStarted()); @@ -406,7 +407,7 @@ void CompilerDriver::CompileAll(jobject class_loader, // 2) Resolve all classes // 3) Attempt to verify all classes // 4) Attempt to initialize image classes, and trivially initialized classes - PreCompile(class_loader, dex_files, verifier_deps, timings); + PreCompile(class_loader, dex_files, timings); if (GetCompilerOptions().IsBootImage()) { // We don't need to setup the intrinsics for non boot image compilation, as // those compilations will pick up a boot image that have the ArtMethod already @@ -433,6 +434,72 @@ INTRINSICS_LIST(SETUP_INTRINSICS) FreeThreadPools(); } +// In-place unquicken the given `dex_files` based on `quickening_info`. +static void Unquicken(const std::vector& dex_files, + const ArrayRef& quickening_info) { + const uint8_t* quickening_info_ptr = quickening_info.data(); + const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size(); + for (const DexFile* dex_file : dex_files) { + for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); + const uint8_t* class_data = dex_file->GetClassData(class_def); + if (class_data == nullptr) { + continue; + } + ClassDataItemIterator it(*dex_file, class_data); + // Skip fields + while (it.HasNextStaticField()) { + it.Next(); + } + while (it.HasNextInstanceField()) { + it.Next(); + } + + // Unquicken each method. + while (it.HasNextDirectMethod()) { + const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + if (code_item != nullptr) { + uint32_t quickening_size = *reinterpret_cast(quickening_info_ptr); + quickening_info_ptr += sizeof(uint32_t); + optimizer::ArtDecompileDEX( + *code_item, ArrayRef(quickening_info_ptr, quickening_size)); + quickening_info_ptr += quickening_size; + } + it.Next(); + } + + while (it.HasNextVirtualMethod()) { + const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + if (code_item != nullptr) { + uint32_t quickening_size = *reinterpret_cast(quickening_info_ptr); + quickening_info_ptr += sizeof(uint32_t); + optimizer::ArtDecompileDEX( + *code_item, ArrayRef(quickening_info_ptr, quickening_size)); + quickening_info_ptr += quickening_size; + } + it.Next(); + } + DCHECK(!it.HasNext()); + } + } + DCHECK_EQ(quickening_info_ptr, quickening_info_end) << "Failed to use all quickening info"; +} + +void CompilerDriver::CompileAll(jobject class_loader, + const std::vector& dex_files, + VdexFile* vdex_file, + TimingLogger* timings) { + if (vdex_file != nullptr) { + // TODO: we unquicken unconditionnally, as we don't know + // if the boot image has changed. How exactly we'll know is under + // experimentation. + Unquicken(dex_files, vdex_file->GetQuickeningInfo()); + Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps( + new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData())); + } + CompileAll(class_loader, dex_files, timings); +} + static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel( Thread* self, const CompilerDriver& driver, Handle class_loader, const DexFile& dex_file, const DexFile::ClassDef& class_def) @@ -673,7 +740,7 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t InitializeThreadPools(); - PreCompile(jclass_loader, dex_files, /* verifier_deps */ nullptr, timings); + PreCompile(jclass_loader, dex_files, timings); // Can we run DEX-to-DEX compiler on this class ? optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level = @@ -870,7 +937,6 @@ inline void CompilerDriver::CheckThreadPools() { void CompilerDriver::PreCompile(jobject class_loader, const std::vector& dex_files, - verifier::VerifierDeps* verifier_deps, TimingLogger* timings) { CheckThreadPools(); @@ -904,7 +970,7 @@ void CompilerDriver::PreCompile(jobject class_loader, VLOG(compiler) << "Resolve const-strings: " << GetMemoryUsageString(false); } - Verify(class_loader, dex_files, verifier_deps, timings); + Verify(class_loader, dex_files, timings); VLOG(compiler) << "Verify: " << GetMemoryUsageString(false); if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) { @@ -1936,8 +2002,10 @@ void CompilerDriver::SetVerified(jobject class_loader, void CompilerDriver::Verify(jobject jclass_loader, const std::vector& dex_files, - verifier::VerifierDeps* verifier_deps, TimingLogger* timings) { + verifier::VerifierDeps* verifier_deps = + Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps(); + // If there is an existing `VerifierDeps`, try to use it for fast verification. if (verifier_deps != nullptr) { TimingLogger::ScopedTiming t("Fast Verify", timings); ScopedObjectAccess soa(Thread::Current()); @@ -1975,16 +2043,15 @@ void CompilerDriver::Verify(jobject jclass_loader, } } - // If there is no passed `verifier_deps` (because of non-existing vdex), or - // the passed `verifier_deps` is not valid anymore, create a new one for + // If there is no existing `verifier_deps` (because of non-existing vdex), or + // the existing `verifier_deps` is not valid anymore, create a new one for // non boot image compilation. The verifier will need it to record the new dependencies. // Then dex2oat can update the vdex file with these new dependencies. if (!GetCompilerOptions().IsBootImage()) { // Create the main VerifierDeps, and set it to this thread. - Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps( - new verifier::VerifierDeps(dex_files)); - Thread::Current()->SetVerifierDeps( - Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps()); + verifier_deps = new verifier::VerifierDeps(dex_files); + Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(verifier_deps); + Thread::Current()->SetVerifierDeps(verifier_deps); // Create per-thread VerifierDeps to avoid contention on the main one. // We will merge them after verification. for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) { @@ -2005,13 +2072,11 @@ void CompilerDriver::Verify(jobject jclass_loader, } if (!GetCompilerOptions().IsBootImage()) { - verifier::VerifierDeps* main_deps = - Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps(); // Merge all VerifierDeps into the main one. for (ThreadPoolWorker* worker : parallel_thread_pool_->GetWorkers()) { verifier::VerifierDeps* thread_deps = worker->GetThread()->GetVerifierDeps(); worker->GetThread()->SetVerifierDeps(nullptr); - main_deps->MergeWith(*thread_deps, dex_files);; + verifier_deps->MergeWith(*thread_deps, dex_files);; delete thread_deps; } Thread::Current()->SetVerifierDeps(nullptr); diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index c7719fb63..7418b006e 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -51,7 +51,6 @@ class DexCache; namespace verifier { class MethodVerifier; -class VerifierDeps; class VerifierDepsTest; } // namespace verifier @@ -69,6 +68,7 @@ class SrcMapElem; using SwapSrcMap = SrcMap>; template class Handle; class TimingLogger; +class VdexFile; class VerificationResults; class VerifiedMethod; @@ -119,7 +119,12 @@ class CompilerDriver { void CompileAll(jobject class_loader, const std::vector& dex_files, - verifier::VerifierDeps* verifier_deps, + TimingLogger* timings) + REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_); + + void CompileAll(jobject class_loader, + const std::vector& dex_files, + VdexFile* vdex_file, TimingLogger* timings) REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_); @@ -420,7 +425,6 @@ class CompilerDriver { private: void PreCompile(jobject class_loader, const std::vector& dex_files, - verifier::VerifierDeps* verifier_deps, TimingLogger* timings) REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_); @@ -443,7 +447,6 @@ class CompilerDriver { void Verify(jobject class_loader, const std::vector& dex_files, - verifier::VerifierDeps* verifier_deps, TimingLogger* timings); void VerifyDexFile(jobject class_loader, diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index eed9d117a..153aff40d 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -403,6 +403,35 @@ bool OatWriter::AddZippedDexFilesSource(File&& zip_fd, return true; } +// Add dex file source(s) from a vdex file specified by a file handle. +bool OatWriter::AddVdexDexFilesSource(const VdexFile& vdex_file, + const char* location, + CreateTypeLookupTable create_type_lookup_table) { + DCHECK(write_state_ == WriteState::kAddingDexFileSources); + const uint8_t* current_dex_data = nullptr; + for (size_t i = 0; ; ++i) { + current_dex_data = vdex_file.GetNextDexFileData(current_dex_data); + if (current_dex_data == nullptr) { + break; + } + if (!DexFile::IsMagicValid(current_dex_data)) { + LOG(ERROR) << "Invalid magic in vdex file created from " << location; + return false; + } + // We used `zipped_dex_file_locations_` to keep the strings in memory. + zipped_dex_file_locations_.push_back(DexFile::GetMultiDexLocation(i, location)); + const char* full_location = zipped_dex_file_locations_.back().c_str(); + oat_dex_files_.emplace_back(full_location, + DexFileSource(current_dex_data), + create_type_lookup_table); + } + if (oat_dex_files_.empty()) { + LOG(ERROR) << "No dex files in vdex file created from " << location; + return false; + } + return true; +} + // Add dex file source from raw memory. bool OatWriter::AddRawDexFileSource(const ArrayRef& data, const char* location, diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index f9671d7c5..0dcf79e54 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -42,6 +42,7 @@ class ProfileCompilationInfo; class OutputStream; class TimingLogger; class TypeLookupTable; +class VdexFile; class ZipEntry; namespace debug { @@ -116,7 +117,8 @@ class OatWriter { // To produce a valid oat file, the user must first add sources with any combination of // - AddDexFileSource(), // - AddZippedDexFilesSource(), - // - AddRawDexFileSource(). + // - AddRawDexFileSource(), + // - AddVdexDexFilesSource(). // Then the user must call in order // - WriteAndOpenDexFiles() // - Initialize() @@ -145,6 +147,11 @@ class OatWriter { const char* location, uint32_t location_checksum, CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); + // Add dex file source(s) from a vdex file. + bool AddVdexDexFilesSource( + const VdexFile& vdex_file, + const char* location, + CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault); dchecked_vector GetSourceLocations() const; // Write raw dex files to the vdex file, mmap the file and open the dex files from it. diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index dcf361972..525a2ee29 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -83,10 +83,13 @@ class VerifierDepsTest : public CommonCompilerTest { // The compiler driver handles the verifier deps in the callbacks, so // remove what this class did for unit testing. verifier_deps_.reset(nullptr); - callbacks_->SetVerifierDeps(nullptr); - compiler_driver_->Verify(class_loader_, dex_files_, deps, &timings); + callbacks_->SetVerifierDeps(deps); + compiler_driver_->Verify(class_loader_, dex_files_, &timings); // The compiler driver may have updated the VerifierDeps in the callback object. - verifier_deps_.reset(callbacks_->GetVerifierDeps()); + if (callbacks_->GetVerifierDeps() != deps) { + verifier_deps_.reset(callbacks_->GetVerifierDeps()); + } + callbacks_->SetVerifierDeps(nullptr); } void SetVerifierDeps(const std::vector& dex_files) { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 20b5bba0e..194c458bd 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -78,6 +78,7 @@ #include "ScopedLocalRef.h" #include "scoped_thread_state_change-inl.h" #include "utils.h" +#include "vdex_file.h" #include "verifier/verifier_deps.h" #include "well_known_classes.h" #include "zip_archive.h" @@ -520,6 +521,7 @@ class Dex2Oat FINAL { oat_fd_(-1), input_vdex_fd_(-1), output_vdex_fd_(-1), + input_vdex_file_(nullptr), zip_fd_(-1), image_base_(0U), image_classes_zip_filename_(nullptr), @@ -710,6 +712,10 @@ class Dex2Oat FINAL { Usage("Output must be supplied with either --oat-file or --oat-fd"); } + if (input_vdex_fd_ != -1 && !input_vdex_.empty()) { + Usage("Can't have both --input-vdex-fd and --input-vdex"); + } + if (!oat_filenames_.empty() && oat_fd_ != -1) { Usage("--oat-file should not be used with --oat-fd"); } @@ -1123,6 +1129,8 @@ class Dex2Oat FINAL { zip_location_ = option.substr(strlen("--zip-location=")).data(); } else if (option.starts_with("--input-vdex-fd=")) { ParseInputVdexFd(option); + } else if (option.starts_with("--input-vdex=")) { + input_vdex_ = option.substr(strlen("--input-vdex=")).data(); } else if (option.starts_with("--output-vdex-fd=")) { ParseOutputVdexFd(option); } else if (option.starts_with("--oat-file=")) { @@ -1266,6 +1274,17 @@ class Dex2Oat FINAL { return false; } oat_files_.push_back(std::move(oat_file)); + DCHECK_EQ(input_vdex_fd_, -1); + if (!input_vdex_.empty()) { + std::string error_msg; + input_vdex_file_.reset(VdexFile::Open(input_vdex_, + /* writable */ false, + /* low_4gb */ false, + &error_msg)); + if (input_vdex_file_ != nullptr && !input_vdex_file_->IsValid()) { + input_vdex_file_.reset(nullptr); + } + } DCHECK_EQ(output_vdex_fd_, -1); std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex"); @@ -1293,6 +1312,31 @@ class Dex2Oat FINAL { } oat_files_.push_back(std::move(oat_file)); + DCHECK_NE(input_vdex_fd_, output_vdex_fd_); + if (input_vdex_fd_ != -1) { + struct stat s; + int rc = TEMP_FAILURE_RETRY(fstat(input_vdex_fd_, &s)); + if (rc == -1) { + PLOG(WARNING) << "Failed getting length of vdex file"; + } else { + std::string error_msg; + input_vdex_file_.reset(VdexFile::Open(input_vdex_fd_, + s.st_size, + "vdex", + /* writable */ false, + /* low_4gb */ false, + &error_msg)); + // If there's any problem with the passed vdex, just warn and proceed + // without it. + if (input_vdex_file_ == nullptr) { + PLOG(WARNING) << "Failed opening vdex file " << error_msg; + } else if (!input_vdex_file_->IsValid()) { + PLOG(WARNING) << "Existing vdex file is invalid"; + input_vdex_file_.reset(nullptr); + } + } + } + DCHECK_NE(output_vdex_fd_, -1); std::string vdex_location = ReplaceFileExtension(oat_location_, "vdex"); std::unique_ptr vdex_file(new File(output_vdex_fd_, vdex_location, /* check_usage */ true)); @@ -1388,7 +1432,6 @@ class Dex2Oat FINAL { // boot class path. bool Setup() { TimingLogger::ScopedTiming t("dex2oat Setup", timings_); - art::MemMap::Init(); // For ZipEntry::ExtractToMemMap. if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) { return false; @@ -1480,8 +1523,11 @@ class Dex2Oat FINAL { // Unzip or copy dex files straight to the oat file. std::unique_ptr opened_dex_files_map; std::vector> opened_dex_files; - // Dexlayout verifies the dex file, so disable dex file verification in that case. - bool verify = compiler_options_->GetCompilerFilter() != CompilerFilter::kLayoutProfile; + // No need to verify the dex file for: + // 1) dexlayout, which already verified it + // 2) when we have a vdex file, which means it was already verified. + bool verify = compiler_options_->GetCompilerFilter() != CompilerFilter::kLayoutProfile && + (input_vdex_file_ == nullptr); if (!oat_writers_[i]->WriteAndOpenDexFiles( kIsVdexEnabled ? vdex_files_[i].get() : oat_files_[i].get(), rodata_.back(), @@ -1665,7 +1711,7 @@ class Dex2Oat FINAL { swap_fd_, profile_compilation_info_.get())); driver_->SetDexFilesForOatFile(dex_files_); - driver_->CompileAll(class_loader_, dex_files_, /* verifier_deps */ nullptr, timings_); + driver_->CompileAll(class_loader_, dex_files_, input_vdex_file_.get(), timings_); } // Notes on the interleaving of creating the images and oat files to @@ -2238,7 +2284,14 @@ class Dex2Oat FINAL { bool AddDexFileSources() { TimingLogger::ScopedTiming t2("AddDexFileSources", timings_); - if (zip_fd_ != -1) { + if (input_vdex_file_ != nullptr) { + DCHECK_EQ(oat_writers_.size(), 1u); + const std::string& name = zip_location_.empty() ? dex_locations_[0] : zip_location_; + DCHECK(!name.empty()); + if (!oat_writers_[0]->AddVdexDexFilesSource(*input_vdex_file_.get(), name.c_str())) { + return false; + } + } else if (zip_fd_ != -1) { DCHECK_EQ(oat_writers_.size(), 1u); if (!oat_writers_[0]->AddZippedDexFilesSource(File(zip_fd_, /* check_usage */ false), zip_location_.c_str())) { @@ -2596,6 +2649,8 @@ class Dex2Oat FINAL { int oat_fd_; int input_vdex_fd_; int output_vdex_fd_; + std::string input_vdex_; + std::unique_ptr input_vdex_file_; std::vector dex_filenames_; std::vector dex_locations_; int zip_fd_; @@ -2792,6 +2847,8 @@ static int dex2oat(int argc, char** argv) { } } + art::MemMap::Init(); // For ZipEntry::ExtractToMemMap, and vdex. + // Check early that the result of compilation can be written if (!dex2oat->OpenFile()) { return EXIT_FAILURE; diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index b3dab581d..843be92c0 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -20,6 +20,7 @@ #include "base/logging.h" #include "base/unix_file/fd_file.h" +#include "dex_file.h" namespace art { @@ -73,10 +74,19 @@ VdexFile* VdexFile::Open(const std::string& vdex_filename, return nullptr; } + return Open(vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, error_msg); +} + +VdexFile* VdexFile::Open(int file_fd, + size_t vdex_length, + const std::string& vdex_filename, + bool writable, + bool low_4gb, + std::string* error_msg) { std::unique_ptr mmap(MemMap::MapFile(vdex_length, writable ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, - vdex_file->Fd(), + file_fd, 0 /* start offset */, low_4gb, vdex_filename.c_str(), @@ -90,4 +100,16 @@ VdexFile* VdexFile::Open(const std::string& vdex_filename, return new VdexFile(mmap.release()); } +const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor) const { + DCHECK(cursor == nullptr || (cursor > Begin() && cursor <= End())); + if (cursor == nullptr) { + // Beginning of the iteration, return the first dex file if there is one. + return HasDexSection() ? DexBegin() : nullptr; + } else { + // Fetch the next dex file. Return null if there is none. + const uint8_t* data = cursor + reinterpret_cast(cursor)->file_size_; + return (data == DexEnd()) ? nullptr : data; + } +} + } // namespace art diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index edd6ffe89..75a0d5e31 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -71,6 +71,13 @@ class VdexFile { bool low_4gb, std::string* error_msg); + static VdexFile* Open(int file_fd, + size_t vdex_length, + const std::string& vdex_filename, + bool writable, + bool low_4gb, + std::string* error_msg); + const uint8_t* Begin() const { return mmap_->Begin(); } const uint8_t* End() const { return mmap_->End(); } size_t Size() const { return mmap_->Size(); } @@ -84,9 +91,37 @@ class VdexFile { Begin() + sizeof(Header) + GetHeader().GetDexSize(), GetHeader().GetVerifierDepsSize()); } + ArrayRef GetQuickeningInfo() const { + return ArrayRef( + GetVerifierDepsData().data() + GetHeader().GetVerifierDepsSize(), + GetHeader().GetQuickeningInfoSize()); + } + + bool IsValid() const { + return mmap_->Size() >= sizeof(Header) && GetHeader().IsValid(); + } + + // This method is for iterating over the dex files in the vdex. If `cursor` is null, + // the first dex file is returned. If `cursor` is not null, it must point to a dex + // file and this method returns the next dex file if there is one, or null if there + // is none. + const uint8_t* GetNextDexFileData(const uint8_t* cursor) const; + private: explicit VdexFile(MemMap* mmap) : mmap_(mmap) {} + bool HasDexSection() const { + return GetHeader().GetDexSize() != 0; + } + + const uint8_t* DexBegin() const { + return Begin() + sizeof(Header); + } + + const uint8_t* DexEnd() const { + return Begin() + sizeof(Header) + GetHeader().GetDexSize(); + } + std::unique_ptr mmap_; DISALLOW_COPY_AND_ASSIGN(VdexFile); diff --git a/test/628-vdex/expected.txt b/test/628-vdex/expected.txt new file mode 100644 index 000000000..d0f61f692 --- /dev/null +++ b/test/628-vdex/expected.txt @@ -0,0 +1,2 @@ +In foo +In foo diff --git a/test/628-vdex/info.txt b/test/628-vdex/info.txt new file mode 100644 index 000000000..e69de29bb diff --git a/test/628-vdex/run b/test/628-vdex/run new file mode 100644 index 000000000..f1b0a95f6 --- /dev/null +++ b/test/628-vdex/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +exec ${RUN} --vdex "${@}" diff --git a/test/628-vdex/src/Main.java b/test/628-vdex/src/Main.java new file mode 100644 index 000000000..7ceab2c95 --- /dev/null +++ b/test/628-vdex/src/Main.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + Main() { + // Will be quickened with RETURN_VOID_NO_BARRIER. + } + + public static void main(String[] args) { + Main m = new Main(); + Object o = m; + // The call and field accesses will be quickened. + m.foo(m.a); + + // The checkcast will be quickened. + m.foo(((Main)o).a); + } + + int a; + void foo(int a) { + System.out.println("In foo"); + } +} + diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index c525b2b9b..bb3a3ad71 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -58,6 +58,7 @@ INSTRUCTION_SET_FEATURES="" ARGS="" EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS. DRY_RUN="n" # if y prepare to run the test but don't run it. +TEST_VDEX="n" while true; do if [ "x$1" = "x--quiet" ]; then @@ -243,6 +244,9 @@ while true; do elif [ "x$1" = "x--dry-run" ]; then DRY_RUN="y" shift + elif [ "x$1" = "x--vdex" ]; then + TEST_VDEX="y" + shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 exit 1 @@ -444,6 +448,7 @@ if [ ${#VDEX_NAME} -gt $max_filename_size ]; then fi dex2oat_cmdline="true" +vdex_cmdline="true" mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA" strip_cmdline="true" @@ -473,6 +478,9 @@ if [ "$PREBUILD" = "y" ]; then # Use -k 1m to SIGKILL it a minute later if it hasn't ended. dex2oat_cmdline="timeout -k 1m -s SIGRTMIN+2 1m ${dex2oat_cmdline}" fi + if [ "$TEST_VDEX" = "y" ]; then + vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex" + fi fi if [ "$STRIP_DEX" = "y" ]; then @@ -513,6 +521,7 @@ dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ROOT/bin/$DALVIKVM \ # Remove whitespace. dex2oat_cmdline=$(echo $dex2oat_cmdline) dalvikvm_cmdline=$(echo $dalvikvm_cmdline) +vdex_cmdline=$(echo $vdex_cmdline) if [ "$HOST" = "n" ]; then adb root > /dev/null @@ -553,6 +562,7 @@ if [ "$HOST" = "n" ]; then export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \ export PATH=$ANDROID_ROOT/bin:$PATH && \ $dex2oat_cmdline && \ + $vdex_cmdline && \ $strip_cmdline && \ $dalvikvm_cmdline" @@ -626,7 +636,7 @@ else fi if [ "$DEV_MODE" = "y" ]; then - echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $strip_cmdline && $cmdline" + echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $cmdline" fi cd $ANDROID_BUILD_TOP @@ -634,6 +644,7 @@ else rm -rf ${DEX_LOCATION}/dalvik-cache/ mkdir -p ${mkdir_locations} || exit 1 $dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; } + $vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; } $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; } # For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use diff --git a/test/run-test b/test/run-test index 37eefb34d..ea9622aa6 100755 --- a/test/run-test +++ b/test/run-test @@ -351,6 +351,9 @@ while true; do elif [ "x$1" = "x--bisection-search" ]; then bisection_search="yes" shift + elif [ "x$1" = "x--vdex" ]; then + run_args="${run_args} --vdex" + shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 usage="yes" @@ -640,6 +643,7 @@ if [ "$usage" = "yes" ]; then echo " --pic-test Compile the test code position independent." echo " --quiet Don't print anything except failure messages" echo " --bisection-search Perform bisection bug search." + echo " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild." ) 1>&2 # Direct to stderr so usage is not printed if --quiet is set. exit 1 fi -- 2.11.0