"compiled_method.cc",
"debug/elf_debug_writer.cc",
"dex/dex_to_dex_compiler.cc",
- "dex/dex_to_dex_decompiler.cc",
"dex/inline_method_analyser.cc",
"dex/verified_method.cc",
"dex/verification_results.cc",
* limitations under the License.
*/
-#include "dex/dex_to_dex_decompiler.h"
+#include "dex_to_dex_decompiler.h"
#include "class_linker.h"
#include "compiler/common_compiler_test.h"
// This method should only be called for classes verified at compile time,
// which have no verifier error, nor has methods that we know will throw
// at runtime.
- AtomicMap::InsertResult result = atomic_verified_methods_.Insert(
+ atomic_verified_methods_.Insert(
ref,
/*expected*/ nullptr,
new VerifiedMethod(/* encountered_error_types */ 0, /* has_runtime_throw */ false));
- DCHECK_EQ(result, AtomicMap::kInsertResultSuccess);
+ // We don't check the result of `Insert` as we could insert twice for the same
+ // MethodReference in the presence of duplicate methods.
}
void VerificationResults::AddRejectedClass(ClassReference ref) {
#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"
FreeThreadPools();
}
-// In-place unquicken the given `dex_files` based on `quickening_info`.
-static void Unquicken(const std::vector<const DexFile*>& dex_files,
- const ArrayRef<const uint8_t>& quickening_info,
- bool decompile_return_instruction) {
- 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();
- }
-
- while (it.HasNextDirectMethod()) {
- const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
- if (code_item != nullptr) {
- uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
- quickening_info_ptr += sizeof(uint32_t);
- optimizer::ArtDecompileDEX(*code_item,
- ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
- decompile_return_instruction);
- 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<const uint32_t*>(quickening_info_ptr);
- quickening_info_ptr += sizeof(uint32_t);
- optimizer::ArtDecompileDEX(*code_item,
- ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
- decompile_return_instruction);
- quickening_info_ptr += quickening_size;
- }
- it.Next();
- }
- DCHECK(!it.HasNext());
- }
- }
- if (quickening_info_ptr != quickening_info_end) {
- LOG(FATAL) << "Failed to use all quickening info";
- }
-}
-
void CompilerDriver::CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
VdexFile* vdex_file,
// TODO: we unquicken unconditionnally, as we don't know
// if the boot image has changed. How exactly we'll know is under
// experimentation.
- if (vdex_file->GetQuickeningInfo().size() != 0) {
- TimingLogger::ScopedTiming t("Unquicken", timings);
- // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening
- // optimization does not depend on the boot image (the optimization relies on not
- // having final fields in a class, which does not change for an app).
- Unquicken(dex_files,
- vdex_file->GetQuickeningInfo(),
- /* decompile_return_instruction */ false);
- }
+ TimingLogger::ScopedTiming t("Unquicken", timings);
+ // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening
+ // optimization does not depend on the boot image (the optimization relies on not
+ // having final fields in a class, which does not change for an app).
+ VdexFile::Unquicken(dex_files, vdex_file->GetQuickeningInfo());
+
Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData()));
}
/* verify */ true,
/* verify_checksum */ true,
&error_msg);
- } else {
- CHECK(oat_dex_file->source_.IsRawFile())
- << static_cast<size_t>(oat_dex_file->source_.GetType());
+ } else if (oat_dex_file->source_.IsRawFile()) {
File* raw_file = oat_dex_file->source_.GetRawFile();
dex_file = DexFile::OpenDex(raw_file->Fd(), location, /* verify_checksum */ true, &error_msg);
+ } else {
+ // The source data is a vdex file.
+ CHECK(oat_dex_file->source_.IsRawData())
+ << static_cast<size_t>(oat_dex_file->source_.GetType());
+ const uint8_t* raw_dex_file = oat_dex_file->source_.GetRawData();
+ // Note: The raw data has already been checked to contain the header
+ // and all the data that the header specifies as the file size.
+ DCHECK(raw_dex_file != nullptr);
+ DCHECK(ValidateDexFileHeader(raw_dex_file, oat_dex_file->GetLocation()));
+ const UnalignedDexFileHeader* header = AsUnalignedDexFileHeader(raw_dex_file);
+ // Since the source may have had its layout changed, or may be quickened, don't verify it.
+ dex_file = DexFile::Open(raw_dex_file,
+ header->file_size_,
+ location,
+ oat_dex_file->dex_file_location_checksum_,
+ nullptr,
+ /* verify */ false,
+ /* verify_checksum */ false,
+ &error_msg);
}
if (dex_file == nullptr) {
LOG(ERROR) << "Failed to open dex file for layout: " << error_msg;
Usage("Can't have both --input-vdex-fd and --input-vdex");
}
+ if (output_vdex_fd_ != -1 && !output_vdex_.empty()) {
+ Usage("Can't have both --output-vdex-fd and --output-vdex");
+ }
+
if (!oat_filenames_.empty() && oat_fd_ != -1) {
Usage("--oat-file should not be used with --oat-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=")) {
+ output_vdex_ = option.substr(strlen("--output-vdex=")).data();
} else if (option.starts_with("--output-vdex-fd=")) {
ParseOutputVdexFd(option);
} else if (option.starts_with("--oat-file=")) {
}
// OAT and VDEX file handling
+ bool eagerly_unquicken_vdex = DoDexLayoutOptimizations();
if (oat_fd_ == -1) {
DCHECK(!oat_filenames_.empty());
input_vdex_file_ = VdexFile::Open(input_vdex_,
/* writable */ false,
/* low_4gb */ false,
+ eagerly_unquicken_vdex,
&error_msg);
}
DCHECK_EQ(output_vdex_fd_, -1);
- std::string vdex_filename = ReplaceFileExtension(oat_filename, "vdex");
- if (vdex_filename == input_vdex_) {
+ std::string vdex_filename = output_vdex_.empty()
+ ? ReplaceFileExtension(oat_filename, "vdex")
+ : output_vdex_;
+ if (vdex_filename == input_vdex_ && output_vdex_.empty()) {
update_input_vdex_ = true;
std::unique_ptr<File> vdex_file(OS::OpenFileReadWrite(vdex_filename.c_str()));
vdex_files_.push_back(std::move(vdex_file));
"vdex",
/* writable */ false,
/* low_4gb */ false,
+ eagerly_unquicken_vdex,
&error_msg);
// If there's any problem with the passed vdex, just warn and proceed
// without it.
return DoProfileGuidedOptimizations();
}
- bool HasInputVdexFile() const {
- return input_vdex_file_ != nullptr || input_vdex_fd_ != -1 || !input_vdex_.empty();
- }
-
bool LoadProfile() {
DCHECK(UseProfile());
return dex_files_size >= very_large_threshold_;
}
- template <typename T>
- static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
- std::vector<T*> result;
- result.reserve(src.size());
- for (const std::unique_ptr<T>& t : src) {
- result.push_back(t.get());
- }
- return result;
- }
-
std::vector<std::string> GetClassPathLocations(const std::string& class_path) {
// This function is used only for apps and for an app we have exactly one oat file.
DCHECK(!IsBootImage());
int input_vdex_fd_;
int output_vdex_fd_;
std::string input_vdex_;
+ std::string output_vdex_;
std::unique_ptr<VdexFile> input_vdex_file_;
std::vector<const char*> dex_filenames_;
std::vector<const char*> dex_locations_;
}
}
- if (dex2oat->DoDexLayoutOptimizations()) {
- if (dex2oat->HasInputVdexFile()) {
- LOG(ERROR) << "Dexlayout is incompatible with an input VDEX";
- return dex2oat::ReturnCode::kOther;
- }
- }
-
art::MemMap::Init(); // For ZipEntry::ExtractToMemMap, and vdex.
// Check early that the result of compilation can be written
/* use_fd */ true,
/* num_profile_classes */ 1,
{ input_vdex, output_vdex },
- /* expect_success */ false);
- EXPECT_EQ(vdex_file2.GetFile()->GetLength(), 0u);
+ /* expect_success */ true);
+ EXPECT_GT(vdex_file2.GetFile()->GetLength(), 0u);
}
ASSERT_EQ(vdex_file1->FlushCloseOrErase(), 0) << "Could not flush and close vdex file";
CheckValidity();
- ASSERT_FALSE(success_);
+ ASSERT_TRUE(success_);
}
void CheckResult(const std::string& dex_location,
std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_name,
false /*writeable*/,
false /*low_4gb*/,
+ false /*unquicken */,
&error_msg /*out*/));
if (vdex == nullptr) {
std::cerr << "Could not open vdex file "
"dex_file_annotations.cc",
"dex_file_verifier.cc",
"dex_instruction.cc",
+ "dex_to_dex_decompiler.cc",
"elf_file.cc",
"exec_utils.cc",
"fault_handler.cc",
to_update.insert(other.begin(), other.end());
}
+// Returns a copy of the passed vector that doesn't memory-own its entries.
+template <typename T>
+static inline std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
+ std::vector<T*> result;
+ result.reserve(src.size());
+ for (const std::unique_ptr<T>& t : src) {
+ result.push_back(t.get());
+ }
+ return result;
+}
+
} // namespace art
#endif // ART_RUNTIME_BASE_STL_UTIL_H_
* limitations under the License.
*/
-#ifndef ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_
-#define ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_
+#ifndef ART_RUNTIME_DEX_TO_DEX_DECOMPILER_H_
+#define ART_RUNTIME_DEX_TO_DEX_DECOMPILER_H_
#include "base/array_ref.h"
#include "dex_file.h"
} // namespace optimizer
} // namespace art
-#endif // ART_COMPILER_DEX_DEX_TO_DEX_DECOMPILER_H_
+#endif // ART_RUNTIME_DEX_TO_DEX_DECOMPILER_H_
bool writable,
bool low_4gb,
std::string* error_msg) {
- vdex_ = VdexFile::Open(vdex_filename, writable, low_4gb, error_msg);
+ vdex_ = VdexFile::Open(vdex_filename, writable, low_4gb, /* unquicken*/ false, error_msg);
if (vdex_.get() == nullptr) {
*error_msg = StringPrintf("Failed to load vdex file '%s' %s",
vdex_filename.c_str(),
std::unique_ptr<VdexFile> vdex = VdexFile::Open(vdex_filename,
/*writeable*/false,
/*low_4gb*/false,
+ /*unquicken*/false,
&error_msg);
if (vdex == nullptr) {
status_ = kOatCannotOpen;
#include "fixed_up_dex_file.h"
#include "dex_file-inl.h"
-// Compiler includes.
-#include "dex/dex_to_dex_decompiler.h"
-
// Runtime includes.
+#include "dex_to_dex_decompiler.h"
#include "oat_file.h"
#include "vdex_file.h"
std::unique_ptr<VdexFile> vdex_file(VdexFile::Open(vdex_filename,
false /* writable */,
false /* low_4gb */,
+ false, /* unquicken */
&error_msg));
if (vdex_file.get() == nullptr) {
return false;
#include <memory>
#include "base/logging.h"
+#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "dex_file.h"
+#include "dex_to_dex_decompiler.h"
namespace art {
std::unique_ptr<VdexFile> VdexFile::Open(const std::string& vdex_filename,
bool writable,
bool low_4gb,
+ bool unquicken,
std::string* error_msg) {
if (!OS::FileExists(vdex_filename.c_str())) {
*error_msg = "File " + vdex_filename + " does not exist.";
return nullptr;
}
- return Open(vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, error_msg);
+ return Open(vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, unquicken, error_msg);
}
std::unique_ptr<VdexFile> VdexFile::Open(int file_fd,
const std::string& vdex_filename,
bool writable,
bool low_4gb,
+ bool unquicken,
std::string* error_msg) {
- std::unique_ptr<MemMap> mmap(MemMap::MapFile(vdex_length,
- writable ? PROT_READ | PROT_WRITE : PROT_READ,
- MAP_SHARED,
- file_fd,
- 0 /* start offset */,
- low_4gb,
- vdex_filename.c_str(),
- error_msg));
+ std::unique_ptr<MemMap> mmap(MemMap::MapFile(
+ vdex_length,
+ (writable || unquicken) ? PROT_READ | PROT_WRITE : PROT_READ,
+ unquicken ? MAP_PRIVATE : MAP_SHARED,
+ file_fd,
+ 0 /* start offset */,
+ low_4gb,
+ vdex_filename.c_str(),
+ error_msg));
if (mmap == nullptr) {
*error_msg = "Failed to mmap file " + vdex_filename + " : " + *error_msg;
return nullptr;
return nullptr;
}
+ if (unquicken) {
+ std::vector<std::unique_ptr<const DexFile>> unique_ptr_dex_files;
+ if (!vdex->OpenAllDexFiles(&unique_ptr_dex_files, error_msg)) {
+ return nullptr;
+ }
+ Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), vdex->GetQuickeningInfo());
+ // Update the quickening info size to pretend there isn't any.
+ reinterpret_cast<Header*>(vdex->mmap_->Begin())->quickening_info_size_ = 0;
+ }
+
*error_msg = "Success";
return vdex;
}
return true;
}
+void VdexFile::Unquicken(const std::vector<const DexFile*>& dex_files,
+ const ArrayRef<const uint8_t>& quickening_info) {
+ if (quickening_info.size() == 0) {
+ // If there is no quickening info, we bail early, as the code below expects at
+ // least the size of quickening data for each method that has a code item.
+ return;
+ }
+ 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();
+ }
+
+ while (it.HasNextDirectMethod()) {
+ const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
+ if (code_item != nullptr) {
+ uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
+ quickening_info_ptr += sizeof(uint32_t);
+ optimizer::ArtDecompileDEX(*code_item,
+ ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+ /* decompile_return_instruction */ false);
+ 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<const uint32_t*>(quickening_info_ptr);
+ quickening_info_ptr += sizeof(uint32_t);
+ optimizer::ArtDecompileDEX(*code_item,
+ ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+ /* decompile_return_instruction */ false);
+ quickening_info_ptr += quickening_size;
+ }
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+ }
+ }
+ if (quickening_info_ptr != quickening_info_end) {
+ LOG(FATAL) << "Failed to use all quickening info";
+ }
+}
+
} // namespace art
uint32_t dex_size_;
uint32_t verifier_deps_size_;
uint32_t quickening_info_size_;
+
+ friend class VdexFile;
};
typedef uint32_t VdexChecksum;
static std::unique_ptr<VdexFile> Open(const std::string& vdex_filename,
bool writable,
bool low_4gb,
+ bool unquicken,
std::string* error_msg);
// Returns nullptr if the vdex file cannot be opened or is not valid.
const std::string& vdex_filename,
bool writable,
bool low_4gb,
+ bool unquicken,
std::string* error_msg);
const uint8_t* Begin() const { return mmap_->Begin(); }
return reinterpret_cast<const uint32_t*>(Begin() + sizeof(Header))[dex_file_index];
}
- // Opens all the dex files contained in this vdex file. This is currently
- // used for dumping tools only, and has not been tested for use by the
- // remainder of the runtime.
+ // Opens all the dex files contained in this vdex file.
bool OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_files,
std::string* error_msg);
+ // In-place unquicken the given `dex_files` based on `quickening_info`.
+ static void Unquicken(const std::vector<const DexFile*>& dex_files,
+ const ArrayRef<const uint8_t>& quickening_info);
+
private:
explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
tmp.GetFilename(),
/*writable*/false,
/*low_4gb*/false,
+ /*quicken*/false,
&error_msg);
EXPECT_TRUE(vdex == nullptr);
- vdex = VdexFile::Open(tmp.GetFilename(), /*writable*/false, /*low_4gb*/false, &error_msg);
+ vdex = VdexFile::Open(
+ tmp.GetFilename(), /*writable*/false, /*low_4gb*/false, /*quicken*/ false, &error_msg);
EXPECT_TRUE(vdex == nullptr);
}
const RegType& referrer = GetDeclaringClass();
if (!referrer.IsUnresolvedTypes() && !referrer.CanAccess(*result)) {
Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: '"
- << referrer << "' -> '" << result << "'";
+ << referrer << "' -> '" << *result << "'";
}
}
return *result;
fi
if [ "$TEST_VDEX" = "y" ]; then
vdex_cmdline="${dex2oat_cmdline} ${VDEX_FILTER} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
+ elif [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
+ vdex_cmdline="${dex2oat_cmdline} --input-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$TEST_NAME.vdex"
fi
fi