diff options
author | 2017-12-04 14:39:21 +0000 | |
---|---|---|
committer | 2018-01-12 17:21:15 +0000 | |
commit | 32210b9f8c30e202e275764200315fe26f22f34c (patch) | |
tree | 31480b479f4a935c54926df5041e9dee42d7bc66 /compiler | |
parent | d97a2d17924bc4b19674c6ec7dd494a4dca3d70e (diff) |
Generate debug symbols for interpreted methods.
Add .symtab symbols for method bytecodes in the dex section
(only if the full --generate-debug-info is enabled for now).
Test: m test-art-host-gtest
Test: testrunner.py -b --host --optimizing
Change-Id: Ie90034c921484bc4ff27c5458da78690b629dd0b
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/debug/debug_info.h | 46 | ||||
-rw-r--r-- | compiler/debug/elf_debug_writer.cc | 45 | ||||
-rw-r--r-- | compiler/debug/elf_debug_writer.h | 7 | ||||
-rw-r--r-- | compiler/debug/elf_gnu_debugdata_writer.h | 13 | ||||
-rw-r--r-- | compiler/debug/elf_symtab_writer.h | 69 | ||||
-rw-r--r-- | compiler/linker/elf_builder.h | 53 |
6 files changed, 177 insertions, 56 deletions
diff --git a/compiler/debug/debug_info.h b/compiler/debug/debug_info.h new file mode 100644 index 0000000000..04c6991ea3 --- /dev/null +++ b/compiler/debug/debug_info.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DEBUG_DEBUG_INFO_H_ +#define ART_COMPILER_DEBUG_DEBUG_INFO_H_ + +#include <map> + +#include "base/array_ref.h" +#include "method_debug_info.h" + +namespace art { +class DexFile; + +namespace debug { + +// References inputs for all debug information which can be written into the ELF file. +struct DebugInfo { + // Describes compiled code in the .text section. + ArrayRef<const MethodDebugInfo> compiled_methods; + + // Describes dex-files in the .dex section. + std::map<uint32_t, const DexFile*> dex_files; // Offset in section -> dex file content. + + bool Empty() const { + return compiled_methods.empty() && dex_files.empty(); + } +}; + +} // namespace debug +} // namespace art + +#endif // ART_COMPILER_DEBUG_DEBUG_INFO_H_ diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc index a6267292bf..bb2a214ecd 100644 --- a/compiler/debug/elf_debug_writer.cc +++ b/compiler/debug/elf_debug_writer.cc @@ -38,18 +38,18 @@ namespace debug { template <typename ElfTypes> void WriteDebugInfo(linker::ElfBuilder<ElfTypes>* builder, - const ArrayRef<const MethodDebugInfo>& method_infos, + const DebugInfo& debug_info, dwarf::CFIFormat cfi_format, bool write_oat_patches) { // Write .strtab and .symtab. - WriteDebugSymbols(builder, method_infos, true /* with_signature */); + WriteDebugSymbols(builder, false /* mini-debug-info */, debug_info); // Write .debug_frame. - WriteCFISection(builder, method_infos, cfi_format, write_oat_patches); + WriteCFISection(builder, debug_info.compiled_methods, cfi_format, write_oat_patches); // Group the methods into compilation units based on class. std::unordered_map<const DexFile::ClassDef*, ElfCompilationUnit> class_to_compilation_unit; - for (const MethodDebugInfo& mi : method_infos) { + for (const MethodDebugInfo& mi : debug_info.compiled_methods) { if (mi.dex_file != nullptr) { auto& dex_class_def = mi.dex_file->GetClassDef(mi.class_def_index); ElfCompilationUnit& cu = class_to_compilation_unit[&dex_class_def]; @@ -108,21 +108,27 @@ void WriteDebugInfo(linker::ElfBuilder<ElfTypes>* builder, std::vector<uint8_t> MakeMiniDebugInfo( InstructionSet isa, const InstructionSetFeatures* features, - uint64_t text_address, - size_t text_size, - const ArrayRef<const MethodDebugInfo>& method_infos) { + uint64_t text_section_address, + size_t text_section_size, + uint64_t dex_section_address, + size_t dex_section_size, + const DebugInfo& debug_info) { if (Is64BitInstructionSet(isa)) { return MakeMiniDebugInfoInternal<ElfTypes64>(isa, features, - text_address, - text_size, - method_infos); + text_section_address, + text_section_size, + dex_section_address, + dex_section_size, + debug_info); } else { return MakeMiniDebugInfoInternal<ElfTypes32>(isa, features, - text_address, - text_size, - method_infos); + text_section_address, + text_section_size, + dex_section_address, + dex_section_size, + debug_info); } } @@ -133,7 +139,8 @@ static std::vector<uint8_t> MakeElfFileForJITInternal( bool mini_debug_info, const MethodDebugInfo& mi) { CHECK_EQ(mi.is_code_address_text_relative, false); - ArrayRef<const MethodDebugInfo> method_infos(&mi, 1); + DebugInfo debug_info{}; + debug_info.compiled_methods = ArrayRef<const debug::MethodDebugInfo>(&mi, 1); std::vector<uint8_t> buffer; buffer.reserve(KB); linker::VectorOutputStream out("Debug ELF file", &buffer); @@ -146,12 +153,14 @@ static std::vector<uint8_t> MakeElfFileForJITInternal( features, mi.code_address, mi.code_size, - method_infos); + /* dex_section_address */ 0, + /* dex_section_size */ 0, + debug_info); builder->WriteSection(".gnu_debugdata", &mdi); } else { builder->GetText()->AllocateVirtualMemory(mi.code_address, mi.code_size); WriteDebugInfo(builder.get(), - method_infos, + debug_info, dwarf::DW_DEBUG_FRAME_FORMAT, false /* write_oat_patches */); } @@ -209,12 +218,12 @@ std::vector<uint8_t> WriteDebugElfFileForClasses(InstructionSet isa, // Explicit instantiations template void WriteDebugInfo<ElfTypes32>( linker::ElfBuilder<ElfTypes32>* builder, - const ArrayRef<const MethodDebugInfo>& method_infos, + const DebugInfo& debug_info, dwarf::CFIFormat cfi_format, bool write_oat_patches); template void WriteDebugInfo<ElfTypes64>( linker::ElfBuilder<ElfTypes64>* builder, - const ArrayRef<const MethodDebugInfo>& method_infos, + const DebugInfo& debug_info, dwarf::CFIFormat cfi_format, bool write_oat_patches); diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h index a47bf076b9..8ad0c4219a 100644 --- a/compiler/debug/elf_debug_writer.h +++ b/compiler/debug/elf_debug_writer.h @@ -23,6 +23,7 @@ #include "base/macros.h" #include "base/mutex.h" #include "debug/dwarf/dwarf_constants.h" +#include "debug/debug_info.h" #include "linker/elf_builder.h" namespace art { @@ -36,7 +37,7 @@ struct MethodDebugInfo; template <typename ElfTypes> void WriteDebugInfo( linker::ElfBuilder<ElfTypes>* builder, - const ArrayRef<const MethodDebugInfo>& method_infos, + const DebugInfo& debug_info, dwarf::CFIFormat cfi_format, bool write_oat_patches); @@ -45,7 +46,9 @@ std::vector<uint8_t> MakeMiniDebugInfo( const InstructionSetFeatures* features, uint64_t text_section_address, size_t text_section_size, - const ArrayRef<const MethodDebugInfo>& method_infos); + uint64_t dex_section_address, + size_t dex_section_size, + const DebugInfo& debug_info); std::vector<uint8_t> MakeElfFileForJIT( InstructionSet isa, diff --git a/compiler/debug/elf_gnu_debugdata_writer.h b/compiler/debug/elf_gnu_debugdata_writer.h index 78b8e2780c..a88c5cb213 100644 --- a/compiler/debug/elf_gnu_debugdata_writer.h +++ b/compiler/debug/elf_gnu_debugdata_writer.h @@ -82,18 +82,23 @@ static std::vector<uint8_t> MakeMiniDebugInfoInternal( const InstructionSetFeatures* features, typename ElfTypes::Addr text_section_address, size_t text_section_size, - const ArrayRef<const MethodDebugInfo>& method_infos) { + typename ElfTypes::Addr dex_section_address, + size_t dex_section_size, + const DebugInfo& debug_info) { std::vector<uint8_t> buffer; buffer.reserve(KB); linker::VectorOutputStream out("Mini-debug-info ELF file", &buffer); std::unique_ptr<linker::ElfBuilder<ElfTypes>> builder( new linker::ElfBuilder<ElfTypes>(isa, features, &out)); builder->Start(false /* write_program_headers */); - // Mirror .text as NOBITS section since the added symbols will reference it. + // Mirror ELF sections as NOBITS since the added symbols will reference them. builder->GetText()->AllocateVirtualMemory(text_section_address, text_section_size); - WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */); + if (dex_section_size != 0) { + builder->GetDex()->AllocateVirtualMemory(dex_section_address, dex_section_size); + } + WriteDebugSymbols(builder.get(), true /* mini-debug-info */, debug_info); WriteCFISection(builder.get(), - method_infos, + debug_info.compiled_methods, dwarf::DW_DEBUG_FRAME_FORMAT, false /* write_oat_paches */); builder->End(); diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h index 57e010f232..95a0b4ff47 100644 --- a/compiler/debug/elf_symtab_writer.h +++ b/compiler/debug/elf_symtab_writer.h @@ -17,9 +17,13 @@ #ifndef ART_COMPILER_DEBUG_ELF_SYMTAB_WRITER_H_ #define ART_COMPILER_DEBUG_ELF_SYMTAB_WRITER_H_ +#include <map> #include <unordered_set> +#include "debug/debug_info.h" #include "debug/method_debug_info.h" +#include "dex/dex_file-inl.h" +#include "dex/code_item_accessors.h" #include "linker/elf_builder.h" #include "utils.h" @@ -35,22 +39,26 @@ namespace debug { // one symbol which marks the whole .text section as code. constexpr bool kGenerateSingleArmMappingSymbol = true; +// Magic name for .symtab symbols which enumerate dex files used +// by this ELF file (currently mmapped inside the .dex section). +constexpr const char* kDexFileSymbolName = "$dexfile"; + template <typename ElfTypes> static void WriteDebugSymbols(linker::ElfBuilder<ElfTypes>* builder, - const ArrayRef<const MethodDebugInfo>& method_infos, - bool with_signature) { + bool mini_debug_info, + const DebugInfo& debug_info) { uint64_t mapping_symbol_address = std::numeric_limits<uint64_t>::max(); auto* strtab = builder->GetStrTab(); auto* symtab = builder->GetSymTab(); - if (method_infos.empty()) { + if (debug_info.Empty()) { return; } // Find all addresses which contain deduped methods. // The first instance of method is not marked deduped_, but the rest is. std::unordered_set<uint64_t> deduped_addresses; - for (const MethodDebugInfo& info : method_infos) { + for (const MethodDebugInfo& info : debug_info.compiled_methods) { if (info.deduped) { deduped_addresses.insert(info.code_address); } @@ -58,9 +66,8 @@ static void WriteDebugSymbols(linker::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) { + // Add symbols for compiled methods. + for (const MethodDebugInfo& info : debug_info.compiled_methods) { if (info.deduped) { continue; // Add symbol only for the first instance. } @@ -69,14 +76,11 @@ static void WriteDebugSymbols(linker::ElfBuilder<ElfTypes>* builder, name_offset = strtab->Write(info.trampoline_name); } else { DCHECK(info.dex_file != nullptr); - std::string name = info.dex_file->PrettyMethod(info.dex_method_index, with_signature); + std::string name = info.dex_file->PrettyMethod(info.dex_method_index, !mini_debug_info); if (deduped_addresses.find(info.code_address) != deduped_addresses.end()) { name += " [DEDUPED]"; } - // If we write method names without signature, we might see the same name multiple times. - name_offset = (name == last_name ? last_name_offset : strtab->Write(name)); - last_name = std::move(name); - last_name_offset = name_offset; + name_offset = strtab->Write(name); } const auto* text = builder->GetText(); @@ -97,13 +101,46 @@ static void WriteDebugSymbols(linker::ElfBuilder<ElfTypes>* builder, } } } + // Add symbols for interpreted methods (with address range of the method's bytecode). + if (!debug_info.dex_files.empty() && builder->GetDex()->Exists()) { + auto dex = builder->GetDex(); + for (auto it : debug_info.dex_files) { + uint64_t dex_address = dex->GetAddress() + it.first /* offset within the section */; + const DexFile* dex_file = it.second; + symtab->Add(strtab->Write(kDexFileSymbolName), dex, dex_address, 0, STB_LOCAL, STT_NOTYPE); + if (mini_debug_info) { + continue; // Don't add interpreter method names to mini-debug-info for now. + } + for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); + const uint8_t* class_data = dex_file->GetClassData(class_def); + if (class_data == nullptr) { + continue; + } + for (ClassDataItemIterator item(*dex_file, class_data); item.HasNext(); item.Next()) { + if (!item.IsAtMethod()) { + continue; + } + const DexFile::CodeItem* code_item = item.GetMethodCodeItem(); + if (code_item == nullptr) { + continue; + } + CodeItemInstructionAccessor code(*dex_file, code_item); + DCHECK(code.HasCodeItem()); + std::string name = dex_file->PrettyMethod(item.GetMemberIndex(), !mini_debug_info); + size_t name_offset = strtab->Write(name); + uint64_t offset = reinterpret_cast<const uint8_t*>(code.Insns()) - dex_file->Begin(); + uint64_t address = dex_address + offset; + size_t size = code.InsnsSizeInCodeUnits() * sizeof(uint16_t); + symtab->Add(name_offset, dex, address, size, STB_GLOBAL, STT_FUNC); + } + } + } + } strtab->End(); // Symbols are buffered and written after names (because they are smaller). - // We could also do two passes in this function to avoid the buffering. - symtab->Start(); - symtab->Write(); - symtab->End(); + symtab->WriteCachedSection(); } } // namespace debug diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h index 5262ab6f3b..3145497091 100644 --- a/compiler/linker/elf_builder.h +++ b/compiler/linker/elf_builder.h @@ -196,6 +196,11 @@ class ElfBuilder FINAL { return section_index_; } + // Returns true if this section has been added. + bool Exists() const { + return section_index_ != 0; + } + private: // Add this section to the list of generated ELF sections (if not there already). // It also ensures the alignment is sufficient to generate valid program headers, @@ -304,42 +309,46 @@ class ElfBuilder FINAL { /* info */ 0, align, /* entsize */ 0), - current_offset_(0) { + current_offset_(0), + last_offset_(0) { } Elf_Word Write(const std::string& name) { if (current_offset_ == 0) { DCHECK(name.empty()); + } else if (name == last_name_) { + return last_offset_; // Very simple string de-duplication. } - Elf_Word offset = current_offset_; + last_name_ = name; + last_offset_ = current_offset_; this->WriteFully(name.c_str(), name.length() + 1); current_offset_ += name.length() + 1; - return offset; + return last_offset_; } private: Elf_Word current_offset_; + std::string last_name_; + Elf_Word last_offset_; }; // Writer of .dynsym and .symtab sections. - class SymbolSection FINAL : public CachedSection { + class SymbolSection FINAL : public Section { public: SymbolSection(ElfBuilder<ElfTypes>* owner, const std::string& name, Elf_Word type, Elf_Word flags, Section* strtab) - : CachedSection(owner, - name, - type, - flags, - strtab, - /* info */ 0, - sizeof(Elf_Off), - sizeof(Elf_Sym)) { - // The symbol table always has to start with NULL symbol. - Elf_Sym null_symbol = Elf_Sym(); - CachedSection::Add(&null_symbol, sizeof(null_symbol)); + : Section(owner, + name, + type, + flags, + strtab, + /* info */ 0, + sizeof(Elf_Off), + sizeof(Elf_Sym)) { + syms_.push_back(Elf_Sym()); // The symbol table always has to start with NULL symbol. } // Buffer symbol for this section. It will be written later. @@ -362,6 +371,7 @@ class ElfBuilder FINAL { Add(name, section_index, addr, size, binding, type); } + // Buffer symbol for this section. It will be written later. void Add(Elf_Word name, Elf_Word section_index, Elf_Addr addr, @@ -375,8 +385,19 @@ class ElfBuilder FINAL { sym.st_other = 0; sym.st_shndx = section_index; sym.st_info = (binding << 4) + (type & 0xf); - CachedSection::Add(&sym, sizeof(sym)); + syms_.push_back(sym); + } + + Elf_Word GetCacheSize() { return syms_.size() * sizeof(Elf_Sym); } + + void WriteCachedSection() { + this->Start(); + this->WriteFully(syms_.data(), syms_.size() * sizeof(Elf_Sym)); + this->End(); } + + private: + std::vector<Elf_Sym> syms_; // Buffered/cached content of the whole section. }; class AbiflagsSection FINAL : public Section { |