Reduce the size of native debug info generated by JIT.
Remove some ELF file overheads:
- Do not produce program headers.
- Do not page align strtab.
- Do not write oat_patches sections.
This more than halves the size of JIT native debug info.
Since we generate many small entries, the overheads added up.
Change-Id: I27d95548c61e2e38c3683d6f5eb870a2db6e812d
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 3d24d19..bc7c83e 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -110,18 +110,27 @@
CHECK(sections.empty() || sections.back()->finished_);
// The first ELF section index is 1. Index 0 is reserved for NULL.
section_index_ = sections.size() + 1;
- // Push this section on the list of written sections.
- sections.push_back(this);
+ // Page-align if we switch between allocated and non-allocated sections,
+ // or if we change the type of allocation (e.g. executable vs non-executable).
+ if (!sections.empty()) {
+ if (header_.sh_flags != sections.back()->header_.sh_flags) {
+ header_.sh_addralign = kPageSize;
+ }
+ }
// Align file position.
if (header_.sh_type != SHT_NOBITS) {
- header_.sh_offset = RoundUp(owner_->stream_.Seek(0, kSeekCurrent), header_.sh_addralign);
- owner_->stream_.Seek(header_.sh_offset, kSeekSet);
+ header_.sh_offset = owner_->AlignFileOffset(header_.sh_addralign);
+ } else {
+ header_.sh_offset = 0;
}
// Align virtual memory address.
if ((header_.sh_flags & SHF_ALLOC) != 0) {
- header_.sh_addr = RoundUp(owner_->virtual_address_, header_.sh_addralign);
- owner_->virtual_address_ = header_.sh_addr;
+ header_.sh_addr = owner_->AlignVirtualAddress(header_.sh_addralign);
+ } else {
+ header_.sh_addr = 0;
}
+ // Push this section on the list of written sections.
+ sections.push_back(this);
}
// Finish writing of this section.
@@ -170,8 +179,8 @@
// and it will be zero-initialized when the ELF file is loaded in the running program.
void WriteNoBitsSection(Elf_Word size) {
DCHECK_NE(header_.sh_flags & SHF_ALLOC, 0u);
- Start();
header_.sh_type = SHT_NOBITS;
+ Start();
header_.sh_size = size;
End();
}
@@ -293,12 +302,13 @@
dynamic_(this, ".dynamic", SHT_DYNAMIC, SHF_ALLOC, &dynstr_, 0, kPageSize, sizeof(Elf_Dyn)),
eh_frame_(this, ".eh_frame", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
eh_frame_hdr_(this, ".eh_frame_hdr", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, 4, 0),
- strtab_(this, ".strtab", 0, kPageSize),
+ strtab_(this, ".strtab", 0, 1),
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),
+ started_(false),
virtual_address_(0) {
text_.phdr_flags_ = PF_R | PF_X;
bss_.phdr_flags_ = PF_R | PF_W;
@@ -357,16 +367,25 @@
virtual_address_ = address;
}
- void Start() {
- // Reserve space for ELF header and program headers.
- // We do not know the number of headers until later, so
- // it is easiest to just reserve a fixed amount of space.
- int size = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * kMaxProgramHeaders;
+ // Reserve space for ELF header and program headers.
+ // We do not know the number of headers until later, so
+ // it is easiest to just reserve a fixed amount of space.
+ // Program headers are required for loading by the linker.
+ // It is possible to omit them for ELF files used for debugging.
+ void Start(bool write_program_headers = true) {
+ int size = sizeof(Elf_Ehdr);
+ if (write_program_headers) {
+ size += sizeof(Elf_Phdr) * kMaxProgramHeaders;
+ }
stream_.Seek(size, kSeekSet);
+ started_ = true;
virtual_address_ += size;
+ write_program_headers_ = write_program_headers;
}
void End() {
+ DCHECK(started_);
+
// Write section names and finish the section headers.
shstrtab_.Start();
shstrtab_.Write("");
@@ -386,8 +405,7 @@
shdrs.push_back(section->header_);
}
Elf_Off section_headers_offset;
- section_headers_offset = RoundUp(stream_.Seek(0, kSeekCurrent), sizeof(Elf_Off));
- stream_.Seek(section_headers_offset, kSeekSet);
+ section_headers_offset = AlignFileOffset(sizeof(Elf_Off));
stream_.WriteFully(shdrs.data(), shdrs.size() * sizeof(shdrs[0]));
// Flush everything else before writing the program headers. This should prevent
@@ -395,14 +413,21 @@
// and partially written data if we suddenly lose power, for example.
stream_.Flush();
- // Write the initial file headers.
- std::vector<Elf_Phdr> phdrs = MakeProgramHeaders();
+ // The main ELF header.
Elf_Ehdr elf_header = MakeElfHeader(isa_);
- elf_header.e_phoff = sizeof(Elf_Ehdr);
elf_header.e_shoff = section_headers_offset;
- elf_header.e_phnum = phdrs.size();
elf_header.e_shnum = shdrs.size();
elf_header.e_shstrndx = shstrtab_.GetSectionIndex();
+
+ // Program headers (i.e. mmap instructions).
+ std::vector<Elf_Phdr> phdrs;
+ if (write_program_headers_) {
+ phdrs = MakeProgramHeaders();
+ CHECK_LE(phdrs.size(), kMaxProgramHeaders);
+ elf_header.e_phoff = sizeof(Elf_Ehdr);
+ elf_header.e_phnum = phdrs.size();
+ }
+
stream_.Seek(0, kSeekSet);
stream_.WriteFully(&elf_header, sizeof(elf_header));
stream_.WriteFully(phdrs.data(), phdrs.size() * sizeof(phdrs[0]));
@@ -492,6 +517,14 @@
return &stream_;
}
+ off_t AlignFileOffset(size_t alignment) {
+ return stream_.Seek(RoundUp(stream_.Seek(0, kSeekCurrent), alignment), kSeekSet);
+ }
+
+ Elf_Addr AlignVirtualAddress(size_t alignment) {
+ return virtual_address_ = RoundUp(virtual_address_, alignment);
+ }
+
private:
static Elf_Ehdr MakeElfHeader(InstructionSet isa) {
Elf_Ehdr elf_header = Elf_Ehdr();
@@ -666,9 +699,13 @@
// List of used section in the order in which they were written.
std::vector<Section*> sections_;
+ bool started_;
+
// Used for allocation of virtual address space.
Elf_Addr virtual_address_;
+ size_t write_program_headers_;
+
DISALLOW_COPY_AND_ASSIGN(ElfBuilder);
};
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index d1f5007..ca8cd68 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -1148,13 +1148,19 @@
writer.Write(types);
}
- void End() {
+ void End(bool write_oat_patches) {
builder_->GetDebugInfo()->End();
- builder_->WritePatches(".debug_info.oat_patches",
- ArrayRef<const uintptr_t>(debug_info_patches_));
+ if (write_oat_patches) {
+ builder_->WritePatches(".debug_info.oat_patches",
+ ArrayRef<const uintptr_t>(debug_info_patches_));
+ }
builder_->WriteSection(".debug_abbrev", &debug_abbrev_buffer_);
- builder_->WriteSection(".debug_loc", &debug_loc_);
- builder_->WriteSection(".debug_ranges", &debug_ranges_);
+ if (!debug_loc_.empty()) {
+ builder_->WriteSection(".debug_loc", &debug_loc_);
+ }
+ if (!debug_ranges_.empty()) {
+ builder_->WriteSection(".debug_ranges", &debug_ranges_);
+ }
}
private:
@@ -1357,10 +1363,12 @@
return buffer.size();
}
- void End() {
+ void End(bool write_oat_patches) {
builder_->GetDebugLine()->End();
- builder_->WritePatches(".debug_line.oat_patches",
- ArrayRef<const uintptr_t>(debug_line_patches));
+ if (write_oat_patches) {
+ builder_->WritePatches(".debug_line.oat_patches",
+ ArrayRef<const uintptr_t>(debug_line_patches));
+ }
}
private:
@@ -1370,7 +1378,8 @@
template<typename ElfTypes>
static void WriteDebugSections(ElfBuilder<ElfTypes>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos) {
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ bool write_oat_patches) {
// Group the methods into compilation units based on source file.
std::vector<CompilationUnit> compilation_units;
const char* last_source_file = nullptr;
@@ -1394,7 +1403,7 @@
for (auto& compilation_unit : compilation_units) {
line_writer.WriteCompilationUnit(compilation_unit);
}
- line_writer.End();
+ line_writer.End(write_oat_patches);
}
// Write .debug_info section.
@@ -1404,7 +1413,7 @@
for (const auto& compilation_unit : compilation_units) {
info_writer.WriteCompilationUnit(compilation_unit);
}
- info_writer.End();
+ info_writer.End(write_oat_patches);
}
}
@@ -1484,13 +1493,14 @@
template <typename ElfTypes>
void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
- CFIFormat cfi_format) {
+ CFIFormat cfi_format,
+ bool write_oat_patches) {
// Add methods to .symtab.
WriteDebugSymbols(builder, method_infos, true /* with_signature */);
// Generate CFI (stack unwinding information).
- WriteCFISection(builder, method_infos, cfi_format, true /* write_oat_patches */);
+ WriteCFISection(builder, method_infos, cfi_format, write_oat_patches);
// Write DWARF .debug_* sections.
- WriteDebugSections(builder, method_infos);
+ WriteDebugSections(builder, method_infos, write_oat_patches);
}
static void XzCompress(const std::vector<uint8_t>* src, std::vector<uint8_t>* dst) {
@@ -1571,10 +1581,12 @@
buffer.reserve(KB);
VectorOutputStream out("Debug ELF file", &buffer);
std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
- builder->Start();
+ // No program headers since the ELF file is not linked and has no allocated sections.
+ builder->Start(false /* write_program_headers */);
WriteDebugInfo(builder.get(),
ArrayRef<const MethodDebugInfo>(&method_info, 1),
- DW_DEBUG_FRAME_FORMAT);
+ DW_DEBUG_FRAME_FORMAT,
+ false /* write_oat_patches */);
builder->End();
CHECK(builder->Good());
// Make a copy of the buffer. We want to shrink it anyway.
@@ -1601,12 +1613,12 @@
buffer.reserve(KB);
VectorOutputStream out("Debug ELF file", &buffer);
std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
- builder->Start();
-
+ // No program headers since the ELF file is not linked and has no allocated sections.
+ builder->Start(false /* write_program_headers */);
DebugInfoWriter<ElfTypes> info_writer(builder.get());
info_writer.Start();
info_writer.WriteTypes(types);
- info_writer.End();
+ info_writer.End(false /* write_oat_patches */);
builder->End();
CHECK(builder->Good());
@@ -1630,11 +1642,13 @@
template void WriteDebugInfo<ElfTypes32>(
ElfBuilder<ElfTypes32>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
- CFIFormat cfi_format);
+ CFIFormat cfi_format,
+ bool write_oat_patches);
template void WriteDebugInfo<ElfTypes64>(
ElfBuilder<ElfTypes64>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
- CFIFormat cfi_format);
+ CFIFormat cfi_format,
+ bool write_oat_patches);
template void WriteMiniDebugInfo<ElfTypes32>(
ElfBuilder<ElfTypes32>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos);
diff --git a/compiler/elf_writer_debug.h b/compiler/elf_writer_debug.h
index e19da08..8e8472f 100644
--- a/compiler/elf_writer_debug.h
+++ b/compiler/elf_writer_debug.h
@@ -33,7 +33,8 @@
template <typename ElfTypes>
void WriteDebugInfo(ElfBuilder<ElfTypes>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
- CFIFormat cfi_format);
+ CFIFormat cfi_format,
+ bool write_oat_patches);
template <typename ElfTypes>
void WriteMiniDebugInfo(ElfBuilder<ElfTypes>* builder,
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 6bf080a..f2a95f2 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -151,7 +151,7 @@
const ArrayRef<const dwarf::MethodDebugInfo>& method_infos) {
if (compiler_options_->GetGenerateDebugInfo()) {
// Generate all the debug information we can.
- dwarf::WriteDebugInfo(builder_.get(), method_infos, kCFIFormat);
+ dwarf::WriteDebugInfo(builder_.get(), method_infos, kCFIFormat, true /* write_oat_patches */);
}
if (compiler_options_->GetGenerateMiniDebugInfo()) {
// Generate only some information and compress it.