diff options
| -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_ |