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
diff --git a/compiler/debug/debug_info.h b/compiler/debug/debug_info.h
new file mode 100644
index 0000000..04c6991
--- /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 a626729..bb2a214 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -38,18 +38,18 @@
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 @@
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 @@
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 @@
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 @@
// 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 a47bf07..8ad0c42 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 @@
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 @@
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 78b8e27..a88c5cb 100644
--- a/compiler/debug/elf_gnu_debugdata_writer.h
+++ b/compiler/debug/elf_gnu_debugdata_writer.h
@@ -82,18 +82,23 @@
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 57e010f..95a0b4f 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 @@
// 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 @@
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 @@
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 @@
}
}
}
+ // 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 5262ab6..3145497 100644
--- a/compiler/linker/elf_builder.h
+++ b/compiler/linker/elf_builder.h
@@ -196,6 +196,11 @@
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 @@
/* 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 @@
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 @@
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 {