-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve type lookup using .debug_names parent chain #108907
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -301,7 +301,8 @@ using Entry = llvm::DWARFDebugNames::Entry; | |||||
/// If any parent does not have an `IDX_parent`, or the Entry data is corrupted, | ||||||
/// nullopt is returned. | ||||||
std::optional<llvm::SmallVector<Entry, 4>> | ||||||
getParentChain(Entry entry, uint32_t max_parents) { | ||||||
getParentChain(Entry entry, | ||||||
uint32_t max_parents = std::numeric_limits<uint32_t>::max()) { | ||||||
llvm::SmallVector<Entry, 4> parent_entries; | ||||||
|
||||||
do { | ||||||
|
@@ -374,6 +375,21 @@ void DebugNamesDWARFIndex::GetFullyQualifiedType( | |||||
m_fallback.GetFullyQualifiedType(context, callback); | ||||||
} | ||||||
|
||||||
bool DebugNamesDWARFIndex::SameAsEntryContext( | ||||||
const CompilerContext &query_context, | ||||||
const DebugNames::Entry &entry) const { | ||||||
// TODO: check dwarf tag matches. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is a long term todo, do we have a github issue that reflects this (and if we do, can we add the number in teh comment) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it is not a long term todo. My 3rd PR in the stack will implement it. |
||||||
// Peek at the AT_name of `entry` and test equality to `name`. | ||||||
auto maybe_dieoffset = entry.getDIEUnitOffset(); | ||||||
if (!maybe_dieoffset) | ||||||
return false; | ||||||
DWARFUnit *unit = GetNonSkeletonUnit(entry); | ||||||
if (!unit) | ||||||
return false; | ||||||
return query_context.name == | ||||||
unit->PeekDIEName(unit->GetOffset() + *maybe_dieoffset); | ||||||
} | ||||||
|
||||||
bool DebugNamesDWARFIndex::SameParentChain( | ||||||
llvm::ArrayRef<llvm::StringRef> parent_names, | ||||||
llvm::ArrayRef<DebugNames::Entry> parent_entries) const { | ||||||
|
@@ -402,6 +418,45 @@ bool DebugNamesDWARFIndex::SameParentChain( | |||||
return true; | ||||||
} | ||||||
|
||||||
bool DebugNamesDWARFIndex::SameParentChain( | ||||||
llvm::ArrayRef<CompilerContext> parent_contexts, | ||||||
llvm::ArrayRef<DebugNames::Entry> parent_entries) const { | ||||||
if (parent_entries.size() != parent_contexts.size()) | ||||||
return false; | ||||||
|
||||||
// If the AT_name of any parent fails to match the expected name, we don't | ||||||
// have a match. | ||||||
for (auto [parent_context, parent_entry] : | ||||||
llvm::zip_equal(parent_contexts, parent_entries)) | ||||||
if (!SameAsEntryContext(parent_context, parent_entry)) | ||||||
return false; | ||||||
return true; | ||||||
} | ||||||
|
||||||
bool DebugNamesDWARFIndex::WithinParentChain( | ||||||
llvm::ArrayRef<CompilerContext> query_contexts, | ||||||
llvm::ArrayRef<DebugNames::Entry> parent_chain) const { | ||||||
if (query_contexts.size() == parent_chain.size()) | ||||||
return SameParentChain(query_contexts, parent_chain); | ||||||
|
||||||
// If parent chain does not have enough entries, we can't possibly have a | ||||||
// match. | ||||||
while (!query_contexts.empty() && | ||||||
query_contexts.size() <= parent_chain.size()) { | ||||||
if (SameAsEntryContext(query_contexts.front(), parent_chain.front())) { | ||||||
query_contexts = query_contexts.drop_front(); | ||||||
parent_chain = parent_chain.drop_front(); | ||||||
} else { | ||||||
// Name does not match, try next parent_chain entry if the current entry | ||||||
// is namespace because the current one can be an inline namespace. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we have a way of guaranteeing it is? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I would love to but I do not think .debug_names tag has information to filter it out. It simply encodes
I guess we could try to PeekDieName() to parse this DIE from dwo files to check if it is an inline vs non-inline namespace but that is the expensive code path this PR wants to avoid. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would that help? I don't see anything in the DIE that let's us identify inline namespaces... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool, did not know godbolt can show dwarfdump output.
llvm-project/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp Lines 3472 to 3473 in 81c3499
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, good to know we have a mechanism to detect inline namespaces. That still leaves the question of how to avoid false results though. I understand peeking at the DIE would be expensive for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The design is made to perform as much filtering as possible in |
||||||
if (parent_chain.front().tag() != DW_TAG_namespace) | ||||||
return false; | ||||||
parent_chain = parent_chain.drop_front(); | ||||||
} | ||||||
} | ||||||
return query_contexts.empty(); | ||||||
} | ||||||
|
||||||
void DebugNamesDWARFIndex::GetTypes( | ||||||
ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) { | ||||||
for (const DebugNames::Entry &entry : | ||||||
|
@@ -444,6 +499,72 @@ void DebugNamesDWARFIndex::GetNamespaces( | |||||
m_fallback.GetNamespaces(name, callback); | ||||||
} | ||||||
|
||||||
llvm::SmallVector<CompilerContext> | ||||||
DebugNamesDWARFIndex::GetTypeQueryParentContexts(TypeQuery &query) { | ||||||
std::vector<lldb_private::CompilerContext> &query_decl_context = | ||||||
query.GetContextRef(); | ||||||
llvm::SmallVector<CompilerContext> parent_contexts; | ||||||
if (!query_decl_context.empty()) { | ||||||
// Skip the last entry as it's the type we're matching parents for. | ||||||
// Reverse the query decl context to match parent chain order. | ||||||
llvm::ArrayRef<CompilerContext> parent_contexts_ref( | ||||||
query_decl_context.data(), query_decl_context.size() - 1); | ||||||
for (const CompilerContext &ctx : llvm::reverse(parent_contexts_ref)) { | ||||||
// Skip any context without name because .debug_names might not encode | ||||||
// them. (e.g. annonymous namespace) | ||||||
if ((ctx.kind & CompilerContextKind::AnyType) != | ||||||
CompilerContextKind::Invalid && | ||||||
!ctx.name.IsEmpty()) | ||||||
parent_contexts.push_back(ctx); | ||||||
} | ||||||
} | ||||||
return parent_contexts; | ||||||
} | ||||||
|
||||||
void DebugNamesDWARFIndex::GetTypesWithQuery( | ||||||
TypeQuery &query, llvm::function_ref<bool(DWARFDIE die)> callback) { | ||||||
ConstString name = query.GetTypeBasename(); | ||||||
std::vector<lldb_private::CompilerContext> query_context = | ||||||
query.GetContextRef(); | ||||||
if (query_context.size() <= 1) | ||||||
return GetTypes(name, callback); | ||||||
|
||||||
llvm::SmallVector<CompilerContext> parent_contexts = | ||||||
GetTypeQueryParentContexts(query); | ||||||
// For each entry, grab its parent chain and check if we have a match. | ||||||
for (const DebugNames::Entry &entry : m_debug_names_up->equal_range(name)) { | ||||||
if (!isType(entry.tag())) | ||||||
continue; | ||||||
|
||||||
// If we get a NULL foreign_tu back, the entry doesn't match the type unit | ||||||
// in the .dwp file, or we were not able to load the .dwo file or the DWO ID | ||||||
// didn't match. | ||||||
std::optional<DWARFTypeUnit *> foreign_tu = GetForeignTypeUnit(entry); | ||||||
if (foreign_tu && foreign_tu.value() == nullptr) | ||||||
continue; | ||||||
|
||||||
std::optional<llvm::SmallVector<Entry, 4>> parent_chain = | ||||||
getParentChain(entry); | ||||||
if (!parent_chain) { | ||||||
// Fallback: use the base class implementation. | ||||||
if (!ProcessEntry(entry, [&](DWARFDIE die) { | ||||||
return ProcessTypeDIEMatchQuery(query, die, callback); | ||||||
})) | ||||||
return; | ||||||
continue; | ||||||
} | ||||||
|
||||||
if (WithinParentChain(parent_contexts, *parent_chain) && | ||||||
!ProcessEntry(entry, [&](DWARFDIE die) { | ||||||
// After .debug_names filtering still sending to base class for | ||||||
// further filtering before calling the callback. | ||||||
return ProcessTypeDIEMatchQuery(query, die, callback); | ||||||
})) | ||||||
return; | ||||||
} | ||||||
m_fallback.GetTypesWithQuery(query, callback); | ||||||
} | ||||||
|
||||||
void DebugNamesDWARFIndex::GetFunctions( | ||||||
const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, | ||||||
const CompilerDeclContext &parent_decl_ctx, | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really a job for this patch, but I'm noticed that this function will return nothing if the parent chain was cut short (e.g. because one of the intermediate types is in a type unit). It'd probably be better to return a partial list (which is marked as partial), as that can still be useful for some filtering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you know what branch in this function will cut short during "intermediate types is in a type unit" situation? Is it "entry.hasParentInformation()" returning false, or "entry.getParentDIEEntry()" return failure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first one. Basically, if you try something like
clang++ -o - -c -g -gpubnames -fdebug-types-section -x c++ - <<<"struct A { struct B {}; }; A a; A::B b;"
, you'll see that the entry forB
has no DW_IDX_parent attribute (I think it could have it, but we'd have to figure out what exactly it should refer to).