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/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc
index a412a99..6bb22ed 100644
--- a/compiler/dwarf/dwarf_test.cc
+++ b/compiler/dwarf/dwarf_test.cc
@@ -237,7 +237,7 @@
std::vector<uintptr_t> debug_line_patches;
std::vector<uintptr_t> expected_patches { 87 }; // NOLINT
WriteDebugLineTable(include_directories, files, opcodes,
- &debug_line_data_, &debug_line_patches);
+ 0, &debug_line_data_, &debug_line_patches);
EXPECT_EQ(expected_patches, debug_line_patches);
CheckObjdumpOutput(is64bit, "-W");
@@ -276,7 +276,7 @@
std::vector<FileEntry> files { { "file.c", 0, 1000, 2000 } }; // NOLINT
std::vector<uintptr_t> debug_line_patches;
WriteDebugLineTable(directories, files, opcodes,
- &debug_line_data_, &debug_line_patches);
+ 0, &debug_line_data_, &debug_line_patches);
CheckObjdumpOutput(is64bit, "-W -WL");
}
@@ -332,7 +332,7 @@
std::vector<uintptr_t> debug_info_patches;
std::vector<uintptr_t> expected_patches { 16, 20, 29, 33, 42, 46 }; // NOLINT
dwarf::WriteDebugInfoCU(0 /* debug_abbrev_offset */, info,
- &debug_info_data_, &debug_info_patches);
+ 0, &debug_info_data_, &debug_info_patches);
EXPECT_EQ(expected_patches, debug_info_patches);
CheckObjdumpOutput(is64bit, "-W");
diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h
index 883d756..633e2f7 100644
--- a/compiler/dwarf/headers.h
+++ b/compiler/dwarf/headers.h
@@ -126,6 +126,7 @@
template<typename Vector>
void WriteDebugInfoCU(uint32_t debug_abbrev_offset,
const DebugInfoEntryWriter<Vector>& entries,
+ size_t debug_info_offset, // offset from start of .debug_info.
std::vector<uint8_t>* debug_info,
std::vector<uintptr_t>* debug_info_patches) {
static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
@@ -141,7 +142,7 @@
writer.UpdateUint32(start, writer.data()->size() - start - 4);
// Copy patch locations and make them relative to .debug_info section.
for (uintptr_t patch_location : entries.GetPatchLocations()) {
- debug_info_patches->push_back(entries_offset + patch_location);
+ debug_info_patches->push_back(debug_info_offset + entries_offset + patch_location);
}
}
@@ -157,6 +158,7 @@
void WriteDebugLineTable(const std::vector<std::string>& include_directories,
const std::vector<FileEntry>& files,
const DebugLineOpCodeWriter<Vector>& opcodes,
+ size_t debug_line_offset, // offset from start of .debug_line.
std::vector<uint8_t>* debug_line,
std::vector<uintptr_t>* debug_line_patches) {
static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type");
@@ -197,7 +199,7 @@
writer.UpdateUint32(header_start, writer.data()->size() - header_start - 4);
// Copy patch locations and make them relative to .debug_line section.
for (uintptr_t patch_location : opcodes.GetPatchLocations()) {
- debug_line_patches->push_back(opcodes_offset + patch_location);
+ debug_line_patches->push_back(debug_line_offset + opcodes_offset + patch_location);
}
}
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 895dfcc..6e8dfd6 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -156,8 +156,13 @@
// Returns the size of the content of this section.
Elf_Word GetSize() const {
- CHECK(finished_);
- return header_.sh_size;
+ if (finished_) {
+ return header_.sh_size;
+ } else {
+ CHECK(started_);
+ CHECK_NE(header_.sh_type, (Elf_Word)SHT_NOBITS);
+ return owner_->Seek(0, kSeekCurrent) - header_.sh_offset;
+ }
}
// Set desired allocation size for .bss section.
@@ -281,6 +286,8 @@
strtab_(this, ".strtab", 0, kPageSize),
symtab_(this, ".symtab", SHT_SYMTAB, 0, &strtab_),
debug_frame_(this, ".debug_frame", SHT_PROGBITS, 0, nullptr, 0, sizeof(Elf_Addr), 0),
+ debug_info_(this, ".debug_info", SHT_PROGBITS, 0, nullptr, 0, 1, 0),
+ debug_line_(this, ".debug_line", SHT_PROGBITS, 0, nullptr, 0, 1, 0),
shstrtab_(this, ".shstrtab", 0, 1),
virtual_address_(0) {
text_.phdr_flags_ = PF_R | PF_X;
@@ -300,6 +307,8 @@
Section* GetEhFrame() { return &eh_frame_; }
Section* GetEhFrameHdr() { return &eh_frame_hdr_; }
Section* GetDebugFrame() { return &debug_frame_; }
+ Section* GetDebugInfo() { return &debug_info_; }
+ Section* GetDebugLine() { return &debug_line_; }
// Encode patch locations as LEB128 list of deltas between consecutive addresses.
// (exposed publicly for tests)
@@ -667,6 +676,8 @@
StringSection strtab_;
SymbolSection symtab_;
Section debug_frame_;
+ Section debug_info_;
+ Section debug_line_;
StringSection shstrtab_;
std::vector<std::unique_ptr<Section>> other_sections_;
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