Add code dumping to oatdump.
Change-Id: I7dccdffb3bac422fee52a4bd82dfac220da91722
diff --git a/src/dex_instruction.cc b/src/dex_instruction.cc
index 2bb53d6..31e9d3a 100644
--- a/src/dex_instruction.cc
+++ b/src/dex_instruction.cc
@@ -290,28 +290,28 @@
std::ostringstream os;
const char* opcode = kInstructionNames[insn.opcode_];
switch (Format()) {
- case k10x: os << opcode; break;
- case k12x: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_; break;
- case k11n: os << opcode << " v" << insn.vA_ << ", #+" << insn.vB_; break;
- case k11x: os << opcode << " v" << insn.vA_; break;
- case k10t: os << opcode << " +" << (int)insn.vA_; break;
- case k20bc: os << opcode << " " << insn.vA_ << ", kind@" << insn.vB_; break;
- case k20t: os << opcode << " +" << (int)insn.vA_; break;
- case k22x: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_; break;
- case k21t: os << opcode << " v" << insn.vA_ << ", +" << insn.vB_; break;
- case k21s: os << opcode << " v" << insn.vA_ << ", #+" << insn.vB_; break;
- case k21h: os << opcode << " v" << insn.vA_ << ", #+" << insn.vB_ << "00000[00000000]"; break;
- case k21c: os << opcode << " " << insn.vA_ << ", thing@" << insn.vB_; break;
- case k23x: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_ << ", v" << insn.vC_; break;
- case k22b: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_ << ", #+" << insn.vC_; break;
- case k22t: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_ << ", +" << insn.vC_; break;
- case k22s: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_ << ", #+" << insn.vC_; break;
- case k22c: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_ << ", thing@" << insn.vC_; break;
- case k32x: os << opcode << " v" << insn.vA_ << ", v" << insn.vB_; break;
- case k30t: os << opcode << " +" << (int)insn.vA_; break;
- case k31t: os << opcode << " v" << insn.vA_ << ", +" << insn.vB_; break;
- case k31i: os << opcode << " v" << insn.vA_ << ", #+" << insn.vB_; break;
- case k31c: os << opcode << " v" << insn.vA_ << ", thing@" << insn.vB_; break;
+ case k10x: os << opcode; break;
+ case k12x: os << StringPrintf("%s v%d, v%d", opcode, insn.vA_, insn.vB_); break;
+ case k11n: os << StringPrintf("%s v%d, #%+d", opcode, insn.vA_, insn.vB_); break;
+ case k11x: os << StringPrintf("%s v%d", opcode, insn.vA_); break;
+ case k10t: os << StringPrintf("%s %+d", opcode, insn.vA_); break;
+ case k20bc: os << StringPrintf("%s %d, kind@%d", opcode, insn.vA_, insn.vB_); break;
+ case k20t: os << StringPrintf("%s %+d", opcode, insn.vA_); break;
+ case k22x: os << StringPrintf("%s v%d, v%d", opcode, insn.vA_, insn.vB_); break;
+ case k21t: os << StringPrintf("%s v%d, %+d", opcode, insn.vA_, insn.vB_); break;
+ case k21s: os << StringPrintf("%s v%d, #%+d", opcode, insn.vA_, insn.vB_); break;
+ case k21h: os << StringPrintf("%s v%d, #%+d00000[00000000]", opcode, insn.vA_, insn.vB_); break;
+ case k21c: os << StringPrintf("%s v%d, thing@%d", opcode, insn.vA_, insn.vB_); break;
+ case k23x: os << StringPrintf("%s v%d, v%d, v%d", opcode, insn.vA_, insn.vB_, insn.vC_); break;
+ case k22b: os << StringPrintf("%s v%d, v%d, #%+d", opcode, insn.vA_, insn.vB_, insn.vC_); break;
+ case k22t: os << StringPrintf("%s v%d, v%d, %+d", opcode, insn.vA_, insn.vB_, insn.vC_); break;
+ case k22s: os << StringPrintf("%s v%d, v%d, #%+d", opcode, insn.vA_, insn.vB_, insn.vC_); break;
+ case k22c: os << StringPrintf("%s v%d, v%d, thing@%d", opcode, insn.vA_, insn.vB_, insn.vC_); break;
+ case k32x: os << StringPrintf("%s v%d, v%d", opcode, insn.vA_, insn.vB_); break;
+ case k30t: os << StringPrintf("%s %+d", opcode, insn.vA_); break;
+ case k31t: os << StringPrintf("%s v%d, %+d", opcode, insn.vA_, insn.vB_); break;
+ case k31i: os << StringPrintf("%s v%d, #%+d", opcode, insn.vA_, insn.vB_); break;
+ case k31c: os << StringPrintf("%s v%d, thing@%d", opcode, insn.vA_, insn.vB_); break;
case k35c: {
switch (insn.opcode_) {
case INVOKE_VIRTUAL:
@@ -321,11 +321,17 @@
case INVOKE_INTERFACE:
if (file != NULL) {
const DexFile::MethodId& meth_id = file->GetMethodId(insn.vB_);
- os << opcode << " {v" << insn.arg_[0] << ", v" << insn.arg_[1] << ", v" << insn.arg_[2]
- << ", v" << insn.arg_[3] << ", v" << insn.arg_[4] << "}, "
- << file->GetMethodDeclaringClassDescriptor(meth_id) << "."
- << file->GetMethodName(meth_id) << file->GetMethodSignature(meth_id)
- << " // method@" << insn.vB_;
+ os << opcode << " {";
+ for (size_t i = 0; i < insn.vA_; ++i) {
+ if (i != 0) {
+ os << ", ";
+ }
+ os << "v" << insn.arg_[i];
+ }
+ os << "}, "
+ << file->GetMethodDeclaringClassDescriptor(meth_id) << "."
+ << file->GetMethodName(meth_id) << file->GetMethodSignature(meth_id)
+ << " // method@" << insn.vB_;
break;
} // else fall-through
default:
@@ -335,8 +341,8 @@
}
break;
}
- case k3rc: os << opcode << " {v" << insn.vC_ << " .. v" << (insn.vC_+ insn.vA_ - 1) << "}, method@" << insn.vB_; break;
- case k51l: os << opcode << " v" << insn.vA_ << ", #+" << insn.vB_; break;
+ case k3rc: os << StringPrintf("%s, {v%d .. v%d}, method@%d", opcode, insn.vC_, (insn.vC_+ insn.vA_ - 1), insn.vB_); break;
+ case k51l: os << StringPrintf("%s v%d, #%+d", opcode, insn.vA_, insn.vB_); break;
default: os << " unknown format (" << DumpHex(5) << ")"; break;
}
return os.str();
diff --git a/src/oat_file.h b/src/oat_file.h
index 83560e8..831934a 100644
--- a/src/oat_file.h
+++ b/src/oat_file.h
@@ -221,7 +221,7 @@
friend class OatClass;
friend class OatDexFile;
- friend class OatDump; // For GetBase and GetLimit
+ friend class OatDumper; // For GetBase and GetLimit
DISALLOW_COPY_AND_ASSIGN(OatFile);
};
diff --git a/src/oatdump.cc b/src/oatdump.cc
index 7c9fa92..337c907 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -24,6 +24,7 @@
#include <vector>
#include "class_linker.h"
+#include "dex_instruction.h"
#include "file.h"
#include "image.h"
#include "object_utils.h"
@@ -79,11 +80,9 @@
"kClassRoots",
};
-class OatDump {
+class OatDumper {
public:
- static void Dump(const std::string& oat_filename,
- std::ostream& os,
- const OatFile& oat_file) {
+ void Dump(const std::string& oat_filename, std::ostream& os, const OatFile& oat_file) {
const OatHeader& oat_header = oat_file.GetOatHeader();
os << "MAGIC:\n";
@@ -107,6 +106,8 @@
os << std::flush;
std::vector<const OatFile::OatDexFile*> oat_dex_files = oat_file.GetOatDexFiles();
+ AddAllOffsets(oat_file, oat_dex_files);
+
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 != NULL);
@@ -115,9 +116,54 @@
}
private:
- static void DumpOatDexFile(std::ostream& os,
- const OatFile& oat_file,
- const OatFile::OatDexFile& oat_dex_file) {
+ void AddAllOffsets(const OatFile& oat_file, std::vector<const OatFile::OatDexFile*>& oat_dex_files) {
+ // We don't know the length of the code for each method, but we need to know where to stop
+ // when disassembling. What we do know is that a region of code will be followed by some other
+ // region, so if we keep a sorted sequence of the start of each region, we can infer the length
+ // of a piece of code by using upper_bound to find the start of the next region.
+ 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 != NULL);
+ UniquePtr<const DexFile> dex_file(oat_dex_file->OpenDexFile());
+ if (dex_file.get() == NULL) {
+ return;
+ }
+ for (size_t class_def_index = 0; class_def_index < dex_file->NumClassDefs(); class_def_index++) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+ UniquePtr<const OatFile::OatClass> oat_class(oat_dex_file->GetOatClass(class_def_index));
+ const byte* class_data = dex_file->GetClassData(class_def);
+ if (class_data != NULL) {
+ ClassDataItemIterator it(*dex_file, class_data);
+ SkipAllFields(it);
+ uint32_t class_method_index = 0;
+ while (it.HasNextDirectMethod()) {
+ AddOffsets(oat_class->GetOatMethod(class_method_index++));
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ AddOffsets(oat_class->GetOatMethod(class_method_index++));
+ it.Next();
+ }
+ }
+ }
+ }
+
+ // If the last thing in the file is code for a method, there won't be an offset for the "next"
+ // thing. Instead of having a special case in the upper_bound code, let's just add an entry
+ // for the end of the file.
+ offsets_.insert(static_cast<uint32_t>(oat_file.End() - oat_file.Begin()));
+ }
+
+ void AddOffsets(const OatFile::OatMethod& oat_method) {
+ offsets_.insert(oat_method.GetCodeOffset());
+ offsets_.insert(oat_method.GetMappingTableOffset());
+ offsets_.insert(oat_method.GetVmapTableOffset());
+ offsets_.insert(oat_method.GetGcMapOffset());
+ offsets_.insert(oat_method.GetInvokeStubOffset());
+ }
+
+ void DumpOatDexFile(std::ostream& os, const OatFile& oat_file,
+ const OatFile::OatDexFile& oat_dex_file) {
os << "OAT DEX FILE:\n";
os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str());
os << StringPrintf("checksum: %08x\n", oat_dex_file.GetDexFileLocationChecksum());
@@ -139,52 +185,51 @@
os << std::flush;
}
- static void DumpOatClass(std::ostream& os,
- const OatFile& oat_file,
- const OatFile::OatClass& oat_class,
- const DexFile& dex_file,
- const DexFile::ClassDef& class_def) {
- const byte* class_data = dex_file.GetClassData(class_def);
- if (class_data == NULL) { // empty class such as a marker interface?
- return;
- }
- ClassDataItemIterator it(dex_file, class_data);
-
- // just skipping through the fields to advance class_data
+ static void SkipAllFields(ClassDataItemIterator& it) {
while (it.HasNextStaticField()) {
it.Next();
}
while (it.HasNextInstanceField()) {
it.Next();
}
+ }
- uint32_t method_index = 0;
+ void DumpOatClass(std::ostream& os, const OatFile& oat_file, const OatFile::OatClass& oat_class,
+ const DexFile& dex_file, const DexFile::ClassDef& class_def) {
+ const byte* class_data = dex_file.GetClassData(class_def);
+ if (class_data == NULL) { // empty class such as a marker interface?
+ return;
+ }
+ ClassDataItemIterator it(dex_file, class_data);
+ SkipAllFields(it);
+
+ uint32_t class_method_index = 0;
while (it.HasNextDirectMethod()) {
- const OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index);
- DumpOatMethod(os, method_index, oat_file, oat_method, dex_file, it.GetMemberIndex());
- method_index++;
+ const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
+ DumpOatMethod(os, class_method_index, oat_file, oat_method, dex_file,
+ it.GetMemberIndex(), it.GetMethodCodeItem());
+ class_method_index++;
it.Next();
}
while (it.HasNextVirtualMethod()) {
- const OatFile::OatMethod oat_method = oat_class.GetOatMethod(method_index);
- DumpOatMethod(os, method_index, oat_file, oat_method, dex_file, it.GetMemberIndex());
- method_index++;
+ const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
+ DumpOatMethod(os, class_method_index, oat_file, oat_method, dex_file,
+ it.GetMemberIndex(), it.GetMethodCodeItem());
+ class_method_index++;
it.Next();
}
DCHECK(!it.HasNext());
os << std::flush;
}
- static void DumpOatMethod(std::ostream& os,
- uint32_t method_index,
- const OatFile& oat_file,
- const OatFile::OatMethod& oat_method,
- const DexFile& dex_file,
- uint32_t method_idx) {
- const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx);
+
+ void DumpOatMethod(std::ostream& os, uint32_t class_method_index, const OatFile& oat_file,
+ const OatFile::OatMethod& oat_method, const DexFile& dex_file,
+ uint32_t dex_method_idx, const DexFile::CodeItem* code_item) {
+ const DexFile::MethodId& method_id = dex_file.GetMethodId(dex_method_idx);
const char* name = dex_file.GetMethodName(method_id);
std::string signature(dex_file.GetMethodSignature(method_id));
- os << StringPrintf("\t%d: %s %s (method_idx=%d)\n",
- method_index, name, signature.c_str(), method_idx);
+ os << StringPrintf("\t%d: %s %s (dex_method_idx=%d)\n",
+ class_method_index, name, signature.c_str(), dex_method_idx);
os << StringPrintf("\t\tcode: %p (offset=%08x)\n",
oat_method.GetCode(), oat_method.GetCodeOffset());
os << StringPrintf("\t\tframe_size_in_bytes: %zd\n",
@@ -195,6 +240,7 @@
oat_method.GetFpSpillMask());
os << StringPrintf("\t\tmapping_table: %p (offset=%08x)\n",
oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
+ DumpMappingTable(os, oat_file, oat_method, dex_file, code_item);
os << StringPrintf("\t\tvmap_table: %p (offset=%08x)\n",
oat_method.GetVmapTable(), oat_method.GetVmapTableOffset());
os << StringPrintf("\t\tgc_map: %p (offset=%08x)\n",
@@ -202,6 +248,48 @@
os << StringPrintf("\t\tinvoke_stub: %p (offset=%08x)\n",
oat_method.GetInvokeStub(), oat_method.GetInvokeStubOffset());
}
+
+ void DumpMappingTable(std::ostream& os,
+ const OatFile& oat_file, const OatFile::OatMethod& oat_method,
+ const DexFile& dex_file, const DexFile::CodeItem* code_item) {
+ const uint32_t* raw_table = oat_method.GetMappingTable();
+ const void* code = oat_method.GetCode();
+ if (raw_table == NULL || code == NULL) {
+ return;
+ }
+
+ uint32_t length = *raw_table;
+ ++raw_table;
+
+ for (size_t i = 0; i < length; i += 2) {
+ uint32_t dex_pc = raw_table[i + 1];
+ const Instruction* instruction = Instruction::At(&code_item->insns_[dex_pc]);
+ os << StringPrintf("\t\t0x%04x: %s\n", dex_pc, instruction->DumpString(&dex_file).c_str());
+
+ // TODO: this is thumb2-specific.
+ const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code) + raw_table[i];
+ const uint8_t* end_native_pc = NULL;
+ if (i + 2 < length) {
+ end_native_pc = reinterpret_cast<const uint8_t*>(code) + raw_table[i + 2];
+ } else {
+ const uint8_t* oat_begin = reinterpret_cast<const uint8_t*>(oat_file.Begin());
+ uint32_t last_offset = static_cast<uint32_t>(native_pc - oat_begin);
+
+ typedef std::set<uint32_t>::iterator It;
+ It it = offsets_.lower_bound(last_offset);
+ CHECK(it != offsets_.end());
+ end_native_pc = reinterpret_cast<const uint8_t*>(oat_begin) + *it;
+ }
+
+ // TODO: insert disassembler here.
+ CHECK(native_pc <= end_native_pc);
+ for (; native_pc < end_native_pc; native_pc += 2) {
+ os << StringPrintf("\t\t\t%p: 0x%04x\n", native_pc, *reinterpret_cast<const uint16_t*>(native_pc));
+ }
+ }
+ }
+
+ std::set<uint32_t> offsets_;
};
class ImageDump {
@@ -292,7 +380,8 @@
os << "\n";
os << std::flush;
- OatDump::Dump(oat_location, os, *oat_file);
+ OatDumper oat_dumper;
+ oat_dumper.Dump(oat_location, os, *oat_file);
}
private:
@@ -632,7 +721,8 @@
fprintf(stderr, "Failed to open oat file from %s\n", oat_filename);
return EXIT_FAILURE;
}
- OatDump::Dump(oat_filename, *os, *oat_file);
+ OatDumper oat_dumper;
+ oat_dumper.Dump(oat_filename, *os, *oat_file);
return EXIT_SUCCESS;
}