diff options
author | 2012-10-09 16:54:26 -0700 | |
---|---|---|
committer | 2012-10-09 16:54:26 -0700 | |
commit | b23a7729cf7855fa05345d03a4d84111d5ec7172 (patch) | |
tree | 5313e076b19387db3cbcac95225d3f098f19451d | |
parent | 137e88f798857321f4007631fdf052d2830ec2c4 (diff) |
Dump maps inline in disassembled code.
In pursuit of Bug: 7250540, dump mapping and GC map tables inline such
as:
0x607333a8: f8dfe11c ldr.w lr, [pc, #284] ; 0x6076416d
0x607333ac: 1c05 mov r5, r0
0x607333ae: f8df0144 ldr.w r0, [pc, #324] ; 0x6003ba08
0x607333b2: 9a0b ldr r2, [sp, #44]
0x607333b4: f04f0b2f orr r11, pc, ThumbExpand(47)
0x607333b8: 1c29 mov r1, r5
0x607333ba: 465b mov r3, r11
0x607333bc: 2900 cmp r1, #0
0x607333be: f0008070 beq.w +224 (0x607334a2)
0x607333c2: 47f0 blx lr
suspend point dex PC: 44
GC map objects: v2 (r7), v3 (r5), v6 ([sp + #84]), v7 (r6)
...
As GC map and mapping tables are inline, don't dump them.
Also dump dex instructions before code.
Change-Id: I9f0c04182a4cda6844027eae22e8151f2827dc99
-rw-r--r-- | src/disassembler.h | 3 | ||||
-rw-r--r-- | src/disassembler_arm.cc | 11 | ||||
-rw-r--r-- | src/disassembler_arm.h | 1 | ||||
-rw-r--r-- | src/disassembler_mips.cc | 5 | ||||
-rw-r--r-- | src/disassembler_mips.h | 1 | ||||
-rw-r--r-- | src/disassembler_x86.cc | 4 | ||||
-rw-r--r-- | src/disassembler_x86.h | 1 | ||||
-rw-r--r-- | src/gc_map.h | 10 | ||||
-rw-r--r-- | src/oatdump.cc | 222 | ||||
-rw-r--r-- | src/stack.h | 4 |
10 files changed, 192 insertions, 70 deletions
diff --git a/src/disassembler.h b/src/disassembler.h index 9f99ca6e0d..afff7f1abc 100644 --- a/src/disassembler.h +++ b/src/disassembler.h @@ -31,6 +31,9 @@ class Disassembler { static Disassembler* Create(InstructionSet instruction_set); virtual ~Disassembler() {} + // Dump a single instruction returning the length of that instruction. + virtual size_t Dump(std::ostream& os, const uint8_t* begin) = 0; + // Dump instructions within a range. virtual void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) = 0; protected: diff --git a/src/disassembler_arm.cc b/src/disassembler_arm.cc index dfaacf2e2a..d047f0e726 100644 --- a/src/disassembler_arm.cc +++ b/src/disassembler_arm.cc @@ -28,6 +28,17 @@ namespace arm { DisassemblerArm::DisassemblerArm() { } +size_t DisassemblerArm::Dump(std::ostream& os, const uint8_t* begin) { + if ((reinterpret_cast<intptr_t>(begin) & 1) == 0) { + DumpArm(os, begin); + return 4; + } else { + // remove thumb specifier bits + begin = reinterpret_cast<const uint8_t*>(reinterpret_cast<uintptr_t>(begin) & ~1); + return DumpThumb16(os, begin); + } +} + void DisassemblerArm::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) { if ((reinterpret_cast<intptr_t>(begin) & 1) == 0) { for (const uint8_t* cur = begin; cur < end; cur += 4) { diff --git a/src/disassembler_arm.h b/src/disassembler_arm.h index c59d395ca8..103876f33b 100644 --- a/src/disassembler_arm.h +++ b/src/disassembler_arm.h @@ -28,6 +28,7 @@ class DisassemblerArm : public Disassembler { public: DisassemblerArm(); + virtual size_t Dump(std::ostream& os, const uint8_t* begin); virtual void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end); private: void DumpArm(std::ostream& os, const uint8_t* instr); diff --git a/src/disassembler_mips.cc b/src/disassembler_mips.cc index ca9f6d7ca7..86a661cbae 100644 --- a/src/disassembler_mips.cc +++ b/src/disassembler_mips.cc @@ -260,6 +260,11 @@ static void DumpMips(std::ostream& os, const uint8_t* instr_ptr) { DisassemblerMips::DisassemblerMips() { } +size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* begin) { + DumpMips(os, begin); + return 4; +} + void DisassemblerMips::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) { for (const uint8_t* cur = begin; cur < end; cur += 4) { DumpMips(os, cur); diff --git a/src/disassembler_mips.h b/src/disassembler_mips.h index 8c3d0dcce8..ed45113db7 100644 --- a/src/disassembler_mips.h +++ b/src/disassembler_mips.h @@ -27,6 +27,7 @@ namespace mips { class DisassemblerMips : public Disassembler { public: DisassemblerMips(); + virtual size_t Dump(std::ostream& os, const uint8_t* begin); virtual void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end); private: diff --git a/src/disassembler_x86.cc b/src/disassembler_x86.cc index 0fc1e1eccb..35593a3e1c 100644 --- a/src/disassembler_x86.cc +++ b/src/disassembler_x86.cc @@ -28,6 +28,10 @@ namespace x86 { DisassemblerX86::DisassemblerX86() { } +size_t DisassemblerX86::Dump(std::ostream& os, const uint8_t* begin) { + return DumpInstruction(os, begin); +} + void DisassemblerX86::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) { size_t length = 0; for (const uint8_t* cur = begin; cur < end; cur += length) { diff --git a/src/disassembler_x86.h b/src/disassembler_x86.h index eab4f8abfe..13f8503720 100644 --- a/src/disassembler_x86.h +++ b/src/disassembler_x86.h @@ -26,6 +26,7 @@ class DisassemblerX86 : public Disassembler { public: DisassemblerX86(); + virtual size_t Dump(std::ostream& os, const uint8_t* begin); virtual void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end); private: size_t DumpInstruction(std::ostream& os, const uint8_t* instr); diff --git a/src/gc_map.h b/src/gc_map.h index 7becda5baa..9e1bed9422 100644 --- a/src/gc_map.h +++ b/src/gc_map.h @@ -51,6 +51,16 @@ class NativePcOffsetToReferenceMap { return result; } + // Does the given offset have an entry? + bool HasEntry(uintptr_t native_pc_offset) { + for (size_t i = 0; i < NumEntries(); ++i) { + if (GetNativePcOffset(i) == native_pc_offset) { + return true; + } + } + return false; + } + // Finds the bitmap associated with the native pc offset. const uint8_t* FindBitMap(uintptr_t native_pc_offset) { size_t num_entries = NumEntries(); diff --git a/src/oatdump.cc b/src/oatdump.cc index e4fc930a9f..88dbaaeb03 100644 --- a/src/oatdump.cc +++ b/src/oatdump.cc @@ -293,38 +293,40 @@ class OatDumper { os << StringPrintf("\t%d: %s (dex_method_idx=%d)\n", class_method_index, PrettyMethod(dex_method_idx, dex_file, true).c_str(), dex_method_idx); - os << StringPrintf("\t\tframe_size_in_bytes: %zd\n", - oat_method.GetFrameSizeInBytes()); - os << StringPrintf("\t\tcore_spill_mask: 0x%08x", - oat_method.GetCoreSpillMask()); + os << StringPrintf("\t\tframe_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes()); + os << StringPrintf("\t\tcore_spill_mask: 0x%08x\n", oat_method.GetCoreSpillMask()); DumpSpillMask(os, oat_method.GetCoreSpillMask(), false); - os << StringPrintf("\n\t\tfp_spill_mask: 0x%08x", - oat_method.GetFpSpillMask()); + os << StringPrintf("\n\t\tfp_spill_mask: 0x%08x\n", oat_method.GetFpSpillMask()); DumpSpillMask(os, oat_method.GetFpSpillMask(), true); - os << StringPrintf("\n\t\tmapping_table: %p (offset=0x%08x)\n", - oat_method.GetMappingTable(), oat_method.GetMappingTableOffset()); - DumpMappingTable(os, oat_method); os << StringPrintf("\t\tvmap_table: %p (offset=0x%08x)\n", oat_method.GetVmapTable(), oat_method.GetVmapTableOffset()); - DumpVmap(os, oat_method.GetVmapTable(), oat_method.GetCoreSpillMask(), - oat_method.GetFpSpillMask()); - os << StringPrintf("\t\tgc_map: %p (offset=0x%08x)\n", - oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset()); - DumpGcMap(os, oat_method.GetCode(), oat_method.GetNativeGcMap()); + DumpVmap(os, oat_method); + const bool kDumpRawMappingTable = false; + if (kDumpRawMappingTable) { + os << StringPrintf("\t\tmapping_table: %p (offset=0x%08x)\n", + oat_method.GetMappingTable(), oat_method.GetMappingTableOffset()); + DumpMappingTable(os, oat_method); + } + const bool kDumpRawGcMap = false; + if (kDumpRawGcMap) { + os << StringPrintf("\t\tgc_map: %p (offset=0x%08x)\n", + oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset()); + DumpGcMap(os, oat_method, code_item); + } + os << "\t\tDEX CODE:\n"; + DumpDexCode(os, dex_file, code_item); os << StringPrintf("\t\tCODE: %p (offset=0x%08x size=%d)%s\n", oat_method.GetCode(), oat_method.GetCodeOffset(), oat_method.GetCodeSize(), oat_method.GetCode() != NULL ? "..." : ""); - DumpCode(os, oat_method.GetCode(), oat_method.GetCodeSize(), oat_method.GetMappingTable(), - dex_file, code_item); + DumpCode(os, oat_method, code_item); os << StringPrintf("\t\tINVOKE STUB: %p (offset=0x%08x size=%d)%s\n", oat_method.GetInvokeStub(), oat_method.GetInvokeStubOffset(), oat_method.GetInvokeStubSize(), oat_method.GetInvokeStub() != NULL ? "..." : ""); - DumpCode(os, reinterpret_cast<const void*>(oat_method.GetInvokeStub()), - oat_method.GetInvokeStubSize(), NULL, dex_file, NULL); + DumpInvokeStub(os, oat_method); } void DumpSpillMask(std::ostream& os, uint32_t spill_mask, bool is_float) { @@ -350,8 +352,8 @@ class OatDumper { os << ")"; } - void DumpVmap(std::ostream& os, const uint16_t* raw_table, uint32_t core_spill_mask, - uint32_t fp_spill_mask) { + void DumpVmap(std::ostream& os, const OatFile::OatMethod& oat_method) { + const uint16_t* raw_table = oat_method.GetVmapTable(); if (raw_table == NULL) { return; } @@ -362,12 +364,12 @@ class OatDumper { uint16_t dex_reg = vmap_table[i]; size_t matches = 0; size_t spill_shifts = 0; - uint32_t spill_mask = core_spill_mask; + uint32_t spill_mask = oat_method.GetCoreSpillMask(); bool processing_fp = false; while (matches != (i + 1)) { if (spill_mask == 0) { CHECK(!processing_fp); - spill_mask = fp_spill_mask; + spill_mask = oat_method.GetFpSpillMask(); processing_fp = true; } matches += spill_mask & 1; // Add 1 if the low bit is set @@ -388,11 +390,43 @@ class OatDumper { os << "\n"; } - void DumpGcMap(std::ostream& os, const void* code, const uint8_t* gc_map_raw) { + void DescribeVReg(std::ostream& os, const OatFile::OatMethod& oat_method, + const DexFile::CodeItem* code_item, size_t reg) { + const uint16_t* raw_table = oat_method.GetVmapTable(); + if (raw_table != NULL) { + const VmapTable vmap_table(raw_table); + uint32_t vmap_offset; + if (vmap_table.IsInContext(reg, vmap_offset)) { + // Compute the register we need to load from the context + uint32_t spill_mask = oat_method.GetCoreSpillMask(); + CHECK_LT(vmap_offset, static_cast<uint32_t>(__builtin_popcount(spill_mask))); + uint32_t matches = 0; + uint32_t spill_shifts = 0; + while (matches != (vmap_offset + 1)) { + DCHECK_NE(spill_mask, 0u); + matches += spill_mask & 1; // Add 1 if the low bit is set + spill_mask >>= 1; + spill_shifts++; + } + spill_shifts--; // wind back one as we want the last match + os << "r" << spill_shifts; + } else { + uint32_t offset = StackVisitor::GetVRegOffset(code_item, oat_method.GetCoreSpillMask(), + oat_method.GetFpSpillMask(), + oat_method.GetFrameSizeInBytes(), reg); + os << "[sp + #" << offset << "]"; + } + } + } + + void DumpGcMap(std::ostream& os, const OatFile::OatMethod& oat_method, + const DexFile::CodeItem* code_item) { + const uint8_t* gc_map_raw = oat_method.GetNativeGcMap(); if (gc_map_raw == NULL) { return; } NativePcOffsetToReferenceMap map(gc_map_raw); + const void* code = oat_method.GetCode(); for (size_t entry = 0; entry < map.NumEntries(); entry++) { const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code) + map.GetNativePcOffset(entry); @@ -403,10 +437,14 @@ class OatDumper { for (size_t reg = 0; reg < num_regs; reg++) { if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) { if (first) { - os << " v" << reg; + os << " v" << reg << " ("; + DescribeVReg(os, oat_method, code_item, reg); + os << ")"; first = false; } else { - os << ", v" << reg; + os << ", v" << reg << " ("; + DescribeVReg(os, oat_method, code_item, reg); + os << ")"; } } } @@ -427,67 +465,115 @@ class OatDumper { uint32_t pc_to_dex_entries = *raw_table; ++raw_table; - os << "\t\t{"; + os << "\t\tsuspend point mappings {"; for (size_t i = 0; i < length; i += 2) { const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code) + raw_table[i]; uint32_t dex_pc = raw_table[i + 1]; os << StringPrintf("%p -> 0x%04x", native_pc, dex_pc); if (i + 2 == pc_to_dex_entries) { // Separate the pc -> dex from dex -> pc sections - os << "}\n\t\t{"; + os << "}\n\t\tcatch entry mappings {"; } else if (i + 2 < length) { os << ", "; } } - os << "}\n" << std::flush; + os << "}\n"; } - void DumpCode(std::ostream& os, const void* code, int code_size, - const uint32_t* raw_mapping_table, - const DexFile& dex_file, const DexFile::CodeItem* code_item) { - if (code == NULL || code_size == 0) { - return; + void DumpMappingAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method, size_t offset, + bool suspend_point_mapping) { + const uint32_t* raw_table = oat_method.GetMappingTable(); + if (raw_table != NULL) { + ++raw_table; + uint32_t length = *raw_table; + ++raw_table; + uint32_t pc_to_dex_entries = *raw_table; + ++raw_table; + size_t start, end; + if (suspend_point_mapping) { + start = 0; + end = pc_to_dex_entries; + } else { + start = pc_to_dex_entries; + end = length; + } + for (size_t i = start; i < end; i += 2) { + if (offset == raw_table[i]) { + if (suspend_point_mapping) { + os << "\t\t\tsuspend point dex PC: "; + } else { + os << "\t\t\tcatch entry dex PC: "; + } + os << raw_table[i + 1] << "\n"; + return; + } + } } + } - const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code); - const uint8_t* end_native_pc = native_pc + code_size; - - /* - * TODO: the mapping table is no longer useful for identifying Dalvik opcodes. This was - * a nice feature, so we ought to come up with another mechanism (at least when debugging). - * Keeping the old Dalvik disassembly code for reference. - */ - disassembler_->Dump(os, native_pc, end_native_pc); - (void)raw_mapping_table; - (void)dex_file; - (void)code_item; - -#if 0 - if (raw_mapping_table == NULL) { - // code but no mapping table is most likely caused by code created by the JNI compiler - disassembler_->Dump(os, native_pc, end_native_pc); - return; + void DumpGcMapAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method, + const DexFile::CodeItem* code_item, size_t offset) { + const uint8_t* gc_map_raw = oat_method.GetNativeGcMap(); + if (gc_map_raw != NULL) { + NativePcOffsetToReferenceMap map(gc_map_raw); + if (map.HasEntry(offset)) { + size_t num_regs = map.RegWidth() * 8; + const uint8_t* reg_bitmap = map.FindBitMap(offset); + bool first = true; + for (size_t reg = 0; reg < num_regs; reg++) { + if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) { + if (first) { + os << "\t\t\tGC map objects: v" << reg << " ("; + DescribeVReg(os, oat_method, code_item, reg); + os << ")"; + first = false; + } else { + os << ", v" << reg << " ("; + DescribeVReg(os, oat_method, code_item, reg); + os << ")"; + } + } + } + if (!first) { + os << "\n"; + } + } } + } - uint32_t length = *raw_mapping_table; - ++raw_mapping_table; - - for (size_t i = 0; i < length; i += 2) { - uint32_t dex_pc = raw_mapping_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()); - - const uint8_t* cur_pc = reinterpret_cast<const uint8_t*>(code) + raw_mapping_table[i]; - const uint8_t* cur_pc_end = NULL; - if (i + 2 < length) { - cur_pc_end = reinterpret_cast<const uint8_t*>(code) + raw_mapping_table[i + 2]; - } else { - cur_pc_end = end_native_pc; + void DumpDexCode(std::ostream& os, const DexFile& dex_file, const DexFile::CodeItem* code_item) { + if (code_item != NULL) { + size_t i = 0; + while (i < code_item->insns_size_in_code_units_) { + const Instruction* instruction = Instruction::At(&code_item->insns_[i]); + os << StringPrintf("\t\t\t0x%04x: %s\n", i, instruction->DumpString(&dex_file).c_str()); + i += instruction->SizeInCodeUnits(); } - CHECK(cur_pc < cur_pc_end); - disassembler_->Dump(os, cur_pc, cur_pc_end); } -#endif + } + + void DumpCode(std::ostream& os, const OatFile::OatMethod& oat_method, + const DexFile::CodeItem* code_item) { + const void* code = oat_method.GetCode(); + size_t code_size = oat_method.GetCodeSize(); + if (code == NULL || code_size == 0) { + os << "\t\t\tNO CODE!\n"; + return; + } + const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code); + size_t offset = 0; + while (offset < code_size) { + DumpMappingAtOffset(os, oat_method, offset, false); + offset += disassembler_->Dump(os, native_pc + offset); + DumpMappingAtOffset(os, oat_method, offset, true); + DumpGcMapAtOffset(os, oat_method, code_item, offset); + } + } + + void DumpInvokeStub(std::ostream& os, const OatFile::OatMethod& oat_method) { + const uint8_t* begin = reinterpret_cast<const uint8_t*>(oat_method.GetInvokeStub()); + const uint8_t* end = begin + oat_method.GetInvokeStubSize(); + disassembler_->Dump(os, begin, end); } const std::string host_prefix_; diff --git a/src/stack.h b/src/stack.h index ca379d4497..70d6f9d0f7 100644 --- a/src/stack.h +++ b/src/stack.h @@ -347,8 +347,8 @@ class StackVisitor { * +========================+ */ static int GetVRegOffset(const DexFile::CodeItem* code_item, - uint32_t core_spills, uint32_t fp_spills, - size_t frame_size, int reg) { + uint32_t core_spills, uint32_t fp_spills, + size_t frame_size, int reg) { DCHECK_EQ(frame_size & (kStackAlignment - 1), 0U); int num_spills = __builtin_popcount(core_spills) + __builtin_popcount(fp_spills) + 1; // Filler. int num_ins = code_item->ins_size_; |