summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--oatdump/oatdump.cc282
-rw-r--r--runtime/dex_instruction.cc17
-rw-r--r--runtime/dex_instruction.h4
3 files changed, 257 insertions, 46 deletions
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 11ccafbe7e..3ce86d872f 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -311,13 +311,23 @@ class OatDumperOptions {
bool dump_vmap,
bool disassemble_code,
bool absolute_addresses,
- const char* method_filter)
+ const char* class_filter,
+ const char* method_filter,
+ bool list_classes,
+ bool list_methods,
+ const char* export_dex_location,
+ uint32_t addr2instr)
: dump_raw_mapping_table_(dump_raw_mapping_table),
dump_raw_gc_map_(dump_raw_gc_map),
dump_vmap_(dump_vmap),
disassemble_code_(disassemble_code),
absolute_addresses_(absolute_addresses),
+ class_filter_(class_filter),
method_filter_(method_filter),
+ list_classes_(list_classes),
+ list_methods_(list_methods),
+ export_dex_location_(export_dex_location),
+ addr2instr_(addr2instr),
class_loader_(nullptr) {}
const bool dump_raw_mapping_table_;
@@ -325,27 +335,34 @@ class OatDumperOptions {
const bool dump_vmap_;
const bool disassemble_code_;
const bool absolute_addresses_;
+ const char* const class_filter_;
const char* const method_filter_;
+ const bool list_classes_;
+ const bool list_methods_;
+ const char* const export_dex_location_;
+ uint32_t addr2instr_;
Handle<mirror::ClassLoader>* class_loader_;
};
class OatDumper {
public:
- explicit OatDumper(const OatFile& oat_file, OatDumperOptions* options)
+ explicit OatDumper(const OatFile& oat_file, const OatDumperOptions& options)
: oat_file_(oat_file),
oat_dex_files_(oat_file.GetOatDexFiles()),
options_(options),
+ resolved_addr2instr_(0),
instruction_set_(oat_file_.GetOatHeader().GetInstructionSet()),
disassembler_(Disassembler::Create(instruction_set_,
- new DisassemblerOptions(options_->absolute_addresses_,
+ new DisassemblerOptions(options_.absolute_addresses_,
oat_file.Begin(),
true /* can_read_litals_ */))) {
- CHECK(options_->class_loader_ != nullptr);
+ CHECK(options_.class_loader_ != nullptr);
+ CHECK(options_.class_filter_ != nullptr);
+ CHECK(options_.method_filter_ != nullptr);
AddAllOffsets();
}
~OatDumper() {
- delete options_;
delete disassembler_;
}
@@ -380,7 +397,7 @@ class OatDumper {
#define DUMP_OAT_HEADER_OFFSET(label, offset) \
os << label " OFFSET:\n"; \
os << StringPrintf("0x%08x", oat_header.offset()); \
- if (oat_header.offset() != 0 && options_->absolute_addresses_) { \
+ if (oat_header.offset() != 0 && options_.absolute_addresses_) { \
os << StringPrintf(" (%p)", oat_file_.Begin() + oat_header.offset()); \
} \
os << StringPrintf("\n\n");
@@ -426,7 +443,7 @@ class OatDumper {
os << "\n";
}
- if (options_->absolute_addresses_) {
+ if (options_.absolute_addresses_) {
os << "BEGIN:\n";
os << reinterpret_cast<const void*>(oat_file_.Begin()) << "\n\n";
@@ -439,11 +456,26 @@ class OatDumper {
os << std::flush;
+ // If set, adjust relative address to be searched
+ if (options_.addr2instr_ != 0) {
+ resolved_addr2instr_ = options_.addr2instr_ + oat_header.GetExecutableOffset();
+ os << "SEARCH ADDRESS (executable offset + input):\n";
+ os << StringPrintf("0x%08x\n\n", resolved_addr2instr_);
+ }
+
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)) {
+ success = false;
+ }
+ } else {
+ if (!DumpOatDexFile(os, *oat_dex_file)) {
+ success = false;
+ }
}
}
os << std::flush;
@@ -553,6 +585,7 @@ class OatDumper {
bool DumpOatDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) {
bool success = true;
+ bool stop_analysis = false;
os << "OatDexFile:\n";
os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str());
os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum());
@@ -571,6 +604,12 @@ class OatDumper {
class_def_index++) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
const char* descriptor = dex_file->GetClassDescriptor(class_def);
+
+ // TODO: Support regex
+ if (DescriptorToDot(descriptor).find(options_.class_filter_) == std::string::npos) {
+ continue;
+ }
+
uint32_t oat_class_offset = oat_dex_file.GetOatClassOffset(class_def_index);
const OatFile::OatClass oat_class = oat_dex_file.GetOatClass(class_def_index);
os << StringPrintf("%zd: %s (offset=0x%08x) (type_idx=%d)",
@@ -580,15 +619,98 @@ class OatDumper {
// TODO: include bitmap here if type is kOatClassSomeCompiled?
Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
std::ostream indented_os(&indent_filter);
- if (!DumpOatClass(indented_os, oat_class, *(dex_file.get()), class_def)) {
+ if (options_.list_classes_) continue;
+ if (!DumpOatClass(indented_os, oat_class, *(dex_file.get()), class_def, &stop_analysis)) {
success = false;
}
+ if (stop_analysis) {
+ os << std::flush;
+ return success;
+ }
}
os << std::flush;
return success;
}
+ bool ExportDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) {
+ std::string error_msg;
+ std::string dex_file_location = oat_dex_file.GetDexFileLocation();
+
+ std::unique_ptr<const DexFile> dex_file(oat_dex_file.OpenDexFile(&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
+ if (fsize == 0 || fsize < sizeof(DexFile::Header)) {
+ os << "Invalid dex file\n";
+ return false;
+ }
+
+ // Verify output directory exists
+ if (!OS::DirectoryExists(options_.export_dex_location_)) {
+ // TODO: Extend OS::DirectoryExists if symlink support is required
+ os << options_.export_dex_location_ << " output directory not found or symlink\n";
+ return false;
+ }
+
+ // Beautify path names
+ if (dex_file_location.size() > PATH_MAX || dex_file_location.size() <= 0) {
+ return false;
+ }
+
+ std::string dex_orig_name;
+ size_t dex_orig_pos = dex_file_location.rfind('/');
+ if (dex_orig_pos == std::string::npos)
+ dex_orig_name = dex_file_location;
+ else
+ dex_orig_name = dex_file_location.substr(dex_orig_pos + 1);
+
+ // A more elegant approach to efficiently name user installed apps is welcome
+ if (dex_orig_name.size() == 8 && !dex_orig_name.compare("base.apk")) {
+ dex_file_location.erase(dex_orig_pos, strlen("base.apk") + 1);
+ size_t apk_orig_pos = dex_file_location.rfind('/');
+ if (apk_orig_pos != std::string::npos) {
+ dex_orig_name = dex_file_location.substr(++apk_orig_pos);
+ }
+ }
+
+ std::string out_dex_path(options_.export_dex_location_);
+ if (out_dex_path.back() != '/') {
+ out_dex_path.append("/");
+ }
+ out_dex_path.append(dex_orig_name);
+ out_dex_path.append("_export.dex");
+ if (out_dex_path.length() > PATH_MAX) {
+ return false;
+ }
+
+ std::unique_ptr<File> file(OS::CreateEmptyFile(out_dex_path.c_str()));
+ if (file.get() == nullptr) {
+ os << "Failed to open output dex file " << out_dex_path;
+ return false;
+ }
+
+ if (!file->WriteFully(dex_file->Begin(), fsize)) {
+ os << "Failed to write dex file";
+ file->Erase();
+ return false;
+ }
+
+ if (file->FlushCloseOrErase() != 0) {
+ os << "Flush and close failed";
+ return false;
+ }
+
+ os << StringPrintf("Dex file exported at %s (%zd bytes)\n", out_dex_path.c_str(), fsize);
+ os << std::flush;
+
+ return true;
+ }
+
static void SkipAllFields(ClassDataItemIterator& it) {
while (it.HasNextStaticField()) {
it.Next();
@@ -599,8 +721,9 @@ class OatDumper {
}
bool DumpOatClass(std::ostream& os, const OatFile::OatClass& oat_class, const DexFile& dex_file,
- const DexFile::ClassDef& class_def) {
+ const DexFile::ClassDef& class_def, bool* stop_analysis) {
bool success = true;
+ bool addr_found = false;
const uint8_t* class_data = dex_file.GetClassData(class_def);
if (class_data == nullptr) { // empty class such as a marker interface?
os << std::flush;
@@ -612,18 +735,26 @@ class OatDumper {
while (it.HasNextDirectMethod()) {
if (!DumpOatMethod(os, class_def, class_method_index, oat_class, dex_file,
it.GetMemberIndex(), it.GetMethodCodeItem(),
- it.GetRawMemberAccessFlags())) {
+ it.GetRawMemberAccessFlags(), &addr_found)) {
success = false;
}
+ if (addr_found) {
+ *stop_analysis = true;
+ return success;
+ }
class_method_index++;
it.Next();
}
while (it.HasNextVirtualMethod()) {
if (!DumpOatMethod(os, class_def, class_method_index, oat_class, dex_file,
it.GetMemberIndex(), it.GetMethodCodeItem(),
- it.GetRawMemberAccessFlags())) {
+ it.GetRawMemberAccessFlags(), &addr_found)) {
success = false;
}
+ if (addr_found) {
+ *stop_analysis = true;
+ return success;
+ }
class_method_index++;
it.Next();
}
@@ -641,20 +772,39 @@ class OatDumper {
uint32_t class_method_index,
const OatFile::OatClass& oat_class, const DexFile& dex_file,
uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
- uint32_t method_access_flags) {
+ uint32_t method_access_flags, bool* addr_found) {
bool success = true;
- std::string pretty_method = PrettyMethod(dex_method_idx, dex_file, true);
- if (pretty_method.find(options_->method_filter_) == std::string::npos) {
+
+ // TODO: Support regex
+ std::string method_name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx));
+ if (method_name.find(options_.method_filter_) == std::string::npos) {
return success;
}
+ std::string pretty_method = PrettyMethod(dex_method_idx, dex_file, true);
os << StringPrintf("%d: %s (dex_method_idx=%d)\n",
class_method_index, pretty_method.c_str(),
dex_method_idx);
+ if (options_.list_methods_) return success;
+
Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
std::unique_ptr<std::ostream> indent1_os(new std::ostream(&indent1_filter));
Indenter indent2_filter(indent1_os->rdbuf(), kIndentChar, kIndentBy1Count);
std::unique_ptr<std::ostream> indent2_os(new std::ostream(&indent2_filter));
+
+ uint32_t oat_method_offsets_offset = oat_class.GetOatMethodOffsetsOffset(class_method_index);
+ const OatMethodOffsets* oat_method_offsets = oat_class.GetOatMethodOffsets(class_method_index);
+ const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
+ uint32_t code_offset = oat_method.GetCodeOffset();
+ uint32_t code_size = oat_method.GetQuickCodeSize();
+ if (resolved_addr2instr_ != 0) {
+ if (resolved_addr2instr_ > code_offset + code_size) {
+ return success;
+ } else {
+ *addr_found = true; // stop analyzing file at next iteration
+ }
+ }
+
{
*indent1_os << "DEX CODE:\n";
DumpDexCode(*indent2_os, dex_file, code_item);
@@ -666,13 +816,9 @@ class OatDumper {
verifier.reset(DumpVerifier(*indent2_os, dex_method_idx, &dex_file, class_def, code_item,
method_access_flags));
}
-
- uint32_t oat_method_offsets_offset = oat_class.GetOatMethodOffsetsOffset(class_method_index);
- const OatMethodOffsets* oat_method_offsets = oat_class.GetOatMethodOffsets(class_method_index);
- const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
{
*indent1_os << "OatMethodOffsets ";
- if (options_->absolute_addresses_) {
+ if (options_.absolute_addresses_) {
*indent1_os << StringPrintf("%p ", oat_method_offsets);
}
*indent1_os << StringPrintf("(offset=0x%08x)\n", oat_method_offsets_offset);
@@ -685,7 +831,6 @@ class OatDumper {
return false;
}
- uint32_t code_offset = oat_method.GetCodeOffset();
*indent2_os << StringPrintf("code_offset: 0x%08x ", code_offset);
uint32_t aligned_code_begin = AlignCodeOffset(oat_method.GetCodeOffset());
if (aligned_code_begin > oat_file_.Size()) {
@@ -697,7 +842,7 @@ class OatDumper {
*indent2_os << "\n";
*indent2_os << "gc_map: ";
- if (options_->absolute_addresses_) {
+ if (options_.absolute_addresses_) {
*indent2_os << StringPrintf("%p ", oat_method.GetGcMap());
}
uint32_t gc_map_offset = oat_method.GetGcMapOffset();
@@ -707,7 +852,7 @@ class OatDumper {
"gc map table offset 0x%08x is past end of file 0x%08zx.\n",
gc_map_offset, oat_file_.Size());
success = false;
- } else if (options_->dump_raw_gc_map_) {
+ } else if (options_.dump_raw_gc_map_) {
Indenter indent3_filter(indent2_os->rdbuf(), kIndentChar, kIndentBy1Count);
std::ostream indent3_os(&indent3_filter);
DumpGcMap(indent3_os, oat_method, code_item);
@@ -718,7 +863,7 @@ class OatDumper {
uint32_t method_header_offset = oat_method.GetOatQuickMethodHeaderOffset();
const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader();
- if (options_->absolute_addresses_) {
+ if (options_.absolute_addresses_) {
*indent1_os << StringPrintf("%p ", method_header);
}
*indent1_os << StringPrintf("(offset=0x%08x)\n", method_header_offset);
@@ -732,7 +877,7 @@ class OatDumper {
}
*indent2_os << "mapping_table: ";
- if (options_->absolute_addresses_) {
+ if (options_.absolute_addresses_) {
*indent2_os << StringPrintf("%p ", oat_method.GetMappingTable());
}
uint32_t mapping_table_offset = oat_method.GetMappingTableOffset();
@@ -744,14 +889,14 @@ class OatDumper {
mapping_table_offset, oat_file_.Size(),
oat_method.GetMappingTableOffsetOffset());
success = false;
- } else if (options_->dump_raw_mapping_table_) {
+ } else if (options_.dump_raw_mapping_table_) {
Indenter indent3_filter(indent2_os->rdbuf(), kIndentChar, kIndentBy1Count);
std::ostream indent3_os(&indent3_filter);
DumpMappingTable(indent3_os, oat_method);
}
*indent2_os << "vmap_table: ";
- if (options_->absolute_addresses_) {
+ if (options_.absolute_addresses_) {
*indent2_os << StringPrintf("%p ", oat_method.GetVmapTable());
}
uint32_t vmap_table_offset = oat_method.GetVmapTableOffset();
@@ -763,7 +908,7 @@ class OatDumper {
vmap_table_offset, oat_file_.Size(),
oat_method.GetVmapTableOffsetOffset());
success = false;
- } else if (options_->dump_vmap_) {
+ } else if (options_.dump_vmap_) {
DumpVmapData(*indent2_os, oat_method, code_item);
}
}
@@ -794,12 +939,10 @@ class OatDumper {
success = false;
} else {
const void* code = oat_method.GetQuickCode();
- uint32_t code_size = oat_method.GetQuickCodeSize();
- uint32_t code_offset = oat_method.GetCodeOffset();
uint32_t aligned_code_begin = AlignCodeOffset(code_offset);
uint64_t aligned_code_end = aligned_code_begin + code_size;
- if (options_->absolute_addresses_) {
+ if (options_.absolute_addresses_) {
*indent1_os << StringPrintf("%p ", code);
}
*indent1_os << StringPrintf("(code_offset=0x%08x size_offset=0x%08x size=%u)%s\n",
@@ -820,7 +963,7 @@ class OatDumper {
aligned_code_end, oat_file_.Size(),
code_size, code_size_offset);
success = false;
- if (options_->disassemble_code_) {
+ if (options_.disassemble_code_) {
if (code_size_offset + kPrologueBytes <= oat_file_.Size()) {
DumpCode(*indent2_os, verifier.get(), oat_method, code_item, true, kPrologueBytes);
}
@@ -832,12 +975,12 @@ class OatDumper {
code_size, kMaxCodeSize,
code_size, code_size_offset);
success = false;
- if (options_->disassemble_code_) {
+ if (options_.disassemble_code_) {
if (code_size_offset + kPrologueBytes <= oat_file_.Size()) {
DumpCode(*indent2_os, verifier.get(), oat_method, code_item, true, kPrologueBytes);
}
}
- } else if (options_->disassemble_code_) {
+ } else if (options_.disassemble_code_) {
DumpCode(*indent2_os, verifier.get(), oat_method, code_item, !success, 0);
}
}
@@ -1175,7 +1318,8 @@ class OatDumper {
size_t i = 0;
while (i < code_item->insns_size_in_code_units_) {
const Instruction* instruction = Instruction::At(&code_item->insns_[i]);
- os << StringPrintf("0x%04zx: %s\n", i, instruction->DumpString(&dex_file).c_str());
+ os << StringPrintf("0x%04zx: ", i) << instruction->DumpHexLE(5)
+ << StringPrintf("\t| %s\n", instruction->DumpString(&dex_file).c_str());
i += instruction->SizeInCodeUnits();
}
}
@@ -1191,10 +1335,10 @@ class OatDumper {
StackHandleScope<1> hs(soa.Self());
Handle<mirror::DexCache> dex_cache(
hs.NewHandle(Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file)));
- DCHECK(options_->class_loader_ != nullptr);
+ DCHECK(options_.class_loader_ != nullptr);
return verifier::MethodVerifier::VerifyMethodAndDump(soa.Self(), os, dex_method_idx, dex_file,
dex_cache,
- *options_->class_loader_,
+ *options_.class_loader_,
&class_def, code_item,
NullHandle<mirror::ArtMethod>(),
method_access_flags);
@@ -1237,7 +1381,8 @@ class OatDumper {
const OatFile& oat_file_;
const std::vector<const OatFile::OatDexFile*> oat_dex_files_;
- const OatDumperOptions* options_;
+ const OatDumperOptions& options_;
+ uint32_t resolved_addr2instr_;
InstructionSet instruction_set_;
std::set<uintptr_t> offsets_;
Disassembler* disassembler_;
@@ -1335,7 +1480,7 @@ class ImageDumper {
stats_.oat_file_bytes = oat_file->Size();
- oat_dumper_.reset(new OatDumper(*oat_file, oat_dumper_options_.release()));
+ oat_dumper_.reset(new OatDumper(*oat_file, *oat_dumper_options_));
for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) {
CHECK(oat_dex_file != nullptr);
@@ -2045,17 +2190,18 @@ static int DumpOatWithRuntime(Runtime* runtime, OatFile* oat_file, OatDumperOpti
soa.Decode<mirror::ClassLoader*>(class_loader));
options->class_loader_ = &loader_handle;
- OatDumper oat_dumper(*oat_file, options);
+ OatDumper oat_dumper(*oat_file, *options);
bool success = oat_dumper.Dump(*os);
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}
static int DumpOatWithoutRuntime(OatFile* oat_file, OatDumperOptions* options, std::ostream* os) {
+ CHECK(oat_file != nullptr && options != nullptr);
// No image = no class loader.
NullHandle<mirror::ClassLoader> null_class_loader;
options->class_loader_ = &null_class_loader;
- OatDumper oat_dumper(*oat_file, options);
+ OatDumper oat_dumper(*oat_file, *options);
bool success = oat_dumper.Dump(*os);
return (success) ? EXIT_SUCCESS : EXIT_FAILURE;
}
@@ -2127,8 +2273,21 @@ struct OatdumpArgs : public CmdlineArgs {
} else if (option.starts_with("--symbolize=")) {
oat_filename_ = option.substr(strlen("--symbolize=")).data();
symbolize_ = true;
+ } else if (option.starts_with("--class-filter=")) {
+ class_filter_ = option.substr(strlen("--class-filter=")).data();
} else if (option.starts_with("--method-filter=")) {
method_filter_ = option.substr(strlen("--method-filter=")).data();
+ } else if (option.starts_with("--list-classes")) {
+ list_classes_ = true;
+ } else if (option.starts_with("--list-methods")) {
+ list_methods_ = true;
+ } else if (option.starts_with("--export-dex-to=")) {
+ export_dex_location_ = option.substr(strlen("--export-dex-to=")).data();
+ } else if (option.starts_with("--addr2instr=")) {
+ if (!ParseUint(option.substr(strlen("--addr2instr=")).data(), &addr2instr_)) {
+ *error_msg = "Address conversion failed";
+ return kParseError;
+ }
} else {
return kParseUnknownArgument;
}
@@ -2191,8 +2350,29 @@ struct OatdumpArgs : public CmdlineArgs {
" --no-disassemble may be used to disable disassembly.\n"
" Example: --no-disassemble\n"
"\n"
+ " --list-classes may be used to list target file classes (can be used with filters).\n"
+ " Example: --list-classes\n"
+ " Example: --list-classes --class-filter=com.example.foo\n"
+ "\n"
+ " --list-methods may be used to list target file methods (can be used with filters).\n"
+ " Example: --list-methods\n"
+ " Example: --list-methods --class-filter=com.example --method-filter=foo\n"
+ "\n"
+ " --symbolize=<file.oat>: output a copy of file.oat with elf symbols included.\n"
+ " Example: --symbolize=/system/framework/boot.oat\n"
+ "\n"
+ " --class-filter=<class name>: only dumps classes that contain the filter.\n"
+ " Example: --class-filter=com.example.foo\n"
+ "\n"
" --method-filter=<method name>: only dumps methods that contain the filter.\n"
" Example: --method-filter=foo\n"
+ "\n"
+ " --export-dex-to=<directory>: may be used to export oat embedded dex files.\n"
+ " Example: --export-dex-to=/data/local/tmp\n"
+ "\n"
+ " --addr2instr=<address>: output matching method disassembled code from relative\n"
+ " address (e.g. PC from crash dump)\n"
+ " Example: --addr2instr=0x00001a3b\n"
"\n";
return usage;
@@ -2200,6 +2380,7 @@ struct OatdumpArgs : public CmdlineArgs {
public:
const char* oat_filename_ = nullptr;
+ const char* class_filter_ = "";
const char* method_filter_ = "";
const char* image_location_ = nullptr;
std::string elf_filename_prefix_;
@@ -2208,6 +2389,10 @@ struct OatdumpArgs : public CmdlineArgs {
bool dump_vmap_ = true;
bool disassemble_code_ = true;
bool symbolize_ = false;
+ bool list_classes_ = false;
+ bool list_methods_ = false;
+ uint32_t addr2instr_ = 0;
+ const char* export_dex_location_ = nullptr;
};
struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
@@ -2223,7 +2408,12 @@ struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
args_->dump_vmap_,
args_->disassemble_code_,
absolute_addresses,
- args_->method_filter_));
+ args_->class_filter_,
+ args_->method_filter_,
+ args_->list_classes_,
+ args_->list_methods_,
+ args_->export_dex_location_,
+ args_->addr2instr_));
return (args_->boot_image_location_ != nullptr || args_->image_location_ != nullptr) &&
!args_->symbolize_;
@@ -2240,7 +2430,7 @@ struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
} else {
return DumpOat(nullptr,
args_->oat_filename_,
- oat_dumper_options_.release(),
+ oat_dumper_options_.get(),
args_->os_) == EXIT_SUCCESS;
}
}
@@ -2251,11 +2441,11 @@ struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
if (args_->oat_filename_ != nullptr) {
return DumpOat(runtime,
args_->oat_filename_,
- oat_dumper_options_.release(),
+ oat_dumper_options_.get(),
args_->os_) == EXIT_SUCCESS;
}
- return DumpImage(runtime, args_->image_location_, oat_dumper_options_.release(), args_->os_)
+ return DumpImage(runtime, args_->image_location_, oat_dumper_options_.get(), args_->os_)
== EXIT_SUCCESS;
}
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index a802759474..92e0f070bc 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -134,6 +134,23 @@ std::string Instruction::DumpHex(size_t code_units) const {
return os.str();
}
+std::string Instruction::DumpHexLE(size_t instr_code_units) const {
+ size_t inst_length = SizeInCodeUnits();
+ if (inst_length > instr_code_units) {
+ inst_length = instr_code_units;
+ }
+ std::ostringstream os;
+ const uint16_t* insn = reinterpret_cast<const uint16_t*>(this);
+ for (size_t i = 0; i < inst_length; i++) {
+ os << StringPrintf("%02x%02x", (uint8_t)(insn[i] & 0x00FF),
+ (uint8_t)((insn[i] & 0xFF00)>>8)) << " ";
+ }
+ for (size_t i = inst_length; i < instr_code_units; i++) {
+ os << " ";
+ }
+ return os.str();
+}
+
std::string Instruction::DumpString(const DexFile* file) const {
std::ostringstream os;
const char* opcode = kInstructionNames[Opcode()];
diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h
index af5d9d00d1..d3b9eb47df 100644
--- a/runtime/dex_instruction.h
+++ b/runtime/dex_instruction.h
@@ -525,6 +525,10 @@ class Instruction {
// Dump code_units worth of this instruction, padding to code_units for shorter instructions
std::string DumpHex(size_t code_units) const;
+ // Little-endian dump code_units worth of this instruction, padding to code_units for
+ // shorter instructions
+ std::string DumpHexLE(size_t instr_code_units) const;
+
uint16_t Fetch16(size_t offset) const {
const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
return insns[offset];