From 46845074557484a82f4dc73647dad399e1c00e89 Mon Sep 17 00:00:00 2001 From: Felipe de Azevedo Piovezan Date: Fri, 19 Jan 2024 16:11:08 -0800 Subject: [PATCH] [lldb][DWARFUnit] Implement PeekDIEName query (#78486) This allows us to query the AT_Name of a DIE without parsing the entire CU. Part of the ongoing effort to support IDX_Parent in accelerator tables [1]. [1]: https://discourse.llvm.org/t/rfc-improve-dwarf-5-debug-names-type-lookup-parsing-speed/74151/44 --- .../SymbolFile/DWARF/DWARFDebugInfo.cpp | 6 ++ .../Plugins/SymbolFile/DWARF/DWARFDebugInfo.h | 5 ++ .../SymbolFile/DWARF/DWARFFormValue.cpp | 24 +++--- .../Plugins/SymbolFile/DWARF/DWARFFormValue.h | 6 ++ .../Plugins/SymbolFile/DWARF/DWARFUnit.cpp | 24 ++++++ .../Plugins/SymbolFile/DWARF/DWARFUnit.h | 5 ++ .../SymbolFile/DWARF/DWARFDIETest.cpp | 83 +++++++++++++++++++ 7 files changed, 143 insertions(+), 10 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp index 553b6a4c551d20..340b9acf80d023 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp @@ -191,3 +191,9 @@ DWARFDebugInfo::GetDIE(const DIERef &die_ref) { return cu->GetNonSkeletonUnit().GetDIE(die_ref.die_offset()); return DWARFDIE(); // Not found } + +llvm::StringRef DWARFDebugInfo::PeekDIEName(const DIERef &die_ref) { + if (DWARFUnit *cu = GetUnit(die_ref)) + return cu->GetNonSkeletonUnit().PeekDIEName(die_ref.die_offset()); + return llvm::StringRef(); +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h index d5e48f312ea0e9..a8b5abc3beed2d 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h @@ -43,6 +43,11 @@ class DWARFDebugInfo { bool ContainsTypeUnits(); DWARFDIE GetDIE(const DIERef &die_ref); + /// Returns the AT_Name of this DIE, if it exists, without parsing the entire + /// compile unit. An empty is string is returned upon error or if the + /// attribute is not present. + llvm::StringRef PeekDIEName(const DIERef &die_ref); + enum { eDumpFlag_Verbose = (1 << 0), // Verbose dumping eDumpFlag_ShowForm = (1 << 1), // Show the DW_form type diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp index 0a7029a55c047b..e1f73f1997e369 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp @@ -18,8 +18,6 @@ #include "DWARFFormValue.h" #include "DWARFUnit.h" -class DWARFUnit; - using namespace lldb_private; using namespace lldb_private::dwarf; using namespace lldb_private::plugin::dwarf; @@ -502,7 +500,8 @@ dw_addr_t DWARFFormValue::Address() const { &offset, index_size); } -DWARFDIE DWARFFormValue::Reference() const { +std::pair +DWARFFormValue::ReferencedUnitAndOffset() const { uint64_t value = m_value.value.uval; switch (m_form) { case DW_FORM_ref1: @@ -516,9 +515,9 @@ DWARFDIE DWARFFormValue::Reference() const { if (!m_unit->ContainsDIEOffset(value)) { m_unit->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( "DW_FORM_ref* DIE reference {0:x16} is outside of its CU", value); - return {}; + return {nullptr, 0}; } - return const_cast(m_unit)->GetDIE(value); + return {const_cast(m_unit), value}; case DW_FORM_ref_addr: { DWARFUnit *ref_cu = @@ -527,24 +526,29 @@ DWARFDIE DWARFFormValue::Reference() const { if (!ref_cu) { m_unit->GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( "DW_FORM_ref_addr DIE reference {0:x16} has no matching CU", value); - return {}; + return {nullptr, 0}; } - return ref_cu->GetDIE(value); + return {ref_cu, value}; } case DW_FORM_ref_sig8: { DWARFTypeUnit *tu = m_unit->GetSymbolFileDWARF().DebugInfo().GetTypeUnitForHash(value); if (!tu) - return {}; - return tu->GetDIE(tu->GetTypeOffset()); + return {nullptr, 0}; + return {tu, tu->GetTypeOffset()}; } default: - return {}; + return {nullptr, 0}; } } +DWARFDIE DWARFFormValue::Reference() const { + auto [unit, offset] = ReferencedUnitAndOffset(); + return unit ? unit->GetDIE(offset) : DWARFDIE(); +} + uint64_t DWARFFormValue::Reference(dw_offset_t base_offset) const { uint64_t value = m_value.value.uval; switch (m_form) { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h index 445749a6aac3ac..fdd5b3c278a4e8 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFFormValue.h @@ -60,6 +60,12 @@ class DWARFFormValue { const DWARFUnit *u); std::optional GetFixedSize() const; DWARFDIE Reference() const; + + /// If this is a reference to another DIE, return the corresponding DWARFUnit + /// and DIE offset such that Unit->GetDIE(offset) produces the desired DIE. + /// Otherwise, a nullptr and unspecified offset are returned. + std::pair ReferencedUnitAndOffset() const; + uint64_t Reference(dw_offset_t offset) const; bool Boolean() const { return m_value.value.uval != 0; } uint64_t Unsigned() const { return m_value.value.uval; } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp index 0e2f4d45543bb5..7a40361cdede7a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -663,6 +663,30 @@ DWARFUnit::GetDIE(dw_offset_t die_offset) { return DWARFDIE(); // Not found } +llvm::StringRef DWARFUnit::PeekDIEName(dw_offset_t die_offset) { + DWARFDebugInfoEntry die; + if (!die.Extract(GetData(), this, &die_offset)) + return llvm::StringRef(); + + // Does die contain a DW_AT_Name? + if (const char *name = + die.GetAttributeValueAsString(this, DW_AT_name, nullptr)) + return name; + + // Does its DW_AT_specification or DW_AT_abstract_origin contain an AT_Name? + for (auto attr : {DW_AT_specification, DW_AT_abstract_origin}) { + DWARFFormValue form_value; + if (!die.GetAttributeValue(this, attr, form_value)) + continue; + auto [unit, offset] = form_value.ReferencedUnitAndOffset(); + if (unit) + if (auto name = unit->PeekDIEName(offset); !name.empty()) + return name; + } + + return llvm::StringRef(); +} + DWARFUnit &DWARFUnit::GetNonSkeletonUnit() { ExtractUnitDIEIfNeeded(); if (m_dwo) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h index 3f528e913d8cfa..bc225a52e1d030 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -187,6 +187,11 @@ class DWARFUnit : public UserID { DWARFDIE GetDIE(dw_offset_t die_offset); + /// Returns the AT_Name of the DIE at `die_offset`, if it exists, without + /// parsing the entire compile unit. An empty is string is returned upon + /// error or if the attribute is not present. + llvm::StringRef PeekDIEName(dw_offset_t die_offset); + DWARFUnit &GetNonSkeletonUnit(); static uint8_t GetAddressByteSize(const DWARFUnit *cu); diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFDIETest.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFDIETest.cpp index 8497855b2f3db5..bcb211815f9f35 100644 --- a/lldb/unittests/SymbolFile/DWARF/DWARFDIETest.cpp +++ b/lldb/unittests/SymbolFile/DWARF/DWARFDIETest.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Plugins/SymbolFile/DWARF/DWARFDIE.h" +#include "Plugins/SymbolFile/DWARF/DWARFDebugInfo.h" #include "TestingSupport/Symbol/YAMLModuleTester.h" #include "llvm/ADT/STLExtras.h" #include "gmock/gmock.h" @@ -104,3 +105,85 @@ TEST(DWARFDIETest, ChildIteration) { DWARFDIE no_children_die(unit, die_child0); EXPECT_TRUE(no_children_die.children().empty()); } + +TEST(DWARFDIETest, PeekName) { + const char *yamldata = R"( +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_386 +DWARF: + debug_str: + - 'NameType1' + - 'NameType2' + debug_abbrev: + - Table: + - Code: 0x00000001 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_language + Form: DW_FORM_data2 + - Code: 0x00000002 + Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Code: 0x00000003 + Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_abstract_origin + Form: DW_FORM_ref1 + - Code: 0x00000004 + Tag: DW_TAG_base_type + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_specification + Form: DW_FORM_ref1 + debug_info: + - Version: 4 + AddrSize: 8 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 0x000000000000000C + - AbbrCode: 0x00000002 + Values: + - Value: 0x0000000000000000 # Name = NameType1 + - AbbrCode: 0x00000002 + Values: + - Value: 0x000000000000000a # Name = NameType2 + - AbbrCode: 0x00000003 + Values: + - Value: 0x000000000000000e # Ref abstract origin to NameType1 DIE. + - AbbrCode: 0x00000004 + Values: + - Value: 0x0000000000000013 # Ref specification to NameType2 DIE. + - AbbrCode: 0x00000000 +)"; + + YAMLModuleTester t(yamldata); + auto *symbol_file = + llvm::cast(t.GetModule()->GetSymbolFile()); + auto &debug_info = symbol_file->DebugInfo(); + + DIERef first_die(std::nullopt, DIERef::Section::DebugInfo, + 11 /*FirstDIEOffset*/); + EXPECT_EQ(debug_info.PeekDIEName(first_die), ""); + + DIERef second_die(std::nullopt, DIERef::Section::DebugInfo, 14); + EXPECT_EQ(debug_info.PeekDIEName(second_die), "NameType1"); + + DIERef third_die(std::nullopt, DIERef::Section::DebugInfo, 19); + EXPECT_EQ(debug_info.PeekDIEName(third_die), "NameType2"); + + DIERef fourth_die(std::nullopt, DIERef::Section::DebugInfo, 24); + EXPECT_EQ(debug_info.PeekDIEName(fourth_die), "NameType1"); + + DIERef fifth_die(std::nullopt, DIERef::Section::DebugInfo, 26); + EXPECT_EQ(debug_info.PeekDIEName(fifth_die), "NameType2"); +}