diff options
author | David Srbecky <dsrbecky@google.com> | 2015-11-09 18:05:48 +0000 |
---|---|---|
committer | David Srbecky <dsrbecky@google.com> | 2015-11-20 16:30:38 +0000 |
commit | 04b0526d60de4e9979fc486d2ba655247d211d0b (patch) | |
tree | f1557aea44abe67f83b74e332fc8d8c28192a3ff | |
parent | 349baca032735e4a386b74709aa94d17f076cbce (diff) | |
download | art-04b0526d60de4e9979fc486d2ba655247d211d0b.tar.gz art-04b0526d60de4e9979fc486d2ba655247d211d0b.tar.bz2 art-04b0526d60de4e9979fc486d2ba655247d211d0b.zip |
Encode function signatures properly in DWARF.
The signatures were previously stored as plain string.
The proper way in DWARF is to store them as structured tree of tags.
(for example, DW_TAG_subprogram containing DW_TAG_formal_parameter)
Note that this makes the debug sections smaller since DWARF
signatures are actually more efficient than just plain strings.
Change-Id: I6afbce28340570666d8674d07c0e324aad561dd5
-rw-r--r-- | compiler/dwarf/debug_info_entry_writer.h | 76 | ||||
-rw-r--r-- | compiler/dwarf/dedup_vector.h | 67 | ||||
-rw-r--r-- | compiler/dwarf/dwarf_test.cc | 10 | ||||
-rw-r--r-- | compiler/dwarf/headers.h | 1 | ||||
-rw-r--r-- | compiler/dwarf/writer.h | 8 | ||||
-rw-r--r-- | compiler/elf_writer_debug.cc | 312 | ||||
-rw-r--r-- | runtime/base/stl_util.h | 7 |
7 files changed, 377 insertions, 104 deletions
diff --git a/compiler/dwarf/debug_info_entry_writer.h b/compiler/dwarf/debug_info_entry_writer.h index d9b367bdf1..aa31036c8b 100644 --- a/compiler/dwarf/debug_info_entry_writer.h +++ b/compiler/dwarf/debug_info_entry_writer.h @@ -20,6 +20,7 @@ #include <cstdint> #include <unordered_map> +#include "base/casts.h" #include "dwarf/dwarf_constants.h" #include "dwarf/writer.h" #include "leb128.h" @@ -47,9 +48,9 @@ struct FNVHash { * It also handles generation of abbreviations. * * Usage: - * StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes); + * StartTag(DW_TAG_compile_unit); * WriteStrp(DW_AT_producer, "Compiler name", debug_str); - * StartTag(DW_TAG_subprogram, DW_CHILDREN_no); + * StartTag(DW_TAG_subprogram); * WriteStrp(DW_AT_name, "Foo", debug_str); * EndTag(); * EndTag(); @@ -59,36 +60,40 @@ class DebugInfoEntryWriter FINAL : private Writer<Vector> { static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type"); public: + static constexpr size_t kCompilationUnitHeaderSize = 11; + // Start debugging information entry. - void StartTag(Tag tag, Children children) { - DCHECK(has_children) << "This tag can not have nested tags"; + // Returns offset of the entry in compilation unit. + size_t StartTag(Tag tag) { if (inside_entry_) { // Write abbrev code for the previous entry. - this->UpdateUleb128(abbrev_code_offset_, EndAbbrev()); + // Parent entry is finalized before any children are written. + this->UpdateUleb128(abbrev_code_offset_, EndAbbrev(DW_CHILDREN_yes)); inside_entry_ = false; } - StartAbbrev(tag, children); + StartAbbrev(tag); // Abbrev code placeholder of sufficient size. abbrev_code_offset_ = this->data()->size(); this->PushUleb128(NextAbbrevCode()); depth_++; inside_entry_ = true; - has_children = (children == DW_CHILDREN_yes); + return abbrev_code_offset_ + kCompilationUnitHeaderSize; } // End debugging information entry. void EndTag() { DCHECK_GT(depth_, 0); if (inside_entry_) { - // Write abbrev code for this tag. - this->UpdateUleb128(abbrev_code_offset_, EndAbbrev()); + // Write abbrev code for this entry. + this->UpdateUleb128(abbrev_code_offset_, EndAbbrev(DW_CHILDREN_no)); inside_entry_ = false; - } - if (has_children) { - this->PushUint8(0); // End of children. + // This entry has no children and so there is no terminator. + } else { + // The entry has been already finalized so it must be parent entry + // and we need to write the terminator required by DW_CHILDREN_yes. + this->PushUint8(0); } depth_--; - has_children = true; // Parent tag obviously has children. } void WriteAddr(Attribute attrib, uint64_t value) { @@ -101,10 +106,10 @@ class DebugInfoEntryWriter FINAL : private Writer<Vector> { } } - void WriteBlock(Attribute attrib, const void* ptr, int size) { + void WriteBlock(Attribute attrib, const void* ptr, size_t num_bytes) { AddAbbrevAttribute(attrib, DW_FORM_block); - this->PushUleb128(size); - this->PushData(ptr, size); + this->PushUleb128(num_bytes); + this->PushData(ptr, num_bytes); } void WriteData1(Attribute attrib, uint8_t value) { @@ -147,12 +152,12 @@ class DebugInfoEntryWriter FINAL : private Writer<Vector> { this->PushUint8(value ? 1 : 0); } - void WriteRef4(Attribute attrib, int cu_offset) { + void WriteRef4(Attribute attrib, uint32_t cu_offset) { AddAbbrevAttribute(attrib, DW_FORM_ref4); this->PushUint32(cu_offset); } - void WriteRef(Attribute attrib, int cu_offset) { + void WriteRef(Attribute attrib, uint32_t cu_offset) { AddAbbrevAttribute(attrib, DW_FORM_ref_udata); this->PushUleb128(cu_offset); } @@ -162,16 +167,21 @@ class DebugInfoEntryWriter FINAL : private Writer<Vector> { this->PushString(value); } - void WriteStrp(Attribute attrib, int address) { + void WriteStrp(Attribute attrib, size_t debug_str_offset) { AddAbbrevAttribute(attrib, DW_FORM_strp); - this->PushUint32(address); + this->PushUint32(dchecked_integral_cast<uint32_t>(debug_str_offset)); } - void WriteStrp(Attribute attrib, const char* value, std::vector<uint8_t>* debug_str) { + void WriteStrp(Attribute attrib, const char* str, size_t len, + std::vector<uint8_t>* debug_str) { AddAbbrevAttribute(attrib, DW_FORM_strp); - int address = debug_str->size(); - debug_str->insert(debug_str->end(), value, value + strlen(value) + 1); - this->PushUint32(address); + this->PushUint32(debug_str->size()); + debug_str->insert(debug_str->end(), str, str + len); + debug_str->push_back(0); + } + + void WriteStrp(Attribute attrib, const char* str, std::vector<uint8_t>* debug_str) { + WriteStrp(attrib, str, strlen(str), debug_str); } bool Is64bit() const { return is64bit_; } @@ -180,7 +190,11 @@ class DebugInfoEntryWriter FINAL : private Writer<Vector> { return patch_locations_; } + int Depth() const { return depth_; } + using Writer<Vector>::data; + using Writer<Vector>::size; + using Writer<Vector>::UpdateUint32; DebugInfoEntryWriter(bool is64bitArch, Vector* debug_abbrev, @@ -196,16 +210,17 @@ class DebugInfoEntryWriter FINAL : private Writer<Vector> { } ~DebugInfoEntryWriter() { + DCHECK(!inside_entry_); DCHECK_EQ(depth_, 0); } private: // Start abbreviation declaration. - void StartAbbrev(Tag tag, Children children) { - DCHECK(!inside_entry_); + void StartAbbrev(Tag tag) { current_abbrev_.clear(); EncodeUnsignedLeb128(¤t_abbrev_, tag); - current_abbrev_.push_back(children); + has_children_offset_ = current_abbrev_.size(); + current_abbrev_.push_back(0); // Place-holder for DW_CHILDREN. } // Add attribute specification. @@ -220,8 +235,9 @@ class DebugInfoEntryWriter FINAL : private Writer<Vector> { } // End abbreviation declaration and return its code. - int EndAbbrev() { - DCHECK(inside_entry_); + int EndAbbrev(Children has_children) { + DCHECK(!current_abbrev_.empty()); + current_abbrev_[has_children_offset_] = has_children; auto it = abbrev_codes_.insert(std::make_pair(std::move(current_abbrev_), NextAbbrevCode())); int abbrev_code = it.first->second; @@ -241,6 +257,7 @@ class DebugInfoEntryWriter FINAL : private Writer<Vector> { // Fields for writing and deduplication of abbrevs. Writer<Vector> debug_abbrev_; Vector current_abbrev_; + size_t has_children_offset_ = 0; std::unordered_map<Vector, int, FNVHash<Vector> > abbrev_codes_; @@ -250,7 +267,6 @@ class DebugInfoEntryWriter FINAL : private Writer<Vector> { int depth_ = 0; size_t abbrev_code_offset_ = 0; // Location to patch once we know the code. bool inside_entry_ = false; // Entry ends at first child (if any). - bool has_children = true; std::vector<uintptr_t> patch_locations_; }; diff --git a/compiler/dwarf/dedup_vector.h b/compiler/dwarf/dedup_vector.h new file mode 100644 index 0000000000..7fb21b76e2 --- /dev/null +++ b/compiler/dwarf/dedup_vector.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2015 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_DWARF_DEDUP_VECTOR_H_ +#define ART_COMPILER_DWARF_DEDUP_VECTOR_H_ + +#include <vector> +#include <unordered_map> + +namespace art { +namespace dwarf { + class DedupVector { + public: + // Returns an offset to previously inserted identical block of data, + // or appends the data at the end of the vector and returns offset to it. + size_t Insert(const uint8_t* ptr, size_t num_bytes) { + // See http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + uint32_t hash = 2166136261u; + for (size_t i = 0; i < num_bytes; i++) { + hash = (hash ^ ptr[i]) * 16777619u; + } + // Try to find existing copy of the data. + const auto& range = hash_to_offset_.equal_range(hash); + for (auto it = range.first; it != range.second; ++it) { + const size_t offset = it->second; + if (offset + num_bytes <= vector_.size() && + memcmp(vector_.data() + offset, ptr, num_bytes) == 0) { + return offset; + } + } + // Append the data at the end of the vector. + const size_t new_offset = vector_.size(); + hash_to_offset_.emplace(hash, new_offset); + vector_.insert(vector_.end(), ptr, ptr + num_bytes); + return new_offset; + } + + const std::vector<uint8_t>& Data() const { return vector_; } + + private: + struct IdentityHash { + size_t operator()(uint32_t v) const { return v; } + }; + + // We store the full hash as the key to simplify growing of the table. + // It avoids storing or referencing the actual data in the hash-table. + std::unordered_multimap<uint32_t, size_t, IdentityHash> hash_to_offset_; + + std::vector<uint8_t> vector_; + }; +} // namespace dwarf +} // namespace art + +#endif // ART_COMPILER_DWARF_DEDUP_VECTOR_H_ diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc index 6bb22eda2f..e9cd421da9 100644 --- a/compiler/dwarf/dwarf_test.cc +++ b/compiler/dwarf/dwarf_test.cc @@ -285,7 +285,7 @@ TEST_F(DwarfTest, DebugInfo) { constexpr bool is64bit = false; DebugInfoEntryWriter<> info(is64bit, &debug_abbrev_data_); DW_CHECK("Contents of the .debug_info section:"); - info.StartTag(dwarf::DW_TAG_compile_unit, dwarf::DW_CHILDREN_yes); + info.StartTag(dwarf::DW_TAG_compile_unit); DW_CHECK("Abbrev Number: 1 (DW_TAG_compile_unit)"); info.WriteStrp(dwarf::DW_AT_producer, "Compiler name", &debug_str_data_); DW_CHECK_NEXT("DW_AT_producer : (indirect string, offset: 0x0): Compiler name"); @@ -293,7 +293,7 @@ TEST_F(DwarfTest, DebugInfo) { DW_CHECK_NEXT("DW_AT_low_pc : 0x1000000"); info.WriteAddr(dwarf::DW_AT_high_pc, 0x02000000); DW_CHECK_NEXT("DW_AT_high_pc : 0x2000000"); - info.StartTag(dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_no); + info.StartTag(dwarf::DW_TAG_subprogram); DW_CHECK("Abbrev Number: 2 (DW_TAG_subprogram)"); info.WriteStrp(dwarf::DW_AT_name, "Foo", &debug_str_data_); DW_CHECK_NEXT("DW_AT_name : (indirect string, offset: 0xe): Foo"); @@ -302,7 +302,7 @@ TEST_F(DwarfTest, DebugInfo) { info.WriteAddr(dwarf::DW_AT_high_pc, 0x01020000); DW_CHECK_NEXT("DW_AT_high_pc : 0x1020000"); info.EndTag(); // DW_TAG_subprogram - info.StartTag(dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_no); + info.StartTag(dwarf::DW_TAG_subprogram); DW_CHECK("Abbrev Number: 2 (DW_TAG_subprogram)"); info.WriteStrp(dwarf::DW_AT_name, "Bar", &debug_str_data_); DW_CHECK_NEXT("DW_AT_name : (indirect string, offset: 0x12): Bar"); @@ -313,7 +313,7 @@ TEST_F(DwarfTest, DebugInfo) { info.EndTag(); // DW_TAG_subprogram info.EndTag(); // DW_TAG_compile_unit // Test that previous list was properly terminated and empty children. - info.StartTag(dwarf::DW_TAG_compile_unit, dwarf::DW_CHILDREN_yes); + info.StartTag(dwarf::DW_TAG_compile_unit); info.EndTag(); // DW_TAG_compile_unit // The abbrev table is just side product, but check it as well. @@ -327,7 +327,7 @@ TEST_F(DwarfTest, DebugInfo) { DW_CHECK_NEXT("DW_AT_name DW_FORM_strp"); DW_CHECK_NEXT("DW_AT_low_pc DW_FORM_addr"); DW_CHECK_NEXT("DW_AT_high_pc DW_FORM_addr"); - DW_CHECK("3 DW_TAG_compile_unit [has children]"); + DW_CHECK("3 DW_TAG_compile_unit [no children]"); std::vector<uintptr_t> debug_info_patches; std::vector<uintptr_t> expected_patches { 16, 20, 29, 33, 42, 46 }; // NOLINT diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h index 633e2f7d88..c75aeacabd 100644 --- a/compiler/dwarf/headers.h +++ b/compiler/dwarf/headers.h @@ -138,6 +138,7 @@ void WriteDebugInfoCU(uint32_t debug_abbrev_offset, writer.PushUint32(debug_abbrev_offset); writer.PushUint8(entries.Is64bit() ? 8 : 4); size_t entries_offset = writer.data()->size(); + DCHECK_EQ(entries_offset, DebugInfoEntryWriter<Vector>::kCompilationUnitHeaderSize); writer.PushData(*entries.data()); writer.UpdateUint32(start, writer.data()->size() - start - 4); // Copy patch locations and make them relative to .debug_info section. diff --git a/compiler/dwarf/writer.h b/compiler/dwarf/writer.h index 00b9dfa303..d2add7f026 100644 --- a/compiler/dwarf/writer.h +++ b/compiler/dwarf/writer.h @@ -114,9 +114,9 @@ class Writer { data_->insert(data_->end(), value, value + strlen(value) + 1); } - void PushData(const void* ptr, size_t size) { + void PushData(const void* ptr, size_t num_bytes) { const char* p = reinterpret_cast<const char*>(ptr); - data_->insert(data_->end(), p, p + size); + data_->insert(data_->end(), p, p + num_bytes); } template<typename Vector2> @@ -164,6 +164,10 @@ class Writer { return data_; } + size_t size() const { + return data_->size(); + } + explicit Writer(Vector* buffer) : data_(buffer) { } private: diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc index e1ab340b28..5e2a8bf50e 100644 --- a/compiler/elf_writer_debug.cc +++ b/compiler/elf_writer_debug.cc @@ -19,9 +19,11 @@ #include <unordered_set> #include "base/casts.h" +#include "base/stl_util.h" #include "compiled_method.h" #include "driver/compiler_driver.h" #include "dex_file-inl.h" +#include "dwarf/dedup_vector.h" #include "dwarf/headers.h" #include "dwarf/register.h" #include "elf_builder.h" @@ -249,10 +251,217 @@ void WriteCFISection(ElfBuilder<ElfTypes>* builder, } } +struct CompilationUnit { + std::vector<const OatWriter::DebugInfo*> methods_; + size_t debug_line_offset_ = 0; + uint32_t low_pc_ = 0xFFFFFFFFU; + uint32_t high_pc_ = 0; +}; + +// Helper class to write .debug_info and its supporting sections. template<typename ElfTypes> class DebugInfoWriter { typedef typename ElfTypes::Addr Elf_Addr; + // Helper class to write one compilation unit. + // It holds helper methods and temporary state. + class CompilationUnitWriter { + public: + explicit CompilationUnitWriter(DebugInfoWriter* owner) + : owner_(owner), + info_(Is64BitInstructionSet(owner_->builder_->GetIsa()), &debug_abbrev_) { + } + + void Write(const CompilationUnit& compilation_unit) { + CHECK(!compilation_unit.methods_.empty()); + const Elf_Addr text_address = owner_->builder_->GetText()->GetAddress(); + + info_.StartTag(DW_TAG_compile_unit); + info_.WriteStrp(DW_AT_producer, owner_->WriteString("Android dex2oat")); + info_.WriteData1(DW_AT_language, DW_LANG_Java); + info_.WriteAddr(DW_AT_low_pc, text_address + compilation_unit.low_pc_); + info_.WriteAddr(DW_AT_high_pc, text_address + compilation_unit.high_pc_); + info_.WriteData4(DW_AT_stmt_list, compilation_unit.debug_line_offset_); + + const char* last_dex_class_desc = nullptr; + for (auto mi : compilation_unit.methods_) { + const DexFile* dex = mi->dex_file_; + const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index_); + const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method); + const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto); + const char* dex_class_desc = dex->GetMethodDeclaringClassDescriptor(dex_method); + + // Enclose the method in correct class definition. + if (last_dex_class_desc != dex_class_desc) { + if (last_dex_class_desc != nullptr) { + EndClassTag(last_dex_class_desc); + } + size_t offset = StartClassTag(dex_class_desc); + type_cache_.emplace(dex_class_desc, offset); + // Check that each class is defined only once. + bool unique = owner_->defined_dex_classes_.insert(dex_class_desc).second; + CHECK(unique) << "Redefinition of " << dex_class_desc; + last_dex_class_desc = dex_class_desc; + } + + std::vector<const char*> param_names; + if (mi->code_item_ != nullptr) { + const uint8_t* stream = dex->GetDebugInfoStream(mi->code_item_); + if (stream != nullptr) { + DecodeUnsignedLeb128(&stream); // line. + uint32_t parameters_size = DecodeUnsignedLeb128(&stream); + for (uint32_t i = 0; i < parameters_size; ++i) { + uint32_t id = DecodeUnsignedLeb128P1(&stream); + param_names.push_back(mi->dex_file_->StringDataByIdx(id)); + } + } + } + + int start_depth = info_.Depth(); + info_.StartTag(DW_TAG_subprogram); + WriteName(dex->GetMethodName(dex_method)); + info_.WriteAddr(DW_AT_low_pc, text_address + mi->low_pc_); + info_.WriteAddr(DW_AT_high_pc, text_address + mi->high_pc_); + WriteLazyType(dex->GetReturnTypeDescriptor(dex_proto)); + if (dex_params != nullptr) { + for (uint32_t i = 0; i < dex_params->Size(); ++i) { + info_.StartTag(DW_TAG_formal_parameter); + // Parameter names may not be always available. + if (i < param_names.size() && param_names[i] != nullptr) { + WriteName(param_names[i]); + } + WriteLazyType(dex->StringByTypeIdx(dex_params->GetTypeItem(i).type_idx_)); + info_.EndTag(); + } + } + info_.EndTag(); + CHECK_EQ(info_.Depth(), start_depth); // Balanced start/end. + } + if (last_dex_class_desc != nullptr) { + EndClassTag(last_dex_class_desc); + } + CHECK_EQ(info_.Depth(), 1); + FinishLazyTypes(); + info_.EndTag(); // DW_TAG_compile_unit + std::vector<uint8_t> buffer; + buffer.reserve(info_.data()->size() + KB); + const size_t offset = owner_->builder_->GetDebugInfo()->GetSize(); + const size_t debug_abbrev_offset = + owner_->debug_abbrev_.Insert(debug_abbrev_.data(), debug_abbrev_.size()); + WriteDebugInfoCU(debug_abbrev_offset, info_, offset, &buffer, &owner_->debug_info_patches_); + owner_->builder_->GetDebugInfo()->WriteFully(buffer.data(), buffer.size()); + } + + // Some types are difficult to define as we go since they need + // to be enclosed in the right set of namespaces. Therefore we + // just define all types lazily at the end of compilation unit. + void WriteLazyType(const char* type_descriptor) { + DCHECK(type_descriptor != nullptr); + if (type_descriptor[0] != 'V') { + lazy_types_.emplace(type_descriptor, info_.size()); + info_.WriteRef4(DW_AT_type, 0); + } + } + + void FinishLazyTypes() { + for (const auto& lazy_type : lazy_types_) { + info_.UpdateUint32(lazy_type.second, WriteType(lazy_type.first)); + } + lazy_types_.clear(); + } + + private: + void WriteName(const char* name) { + info_.WriteStrp(DW_AT_name, owner_->WriteString(name)); + } + + // Convert dex type descriptor to DWARF. + // Returns offset in the compilation unit. + size_t WriteType(const char* desc) { + const auto& it = type_cache_.find(desc); + if (it != type_cache_.end()) { + return it->second; + } + + size_t offset; + if (*desc == 'L') { + // Class type. For example: Lpackage/name; + offset = StartClassTag(desc); + info_.WriteFlag(DW_AT_declaration, true); + EndClassTag(desc); + } else if (*desc == '[') { + // Array type. + size_t element_type = WriteType(desc + 1); + offset = info_.StartTag(DW_TAG_array_type); + info_.WriteRef(DW_AT_type, element_type); + info_.EndTag(); + } else { + // Primitive types. + const char* name; + switch (*desc) { + case 'B': name = "byte"; break; + case 'C': name = "char"; break; + case 'D': name = "double"; break; + case 'F': name = "float"; break; + case 'I': name = "int"; break; + case 'J': name = "long"; break; + case 'S': name = "short"; break; + case 'Z': name = "boolean"; break; + case 'V': name = "void"; break; + default: + LOG(FATAL) << "Unknown dex type descriptor: " << desc; + UNREACHABLE(); + } + offset = info_.StartTag(DW_TAG_base_type); + WriteName(name); + info_.EndTag(); + } + + type_cache_.emplace(desc, offset); + return offset; + } + + // Start DW_TAG_class_type tag nested in DW_TAG_namespace tags. + // Returns offset of the class tag in the compilation unit. + size_t StartClassTag(const char* desc) { + DCHECK(desc != nullptr && desc[0] == 'L'); + // Enclose the type in namespace tags. + const char* end; + for (desc = desc + 1; (end = strchr(desc, '/')) != nullptr; desc = end + 1) { + info_.StartTag(DW_TAG_namespace); + WriteName(std::string(desc, end - desc).c_str()); + } + // Start the class tag. + size_t offset = info_.StartTag(DW_TAG_class_type); + end = strchr(desc, ';'); + CHECK(end != nullptr); + WriteName(std::string(desc, end - desc).c_str()); + return offset; + } + + void EndClassTag(const char* desc) { + DCHECK(desc != nullptr && desc[0] == 'L'); + // End the class tag. + info_.EndTag(); + // Close namespace tags. + const char* end; + for (desc = desc + 1; (end = strchr(desc, '/')) != nullptr; desc = end + 1) { + info_.EndTag(); + } + } + + // For access to the ELF sections. + DebugInfoWriter<ElfTypes>* owner_; + // Debug abbrevs for this compilation unit only. + std::vector<uint8_t> debug_abbrev_; + // Temporary buffer to create and store the entries. + DebugInfoEntryWriter<> info_; + // Cache of already translated type descriptors. + std::map<const char*, size_t, CStringLess> type_cache_; // type_desc -> definition_offset. + // 32-bit references which need to be resolved to a type later. + std::multimap<const char*, size_t, CStringLess> lazy_types_; // type_desc -> patch_offset. + }; + public: explicit DebugInfoWriter(ElfBuilder<ElfTypes>* builder) : builder_(builder) { } @@ -261,54 +470,29 @@ class DebugInfoWriter { builder_->GetDebugInfo()->Start(); } - 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 : 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_); - info.StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes); - 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_offset); - for (auto method_info : method_infos) { - std::string method_name = PrettyMethod(method_info->dex_method_index_, - *method_info->dex_file_, true); - info.StartTag(DW_TAG_subprogram, DW_CHILDREN_no); - 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 - 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()); + void WriteCompilationUnit(const CompilationUnit& compilation_unit) { + CompilationUnitWriter writer(this); + writer.Write(compilation_unit); } 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_); + builder_->WriteSection(".debug_abbrev", &debug_abbrev_.Data()); + builder_->WriteSection(".debug_str", &debug_str_.Data()); } private: + size_t WriteString(const char* str) { + return debug_str_.Insert(reinterpret_cast<const uint8_t*>(str), strlen(str) + 1); + } + ElfBuilder<ElfTypes>* builder_; std::vector<uintptr_t> debug_info_patches_; - std::vector<uint8_t> debug_abbrev_; - std::vector<uint8_t> debug_str_; + DedupVector debug_abbrev_; + DedupVector debug_str_; + + std::unordered_set<const char*> defined_dex_classes_; // For CHECKs only. }; template<typename ElfTypes> @@ -325,15 +509,11 @@ class DebugLineWriter { // 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) { + size_t WriteCompilationUnit(CompilationUnit& compilation_unit) { 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_); - } + + compilation_unit.debug_line_offset_ = builder_->GetDebugLine()->GetSize(); std::vector<FileEntry> files; std::unordered_map<std::string, size_t> files_map; @@ -358,11 +538,17 @@ class DebugLineWriter { break; } DebugLineOpCodeWriter<> opcodes(is64bit, code_factor_bits_); - opcodes.SetAddress(text_address + cunit_low_pc); + opcodes.SetAddress(text_address + compilation_unit.low_pc_); if (dwarf_isa != -1) { opcodes.SetISA(dwarf_isa); } - for (const OatWriter::DebugInfo* mi : method_infos) { + for (const OatWriter::DebugInfo* mi : compilation_unit.methods_) { + // Ignore function if we have already generated line table for the same address. + // It would confuse the debugger and the DWARF specification forbids it. + if (mi->deduped_) { + continue; + } + struct DebugInfoCallbacks { static bool NewPosition(void* ctx, uint32_t address, uint32_t line) { auto* context = reinterpret_cast<DebugInfoCallbacks*>(ctx); @@ -461,7 +647,7 @@ class DebugLineWriter { opcodes.AddRow(method_address, 0); } } - opcodes.AdvancePC(text_address + cunit_high_pc); + opcodes.AdvancePC(text_address + compilation_unit.high_pc_); opcodes.EndSequence(); std::vector<uint8_t> buffer; buffer.reserve(opcodes.data()->size() + KB); @@ -484,36 +670,28 @@ class DebugLineWriter { 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; + 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()); } + CompilationUnit& cu = compilation_units.back(); + cu.methods_.push_back(&mi); + cu.low_pc_ = std::min(cu.low_pc_, mi.low_pc_); + cu.high_pc_ = std::max(cu.high_pc_, mi.high_pc_); + 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.WriteCompilationUnit(compilation_unit); } line_writer.End(); } @@ -523,7 +701,7 @@ void WriteDebugSections(ElfBuilder<ElfTypes>* builder, 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.WriteCompilationUnit(compilation_unit); } info_writer.End(); } diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h index 0949619640..324ab218d2 100644 --- a/runtime/base/stl_util.h +++ b/runtime/base/stl_util.h @@ -149,6 +149,13 @@ bool ContainsElement(const Container& container, const T& value, size_t start_po return it != container.end(); } +// const char* compare function suitable for std::map or std::set. +struct CStringLess { + bool operator()(const char* lhs, const char* rhs) const { + return strcmp(lhs, rhs) < 0; + } +}; + } // namespace art #endif // ART_RUNTIME_BASE_STL_UTIL_H_ |