Skip to content

Commit 69b53a2

Browse files
committed
[lldb][DWARF] Change GetAttributes to always visit current DIE before recursing
`GetAttributes` returns all attributes on a given DIE, including any attributes in referenced `DW_AT_abstract_origin` or `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`. There is currently no good 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 #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.
1 parent 7e01a32 commit 69b53a2

File tree

3 files changed

+680
-29
lines changed

3 files changed

+680
-29
lines changed

lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -281,22 +281,28 @@ bool DWARFDebugInfoEntry::GetDIENamesAndRanges(
281281
return !ranges.empty();
282282
}
283283

284-
// Get all attribute values for a given DIE, including following any
285-
// specification or abstract origin attributes and including those in the
286-
// results. Any duplicate attributes will have the first instance take
287-
// precedence (this can happen for declaration attributes).
288-
void DWARFDebugInfoEntry::GetAttributes(DWARFUnit *cu,
289-
DWARFAttributes &attributes,
290-
Recurse recurse,
291-
uint32_t curr_depth) const {
292-
const auto *abbrevDecl = GetAbbreviationDeclarationPtr(cu);
293-
if (!abbrevDecl) {
294-
attributes.Clear();
295-
return;
296-
}
284+
bool DWARFDebugInfoEntry::GetAttributes(
285+
DWARFUnit const *cu, llvm::SmallVector<DWARFDIE> &worklist,
286+
llvm::SmallSet<DWARFDebugInfoEntry const *, 3> &seen,
287+
DWARFAttributes &attributes) const {
288+
assert(!worklist.empty());
289+
assert(cu);
290+
291+
DWARFDIE current = worklist.pop_back_val();
292+
293+
const auto *entry = current.GetDIE();
294+
if (!entry)
295+
return false;
296+
297+
const auto *abbrevDecl =
298+
entry->GetAbbreviationDeclarationPtr(current.GetCU());
299+
if (!abbrevDecl)
300+
return false;
297301

298302
const DWARFDataExtractor &data = cu->GetData();
299-
lldb::offset_t offset = GetFirstAttributeOffset();
303+
lldb::offset_t offset = current.GetDIE()->GetFirstAttributeOffset();
304+
305+
const bool is_first_die = seen.size() == 1;
300306

301307
for (const auto &attribute : abbrevDecl->attributes()) {
302308
DWARFFormValue form_value(cu);
@@ -309,10 +315,10 @@ void DWARFDebugInfoEntry::GetAttributes(DWARFUnit *cu,
309315
switch (attr) {
310316
case DW_AT_sibling:
311317
case DW_AT_declaration:
312-
if (curr_depth > 0) {
318+
if (seen.size() > 1 && !is_first_die) {
313319
// This attribute doesn't make sense when combined with the DIE that
314320
// references this DIE. We know a DIE is referencing this DIE because
315-
// curr_depth is not zero
321+
// we've visited more than one DIE already.
316322
break;
317323
}
318324
[[fallthrough]];
@@ -321,13 +327,12 @@ void DWARFDebugInfoEntry::GetAttributes(DWARFUnit *cu,
321327
break;
322328
}
323329

324-
if (recurse == Recurse::yes &&
325-
((attr == DW_AT_specification) || (attr == DW_AT_abstract_origin))) {
330+
if (attr == DW_AT_specification || attr == DW_AT_abstract_origin) {
326331
if (form_value.ExtractValue(data, &offset)) {
327-
DWARFDIE spec_die = form_value.Reference();
328-
if (spec_die)
329-
spec_die.GetDIE()->GetAttributes(spec_die.GetCU(), attributes,
330-
recurse, curr_depth + 1);
332+
if (DWARFDIE spec_die = form_value.Reference()) {
333+
if (seen.insert(spec_die.GetDIE()).second)
334+
worklist.push_back(spec_die);
335+
}
331336
}
332337
} else {
333338
const dw_form_t form = form_value.Form();
@@ -339,6 +344,39 @@ void DWARFDebugInfoEntry::GetAttributes(DWARFUnit *cu,
339344
DWARFFormValue::SkipValue(form, data, &offset, cu);
340345
}
341346
}
347+
348+
return true;
349+
}
350+
351+
DWARFAttributes DWARFDebugInfoEntry::GetAttributes(DWARFUnit *cu,
352+
Recurse recurse) const {
353+
// FIXME: use ElaboratingDIEIterator to follow specifications/abstract origins
354+
// instead of maintaining our own worklist/seen list.
355+
356+
DWARFAttributes attributes;
357+
358+
llvm::SmallVector<DWARFDIE, 3> worklist;
359+
worklist.emplace_back(cu, this);
360+
361+
// Keep track if DIEs already seen to prevent infinite recursion.
362+
// Value of '3' was picked for the same reason that
363+
// DWARFDie::findRecursively does.
364+
llvm::SmallSet<DWARFDebugInfoEntry const *, 3> seen;
365+
seen.insert(this);
366+
367+
while (!worklist.empty()) {
368+
if (!GetAttributes(cu, worklist, seen, attributes)) {
369+
attributes.Clear();
370+
break;
371+
}
372+
373+
// We visited the current DIE already and were asked not to check the
374+
// rest of the worklist. So bail out.
375+
if (recurse == Recurse::no)
376+
break;
377+
}
378+
379+
return attributes;
342380
}
343381

344382
// GetAttributeValue

lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,21 @@ class DWARFDebugInfoEntry {
5252
lldb::offset_t *offset_ptr);
5353

5454
using Recurse = DWARFBaseDIE::Recurse;
55+
56+
// Get all attribute values for a given DIE, including following any
57+
// specification or abstract origin attributes and including those in the
58+
// results.
59+
//
60+
// When following specifications/abstract origins, the attributes
61+
// on the referring DIE are guaranteed to be visited before the attributes of
62+
// the referenced DIE.
63+
//
64+
// \param[in]
65+
// \param[in]
66+
//
67+
// \returns output may have duplicate entries
5568
DWARFAttributes GetAttributes(DWARFUnit *cu,
56-
Recurse recurse = Recurse::yes) const {
57-
DWARFAttributes attrs;
58-
GetAttributes(cu, attrs, recurse, 0 /* curr_depth */);
59-
return attrs;
60-
}
69+
Recurse recurse = Recurse::yes) const;
6170

6271
dw_offset_t GetAttributeValue(const DWARFUnit *cu, const dw_attr_t attr,
6372
DWARFFormValue &formValue,
@@ -180,8 +189,10 @@ class DWARFDebugInfoEntry {
180189
dw_tag_t m_tag = llvm::dwarf::DW_TAG_null;
181190

182191
private:
183-
void GetAttributes(DWARFUnit *cu, DWARFAttributes &attrs, Recurse recurse,
184-
uint32_t curr_depth) const;
192+
// Helper for the public \ref DWARFDebugInfoEntry::GetAttributes API.
193+
bool GetAttributes(DWARFUnit const *cu, llvm::SmallVector<DWARFDIE> &worklist,
194+
llvm::SmallSet<DWARFDebugInfoEntry const *, 3> &seen,
195+
DWARFAttributes &attributes) const;
185196
};
186197
} // namespace dwarf
187198
} // namespace lldb_private::plugin

0 commit comments

Comments
 (0)