Stream DWARF debug section directly to disk.
Change the structure so that the debug sections are written to
disk as we go. There are still some temporary buffers, however,
we no longer hold all of the data in memory before writing it.
We can not avoid buffering of some things (e.g. .debug_str).
Change-Id: Id4940cf10ae5b6f3cac7fb8d20197f0304079b92
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index 7326233..e1ab340 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -250,84 +250,98 @@
}
template<typename ElfTypes>
-void WriteDebugSections(ElfBuilder<ElfTypes>* builder,
- const std::vector<OatWriter::DebugInfo>& method_infos) {
+class DebugInfoWriter {
typedef typename ElfTypes::Addr Elf_Addr;
- const bool is64bit = Is64BitInstructionSet(builder->GetIsa());
- Elf_Addr text_address = builder->GetText()->GetAddress();
- // Find all addresses (low_pc) which contain deduped methods.
- // The first instance of method is not marked deduped_, but the rest is.
- std::unordered_set<uint32_t> deduped_addresses;
- for (const OatWriter::DebugInfo& mi : method_infos) {
- if (mi.deduped_) {
- deduped_addresses.insert(mi.low_pc_);
- }
+ public:
+ explicit DebugInfoWriter(ElfBuilder<ElfTypes>* builder) : builder_(builder) {
}
- // Group the methods into compilation units based on source file.
- std::vector<std::vector<const OatWriter::DebugInfo*>> compilation_units;
- const char* last_source_file = nullptr;
- for (const OatWriter::DebugInfo& mi : method_infos) {
- // Attribute given instruction range only to single method.
- // Otherwise the debugger might get really confused.
- if (!mi.deduped_) {
- auto& dex_class_def = mi.dex_file_->GetClassDef(mi.class_def_index_);
- const char* source_file = mi.dex_file_->GetSourceFile(dex_class_def);
- if (compilation_units.empty() || source_file != last_source_file) {
- compilation_units.push_back(std::vector<const OatWriter::DebugInfo*>());
- }
- compilation_units.back().push_back(&mi);
- last_source_file = source_file;
- }
+ void Start() {
+ builder_->GetDebugInfo()->Start();
}
- // Write .debug_info section.
- std::vector<uint8_t> debug_info;
- std::vector<uintptr_t> debug_info_patches;
- std::vector<uint8_t> debug_abbrev;
- std::vector<uint8_t> debug_str;
- std::vector<uint8_t> debug_line;
- std::vector<uintptr_t> debug_line_patches;
- for (const auto& compilation_unit : compilation_units) {
+ void Write(const std::vector<const OatWriter::DebugInfo*>& method_infos,
+ size_t debug_line_offset) {
+ const bool is64bit = Is64BitInstructionSet(builder_->GetIsa());
+ const Elf_Addr text_address = builder_->GetText()->GetAddress();
uint32_t cunit_low_pc = 0xFFFFFFFFU;
uint32_t cunit_high_pc = 0;
- for (auto method_info : compilation_unit) {
+ 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_);
}
- size_t debug_abbrev_offset = debug_abbrev.size();
- DebugInfoEntryWriter<> info(is64bit, &debug_abbrev);
+ size_t debug_abbrev_offset = debug_abbrev_.size();
+ DebugInfoEntryWriter<> info(is64bit, &debug_abbrev_);
info.StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes);
- info.WriteStrp(DW_AT_producer, "Android dex2oat", &debug_str);
+ info.WriteStrp(DW_AT_producer, "Android dex2oat", &debug_str_);
info.WriteData1(DW_AT_language, DW_LANG_Java);
info.WriteAddr(DW_AT_low_pc, text_address + cunit_low_pc);
info.WriteAddr(DW_AT_high_pc, text_address + cunit_high_pc);
- info.WriteData4(DW_AT_stmt_list, debug_line.size());
- for (auto method_info : compilation_unit) {
+ info.WriteData4(DW_AT_stmt_list, debug_line_offset);
+ for (auto method_info : method_infos) {
std::string method_name = PrettyMethod(method_info->dex_method_index_,
*method_info->dex_file_, true);
- if (deduped_addresses.find(method_info->low_pc_) != deduped_addresses.end()) {
- method_name += " [DEDUPED]";
- }
info.StartTag(DW_TAG_subprogram, DW_CHILDREN_no);
- info.WriteStrp(DW_AT_name, method_name.data(), &debug_str);
+ info.WriteStrp(DW_AT_name, method_name.data(), &debug_str_);
info.WriteAddr(DW_AT_low_pc, text_address + method_info->low_pc_);
info.WriteAddr(DW_AT_high_pc, text_address + method_info->high_pc_);
info.EndTag(); // DW_TAG_subprogram
}
info.EndTag(); // DW_TAG_compile_unit
- WriteDebugInfoCU(debug_abbrev_offset, info, &debug_info, &debug_info_patches);
+ std::vector<uint8_t> buffer;
+ buffer.reserve(info.data()->size() + KB);
+ size_t offset = builder_->GetDebugInfo()->GetSize();
+ WriteDebugInfoCU(debug_abbrev_offset, info, offset, &buffer, &debug_info_patches_);
+ builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size());
+ }
- // Write .debug_line section.
+ void End() {
+ builder_->GetDebugInfo()->End();
+ builder_->WritePatches(".debug_info.oat_patches", &debug_info_patches_);
+ builder_->WriteSection(".debug_abbrev", &debug_abbrev_);
+ builder_->WriteSection(".debug_str", &debug_str_);
+ }
+
+ private:
+ ElfBuilder<ElfTypes>* builder_;
+ std::vector<uintptr_t> debug_info_patches_;
+ std::vector<uint8_t> debug_abbrev_;
+ std::vector<uint8_t> debug_str_;
+};
+
+template<typename ElfTypes>
+class DebugLineWriter {
+ typedef typename ElfTypes::Addr Elf_Addr;
+
+ public:
+ explicit DebugLineWriter(ElfBuilder<ElfTypes>* builder) : builder_(builder) {
+ }
+
+ void Start() {
+ builder_->GetDebugLine()->Start();
+ }
+
+ // Write line table for given set of methods.
+ // Returns the number of bytes written.
+ size_t Write(const std::vector<const OatWriter::DebugInfo*>& method_infos) {
+ const bool is64bit = Is64BitInstructionSet(builder_->GetIsa());
+ const Elf_Addr text_address = builder_->GetText()->GetAddress();
+ uint32_t cunit_low_pc = 0xFFFFFFFFU;
+ uint32_t cunit_high_pc = 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_);
+ }
+
std::vector<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 (builder->GetIsa()) {
+ switch (builder_->GetIsa()) {
case kArm: // arm actually means thumb2.
case kThumb2:
code_factor_bits_ = 1; // 16-bit instuctions
@@ -348,7 +362,7 @@
if (dwarf_isa != -1) {
opcodes.SetISA(dwarf_isa);
}
- for (const OatWriter::DebugInfo* mi : compilation_unit) {
+ for (const OatWriter::DebugInfo* mi : method_infos) {
struct DebugInfoCallbacks {
static bool NewPosition(void* ctx, uint32_t address, uint32_t line) {
auto* context = reinterpret_cast<DebugInfoCallbacks*>(ctx);
@@ -449,14 +463,70 @@
}
opcodes.AdvancePC(text_address + cunit_high_pc);
opcodes.EndSequence();
- WriteDebugLineTable(directories, files, opcodes, &debug_line, &debug_line_patches);
+ std::vector<uint8_t> buffer;
+ buffer.reserve(opcodes.data()->size() + KB);
+ size_t offset = builder_->GetDebugLine()->GetSize();
+ WriteDebugLineTable(directories, files, opcodes, offset, &buffer, &debug_line_patches);
+ builder_->GetDebugLine()->WriteFully(buffer.data(), buffer.size());
+ return buffer.size();
}
- builder->WriteSection(".debug_line", &debug_line);
- builder->WritePatches(".debug_line.oat_patches", &debug_line_patches);
- builder->WriteSection(".debug_info", &debug_info);
- builder->WritePatches(".debug_info.oat_patches", &debug_info_patches);
- builder->WriteSection(".debug_abbrev", &debug_abbrev);
- builder->WriteSection(".debug_str", &debug_str);
+
+ void End() {
+ builder_->GetDebugLine()->End();
+ builder_->WritePatches(".debug_line.oat_patches", &debug_line_patches);
+ }
+
+ private:
+ ElfBuilder<ElfTypes>* builder_;
+ std::vector<uintptr_t> debug_line_patches;
+};
+
+template<typename ElfTypes>
+void WriteDebugSections(ElfBuilder<ElfTypes>* builder,
+ const std::vector<OatWriter::DebugInfo>& method_infos) {
+ struct CompilationUnit {
+ std::vector<const OatWriter::DebugInfo*> methods_;
+ size_t debug_line_offset_ = 0;
+ };
+
+ // Group the methods into compilation units based on source file.
+ std::vector<CompilationUnit> compilation_units;
+ const char* last_source_file = nullptr;
+ for (const OatWriter::DebugInfo& mi : method_infos) {
+ // Attribute given instruction range only to single method.
+ // Otherwise the debugger might get really confused.
+ if (!mi.deduped_) {
+ auto& dex_class_def = mi.dex_file_->GetClassDef(mi.class_def_index_);
+ const char* source_file = mi.dex_file_->GetSourceFile(dex_class_def);
+ if (compilation_units.empty() || source_file != last_source_file) {
+ compilation_units.push_back(CompilationUnit());
+ }
+ compilation_units.back().methods_.push_back(&mi);
+ last_source_file = source_file;
+ }
+ }
+
+ // Write .debug_line section.
+ {
+ DebugLineWriter<ElfTypes> line_writer(builder);
+ line_writer.Start();
+ size_t offset = 0;
+ for (auto& compilation_unit : compilation_units) {
+ compilation_unit.debug_line_offset_ = offset;
+ offset += line_writer.Write(compilation_unit.methods_);
+ }
+ line_writer.End();
+ }
+
+ // Write .debug_info section.
+ {
+ DebugInfoWriter<ElfTypes> info_writer(builder);
+ info_writer.Start();
+ for (const auto& compilation_unit : compilation_units) {
+ info_writer.Write(compilation_unit.methods_, compilation_unit.debug_line_offset_);
+ }
+ info_writer.End();
+ }
}
// Explicit instantiations