// Write line table for given set of methods. // Returns the number of bytes written.
size_t WriteCompilationUnit(ElfCompilationUnit& compilation_unit) { const InstructionSet isa = builder_->GetIsa(); constbool is64bit = Is64BitInstructionSet(isa); const Elf_Addr base_address = compilation_unit.is_code_address_text_relative
? builder_->GetText()->GetAddress()
: 0;
std::vector<dwarf::FileEntry> files;
std::unordered_map<std::string, size_t> files_map;
std::vector<std::string> directories;
std::unordered_map<std::string, size_t> directories_map; int code_factor_bits_ = 0; int dwarf_isa = -1; switch (isa) { case InstructionSet::kArm: // arm actually means thumb2. case InstructionSet::kThumb2:
code_factor_bits_ = 1; // 16-bit instuctions
dwarf_isa = 1; // DW_ISA_ARM_thumb. break; case InstructionSet::kArm64:
code_factor_bits_ = 2; // 32-bit instructions break; case InstructionSet::kNone: case InstructionSet::kRiscv64: case InstructionSet::kX86: case InstructionSet::kX86_64: break;
}
std::unordered_set<uint64_t> seen_addresses(compilation_unit.methods.size());
dwarf::DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_); for (const MethodDebugInfo* mi : compilation_unit.methods) { // Ignore function if we have already generated line table for the same address. // It would confuse the debugger and the DWARF specification forbids it. // We allow the line table for method to be replicated in different compilation unit. // This ensures that each compilation unit contains line table for all its methods. if (!seen_addresses.insert(mi->code_address).second) { continue;
}
uint32_t prologue_end = std::numeric_limits<uint32_t>::max();
std::vector<SrcMapElem> pc2dex_map; if (mi->code_info != nullptr) { // Use stack maps to create mapping table from pc to dex. const CodeInfo code_info(mi->code_info);
pc2dex_map.reserve(code_info.GetNumberOfStackMaps()); for (StackMap stack_map : code_info.GetStackMaps()) { const uint32_t pc = stack_map.GetNativePcOffset(isa); const int32_t dex = stack_map.GetDexPc();
pc2dex_map.push_back({pc, dex}); if (stack_map.HasDexRegisterMap()) { // Guess that the first map with local variables is the end of prologue.
prologue_end = std::min(prologue_end, pc);
}
}
std::sort(pc2dex_map.begin(), pc2dex_map.end());
}
if (pc2dex_map.empty()) { continue;
}
// Compensate for compiler's off-by-one-instruction error. // // The compiler generates stackmap with PC *after* the branch instruction // (because this is the PC which is easier to obtain when unwinding). // // However, the debugger is more clever and it will ask us for line-number // mapping at the location of the branch instruction (since the following // instruction could belong to other line, this is the correct thing to do). // // So we really want to just decrement the PC by one instruction so that the // branch instruction is covered as well. However, we do not know the size // of the previous instruction, and we can not subtract just a fixed amount // (the debugger would trust us that the PC is valid; it might try to set // breakpoint there at some point, and setting breakpoint in mid-instruction // would make the process crash in spectacular way). // // Therefore, we say that the PC which the compiler gave us for the stackmap // is the end of its associated address range, and we use the PC from the // previous stack map as the start of the range. This ensures that the PC is // valid and that the branch instruction is covered. // // This ensures we have correct line number mapping at call sites (which is // important for backtraces), but there is nothing we can do for non-call // sites (so stepping through optimized code in debugger is not possible). // // We do not adjust the stackmaps if the code was compiled as debuggable. // In that case, the stackmaps should accurately cover all instructions. if (!mi->is_native_debuggable) { for (size_t i = pc2dex_map.size() - 1; i > 0; --i) {
pc2dex_map[i].from_ = pc2dex_map[i - 1].from_;
}
pc2dex_map[0].from_ = 0;
}
// Generate mapping opcodes from PC to Java lines. if (file_index != 0) { // If the method was not compiled as native-debuggable, we still generate all available // lines, but we try to prevent the debugger from stepping and setting breakpoints since // the information is too inaccurate for that (breakpoints would be set after the calls). constbool default_is_stmt = mi->is_native_debuggable; bool first = true; for (SrcMapElem pc2dex : pc2dex_map) {
uint32_t pc = pc2dex.from_; int dex_pc = pc2dex.to_; // Find mapping with address with is greater than our dex pc; then go back one step. auto dex2line = std::upper_bound(
dex2line_map.begin(),
dex2line_map.end(),
dex_pc,
[](uint32_t address, const DexFile::PositionInfo& entry) { return address < entry.address_;
}); // Look for first valid mapping after the prologue. if (dex2line != dex2line_map.begin() && pc >= prologue_end) { int line = (--dex2line)->line_; if (first) {
first = false; if (pc > 0) { // Assume that any preceding code is prologue. int first_line = dex2line_map.front().line_; // Prologue is not a sensible place for a breakpoint.
opcodes.SetIsStmt(false);
opcodes.AddRow(method_address, first_line);
opcodes.SetPrologueEnd();
}
opcodes.SetIsStmt(default_is_stmt);
opcodes.AddRow(method_address + pc, line);
} elseif (line != opcodes.CurrentLine()) {
opcodes.SetIsStmt(default_is_stmt);
opcodes.AddRow(method_address + pc, line);
}
}
}
} else { // line 0 - instruction cannot be attributed to any source line.
opcodes.AddRow(method_address, 0);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.