[Lldb-commits] [lldb] [lldb][DWARF] Change GetAttributes to always visit current DIE before recursing (PR #123261)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Jan 16 16:22:43 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: Michael Buch (Michael137)
<details>
<summary>Changes</summary>
`GetAttributes` returns all attributes on a given DIE, including any attributes that the DIE references via `DW_AT_abstract_origin` and `DW_AT_specification`. However, if an attribute exists on both the referring DIE and the referenced DIE, the first one encountered will be the one that takes precendence when querying the returned `DWARFAttributes`. But there was no guarantee in which order those attributes get visited. That means there's no convenient way of ensuring that an attribute of a definition doesn't get shadowed by one found on the declaration. One use-case where we don't want this to happen is for `DW_AT_object_pointer` (which can exist on both definitions and declarations, see https://github.com/llvm/llvm-project/pull/123089).
This patch makes sure we visit the current DIE's attributes before following DIE references. I tried keeping as much of the original `GetAttributes` unchanged and just add an outer `GetAttributes` that keeps track of the DIEs we need to visit next.
There's precendent for this iteration order in
`llvm::DWARFDie::findRecursively` and also
`lldb_private::ElaboratingDIEIterator`. We could use the latter to implement `GetAttributes`, though it also follows `DW_AT_signature` so I decided to leave it for follow-up.
---
Patch is 25.23 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/123261.diff
3 Files Affected:
- (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp (+60-22)
- (modified) lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h (+18-7)
- (modified) lldb/unittests/SymbolFile/DWARF/DWARFDIETest.cpp (+602)
``````````diff
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
index 6d073411de876c..d26d302c33dd28 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp
@@ -281,22 +281,28 @@ bool DWARFDebugInfoEntry::GetDIENamesAndRanges(
return !ranges.empty();
}
-// Get all attribute values for a given DIE, including following any
-// specification or abstract origin attributes and including those in the
-// results. Any duplicate attributes will have the first instance take
-// precedence (this can happen for declaration attributes).
-void DWARFDebugInfoEntry::GetAttributes(DWARFUnit *cu,
- DWARFAttributes &attributes,
- Recurse recurse,
- uint32_t curr_depth) const {
- const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu);
- if (!abbrevDecl) {
- attributes.Clear();
- return;
- }
+bool DWARFDebugInfoEntry::GetAttributes(
+ DWARFUnit const *cu, llvm::SmallVector<DWARFDIE> &worklist,
+ llvm::SmallSet<DWARFDebugInfoEntry const *, 3> &seen,
+ DWARFAttributes &attributes) const {
+ assert(!worklist.empty());
+ assert(cu);
+
+ DWARFDIE current = worklist.pop_back_val();
+
+ const auto *entry = current.GetDIE();
+ if (!entry)
+ return false;
+
+ const auto *abbrevDecl =
+ entry->GetAbbreviationDeclarationPtr(current.GetCU());
+ if (!abbrevDecl)
+ return false;
const DWARFDataExtractor &data = cu->GetData();
- lldb::offset_t offset = GetFirstAttributeOffset();
+ lldb::offset_t offset = current.GetDIE()->GetFirstAttributeOffset();
+
+ const bool is_first_die = seen.size() == 1;
for (const auto &attribute : abbrevDecl->attributes()) {
DWARFFormValue form_value(cu);
@@ -309,10 +315,10 @@ void DWARFDebugInfoEntry::GetAttributes(DWARFUnit *cu,
switch (attr) {
case DW_AT_sibling:
case DW_AT_declaration:
- if (curr_depth > 0) {
+ if (seen.size() > 1 && !is_first_die) {
// This attribute doesn't make sense when combined with the DIE that
// references this DIE. We know a DIE is referencing this DIE because
- // curr_depth is not zero
+ // we've visited more than one DIE already.
break;
}
[[fallthrough]];
@@ -321,13 +327,12 @@ void DWARFDebugInfoEntry::GetAttributes(DWARFUnit *cu,
break;
}
- if (recurse == Recurse::yes &&
- ((attr == DW_AT_specification) || (attr == DW_AT_abstract_origin))) {
+ if (attr == DW_AT_specification || attr == DW_AT_abstract_origin) {
if (form_value.ExtractValue(data, &offset)) {
- DWARFDIE spec_die = form_value.Reference();
- if (spec_die)
- spec_die.GetDIE()->GetAttributes(spec_die.GetCU(), attributes,
- recurse, curr_depth + 1);
+ if (DWARFDIE spec_die = form_value.Reference()) {
+ if (seen.insert(spec_die.GetDIE()).second)
+ worklist.push_back(spec_die);
+ }
}
} else {
const dw_form_t form = form_value.Form();
@@ -339,6 +344,39 @@ void DWARFDebugInfoEntry::GetAttributes(DWARFUnit *cu,
DWARFFormValue::SkipValue(form, data, &offset, cu);
}
}
+
+ return true;
+}
+
+DWARFAttributes DWARFDebugInfoEntry::GetAttributes(DWARFUnit *cu,
+ Recurse recurse) const {
+ // FIXME: use ElaboratingDIEIterator to follow specifications/abstract origins
+ // instead of maintaining our own worklist/seen list.
+
+ DWARFAttributes attributes;
+
+ llvm::SmallVector<DWARFDIE, 3> worklist;
+ worklist.emplace_back(cu, this);
+
+ // Keep track if DIEs already seen to prevent infinite recursion.
+ // Value of '3' was picked for the same reason that
+ // DWARFDie::findRecursively does.
+ llvm::SmallSet<DWARFDebugInfoEntry const *, 3> seen;
+ seen.insert(this);
+
+ while (!worklist.empty()) {
+ if (!GetAttributes(cu, worklist, seen, attributes)) {
+ attributes.Clear();
+ break;
+ }
+
+ // We visited the current DIE already and were asked not to check the
+ // rest of the worklist. So bail out.
+ if (recurse == Recurse::no)
+ break;
+ }
+
+ return attributes;
}
// GetAttributeValue
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
index de6bbf1d527899..28381114634319 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h
@@ -52,12 +52,21 @@ class DWARFDebugInfoEntry {
lldb::offset_t *offset_ptr);
using Recurse = DWARFBaseDIE::Recurse;
+
+ // Get all attribute values for a given DIE, including following any
+ // specification or abstract origin attributes and including those in the
+ // results.
+ //
+ // When following specifications/abstract origins, the attributes
+ // on the referring DIE are guaranteed to be visited before the attributes of
+ // the referenced DIE.
+ //
+ // \param[in]
+ // \param[in]
+ //
+ // \returns output may have duplicate entries
DWARFAttributes GetAttributes(DWARFUnit *cu,
- Recurse recurse = Recurse::yes) const {
- DWARFAttributes attrs;
- GetAttributes(cu, attrs, recurse, 0 /* curr_depth */);
- return attrs;
- }
+ Recurse recurse = Recurse::yes) const;
dw_offset_t GetAttributeValue(const DWARFUnit *cu, const dw_attr_t attr,
DWARFFormValue &formValue,
@@ -180,8 +189,10 @@ class DWARFDebugInfoEntry {
dw_tag_t m_tag = llvm::dwarf::DW_TAG_null;
private:
- void GetAttributes(DWARFUnit *cu, DWARFAttributes &attrs, Recurse recurse,
- uint32_t curr_depth) const;
+ // Helper for the public \ref DWARFDebugInfoEntry::GetAttributes API.
+ bool GetAttributes(DWARFUnit const *cu, llvm::SmallVector<DWARFDIE> &worklist,
+ llvm::SmallSet<DWARFDebugInfoEntry const *, 3> &seen,
+ DWARFAttributes &attributes) const;
};
} // namespace dwarf
} // namespace lldb_private::plugin
diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFDIETest.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFDIETest.cpp
index 1e4c8f3ba07787..4f544656a6312c 100644
--- a/lldb/unittests/SymbolFile/DWARF/DWARFDIETest.cpp
+++ b/lldb/unittests/SymbolFile/DWARF/DWARFDIETest.cpp
@@ -394,3 +394,605 @@ TEST(DWARFDIETest, GetContextInFunction) {
EXPECT_THAT(foo_struct_die.GetTypeLookupContext(),
testing::ElementsAre(make_struct("struct_t")));
}
+
+struct GetAttributesTestFixture : public testing::TestWithParam<dw_attr_t> {};
+
+TEST_P(GetAttributesTestFixture, TestGetAttributes_IterationOrder) {
+ // Tests that we accumulate all current DIE's attributes first
+ // before checking the attributes of the specification.
+
+ const char *yamldata = R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_AARCH64
+DWARF:
+ debug_str:
+ - func
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Code: 0x2
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data4
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Attribute: DW_AT_external
+ Form: DW_FORM_flag_present
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_data4
+ - Code: 0x3
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_high_pc
+ Form: DW_FORM_data4
+ - Attribute: {0}
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_data4
+ debug_info:
+ - Version: 5
+ UnitType: DW_UT_compile
+ AddrSize: 8
+ Entries:
+
+# DW_TAG_compile_unit
+# DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus)
+
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x04
+
+# DW_TAG_subprogram
+# DW_AT_high_pc [DW_FORM_data4]
+# DW_AT_name [DW_FORM_strp] ("func")
+# DW_AT_low_pc [DW_FORM_data4]
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0xdeadbeef
+ - Value: 0x0
+ - Value: 0x1
+ - Value: 0x1
+ - Value: 0xdeadbeef
+
+# DW_TAG_subprogram
+# DW_AT_high_pc [DW_FORM_data4]
+# DW_AT_specification [DW_FORM_ref4] ("func")
+# DW_AT_low_pc [DW_FORM_data4]
+ - AbbrCode: 0x3
+ Values:
+ - Value: 0xf00dcafe
+ - Value: 0xf
+ - Value: 0xf00dcafe
+
+ - AbbrCode: 0x0
+...
+)";
+ YAMLModuleTester t(llvm::formatv(yamldata, GetParam()).str());
+
+ DWARFUnit *unit = t.GetDwarfUnit();
+ ASSERT_NE(unit, nullptr);
+ const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
+ ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
+ ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
+ DWARFDIE cu_die(unit, cu_entry);
+
+ auto declaration = cu_die.GetFirstChild();
+ ASSERT_TRUE(declaration.IsValid());
+ ASSERT_EQ(declaration.Tag(), DW_TAG_subprogram);
+
+ auto definition = declaration.GetSibling();
+ ASSERT_TRUE(definition.IsValid());
+ ASSERT_EQ(definition.Tag(), DW_TAG_subprogram);
+ ASSERT_FALSE(definition.GetAttributeValueAsOptionalUnsigned(DW_AT_external));
+
+ auto attrs = definition.GetAttributes(DWARFDebugInfoEntry::Recurse::yes);
+ EXPECT_EQ(attrs.Size(), 7U);
+
+ // Check that the attributes on the definition (that are also present
+ // on the declaration) take precedence.
+ for (auto attr : {DW_AT_low_pc, DW_AT_high_pc}) {
+ auto idx = attrs.FindAttributeIndex(attr);
+ EXPECT_NE(idx, UINT32_MAX);
+
+ DWARFFormValue form_value;
+ auto success = attrs.ExtractFormValueAtIndex(idx, form_value);
+ EXPECT_TRUE(success);
+
+ EXPECT_EQ(form_value.Unsigned(), 0xf00dcafe);
+ }
+}
+
+TEST_P(GetAttributesTestFixture, TestGetAttributes_Cycle) {
+ // Tests that GetAttributes can deal with cycles in
+ // specifications/abstract origins.
+
+ const char *yamldata = R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_AARCH64
+DWARF:
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Code: 0x2
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: {0}
+ Form: DW_FORM_ref4
+ debug_info:
+ - Version: 5
+ UnitType: DW_UT_compile
+ AddrSize: 8
+ Entries:
+
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x04
+
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x19
+
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0xf
+
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x14
+
+ - AbbrCode: 0x0
+...
+)";
+ YAMLModuleTester t(llvm::formatv(yamldata, GetParam()).str());
+
+ DWARFUnit *unit = t.GetDwarfUnit();
+ ASSERT_NE(unit, nullptr);
+ const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
+ ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
+ ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
+ DWARFDIE cu_die(unit, cu_entry);
+
+ auto func = cu_die.GetFirstChild();
+ ASSERT_TRUE(func.IsValid());
+ ASSERT_EQ(func.Tag(), DW_TAG_subprogram);
+
+ auto attrs = func.GetAttributes(DWARFDebugInfoEntry::Recurse::yes);
+ EXPECT_EQ(attrs.Size(), 3U);
+
+ // TODO: check that we do form a cycle with the DIEs
+}
+
+TEST_P(GetAttributesTestFixture,
+ TestGetAttributes_SkipNonApplicableAttributes) {
+ // Tests that GetAttributes will omit attributes found through
+ // specifications/abstract origins which are not applicable.
+
+ const char *yamldata = R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_AARCH64
+DWARF:
+ debug_str:
+ - func
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Code: 0x2
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Attribute: DW_AT_sibling
+ Form: DW_FORM_ref4
+ - Code: 0x3
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_declaration
+ Form: DW_FORM_flag_present
+ - Attribute: {0}
+ Form: DW_FORM_ref4
+ - Attribute: DW_AT_sibling
+ Form: DW_FORM_ref4
+ debug_info:
+ - Version: 5
+ UnitType: DW_UT_compile
+ AddrSize: 8
+ Entries:
+
+# DW_TAG_compile_unit
+# DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus)
+
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x04
+
+# DW_TAG_subprogram
+# DW_AT_declaration
+# DW_AT_name [DW_FORM_strp] ("func")
+# DW_AT_sibling
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x1
+ - Value: 0x0
+ - Value: 0x18
+
+# DW_TAG_subprogram
+# DW_AT_declaration
+# DW_AT_specification [DW_FORM_ref4] ("func")
+# DW_AT_sibling
+ - AbbrCode: 0x3
+ Values:
+ - Value: 0x1
+ - Value: 0xf
+ - Value: 0xdeadbeef
+
+ - AbbrCode: 0x0
+...
+)";
+ YAMLModuleTester t(llvm::formatv(yamldata, GetParam()).str());
+
+ DWARFUnit *unit = t.GetDwarfUnit();
+ ASSERT_NE(unit, nullptr);
+ const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
+ ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
+ ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
+ DWARFDIE cu_die(unit, cu_entry);
+
+ auto declaration = cu_die.GetFirstChild();
+ ASSERT_TRUE(declaration.IsValid());
+ ASSERT_EQ(declaration.Tag(), DW_TAG_subprogram);
+
+ auto definition = declaration.GetSibling();
+ ASSERT_TRUE(definition.IsValid());
+ ASSERT_EQ(definition.Tag(), DW_TAG_subprogram);
+
+ auto attrs = definition.GetAttributes(DWARFDebugInfoEntry::Recurse::yes);
+ EXPECT_EQ(attrs.Size(), 4U);
+ EXPECT_NE(attrs.FindAttributeIndex(DW_AT_name), UINT32_MAX);
+ EXPECT_NE(attrs.FindAttributeIndex(GetParam()), UINT32_MAX);
+
+ auto sibling_idx = attrs.FindAttributeIndex(DW_AT_sibling);
+ EXPECT_NE(sibling_idx, UINT32_MAX);
+
+ DWARFFormValue form_value;
+ auto success = attrs.ExtractFormValueAtIndex(sibling_idx, form_value);
+ ASSERT_TRUE(success);
+
+ EXPECT_EQ(form_value.Unsigned(), 0xdeadbeef);
+}
+
+TEST_P(GetAttributesTestFixture, TestGetAttributes_NoRecurse) {
+ // Tests that GetAttributes will not recurse if Recurse::No is passed to it.
+
+ const char *yamldata = R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_AARCH64
+DWARF:
+ debug_str:
+ - func
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Code: 0x2
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Code: 0x3
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_low_pc
+ Form: DW_FORM_data4
+ - Attribute: {0}
+ Form: DW_FORM_ref4
+ debug_info:
+ - Version: 5
+ UnitType: DW_UT_compile
+ AddrSize: 8
+ Entries:
+
+# DW_TAG_compile_unit
+# DW_AT_language [DW_FORM_data2] (DW_LANG_C_plus_plus)
+
+ - AbbrCode: 0x1
+ Values:
+ - Value: 0x04
+
+# DW_TAG_subprogram
+# DW_AT_name [DW_FORM_strp] ("func")
+ - AbbrCode: 0x2
+ Values:
+ - Value: 0x0
+
+# DW_TAG_subprogram
+# DW_AT_low_pc [DW_FORM_data4]
+# DW_AT_specification [DW_FORM_ref4]
+ - AbbrCode: 0x3
+ Values:
+ - Value: 0xdeadbeef
+ - Value: 0xf
+
+ - AbbrCode: 0x0
+...
+)";
+ YAMLModuleTester t(llvm::formatv(yamldata, GetParam()).str());
+
+ DWARFUnit *unit = t.GetDwarfUnit();
+ ASSERT_NE(unit, nullptr);
+ const DWARFDebugInfoEntry *cu_entry = unit->DIE().GetDIE();
+ ASSERT_EQ(cu_entry->Tag(), DW_TAG_compile_unit);
+ ASSERT_EQ(unit->GetDWARFLanguageType(), DW_LANG_C_plus_plus);
+ DWARFDIE cu_die(unit, cu_entry);
+
+ auto declaration = cu_die.GetFirstChild();
+ ASSERT_TRUE(declaration.IsValid());
+ ASSERT_EQ(declaration.Tag(), DW_TAG_subprogram);
+
+ auto definition = declaration.GetSibling();
+ ASSERT_TRUE(definition.IsValid());
+ ASSERT_EQ(definition.Tag(), DW_TAG_subprogram);
+
+ auto attrs = definition.GetAttributes(DWARFDebugInfoEntry::Recurse::no);
+ EXPECT_EQ(attrs.Size(), 2U);
+ EXPECT_EQ(attrs.FindAttributeIndex(DW_AT_name), UINT32_MAX);
+ EXPECT_NE(attrs.FindAttributeIndex(GetParam()), UINT32_MAX);
+ EXPECT_NE(attrs.FindAttributeIndex(DW_AT_low_pc), UINT32_MAX);
+}
+
+TEST_P(GetAttributesTestFixture, TestGetAttributes_InvalidSpec) {
+ // Test that GetAttributes doesn't try following invalid
+ // specifications (but still add it to the list of attributes).
+
+ const char *yamldata = R"(
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_AARCH64
+DWARF:
+ debug_str:
+ - func
+ debug_abbrev:
+ - ID: 0
+ Table:
+ - Code: 0x1
+ Tag: DW_TAG_compile_unit
+ Children: DW_CHILDREN_yes
+ Attributes:
+ - Attribute: DW_AT_language
+ Form: DW_FORM_data2
+ - Code: 0x2
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: DW_AT_name
+ Form: DW_FORM_strp
+ - Code: 0x3
+ Tag: DW_TAG_subprogram
+ Children: DW_CHILDREN_no
+ Attributes:
+ - Attribute: {0}
+ Form: DW_FORM_ref4
+ debug_info:
+ ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/123261
More information about the lldb-commits
mailing list