Merge "Make run-jdwp-tests.sh kill zombie dalvikvm processes"
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 528cf3a..c41e9da 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1809,7 +1809,13 @@
// if the boot image has changed. How exactly we'll know is under
// experimentation.
TimingLogger::ScopedTiming time_unquicken("Unquicken", timings_);
- VdexFile::Unquicken(dex_files_, input_vdex_file_->GetQuickeningInfo());
+
+ // 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_,
+ input_vdex_file_->GetQuickeningInfo(),
+ /* decompile_return_instruction */ false);
} else {
// Create the main VerifierDeps, here instead of in the compiler since we want to aggregate
// the results for all the dex files, not just the results for the current dex file.
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index bcf007b..bdc36e8 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -412,6 +412,8 @@
return instruction_set_;
}
+ typedef std::vector<std::unique_ptr<const DexFile>> DexFileUniqV;
+
bool Dump(std::ostream& os) {
bool success = true;
const OatHeader& oat_header = oat_file_.GetOatHeader();
@@ -563,14 +565,50 @@
for (size_t i = 0; i < oat_dex_files_.size(); i++) {
const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
CHECK(oat_dex_file != nullptr);
+ if (!DumpOatDexFile(os, *oat_dex_file)) {
+ success = false;
+ }
+ }
+ }
- // If file export selected skip file analysis
- if (options_.export_dex_location_) {
- if (!ExportDexFile(os, *oat_dex_file)) {
+ if (options_.export_dex_location_) {
+ if (kIsVdexEnabled) {
+ std::string error_msg;
+ std::string vdex_filename = GetVdexFilename(oat_file_.GetLocation());
+ if (!OS::FileExists(vdex_filename.c_str())) {
+ os << "File " << vdex_filename.c_str() << " does not exist\n";
+ return false;
+ }
+
+ DexFileUniqV vdex_dex_files;
+ std::unique_ptr<const VdexFile> vdex_file = OpenVdexUnquicken(vdex_filename,
+ &vdex_dex_files,
+ &error_msg);
+ if (vdex_file.get() == nullptr) {
+ os << "Failed to open vdex file: " << error_msg << "\n";
+ return false;
+ }
+ if (oat_dex_files_.size() != vdex_dex_files.size()) {
+ os << "Dex files number in Vdex file does not match Dex files number in Oat file: "
+ << vdex_dex_files.size() << " vs " << oat_dex_files_.size() << '\n';
+ return false;
+ }
+
+ size_t i = 0;
+ for (const auto& vdex_dex_file : vdex_dex_files) {
+ const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
+ CHECK(oat_dex_file != nullptr);
+ CHECK(vdex_dex_file != nullptr);
+ if (!ExportDexFile(os, *oat_dex_file, vdex_dex_file.get())) {
success = false;
}
- } else {
- if (!DumpOatDexFile(os, *oat_dex_file)) {
+ i++;
+ }
+ } else {
+ for (size_t i = 0; i < oat_dex_files_.size(); i++) {
+ const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i];
+ CHECK(oat_dex_file != nullptr);
+ if (!ExportDexFile(os, *oat_dex_file, /* vdex_dex_file */ nullptr)) {
success = false;
}
}
@@ -628,6 +666,57 @@
return nullptr;
}
+ // Returns nullptr and updates error_msg if the Vdex file cannot be opened, otherwise all Dex
+ // files are fully unquickened and stored in dex_files
+ std::unique_ptr<const VdexFile> OpenVdexUnquicken(const std::string& vdex_filename,
+ /* out */ DexFileUniqV* dex_files,
+ /* out */ std::string* error_msg) {
+ std::unique_ptr<const File> file(OS::OpenFileForReading(vdex_filename.c_str()));
+ if (file == nullptr) {
+ *error_msg = "Could not open file " + vdex_filename + " for reading.";
+ return nullptr;
+ }
+
+ int64_t vdex_length = file->GetLength();
+ if (vdex_length == -1) {
+ *error_msg = "Could not read the length of file " + vdex_filename;
+ return nullptr;
+ }
+
+ std::unique_ptr<MemMap> mmap(MemMap::MapFile(
+ file->GetLength(),
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ file->Fd(),
+ /* start offset */ 0,
+ /* low_4gb */ false,
+ vdex_filename.c_str(),
+ error_msg));
+ if (mmap == nullptr) {
+ *error_msg = "Failed to mmap file " + vdex_filename + ": " + *error_msg;
+ return nullptr;
+ }
+
+ std::unique_ptr<VdexFile> vdex_file(new VdexFile(mmap.release()));
+ if (!vdex_file->IsValid()) {
+ *error_msg = "Vdex file is not valid";
+ return nullptr;
+ }
+
+ DexFileUniqV tmp_dex_files;
+ if (!vdex_file->OpenAllDexFiles(&tmp_dex_files, error_msg)) {
+ *error_msg = "Failed to open Dex files from Vdex: " + *error_msg;
+ return nullptr;
+ }
+
+ vdex_file->Unquicken(MakeNonOwningPointerVector(tmp_dex_files),
+ vdex_file->GetQuickeningInfo(),
+ /* decompile_return_instruction */ true);
+
+ *dex_files = std::move(tmp_dex_files);
+ return vdex_file;
+ }
+
struct Stats {
enum ByteKind {
kByteKindCode,
@@ -1024,15 +1113,15 @@
return success;
}
- bool ExportDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) {
+ // Backwards compatible Dex file export. If dex_file is nullptr (valid Vdex file not present) the
+ // Dex resource is extracted from the oat_dex_file and its checksum is repaired since it's not
+ // unquickened. Otherwise the dex_file has been fully unquickened and is expected to verify the
+ // original checksum.
+ bool ExportDexFile(std::ostream& os,
+ const OatFile::OatDexFile& oat_dex_file,
+ const DexFile* dex_file) {
std::string error_msg;
std::string dex_file_location = oat_dex_file.GetDexFileLocation();
-
- const DexFile* const dex_file = OpenDexFile(&oat_dex_file, &error_msg);
- if (dex_file == nullptr) {
- os << "Failed to open dex file '" << dex_file_location << "': " << error_msg;
- return false;
- }
size_t fsize = oat_dex_file.FileSize();
// Some quick checks just in case
@@ -1041,6 +1130,27 @@
return false;
}
+ if (dex_file == nullptr) {
+ // Exported bytecode is quickened (dex-to-dex transformations present)
+ dex_file = OpenDexFile(&oat_dex_file, &error_msg);
+ if (dex_file == nullptr) {
+ os << "Failed to open dex file '" << dex_file_location << "': " << error_msg;
+ return false;
+ }
+
+ // Recompute checksum
+ reinterpret_cast<DexFile::Header*>(const_cast<uint8_t*>(dex_file->Begin()))->checksum_ =
+ dex_file->CalculateChecksum();
+ } else {
+ // Vdex unquicken output should match original input bytecode
+ uint32_t orig_checksum =
+ reinterpret_cast<DexFile::Header*>(const_cast<uint8_t*>(dex_file->Begin()))->checksum_;
+ if (orig_checksum != dex_file->CalculateChecksum()) {
+ os << "Unexpected checksum from unquicken dex file '" << dex_file_location << "'\n";
+ return false;
+ }
+ }
+
// Verify output directory exists
if (!OS::DirectoryExists(options_.export_dex_location_)) {
// TODO: Extend OS::DirectoryExists if symlink support is required
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index 7260d74..0034469 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -70,5 +70,17 @@
std::string error_msg;
ASSERT_TRUE(Exec(kStatic, kModeSymbolize, {}, kListOnly, &error_msg)) << error_msg;
}
+
+TEST_F(OatDumpTest, TestExportDex) {
+ std::string error_msg;
+ ASSERT_TRUE(Exec(kDynamic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly, &error_msg))
+ << error_msg;
+}
+TEST_F(OatDumpTest, TestExportDexStatic) {
+ TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+ std::string error_msg;
+ ASSERT_TRUE(Exec(kStatic, kModeOat, {"--export-dex-to=" + tmp_dir_}, kListOnly, &error_msg))
+ << error_msg;
+}
#endif
} // namespace art
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index 1d5c536..52fe973 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -43,6 +43,24 @@
CommonRuntimeTest::SetUp();
core_art_location_ = GetCoreArtLocation();
core_oat_location_ = GetSystemImageFilename(GetCoreOatLocation().c_str(), kRuntimeISA);
+ tmp_dir_ = GetScratchDir();
+ }
+
+ virtual void TearDown() {
+ ClearDirectory(tmp_dir_.c_str(), /*recursive*/ false);
+ ASSERT_EQ(rmdir(tmp_dir_.c_str()), 0);
+ CommonRuntimeTest::TearDown();
+ }
+
+ std::string GetScratchDir() {
+ // ANDROID_DATA needs to be set
+ CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA"));
+ std::string dir = getenv("ANDROID_DATA");
+ dir += "/oatdump-tmp-dir-XXXXXX";
+ if (mkdtemp(&dir[0]) == nullptr) {
+ PLOG(FATAL) << "mkdtemp(\"" << &dir[0] << "\") failed";
+ }
+ return dir;
}
// Linking flavor.
@@ -217,6 +235,8 @@
return result;
}
+ std::string tmp_dir_;
+
private:
std::string core_art_location_;
std::string core_oat_location_;
diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S
index b67df20..264c411 100644
--- a/runtime/interpreter/mterp/mips64/header.S
+++ b/runtime/interpreter/mterp/mips64/header.S
@@ -14,17 +14,50 @@
* limitations under the License.
*/
-#include <machine/regdef.h>
+#define zero $$0 /* always zero */
+#define AT $$at /* assembler temp */
+#define v0 $$2 /* return value */
+#define v1 $$3
+#define a0 $$4 /* argument registers */
+#define a1 $$5
+#define a2 $$6
+#define a3 $$7
+#define a4 $$8 /* expanded register arguments */
+#define a5 $$9
+#define a6 $$10
+#define a7 $$11
+#define ta0 $$8 /* alias */
+#define ta1 $$9
+#define ta2 $$10
+#define ta3 $$11
+#define t0 $$12 /* temp registers (not saved across subroutine calls) */
+#define t1 $$13
+#define t2 $$14
+#define t3 $$15
-/* TODO: add the missing file and use its FP register definitions. */
-/* #include <machine/fpregdef.h> */
-/* FP register definitions */
-#define f0 $$f0
-#define f1 $$f1
-#define f2 $$f2
-#define f3 $$f3
-#define f12 $$f12
-#define f13 $$f13
+#define s0 $$16 /* saved across subroutine calls (callee saved) */
+#define s1 $$17
+#define s2 $$18
+#define s3 $$19
+#define s4 $$20
+#define s5 $$21
+#define s6 $$22
+#define s7 $$23
+#define t8 $$24 /* two more temp registers */
+#define t9 $$25
+#define k0 $$26 /* kernel temporary */
+#define k1 $$27
+#define gp $$28 /* global pointer */
+#define sp $$29 /* stack pointer */
+#define s8 $$30 /* one more callee saved */
+#define ra $$31 /* return address */
+
+#define f0 $$f0
+#define f1 $$f1
+#define f2 $$f2
+#define f3 $$f3
+#define f12 $$f12
+#define f13 $$f13
/*
* It looks like the GNU assembler currently does not support the blec and bgtc
diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S
index 408267e..27fa318 100644
--- a/runtime/interpreter/mterp/out/mterp_mips64.S
+++ b/runtime/interpreter/mterp/out/mterp_mips64.S
@@ -21,17 +21,50 @@
* limitations under the License.
*/
-#include <machine/regdef.h>
+#define zero $0 /* always zero */
+#define AT $at /* assembler temp */
+#define v0 $2 /* return value */
+#define v1 $3
+#define a0 $4 /* argument registers */
+#define a1 $5
+#define a2 $6
+#define a3 $7
+#define a4 $8 /* expanded register arguments */
+#define a5 $9
+#define a6 $10
+#define a7 $11
+#define ta0 $8 /* alias */
+#define ta1 $9
+#define ta2 $10
+#define ta3 $11
+#define t0 $12 /* temp registers (not saved across subroutine calls) */
+#define t1 $13
+#define t2 $14
+#define t3 $15
-/* TODO: add the missing file and use its FP register definitions. */
-/* #include <machine/fpregdef.h> */
-/* FP register definitions */
-#define f0 $f0
-#define f1 $f1
-#define f2 $f2
-#define f3 $f3
-#define f12 $f12
-#define f13 $f13
+#define s0 $16 /* saved across subroutine calls (callee saved) */
+#define s1 $17
+#define s2 $18
+#define s3 $19
+#define s4 $20
+#define s5 $21
+#define s6 $22
+#define s7 $23
+#define t8 $24 /* two more temp registers */
+#define t9 $25
+#define k0 $26 /* kernel temporary */
+#define k1 $27
+#define gp $28 /* global pointer */
+#define sp $29 /* stack pointer */
+#define s8 $30 /* one more callee saved */
+#define ra $31 /* return address */
+
+#define f0 $f0
+#define f1 $f1
+#define f2 $f2
+#define f3 $f3
+#define f12 $f12
+#define f13 $f13
/*
* It looks like the GNU assembler currently does not support the blec and bgtc
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 55bc9ec..955098d 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -119,7 +119,9 @@
if (!vdex->OpenAllDexFiles(&unique_ptr_dex_files, error_msg)) {
return nullptr;
}
- Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), vdex->GetQuickeningInfo());
+ Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files),
+ vdex->GetQuickeningInfo(),
+ /* decompile_return_instruction */ false);
// Update the quickening info size to pretend there isn't any.
reinterpret_cast<Header*>(vdex->mmap_->Begin())->quickening_info_size_ = 0;
}
@@ -218,23 +220,55 @@
};
void VdexFile::Unquicken(const std::vector<const DexFile*>& dex_files,
- const ArrayRef<const uint8_t>& quickening_info) {
- if (quickening_info.size() == 0) {
- // Bail early if there is no quickening info.
+ const ArrayRef<const uint8_t>& quickening_info,
+ bool decompile_return_instruction) {
+ if (quickening_info.size() == 0 && !decompile_return_instruction) {
+ // Bail early if there is no quickening info and no need to decompile
+ // RETURN_VOID_NO_BARRIER instructions to RETURN_VOID instructions.
return;
}
- // 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).
- constexpr bool kDecompileReturnInstruction = false;
- for (uint32_t i = 0; i < dex_files.size(); ++i) {
- for (QuickeningInfoIterator it(i, dex_files.size(), quickening_info);
- !it.Done();
- it.Advance()) {
- optimizer::ArtDecompileDEX(
- *dex_files[i]->GetCodeItem(it.GetCurrentCodeItemOffset()),
- it.GetCurrentQuickeningInfo(),
- kDecompileReturnInstruction);
+
+ // When we do not decompile RETURN_VOID_NO_BARRIER use the faster
+ // QuickeningInfoIterator, otherwise use the slower ClassDataItemIterator
+ if (!decompile_return_instruction) {
+ for (uint32_t i = 0; i < dex_files.size(); ++i) {
+ for (QuickeningInfoIterator it(i, dex_files.size(), quickening_info);
+ !it.Done();
+ it.Advance()) {
+ optimizer::ArtDecompileDEX(
+ *dex_files[i]->GetCodeItem(it.GetCurrentCodeItemOffset()),
+ it.GetCurrentQuickeningInfo(),
+ decompile_return_instruction);
+ }
+ }
+ } else {
+ for (uint32_t i = 0; i < dex_files.size(); ++i) {
+ QuickeningInfoIterator quick_it(i, dex_files.size(), quickening_info);
+ for (uint32_t j = 0; j < dex_files[i]->NumClassDefs(); ++j) {
+ const DexFile::ClassDef& class_def = dex_files[i]->GetClassDef(j);
+ const uint8_t* class_data = dex_files[i]->GetClassData(class_def);
+ if (class_data != nullptr) {
+ for (ClassDataItemIterator class_it(*dex_files[i], class_data);
+ class_it.HasNext();
+ class_it.Next()) {
+ if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) {
+ uint32_t offset = class_it.GetMethodCodeItemOffset();
+ if (!quick_it.Done() && offset == quick_it.GetCurrentCodeItemOffset()) {
+ optimizer::ArtDecompileDEX(
+ *class_it.GetMethodCodeItem(),
+ quick_it.GetCurrentQuickeningInfo(),
+ decompile_return_instruction);
+ quick_it.Advance();
+ } else {
+ optimizer::ArtDecompileDEX(*class_it.GetMethodCodeItem(),
+ /* quickened_info */ {},
+ decompile_return_instruction);
+ }
+ }
+ }
+ }
+ }
+ DCHECK(quick_it.Done()) << "Failed to use all quickening info";
}
}
}
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 63058cf..11f1f52 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -87,6 +87,8 @@
typedef uint32_t VdexChecksum;
+ explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
+
// Returns nullptr if the vdex file cannot be opened or is not valid.
static std::unique_ptr<VdexFile> Open(const std::string& vdex_filename,
bool writable,
@@ -143,8 +145,12 @@
std::string* error_msg);
// In-place unquicken the given `dex_files` based on `quickening_info`.
+ // `decompile_return_instruction` controls if RETURN_VOID_BARRIER instructions are
+ // decompiled to RETURN_VOID instructions using the slower ClassDataItemIterator
+ // instead of the faster QuickeningInfoIterator.
static void Unquicken(const std::vector<const DexFile*>& dex_files,
- const ArrayRef<const uint8_t>& quickening_info);
+ const ArrayRef<const uint8_t>& quickening_info,
+ bool decompile_return_instruction);
// Fully unquicken `target_dex_file` based on quickening info stored
// in this vdex file for `original_dex_file`.
@@ -155,8 +161,6 @@
const uint8_t* GetQuickenedInfoOf(const DexFile& dex_file, uint32_t code_item_offset) const;
private:
- explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
-
bool HasDexSection() const {
return GetHeader().GetDexSize() != 0;
}