From: David Srbecky Date: Mon, 30 Mar 2015 13:21:42 +0000 (+0100) Subject: Write .debug_line section using the new DWARF library. X-Git-Tag: android-x86-7.1-r1~889^2~1646^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=6f7158927fee233255f8e96719c374694b10cad3;p=android-x86%2Fart.git Write .debug_line section using the new DWARF library. Also simplify dex to java mapping and handle mapping in prologues and epilogues. Change-Id: I410f06024580f2a8788f2c93fe9bca132805029a --- diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index 1849e7ef6..03370db6c 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -142,7 +142,6 @@ CompiledMethod::CompiledMethod(CompilerDriver* driver, if (src_mapping_table == nullptr) { src_mapping_table_ = new SwapSrcMap(driver->GetSwapSpaceAllocator()); } else { - src_mapping_table->Arrange(); src_mapping_table_ = new SwapSrcMap(src_mapping_table->begin(), src_mapping_table->end(), driver->GetSwapSpaceAllocator()); } @@ -159,7 +158,7 @@ CompiledMethod::CompiledMethod(CompilerDriver* driver, } else { src_mapping_table_ = src_mapping_table == nullptr ? driver->DeduplicateSrcMappingTable(ArrayRef()) : - driver->DeduplicateSrcMappingTable(ArrayRef(src_mapping_table->Arrange())); + driver->DeduplicateSrcMappingTable(ArrayRef(*src_mapping_table)); mapping_table_ = mapping_table.empty() ? nullptr : driver->DeduplicateMappingTable(mapping_table); vmap_table_ = driver->DeduplicateVMapTable(vmap_table); diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 23869140a..7497b175f 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -94,20 +94,12 @@ class SrcMapElem { uint32_t from_; int32_t to_; - explicit operator int64_t() const { - return (static_cast(to_) << 32) | from_; - } - - bool operator<(const SrcMapElem& sme) const { - return int64_t(*this) < int64_t(sme); - } - - bool operator==(const SrcMapElem& sme) const { - return int64_t(*this) == int64_t(sme); - } - - explicit operator uint8_t() const { - return static_cast(from_ + to_); + // Lexicographical compare. + bool operator<(const SrcMapElem& other) const { + if (from_ != other.from_) { + return from_ < other.from_; + } + return to_ < other.to_; } }; @@ -129,49 +121,33 @@ class SrcMap FINAL : public std::vector { SrcMap(InputIt first, InputIt last, const Allocator& alloc) : std::vector(first, last, alloc) {} - void SortByFrom() { - std::sort(begin(), end(), [] (const SrcMapElem& lhs, const SrcMapElem& rhs) -> bool { - return lhs.from_ < rhs.from_; - }); - } - - const_iterator FindByTo(int32_t to) const { - return std::lower_bound(begin(), end(), SrcMapElem({0, to})); - } - - SrcMap& Arrange() { - if (!empty()) { - std::sort(begin(), end()); - resize(std::unique(begin(), end()) - begin()); - shrink_to_fit(); - } - return *this; - } - - void DeltaFormat(const SrcMapElem& start, uint32_t highest_pc) { - // Convert from abs values to deltas. + void push_back(const SrcMapElem& elem) { if (!empty()) { - SortByFrom(); - - // TODO: one PC can be mapped to several Java src lines. - // do we want such a one-to-many correspondence? - - // get rid of the highest values - size_t i = size() - 1; - for (; i > 0 ; i--) { - if ((*this)[i].from_ < highest_pc) { - break; - } + // Check that the addresses are inserted in sorted order. + DCHECK_GE(elem.from_, this->back().from_); + // If two consequitive entries map to the same value, ignore the later. + // E.g. for map {{0, 1}, {4, 1}, {8, 2}}, all values in [0,8) map to 1. + if (elem.to_ == this->back().to_) { + return; } - this->resize(i + 1); - - for (i = size(); --i >= 1; ) { - (*this)[i].from_ -= (*this)[i-1].from_; - (*this)[i].to_ -= (*this)[i-1].to_; - } - DCHECK((*this)[0].from_ >= start.from_); - (*this)[0].from_ -= start.from_; - (*this)[0].to_ -= start.to_; + } + std::vector::push_back(elem); + } + + // Returns true and the corresponding "to" value if the mapping is found. + // Oterwise returns false and 0. + std::pair Find(uint32_t from) const { + // Finds first mapping such that lb.from_ >= from. + auto lb = std::lower_bound(begin(), end(), SrcMapElem {from, INT32_MIN}); + if (lb != end() && lb->from_ == from) { + // Found exact match. + return std::make_pair(true, lb->to_); + } else if (lb != begin()) { + // The previous mapping is still in effect. + return std::make_pair(true, (--lb)->to_); + } else { + // Not found because 'from' is smaller than first entry in the map. + return std::make_pair(false, 0); } } }; @@ -428,7 +404,7 @@ class CompiledMethod FINAL : public CompiledCode { const uint32_t core_spill_mask_; // For quick code, a bit mask describing spilled FPR callee-save registers. const uint32_t fp_spill_mask_; - // For quick code, a set of pairs (PC, Line) mapping from native PC offset to Java line + // For quick code, a set of pairs (PC, DEX) mapping from native PC offset to DEX offset. SwapSrcMap* src_mapping_table_; // For quick code, a uleb128 encoded map from native PC offset to dex PC aswell as dex PC to // native PC offset. Size prefixed. diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 39725dee3..0acdd422d 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -99,14 +99,16 @@ std::ostream& operator<<(std::ostream& os, const BBType& code); // Shared pseudo opcodes - must be < 0. enum LIRPseudoOpcode { - kPseudoExportedPC = -16, - kPseudoSafepointPC = -15, - kPseudoIntrinsicRetry = -14, - kPseudoSuspendTarget = -13, - kPseudoThrowTarget = -12, - kPseudoCaseLabel = -11, - kPseudoMethodEntry = -10, - kPseudoMethodExit = -9, + kPseudoPrologueBegin = -18, + kPseudoPrologueEnd = -17, + kPseudoEpilogueBegin = -16, + kPseudoEpilogueEnd = -15, + kPseudoExportedPC = -14, + kPseudoSafepointPC = -13, + kPseudoIntrinsicRetry = -12, + kPseudoSuspendTarget = -11, + kPseudoThrowTarget = -10, + kPseudoCaseLabel = -9, kPseudoBarrier = -8, kPseudoEntryBlock = -7, kPseudoExitBlock = -6, diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index 3e6987884..c5ac4c150 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -1083,7 +1083,9 @@ void ArmMir2Lir::InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir) { #define PADDING_MOV_R5_R5 0x1C2D uint8_t* ArmMir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { + uint8_t* const write_buffer = write_pos; for (; lir != NULL; lir = NEXT_LIR(lir)) { + lir->offset = (write_pos - write_buffer); if (!lir->flags.is_nop) { int opcode = lir->opcode; if (IsPseudoLirOp(opcode)) { diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 3081c9e75..e6158c320 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -372,7 +372,6 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { * a leaf *and* our frame size < fudge factor. */ bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kArm); - NewLIR0(kPseudoMethodEntry); const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm); bool large_frame = (static_cast(frame_size_) > kStackOverflowReservedUsableBytes); bool generate_explicit_stack_overflow_check = large_frame || @@ -507,7 +506,6 @@ void ArmMir2Lir::GenExitSequence() { LockTemp(rs_r0); LockTemp(rs_r1); - NewLIR0(kPseudoMethodExit); OpRegImm(kOpAdd, rs_rARM_SP, frame_size_ - (spill_count * 4)); /* Need to restore any FP callee saves? */ if (num_fp_spills_) { diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc index a59deb5db..2f1ae66bf 100644 --- a/compiler/dex/quick/arm64/assemble_arm64.cc +++ b/compiler/dex/quick/arm64/assemble_arm64.cc @@ -686,7 +686,9 @@ void Arm64Mir2Lir::InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir) #define PADDING_NOP (UINT32_C(0xd503201f)) uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { + uint8_t* const write_buffer = write_pos; for (; lir != nullptr; lir = NEXT_LIR(lir)) { + lir->offset = (write_pos - write_buffer); bool opcode_is_wide = IS_WIDE(lir->opcode); A64Opcode opcode = UNWIDE(lir->opcode); diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc index 3316945ed..6b47bba88 100644 --- a/compiler/dex/quick/arm64/call_arm64.cc +++ b/compiler/dex/quick/arm64/call_arm64.cc @@ -312,8 +312,6 @@ void Arm64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kArm64); - NewLIR0(kPseudoMethodEntry); - const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm64); const bool large_frame = static_cast(frame_size_) > kStackOverflowReservedUsableBytes; bool generate_explicit_stack_overflow_check = large_frame || @@ -401,9 +399,6 @@ void Arm64Mir2Lir::GenExitSequence() { */ LockTemp(rs_x0); LockTemp(rs_x1); - - NewLIR0(kPseudoMethodExit); - UnspillRegs(rs_sp, core_spill_mask_, fp_spill_mask_, frame_size_); // Finally return. diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 509d4487a..483a5d06c 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -203,12 +203,17 @@ void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr) { /* Handle pseudo-ops individually, and all regular insns as a group */ switch (lir->opcode) { - case kPseudoMethodEntry: - LOG(INFO) << "-------- method entry " - << PrettyMethod(cu_->method_idx, *cu_->dex_file); + case kPseudoPrologueBegin: + LOG(INFO) << "-------- PrologueBegin"; break; - case kPseudoMethodExit: - LOG(INFO) << "-------- Method_Exit"; + case kPseudoPrologueEnd: + LOG(INFO) << "-------- PrologueEnd"; + break; + case kPseudoEpilogueBegin: + LOG(INFO) << "-------- EpilogueBegin"; + break; + case kPseudoEpilogueEnd: + LOG(INFO) << "-------- EpilogueEnd"; break; case kPseudoBarrier: LOG(INFO) << "-------- BARRIER"; @@ -267,8 +272,9 @@ void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr) { lir, base_addr)); std::string op_operands(BuildInsnString(GetTargetInstFmt(lir->opcode), lir, base_addr)); - LOG(INFO) << StringPrintf("%5p: %-9s%s%s", + LOG(INFO) << StringPrintf("%5p|0x%02x: %-9s%s%s", base_addr + offset, + lir->dalvik_offset, op_name.c_str(), op_operands.c_str(), lir->flags.is_nop ? "(nop)" : ""); } @@ -713,14 +719,17 @@ void Mir2Lir::CreateMappingTables() { DCHECK_EQ(static_cast(write_pos - &encoded_mapping_table_[0]), hdr_data_size); uint8_t* write_pos2 = write_pos + pc2dex_data_size; + bool is_in_prologue_or_epilogue = false; pc2dex_offset = 0u; pc2dex_dalvik_offset = 0u; dex2pc_offset = 0u; dex2pc_dalvik_offset = 0u; for (LIR* tgt_lir = first_lir_insn_; tgt_lir != nullptr; tgt_lir = NEXT_LIR(tgt_lir)) { - if (generate_src_map && !tgt_lir->flags.is_nop) { - src_mapping_table_.push_back(SrcMapElem({tgt_lir->offset, - static_cast(tgt_lir->dalvik_offset)})); + if (generate_src_map && !tgt_lir->flags.is_nop && tgt_lir->opcode >= 0) { + if (!is_in_prologue_or_epilogue) { + src_mapping_table_.push_back(SrcMapElem({tgt_lir->offset, + static_cast(tgt_lir->dalvik_offset)})); + } } if (!tgt_lir->flags.is_nop && (tgt_lir->opcode == kPseudoSafepointPC)) { DCHECK(pc2dex_offset <= tgt_lir->offset); @@ -738,6 +747,12 @@ void Mir2Lir::CreateMappingTables() { dex2pc_offset = tgt_lir->offset; dex2pc_dalvik_offset = tgt_lir->dalvik_offset; } + if (tgt_lir->opcode == kPseudoPrologueBegin || tgt_lir->opcode == kPseudoEpilogueBegin) { + is_in_prologue_or_epilogue = true; + } + if (tgt_lir->opcode == kPseudoPrologueEnd || tgt_lir->opcode == kPseudoEpilogueEnd) { + is_in_prologue_or_epilogue = false; + } } DCHECK_EQ(static_cast(write_pos - &encoded_mapping_table_[0]), hdr_data_size + pc2dex_data_size); diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index de66b3541..c932df6dc 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -275,7 +275,6 @@ void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) */ skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, target); - NewLIR0(kPseudoMethodEntry); RegStorage check_reg = AllocPtrSizeTemp(); RegStorage new_sp = AllocPtrSizeTemp(); const RegStorage rs_sp = TargetPtrReg(kSp); @@ -345,7 +344,6 @@ void MipsMir2Lir::GenExitSequence() { LockTemp(TargetPtrReg(kRet0)); LockTemp(TargetPtrReg(kRet1)); - NewLIR0(kPseudoMethodExit); UnSpillCoreRegs(); OpReg(kOpBx, TargetPtrReg(kLr)); } diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index 0b480a09c..ed8e21e81 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -1250,10 +1250,14 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { if (bb->block_type == kEntryBlock) { ResetRegPool(); int start_vreg = mir_graph_->GetFirstInVR(); + AppendLIR(NewLIR0(kPseudoPrologueBegin)); GenEntrySequence(&mir_graph_->reg_location_[start_vreg], mir_graph_->GetMethodLoc()); + AppendLIR(NewLIR0(kPseudoPrologueEnd)); } else if (bb->block_type == kExitBlock) { ResetRegPool(); + AppendLIR(NewLIR0(kPseudoEpilogueBegin)); GenExitSequence(); + AppendLIR(NewLIR0(kPseudoEpilogueEnd)); } for (mir = bb->first_mir_insn; mir != NULL; mir = mir->next) { diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index e81228a5e..89f6c6edb 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -186,7 +186,6 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { stack_decrement_ = OpRegImm(kOpSub, rs_rSP, frame_size_ - GetInstructionSetPointerSize(cu_->instruction_set)); - NewLIR0(kPseudoMethodEntry); /* Spill core callee saves */ SpillCoreRegs(); SpillFPRegs(); @@ -259,7 +258,6 @@ void X86Mir2Lir::GenExitSequence() { LockTemp(rs_rX86_RET0); LockTemp(rs_rX86_RET1); - NewLIR0(kPseudoMethodExit); UnSpillCoreRegs(); UnSpillFPRegs(); /* Remove frame except for return address */ diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index ca5ec6638..a92ce69c6 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -25,6 +25,8 @@ #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "dwarf.h" +#include "dwarf/debug_frame_writer.h" +#include "dwarf/debug_line_writer.h" #include "elf_builder.h" #include "elf_file.h" #include "elf_utils.h" @@ -275,96 +277,8 @@ bool ElfWriterQuickWrite(); } -class LineTableGenerator FINAL : public Leb128Encoder { - public: - LineTableGenerator(int line_base, int line_range, int opcode_base, - std::vector* data, uintptr_t current_address, - size_t current_line) - : Leb128Encoder(data), line_base_(line_base), line_range_(line_range), - opcode_base_(opcode_base), current_address_(current_address), - current_line_(current_line), current_file_index_(0) {} - - void PutDelta(unsigned delta_addr, int delta_line) { - current_line_ += delta_line; - current_address_ += delta_addr; - - if (delta_line >= line_base_ && delta_line < line_base_ + line_range_) { - unsigned special_opcode = (delta_line - line_base_) + - (line_range_ * delta_addr) + opcode_base_; - if (special_opcode <= 255) { - PushByte(data_, special_opcode); - return; - } - } - - // generate standart opcode for address advance - if (delta_addr != 0) { - PushByte(data_, DW_LNS_advance_pc); - PushBackUnsigned(delta_addr); - } - - // generate standart opcode for line delta - if (delta_line != 0) { - PushByte(data_, DW_LNS_advance_line); - PushBackSigned(delta_line); - } - - // generate standart opcode for new LTN entry - PushByte(data_, DW_LNS_copy); - } - - void SetAddr(uintptr_t addr) { - if (current_address_ == addr) { - return; - } - - current_address_ = addr; - - PushByte(data_, 0); // extended opcode: - PushByte(data_, 1 + 4); // length: opcode_size + address_size - PushByte(data_, DW_LNE_set_address); - Push32(data_, addr); - } - - void SetLine(unsigned line) { - int delta_line = line - current_line_; - if (delta_line) { - current_line_ = line; - PushByte(data_, DW_LNS_advance_line); - PushBackSigned(delta_line); - } - } - - void SetFile(unsigned file_index) { - if (current_file_index_ != file_index) { - current_file_index_ = file_index; - PushByte(data_, DW_LNS_set_file); - PushBackUnsigned(file_index); - } - } - - void EndSequence() { - // End of Line Table Program - // 0(=ext), 1(len), DW_LNE_end_sequence - PushByte(data_, 0); - PushByte(data_, 1); - PushByte(data_, DW_LNE_end_sequence); - } - - private: - const int line_base_; - const int line_range_; - const int opcode_base_; - uintptr_t current_address_; - size_t current_line_; - unsigned current_file_index_; - - DISALLOW_COPY_AND_ASSIGN(LineTableGenerator); -}; - // TODO: rewriting it using DexFile::DecodeDebugInfo needs unneeded stuff. -static void GetLineInfoForJava(const uint8_t* dbgstream, const SwapSrcMap& pc2dex, - DefaultSrcMap* result, uint32_t start_pc = 0) { +static void GetLineInfoForJava(const uint8_t* dbgstream, DefaultSrcMap* dex2line) { if (dbgstream == nullptr) { return; } @@ -419,12 +333,7 @@ static void GetLineInfoForJava(const uint8_t* dbgstream, const SwapSrcMap& pc2de adjopcode = opcode - DexFile::DBG_FIRST_SPECIAL; dex_offset += adjopcode / DexFile::DBG_LINE_RANGE; java_line += DexFile::DBG_LINE_BASE + (adjopcode % DexFile::DBG_LINE_RANGE); - - for (SwapSrcMap::const_iterator found = pc2dex.FindByTo(dex_offset); - found != pc2dex.end() && found->to_ == static_cast(dex_offset); - found++) { - result->push_back({found->from_ + start_pc, static_cast(java_line)}); - } + dex2line->push_back({dex_offset, static_cast(java_line)}); break; } } @@ -443,71 +352,78 @@ static void FillInCFIInformation(OatWriter* oat_writer, std::vector* dbg_str, std::vector* dbg_line, uint32_t text_section_offset) { - const std::vector& method_info = oat_writer->GetCFIMethodInfo(); + const std::vector& method_infos = oat_writer->GetCFIMethodInfo(); uint32_t producer_str_offset = PushStr(dbg_str, "Android dex2oat"); + constexpr bool use_64bit_addresses = false; + // Create the debug_abbrev section with boilerplate information. // We only care about low_pc and high_pc right now for the compilation // unit and methods. // Tag 1: Compilation unit: DW_TAG_compile_unit. PushByte(dbg_abbrev, 1); - PushByte(dbg_abbrev, DW_TAG_compile_unit); + PushByte(dbg_abbrev, dwarf::DW_TAG_compile_unit); // There are children (the methods). - PushByte(dbg_abbrev, DW_CHILDREN_yes); + PushByte(dbg_abbrev, dwarf::DW_CHILDREN_yes); // DW_AT_producer DW_FORM_data1. // REVIEW: we can get rid of dbg_str section if // DW_FORM_string (immediate string) was used everywhere instead of // DW_FORM_strp (ref to string from .debug_str section). // DW_FORM_strp makes sense only if we reuse the strings. - PushByte(dbg_abbrev, DW_AT_producer); - PushByte(dbg_abbrev, DW_FORM_strp); + PushByte(dbg_abbrev, dwarf::DW_AT_producer); + PushByte(dbg_abbrev, dwarf::DW_FORM_strp); // DW_LANG_Java DW_FORM_data1. - PushByte(dbg_abbrev, DW_AT_language); - PushByte(dbg_abbrev, DW_FORM_data1); + PushByte(dbg_abbrev, dwarf::DW_AT_language); + PushByte(dbg_abbrev, dwarf::DW_FORM_data1); // DW_AT_low_pc DW_FORM_addr. - PushByte(dbg_abbrev, DW_AT_low_pc); - PushByte(dbg_abbrev, DW_FORM_addr); + PushByte(dbg_abbrev, dwarf::DW_AT_low_pc); + PushByte(dbg_abbrev, dwarf::DW_FORM_addr); // DW_AT_high_pc DW_FORM_addr. - PushByte(dbg_abbrev, DW_AT_high_pc); - PushByte(dbg_abbrev, DW_FORM_addr); + PushByte(dbg_abbrev, dwarf::DW_AT_high_pc); + PushByte(dbg_abbrev, dwarf::DW_FORM_addr); if (dbg_line != nullptr) { // DW_AT_stmt_list DW_FORM_sec_offset. - PushByte(dbg_abbrev, DW_AT_stmt_list); - PushByte(dbg_abbrev, DW_FORM_sec_offset); + PushByte(dbg_abbrev, dwarf::DW_AT_stmt_list); + PushByte(dbg_abbrev, dwarf::DW_FORM_data4); } // End of DW_TAG_compile_unit. - PushHalf(dbg_abbrev, 0); + PushByte(dbg_abbrev, 0); // DW_AT. + PushByte(dbg_abbrev, 0); // DW_FORM. // Tag 2: Compilation unit: DW_TAG_subprogram. PushByte(dbg_abbrev, 2); - PushByte(dbg_abbrev, DW_TAG_subprogram); + PushByte(dbg_abbrev, dwarf::DW_TAG_subprogram); // There are no children. - PushByte(dbg_abbrev, DW_CHILDREN_no); + PushByte(dbg_abbrev, dwarf::DW_CHILDREN_no); // Name of the method. - PushByte(dbg_abbrev, DW_AT_name); - PushByte(dbg_abbrev, DW_FORM_strp); + PushByte(dbg_abbrev, dwarf::DW_AT_name); + PushByte(dbg_abbrev, dwarf::DW_FORM_strp); // DW_AT_low_pc DW_FORM_addr. - PushByte(dbg_abbrev, DW_AT_low_pc); - PushByte(dbg_abbrev, DW_FORM_addr); + PushByte(dbg_abbrev, dwarf::DW_AT_low_pc); + PushByte(dbg_abbrev, dwarf::DW_FORM_addr); // DW_AT_high_pc DW_FORM_addr. - PushByte(dbg_abbrev, DW_AT_high_pc); - PushByte(dbg_abbrev, DW_FORM_addr); + PushByte(dbg_abbrev, dwarf::DW_AT_high_pc); + PushByte(dbg_abbrev, dwarf::DW_FORM_addr); // End of DW_TAG_subprogram. - PushHalf(dbg_abbrev, 0); + PushByte(dbg_abbrev, 0); // DW_AT. + PushByte(dbg_abbrev, 0); // DW_FORM. + + // End of abbrevs for compilation unit + PushByte(dbg_abbrev, 0); // Start the debug_info section with the header information // 'unit_length' will be filled in later. @@ -520,8 +436,8 @@ static void FillInCFIInformation(OatWriter* oat_writer, // Offset into .debug_abbrev section (always 0). Push32(dbg_info, 0); - // Address size: 4. - PushByte(dbg_info, 4); + // Address size: 4 or 8. + PushByte(dbg_info, use_64bit_addresses ? 8 : 4); // Start the description for the compilation unit. // This uses tag 1. @@ -531,31 +447,34 @@ static void FillInCFIInformation(OatWriter* oat_writer, Push32(dbg_info, producer_str_offset); // The language is Java. - PushByte(dbg_info, DW_LANG_Java); + PushByte(dbg_info, dwarf::DW_LANG_Java); // low_pc and high_pc. - uint32_t cunit_low_pc = 0 - 1; + uint32_t cunit_low_pc = static_cast(-1); uint32_t cunit_high_pc = 0; - int cunit_low_pc_pos = dbg_info->size(); - Push32(dbg_info, 0); - Push32(dbg_info, 0); + for (auto method_info : method_infos) { + cunit_low_pc = std::min(cunit_low_pc, method_info.low_pc_); + cunit_high_pc = std::max(cunit_high_pc, method_info.high_pc_); + } + Push32(dbg_info, cunit_low_pc + text_section_offset); + Push32(dbg_info, cunit_high_pc + text_section_offset); - if (dbg_line == nullptr) { - for (size_t i = 0; i < method_info.size(); ++i) { - const OatWriter::DebugInfo &dbg = method_info[i]; + if (dbg_line != nullptr) { + // Line number table offset. + Push32(dbg_info, dbg_line->size()); + } - cunit_low_pc = std::min(cunit_low_pc, dbg.low_pc_); - cunit_high_pc = std::max(cunit_high_pc, dbg.high_pc_); + for (auto method_info : method_infos) { + // Start a new TAG: subroutine (2). + PushByte(dbg_info, 2); - // Start a new TAG: subroutine (2). - PushByte(dbg_info, 2); + // Enter name, low_pc, high_pc. + Push32(dbg_info, PushStr(dbg_str, method_info.method_name_)); + Push32(dbg_info, method_info.low_pc_ + text_section_offset); + Push32(dbg_info, method_info.high_pc_ + text_section_offset); + } - // Enter name, low_pc, high_pc. - Push32(dbg_info, PushStr(dbg_str, dbg.method_name_)); - Push32(dbg_info, dbg.low_pc_ + text_section_offset); - Push32(dbg_info, dbg.high_pc_ + text_section_offset); - } - } else { + if (dbg_line != nullptr) { // TODO: in gdb info functions - reports Java functions, but // source file is because .debug_line is formed as one // compilation unit. To fix this it is possible to generate @@ -563,110 +482,135 @@ static void FillInCFIInformation(OatWriter* oat_writer, // Each of the these compilation units can have several non-adjacent // method ranges. - // Line number table offset - Push32(dbg_info, dbg_line->size()); + std::vector::FileEntry> files; + std::unordered_map files_map; + std::vector directories; + std::unordered_map directories_map; + + int code_factor_bits_ = 0; + int isa = -1; + switch (oat_writer->GetOatHeader().GetInstructionSet()) { + case kThumb2: + code_factor_bits_ = 1; // 16-bit instuctions + isa = 1; // DW_ISA_ARM_thumb. + break; + case kArm: + code_factor_bits_ = 2; // 32-bit instructions + isa = 2; // DW_ISA_ARM_arm. + break; + case kArm64: + case kMips: + case kMips64: + code_factor_bits_ = 2; // 32-bit instructions + break; + case kNone: + case kX86: + case kX86_64: + break; + } - size_t lnt_length = dbg_line->size(); - Push32(dbg_line, 0); - - PushHalf(dbg_line, 4); // LNT Version DWARF v4 => 4 - - size_t lnt_hdr_length = dbg_line->size(); - Push32(dbg_line, 0); // TODO: 64-bit uses 8-byte here - - PushByte(dbg_line, 1); // minimum_instruction_length (ubyte) - PushByte(dbg_line, 1); // maximum_operations_per_instruction (ubyte) = always 1 - PushByte(dbg_line, 1); // default_is_stmt (ubyte) - - const int8_t LINE_BASE = -5; - PushByte(dbg_line, LINE_BASE); // line_base (sbyte) - - const uint8_t LINE_RANGE = 14; - PushByte(dbg_line, LINE_RANGE); // line_range (ubyte) - - const uint8_t OPCODE_BASE = 13; - PushByte(dbg_line, OPCODE_BASE); // opcode_base (ubyte) - - // Standard_opcode_lengths (array of ubyte). - PushByte(dbg_line, 0); PushByte(dbg_line, 1); PushByte(dbg_line, 1); - PushByte(dbg_line, 1); PushByte(dbg_line, 1); PushByte(dbg_line, 0); - PushByte(dbg_line, 0); PushByte(dbg_line, 0); PushByte(dbg_line, 1); - PushByte(dbg_line, 0); PushByte(dbg_line, 0); PushByte(dbg_line, 1); - - PushByte(dbg_line, 0); // include_directories (sequence of path names) = EMPTY - - // File_names (sequence of file entries). - std::unordered_map files; - for (size_t i = 0; i < method_info.size(); ++i) { - const OatWriter::DebugInfo &dbg = method_info[i]; - // TODO: add package directory to the file name - const char* file_name = dbg.src_file_name_ == nullptr ? "null" : dbg.src_file_name_; - auto found = files.find(file_name); - if (found == files.end()) { - size_t file_index = 1 + files.size(); - files[file_name] = file_index; - PushStr(dbg_line, file_name); - PushByte(dbg_line, 0); // include directory index = LEB128(0) - no directory - PushByte(dbg_line, 0); // modification time = LEB128(0) - NA - PushByte(dbg_line, 0); // file length = LEB128(0) - NA - } + dwarf::DebugLineOpCodeWriter<> opcodes(use_64bit_addresses, code_factor_bits_); + opcodes.SetAddress(text_section_offset + cunit_low_pc); + if (isa != -1) { + opcodes.SetISA(isa); } - PushByte(dbg_line, 0); // End of file_names. - - // Set lnt header length. - UpdateWord(dbg_line, lnt_hdr_length, dbg_line->size() - lnt_hdr_length - 4); - - // Generate Line Number Program code, one long program for all methods. - LineTableGenerator line_table_generator(LINE_BASE, LINE_RANGE, OPCODE_BASE, - dbg_line, 0, 1); - - DefaultSrcMap pc2java_map; - for (size_t i = 0; i < method_info.size(); ++i) { - const OatWriter::DebugInfo &dbg = method_info[i]; - const char* file_name = (dbg.src_file_name_ == nullptr) ? "null" : dbg.src_file_name_; - size_t file_index = files[file_name]; - DCHECK_NE(file_index, 0U) << file_name; - - cunit_low_pc = std::min(cunit_low_pc, dbg.low_pc_); - cunit_high_pc = std::max(cunit_high_pc, dbg.high_pc_); - - // Start a new TAG: subroutine (2). - PushByte(dbg_info, 2); - - // Enter name, low_pc, high_pc. - Push32(dbg_info, PushStr(dbg_str, dbg.method_name_)); - Push32(dbg_info, dbg.low_pc_ + text_section_offset); - Push32(dbg_info, dbg.high_pc_ + text_section_offset); - - GetLineInfoForJava(dbg.dbgstream_, dbg.compiled_method_->GetSrcMappingTable(), - &pc2java_map, dbg.low_pc_); - pc2java_map.DeltaFormat({dbg.low_pc_, 1}, dbg.high_pc_); - if (!pc2java_map.empty()) { - line_table_generator.SetFile(file_index); - line_table_generator.SetAddr(dbg.low_pc_ + text_section_offset); - line_table_generator.SetLine(1); - for (auto& src_map_elem : pc2java_map) { - line_table_generator.PutDelta(src_map_elem.from_, src_map_elem.to_); + DefaultSrcMap dex2line_map; + for (size_t i = 0; i < method_infos.size(); i++) { + const OatWriter::DebugInfo& method_info = method_infos[i]; + + // Addresses in the line table should be unique and increasing. + if (method_info.deduped_) { + continue; + } + + // Get and deduplicate directory and filename. + int file_index = 0; // 0 - primary source file of the compilation. + if (method_info.src_file_name_ != nullptr) { + std::string file_name(method_info.src_file_name_); + size_t file_name_slash = file_name.find_last_of('/'); + std::string class_name(method_info.class_descriptor_); + size_t class_name_slash = class_name.find_last_of('/'); + std::string full_path(file_name); + + // Guess directory from package name. + int directory_index = 0; // 0 - current directory of the compilation. + if (file_name_slash == std::string::npos && // Just filename. + class_name.front() == 'L' && // Type descriptor for a class. + class_name_slash != std::string::npos) { // Has package name. + std::string package_name = class_name.substr(1, class_name_slash - 1); + auto it = directories_map.find(package_name); + if (it == directories_map.end()) { + directory_index = 1 + directories.size(); + directories_map.emplace(package_name, directory_index); + directories.push_back(package_name); + } else { + directory_index = it->second; + } + full_path = package_name + "/" + file_name; + } + + // Add file entry. + auto it2 = files_map.find(full_path); + if (it2 == files_map.end()) { + file_index = 1 + files.size(); + files_map.emplace(full_path, file_index); + files.push_back(dwarf::DebugLineWriter<>::FileEntry { + file_name, + directory_index, + 0, // Modification time - NA. + 0, // File size - NA. + }); + } else { + file_index = it2->second; } - pc2java_map.clear(); + } + opcodes.SetFile(file_index); + + // Generate mapping opcodes from PC to Java lines. + dex2line_map.clear(); + GetLineInfoForJava(method_info.dbgstream_, &dex2line_map); + uint32_t low_pc = text_section_offset + method_info.low_pc_; + if (file_index != 0 && !dex2line_map.empty()) { + bool first = true; + for (SrcMapElem pc2dex : method_info.compiled_method_->GetSrcMappingTable()) { + uint32_t pc = pc2dex.from_; + int dex = pc2dex.to_; + auto dex2line = dex2line_map.Find(static_cast(dex)); + if (dex2line.first) { + int line = dex2line.second; + if (first) { + first = false; + if (pc > 0) { + // Assume that any preceding code is prologue. + int first_line = dex2line_map.front().to_; + // Prologue is not a sensible place for a breakpoint. + opcodes.NegateStmt(); + opcodes.AddRow(low_pc, first_line); + opcodes.NegateStmt(); + opcodes.SetPrologueEnd(); + } + opcodes.AddRow(low_pc + pc, line); + } else if (line != opcodes.CurrentLine()) { + opcodes.AddRow(low_pc + pc, line); + } + } + } + } else { + // line 0 - instruction cannot be attributed to any source line. + opcodes.AddRow(low_pc, 0); } } - // End Sequence should have the highest address set. - line_table_generator.SetAddr(cunit_high_pc + text_section_offset); - line_table_generator.EndSequence(); + opcodes.AdvancePC(text_section_offset + cunit_high_pc); + opcodes.EndSequence(); - // set lnt length - UpdateWord(dbg_line, lnt_length, dbg_line->size() - lnt_length - 4); + dwarf::DebugLineWriter<> dbg_line_writer(dbg_line); + dbg_line_writer.WriteTable(directories, files, opcodes); } - // One byte terminator + // One byte terminator. PushByte(dbg_info, 0); - // Fill in cunit's low_pc and high_pc. - UpdateWord(dbg_info, cunit_low_pc_pos, cunit_low_pc + text_section_offset); - UpdateWord(dbg_info, cunit_low_pc_pos + 4, cunit_high_pc + text_section_offset); - // We have now walked all the methods. Fill in lengths. UpdateWord(dbg_info, cunit_length, dbg_info->size() - cunit_length - 4); } @@ -690,8 +634,11 @@ static void WriteDebugSymbols(const CompilerDriver* compiler_driver, ElfSymtabBuilder* symtab = builder->GetSymtabBuilder(); for (auto it = method_info.begin(); it != method_info.end(); ++it) { - symtab->AddSymbol(it->method_name_, &builder->GetTextBuilder(), it->low_pc_, true, - it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC); + uint32_t low_pc = it->low_pc_; + // Add in code delta, e.g., thumb bit 0 for Thumb2 code. + low_pc += it->compiled_method_->CodeDelta(); + symtab->AddSymbol(it->method_name_, &builder->GetTextBuilder(), low_pc, + true, it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC); // Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2 // instructions, so that disassembler tools can correctly disassemble. diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 04f0db657..880ac1532 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -1069,10 +1069,12 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { } const uint32_t quick_code_start = quick_code_offset - - writer_->oat_header_->GetExecutableOffset(); + writer_->oat_header_->GetExecutableOffset() - thumb_offset; const DexFile::CodeItem *code_item = it.GetMethodCodeItem(); - writer_->method_info_.push_back(DebugInfo(name, - dex_file_->GetSourceFile(dex_file_->GetClassDef(class_def_index_)), + const DexFile::ClassDef& class_def = dex_file_->GetClassDef(class_def_index_); + writer_->method_info_.push_back(DebugInfo(name, deduped, + dex_file_->GetClassDescriptor(class_def), + dex_file_->GetSourceFile(class_def), quick_code_start, quick_code_start + code_size, code_item == nullptr ? nullptr : dex_file_->GetDebugInfoStream(code_item), compiled_method)); diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index 676d62845..4538694c0 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -114,14 +114,18 @@ class OatWriter { ~OatWriter(); struct DebugInfo { - DebugInfo(const std::string& method_name, const char* src_file_name, - uint32_t low_pc, uint32_t high_pc, const uint8_t* dbgstream, - CompiledMethod* compiled_method) - : method_name_(method_name), src_file_name_(src_file_name), - low_pc_(low_pc), high_pc_(high_pc), dbgstream_(dbgstream), - compiled_method_(compiled_method) { + DebugInfo(const std::string& method_name, bool deduped, + const char* class_descriptor, const char* src_file_name, + uint32_t low_pc, uint32_t high_pc, + const uint8_t* dbgstream, CompiledMethod* compiled_method) + : method_name_(method_name), deduped_(deduped), + class_descriptor_(class_descriptor), src_file_name_(src_file_name), + low_pc_(low_pc), high_pc_(high_pc), + dbgstream_(dbgstream), compiled_method_(compiled_method) { } std::string method_name_; // Note: this name is a pretty-printed name. + bool deduped_; + const char* class_descriptor_; const char* src_file_name_; uint32_t low_pc_; uint32_t high_pc_; diff --git a/runtime/dwarf.h b/runtime/dwarf.h index 7daa5f148..b491f4767 100644 --- a/runtime/dwarf.h +++ b/runtime/dwarf.h @@ -18,6 +18,7 @@ #define ART_RUNTIME_DWARF_H_ namespace art { +namespace dwarf { // Based on the Dwarf 4 specification at dwarfstd.com and issues marked // for inclusion in Dwarf 5 on same. Values not specified in the Dwarf 4 @@ -657,6 +658,7 @@ enum CallFrameInstruction : uint8_t { DW_CFA_hi_user = 0x3f }; +} // namespace dwarf } // namespace art #endif // ART_RUNTIME_DWARF_H_ diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index 3490bcf86..bc5cf9b1f 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -1665,7 +1665,6 @@ struct PACKED(1) DebugLineHeader { uint16_t version_; uint32_t header_length_; // TODO 32-bit specific size uint8_t minimum_instruction_lenght_; - uint8_t maximum_operations_per_instruction_; uint8_t default_is_stmt_; int8_t line_base_; uint8_t line_range_; @@ -1691,7 +1690,7 @@ struct PACKED(1) DebugLineHeader { return length_field + length; } else if (!IsStandardOpcode(op)) { return op + 1; - } else if (*op == DW_LNS_fixed_advance_pc) { + } else if (*op == dwarf::DW_LNS_fixed_advance_pc) { return op + 1 + sizeof(uint16_t); } else { uint8_t num_args = GetStandardOpcodeLengths()[*op - 1]; @@ -1774,8 +1773,8 @@ class DebugLineInstructionIterator FINAL { }; static bool FixupDebugLine(off_t base_offset_delta, DebugLineInstructionIterator* iter) { - while (iter->Next()) { - if (iter->IsExtendedOpcode() && iter->GetOpcode() == DW_LNE_set_address) { + for (; iter->GetInstruction(); iter->Next()) { + if (iter->IsExtendedOpcode() && iter->GetOpcode() == dwarf::DW_LNE_set_address) { *reinterpret_cast(iter->GetArguments()) += base_offset_delta; } } @@ -1792,39 +1791,39 @@ struct PACKED(1) DebugInfoHeader { // Returns -1 if it is variable length, which we will just disallow for now. static int32_t FormLength(uint32_t att) { switch (att) { - case DW_FORM_data1: - case DW_FORM_flag: - case DW_FORM_flag_present: - case DW_FORM_ref1: + case dwarf::DW_FORM_data1: + case dwarf::DW_FORM_flag: + case dwarf::DW_FORM_flag_present: + case dwarf::DW_FORM_ref1: return 1; - case DW_FORM_data2: - case DW_FORM_ref2: + case dwarf::DW_FORM_data2: + case dwarf::DW_FORM_ref2: return 2; - case DW_FORM_addr: // TODO 32-bit only - case DW_FORM_ref_addr: // TODO 32-bit only - case DW_FORM_sec_offset: // TODO 32-bit only - case DW_FORM_strp: // TODO 32-bit only - case DW_FORM_data4: - case DW_FORM_ref4: + case dwarf::DW_FORM_addr: // TODO 32-bit only + case dwarf::DW_FORM_ref_addr: // TODO 32-bit only + case dwarf::DW_FORM_sec_offset: // TODO 32-bit only + case dwarf::DW_FORM_strp: // TODO 32-bit only + case dwarf::DW_FORM_data4: + case dwarf::DW_FORM_ref4: return 4; - case DW_FORM_data8: - case DW_FORM_ref8: - case DW_FORM_ref_sig8: + case dwarf::DW_FORM_data8: + case dwarf::DW_FORM_ref8: + case dwarf::DW_FORM_ref_sig8: return 8; - case DW_FORM_block: - case DW_FORM_block1: - case DW_FORM_block2: - case DW_FORM_block4: - case DW_FORM_exprloc: - case DW_FORM_indirect: - case DW_FORM_ref_udata: - case DW_FORM_sdata: - case DW_FORM_string: - case DW_FORM_udata: + case dwarf::DW_FORM_block: + case dwarf::DW_FORM_block1: + case dwarf::DW_FORM_block2: + case dwarf::DW_FORM_block4: + case dwarf::DW_FORM_exprloc: + case dwarf::DW_FORM_indirect: + case dwarf::DW_FORM_ref_udata: + case dwarf::DW_FORM_sdata: + case dwarf::DW_FORM_string: + case dwarf::DW_FORM_udata: default: return -1; } @@ -2047,13 +2046,13 @@ class DebugInfoIterator { static bool FixupDebugInfo(off_t base_address_delta, DebugInfoIterator* iter) { do { - if (iter->GetCurrentTag()->GetAttrSize(DW_AT_low_pc) != sizeof(int32_t) || - iter->GetCurrentTag()->GetAttrSize(DW_AT_high_pc) != sizeof(int32_t)) { + if (iter->GetCurrentTag()->GetAttrSize(dwarf::DW_AT_low_pc) != sizeof(int32_t) || + iter->GetCurrentTag()->GetAttrSize(dwarf::DW_AT_high_pc) != sizeof(int32_t)) { LOG(ERROR) << "DWARF information with 64 bit pointers is not supported yet."; return false; } - uint32_t* PC_low = reinterpret_cast(iter->GetPointerToField(DW_AT_low_pc)); - uint32_t* PC_high = reinterpret_cast(iter->GetPointerToField(DW_AT_high_pc)); + uint32_t* PC_low = reinterpret_cast(iter->GetPointerToField(dwarf::DW_AT_low_pc)); + uint32_t* PC_high = reinterpret_cast(iter->GetPointerToField(dwarf::DW_AT_high_pc)); if (PC_low != nullptr && PC_high != nullptr) { *PC_low += base_address_delta; *PC_high += base_address_delta;