diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/dex/quick/codegen_util.cc | 2 | ||||
| -rw-r--r-- | compiler/driver/compiler_options.cc | 6 | ||||
| -rw-r--r-- | compiler/driver/compiler_options.h | 12 | ||||
| -rw-r--r-- | compiler/elf_builder.h | 17 | ||||
| -rw-r--r-- | compiler/elf_writer_debug.cc | 146 | ||||
| -rw-r--r-- | compiler/elf_writer_debug.h | 4 | ||||
| -rw-r--r-- | compiler/elf_writer_quick.cc | 9 | ||||
| -rw-r--r-- | compiler/jni/quick/jni_compiler.cc | 2 | ||||
| -rw-r--r-- | compiler/oat_writer.cc | 2 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 2 |
10 files changed, 177 insertions, 25 deletions
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index d68835a9cf..af6f91f21d 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -1076,7 +1076,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena pc_rel_temp_(nullptr), dex_cache_arrays_min_offset_(std::numeric_limits<uint32_t>::max()), cfi_(&last_lir_insn_, - cu->compiler_driver->GetCompilerOptions().GetGenerateDebugInfo(), + cu->compiler_driver->GetCompilerOptions().GenerateAnyDebugInfo(), arena), in_to_reg_storage_mapping_(arena) { switch_tables_.reserve(4); diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc index 4f6e922665..9285b8c927 100644 --- a/compiler/driver/compiler_options.cc +++ b/compiler/driver/compiler_options.cc @@ -37,6 +37,7 @@ CompilerOptions::CompilerOptions() debuggable_(false), native_debuggable_(kDefaultNativeDebuggable), generate_debug_info_(kDefaultGenerateDebugInfo), + generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo), implicit_null_checks_(true), implicit_so_checks_(true), implicit_suspend_checks_(false), @@ -91,6 +92,7 @@ CompilerOptions::CompilerOptions(CompilerFilter compiler_filter, debuggable_(debuggable), native_debuggable_(kDefaultNativeDebuggable), generate_debug_info_(generate_debug_info), + generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo), implicit_null_checks_(implicit_null_checks), implicit_so_checks_(implicit_so_checks), implicit_suspend_checks_(implicit_suspend_checks), @@ -215,6 +217,10 @@ bool CompilerOptions::ParseCompilerOption(const StringPiece& option, UsageFn Usa generate_debug_info_ = true; } else if (option == "--no-generate-debug-info") { generate_debug_info_ = false; + } else if (option == "--generate-mini-debug-info") { + generate_mini_debug_info_ = true; + } else if (option == "--no-generate-mini-debug-info") { + generate_mini_debug_info_ = false; } else if (option == "--debuggable") { debuggable_ = true; } else if (option == "--native-debuggable") { diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index 9d51b750bf..6989bd5bae 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -51,6 +51,7 @@ class CompilerOptions FINAL { static constexpr double kDefaultTopKProfileThreshold = 90.0; static const bool kDefaultNativeDebuggable = false; static const bool kDefaultGenerateDebugInfo = false; + static const bool kDefaultGenerateMiniDebugInfo = false; static const bool kDefaultIncludePatchInformation = false; static const size_t kDefaultInlineDepthLimit = 3; static const size_t kDefaultInlineMaxCodeUnits = 32; @@ -170,10 +171,20 @@ class CompilerOptions FINAL { return native_debuggable_; } + // This flag controls whether the compiler collects debugging information. + // The other flags control how the information is written to disk. + bool GenerateAnyDebugInfo() const { + return GetGenerateDebugInfo() || GetGenerateMiniDebugInfo(); + } + bool GetGenerateDebugInfo() const { return generate_debug_info_; } + bool GetGenerateMiniDebugInfo() const { + return generate_mini_debug_info_; + } + bool GetImplicitNullChecks() const { return implicit_null_checks_; } @@ -268,6 +279,7 @@ class CompilerOptions FINAL { bool debuggable_; bool native_debuggable_; bool generate_debug_info_; + bool generate_mini_debug_info_; bool implicit_null_checks_; bool implicit_so_checks_; bool implicit_suspend_checks_; diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h index 46484b1cd6..3d24d19919 100644 --- a/compiler/elf_builder.h +++ b/compiler/elf_builder.h @@ -165,10 +165,15 @@ class ElfBuilder FINAL { } } - // Set desired allocation size for .bss section. - void SetSize(Elf_Word size) { - CHECK_EQ(header_.sh_type, (Elf_Word)SHT_NOBITS); + // Write this section as "NOBITS" section. (used for the .bss section) + // This means that the ELF file does not contain the initial data for this section + // 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; header_.sh_size = size; + End(); } // This function always succeeds to simplify code. @@ -346,6 +351,12 @@ class ElfBuilder FINAL { other_sections_.push_back(std::move(s)); } + // Set where the next section will be allocated in the virtual address space. + void SetVirtualAddress(Elf_Addr address) { + DCHECK_GE(address, virtual_address_); + virtual_address_ = address; + } + void Start() { // Reserve space for ELF header and program headers. // We do not know the number of headers until later, so diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc index 73e6aa38f0..2e98b69c47 100644 --- a/compiler/elf_writer_debug.cc +++ b/compiler/elf_writer_debug.cc @@ -16,6 +16,7 @@ #include "elf_writer_debug.h" +#include <algorithm> #include <unordered_set> #include <vector> #include <cstdio> @@ -40,6 +41,11 @@ #include "stack_map.h" #include "utils.h" +// liblzma. +#include "XzEnc.h" +#include "7zCrc.h" +#include "XzCrc64.h" + namespace art { namespace dwarf { @@ -222,7 +228,8 @@ static void WriteCIE(InstructionSet isa, template<typename ElfTypes> void WriteCFISection(ElfBuilder<ElfTypes>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, - CFIFormat format) { + CFIFormat format, + bool write_oat_patches) { CHECK(format == DW_DEBUG_FRAME_FORMAT || format == DW_EH_FRAME_FORMAT); typedef typename ElfTypes::Addr Elf_Addr; @@ -238,6 +245,24 @@ void WriteCFISection(ElfBuilder<ElfTypes>* builder, patch_locations.reserve(method_infos.size()); } + // The methods can be written any order. + // Let's therefore sort them in the lexicographical order of the opcodes. + // This has no effect on its own. However, if the final .debug_frame section is + // compressed it reduces the size since similar opcodes sequences are grouped. + std::vector<const MethodDebugInfo*> sorted_method_infos; + sorted_method_infos.reserve(method_infos.size()); + for (size_t i = 0; i < method_infos.size(); i++) { + sorted_method_infos.push_back(&method_infos[i]); + } + std::sort( + sorted_method_infos.begin(), + sorted_method_infos.end(), + [](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) { + ArrayRef<const uint8_t> l = lhs->compiled_method_->GetCFIInfo(); + ArrayRef<const uint8_t> r = rhs->compiled_method_->GetCFIInfo(); + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); + }); + // Write .eh_frame/.debug_frame section. auto* cfi_section = (format == DW_DEBUG_FRAME_FORMAT ? builder->GetDebugFrame() @@ -256,11 +281,11 @@ void WriteCFISection(ElfBuilder<ElfTypes>* builder, cfi_section->WriteFully(buffer.data(), buffer.size()); buffer_address += buffer.size(); buffer.clear(); - for (const MethodDebugInfo& mi : method_infos) { - if (!mi.deduped_) { // Only one FDE per unique address. - ArrayRef<const uint8_t> opcodes = mi.compiled_method_->GetCFIInfo(); + for (const MethodDebugInfo* mi : sorted_method_infos) { + if (!mi->deduped_) { // Only one FDE per unique address. + ArrayRef<const uint8_t> opcodes = mi->compiled_method_->GetCFIInfo(); if (!opcodes.empty()) { - const Elf_Addr code_address = text_address + mi.low_pc_; + const Elf_Addr code_address = text_address + mi->low_pc_; if (format == DW_EH_FRAME_FORMAT) { binary_search_table.push_back( dchecked_integral_cast<uint32_t>(code_address)); @@ -268,7 +293,7 @@ void WriteCFISection(ElfBuilder<ElfTypes>* builder, dchecked_integral_cast<uint32_t>(buffer_address)); } WriteFDE(is64bit, cfi_address, cie_address, - code_address, mi.high_pc_ - mi.low_pc_, + code_address, mi->high_pc_ - mi->low_pc_, opcodes, format, buffer_address, &buffer, &patch_locations); cfi_section->WriteFully(buffer.data(), buffer.size()); @@ -309,8 +334,10 @@ void WriteCFISection(ElfBuilder<ElfTypes>* builder, header_section->WriteFully(binary_search_table.data(), binary_search_table.size()); header_section->End(); } else { - builder->WritePatches(".debug_frame.oat_patches", - ArrayRef<const uintptr_t>(patch_locations)); + if (write_oat_patches) { + builder->WritePatches(".debug_frame.oat_patches", + ArrayRef<const uintptr_t>(patch_locations)); + } } } @@ -1338,8 +1365,9 @@ static void WriteDebugSections(ElfBuilder<ElfTypes>* builder, } template <typename ElfTypes> -void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, - const ArrayRef<const MethodDebugInfo>& method_infos) { +static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, + const ArrayRef<const MethodDebugInfo>& method_infos, + bool with_signature) { bool generated_mapping_symbol = false; auto* strtab = builder->GetStrTab(); auto* symtab = builder->GetSymTab(); @@ -1359,22 +1387,31 @@ void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, strtab->Start(); strtab->Write(""); // strtab should start with empty string. + std::string last_name; + size_t last_name_offset = 0; for (const MethodDebugInfo& info : method_infos) { if (info.deduped_) { continue; // Add symbol only for the first instance. } - std::string name = PrettyMethod(info.dex_method_index_, *info.dex_file_, true); + std::string name = PrettyMethod(info.dex_method_index_, *info.dex_file_, with_signature); if (deduped_addresses.find(info.low_pc_) != deduped_addresses.end()) { name += " [DEDUPED]"; } + // If we write method names without signature, we might see the same name multiple times. + size_t name_offset = (name == last_name ? last_name_offset : strtab->Write(name)); const auto* text = builder->GetText()->Exists() ? builder->GetText() : nullptr; const bool is_relative = (text != nullptr); uint32_t low_pc = info.low_pc_; // Add in code delta, e.g., thumb bit 0 for Thumb2 code. low_pc += info.compiled_method_->CodeDelta(); - symtab->Add(strtab->Write(name), text, low_pc, - is_relative, info.high_pc_ - info.low_pc_, STB_GLOBAL, STT_FUNC); + symtab->Add(name_offset, + text, + low_pc, + is_relative, + info.high_pc_ - info.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. @@ -1387,6 +1424,9 @@ void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder, generated_mapping_symbol = true; } } + + last_name = std::move(name); + last_name_offset = name_offset; } strtab->End(); @@ -1402,13 +1442,83 @@ void WriteDebugInfo(ElfBuilder<ElfTypes>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, CFIFormat cfi_format) { // Add methods to .symtab. - WriteDebugSymbols(builder, method_infos); + WriteDebugSymbols(builder, method_infos, true /* with_signature */); // Generate CFI (stack unwinding information). - WriteCFISection(builder, method_infos, cfi_format); + WriteCFISection(builder, method_infos, cfi_format, true /* write_oat_patches */); // Write DWARF .debug_* sections. WriteDebugSections(builder, method_infos); } +static void XzCompress(const std::vector<uint8_t>* src, std::vector<uint8_t>* dst) { + // Configure the compression library. + CrcGenerateTable(); + Crc64GenerateTable(); + CLzma2EncProps lzma2Props; + Lzma2EncProps_Init(&lzma2Props); + lzma2Props.lzmaProps.level = 1; // Fast compression. + Lzma2EncProps_Normalize(&lzma2Props); + CXzProps props; + XzProps_Init(&props); + props.lzma2Props = &lzma2Props; + // Implement the required interface for communication (written in C so no virtual methods). + struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress { + static SRes ReadImpl(void* p, void* buf, size_t* size) { + auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqInStream*>(p)); + *size = std::min(*size, ctx->src_->size() - ctx->src_pos_); + memcpy(buf, ctx->src_->data() + ctx->src_pos_, *size); + ctx->src_pos_ += *size; + return SZ_OK; + } + static size_t WriteImpl(void* p, const void* buf, size_t size) { + auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqOutStream*>(p)); + const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buf); + ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size); + return size; + } + static SRes ProgressImpl(void* , UInt64, UInt64) { + return SZ_OK; + } + size_t src_pos_; + const std::vector<uint8_t>* src_; + std::vector<uint8_t>* dst_; + }; + XzCallbacks callbacks; + callbacks.Read = XzCallbacks::ReadImpl; + callbacks.Write = XzCallbacks::WriteImpl; + callbacks.Progress = XzCallbacks::ProgressImpl; + callbacks.src_pos_ = 0; + callbacks.src_ = src; + callbacks.dst_ = dst; + // Compress. + SRes res = Xz_Encode(&callbacks, &callbacks, &props, &callbacks); + CHECK_EQ(res, SZ_OK); +} + +template <typename ElfTypes> +void WriteMiniDebugInfo(ElfBuilder<ElfTypes>* parent_builder, + const ArrayRef<const MethodDebugInfo>& method_infos) { + const InstructionSet isa = parent_builder->GetIsa(); + std::vector<uint8_t> buffer; + buffer.reserve(KB); + VectorOutputStream out("Mini-debug-info ELF file", &buffer); + std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out)); + builder->Start(); + // Write .rodata and .text as NOBITS sections. + // This allows tools to detect virtual address relocation of the parent ELF file. + builder->SetVirtualAddress(parent_builder->GetRoData()->GetAddress()); + builder->GetRoData()->WriteNoBitsSection(parent_builder->GetRoData()->GetSize()); + builder->SetVirtualAddress(parent_builder->GetText()->GetAddress()); + builder->GetText()->WriteNoBitsSection(parent_builder->GetText()->GetSize()); + WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */); + WriteCFISection(builder.get(), method_infos, DW_DEBUG_FRAME_FORMAT, false /* write_oat_paches */); + builder->End(); + CHECK(builder->Good()); + std::vector<uint8_t> compressed_buffer; + compressed_buffer.reserve(buffer.size() / 4); + XzCompress(&buffer, &compressed_buffer); + parent_builder->WriteSection(".gnu_debugdata", &compressed_buffer); +} + template <typename ElfTypes> static ArrayRef<const uint8_t> WriteDebugElfFileForMethodInternal( const dwarf::MethodDebugInfo& method_info) { @@ -1481,6 +1591,12 @@ template void WriteDebugInfo<ElfTypes64>( ElfBuilder<ElfTypes64>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, CFIFormat cfi_format); +template void WriteMiniDebugInfo<ElfTypes32>( + ElfBuilder<ElfTypes32>* builder, + const ArrayRef<const MethodDebugInfo>& method_infos); +template void WriteMiniDebugInfo<ElfTypes64>( + ElfBuilder<ElfTypes64>* builder, + const ArrayRef<const MethodDebugInfo>& method_infos); } // namespace dwarf } // namespace art diff --git a/compiler/elf_writer_debug.h b/compiler/elf_writer_debug.h index e4bc856c5e..e19da088da 100644 --- a/compiler/elf_writer_debug.h +++ b/compiler/elf_writer_debug.h @@ -35,6 +35,10 @@ void WriteDebugInfo(ElfBuilder<ElfTypes>* builder, const ArrayRef<const MethodDebugInfo>& method_infos, CFIFormat cfi_format); +template <typename ElfTypes> +void WriteMiniDebugInfo(ElfBuilder<ElfTypes>* builder, + const ArrayRef<const MethodDebugInfo>& method_infos); + ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const dwarf::MethodDebugInfo& method_info); ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa, diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 7b1bdd72e5..6bf080a083 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -137,9 +137,7 @@ template <typename ElfTypes> void ElfWriterQuick<ElfTypes>::SetBssSize(size_t bss_size) { auto* bss = builder_->GetBss(); if (bss_size != 0u) { - bss->Start(); - bss->SetSize(bss_size); - bss->End(); + bss->WriteNoBitsSection(bss_size); } } @@ -152,8 +150,13 @@ template <typename ElfTypes> void ElfWriterQuick<ElfTypes>::WriteDebugInfo( 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); } + if (compiler_options_->GetGenerateMiniDebugInfo()) { + // Generate only some information and compress it. + dwarf::WriteMiniDebugInfo(builder_.get(), method_infos); + } } template <typename ElfTypes> diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 52a238233b..e92046057c 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -95,7 +95,7 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, // Assembler that holds generated instructions std::unique_ptr<Assembler> jni_asm(Assembler::Create(instruction_set, instruction_set_features)); - jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GetGenerateDebugInfo()); + jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GenerateAnyDebugInfo()); // Offsets into data structures // TODO: if cross compiling these offsets are for the host not the target diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index a5421622b7..569e0f4e19 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -806,7 +806,7 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { } } - if (writer_->compiler_driver_->GetCompilerOptions().GetGenerateDebugInfo()) { + if (writer_->compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo()) { // Record debug information for this function if we are doing that. const uint32_t quick_code_start = quick_code_offset - writer_->oat_header_->GetExecutableOffset() - thumb_offset; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index fffd00535c..3fac914017 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -676,7 +676,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, return nullptr; } codegen->GetAssembler()->cfi().SetEnabled( - compiler_driver->GetCompilerOptions().GetGenerateDebugInfo()); + compiler_driver->GetCompilerOptions().GenerateAnyDebugInfo()); PassObserver pass_observer(graph, codegen.get(), |