[Lldb-commits] [lldb] d95dadf - [LLDB][NativePDB] Allow type lookup in namespaces (#149876)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Aug 4 00:56:07 PDT 2025
Author: nerix
Date: 2025-08-04T08:56:04+01:00
New Revision: d95dadff8f094e793b79eec57737ec397fad7724
URL: https://github.com/llvm/llvm-project/commit/d95dadff8f094e793b79eec57737ec397fad7724
DIFF: https://github.com/llvm/llvm-project/commit/d95dadff8f094e793b79eec57737ec397fad7724.diff
LOG: [LLDB][NativePDB] Allow type lookup in namespaces (#149876)
Previously, `type lookup` for types in namespaces didn't work with the
native PDB plugin, because `FindTypes` would only look for types whose
base name was equal to their full name. PDB/CodeView does not store the
base names in the TPI stream, but the types have their full name (e.g.
`std::thread` instead of `thread`). So `findRecordsByName` would only
return types in the top level namespace.
This PR changes the lookup to go through all types and check their base
name. As that could be a bit expensive, the names are first cached
(similar to the function lookup in the DIA PDB plugin). Potential types
are checked with `TypeQuery::ContextMatches`.
To be able to handle anonymous namespaces, I changed
`TypeQuery::ContextMatches`. The [`TypeQuery`
constructor](https://github.com/llvm/llvm-project/blob/9ad7edef4276207ca4cefa6b39d11145f4145a72/lldb/source/Symbol/Type.cpp#L76-L79)
inserts all name components as `CompilerContextKind::AnyDeclContext`. To
skip over anonymous namespaces, `ContextMatches` checked if a component
was empty and exactly of kind `Namespace`. For our query, the last check
was always false, so we never skipped anonymous namespaces. DWARF
doesn't have this problem, as it [constructs the context
outside](https://github.com/llvm/llvm-project/blob/abe93d9d7e891a2a6596ddb0c6324280137c89dc/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp#L154-L160)
and has proper information about namespaces. I'm not fully sure if my
change is correct and that it doesn't break other users of `TypeQuery`.
This enables `type lookup <type>` to work on types in namespaces.
However, expressions don't work with this yet, because `FindNamespace`
is unimplemented for native PDB.
Added:
lldb/test/Shell/SymbolFile/NativePDB/namespace-access.test
Modified:
lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
lldb/source/Symbol/Type.cpp
Removed:
################################################################################
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
index 98b965c361a77..36e075b04f26f 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h
@@ -72,6 +72,16 @@ struct CVTagRecord {
return cvunion.Name;
}
+ CompilerContextKind contextKind() const {
+ if (m_kind == Struct || m_kind == Class)
+ return CompilerContextKind::ClassOrStruct;
+ if (m_kind == Enum)
+ return CompilerContextKind::Enum;
+
+ assert(m_kind == Union);
+ return CompilerContextKind::Union;
+ }
+
private:
CVTagRecord(llvm::codeview::ClassRecord &&c);
CVTagRecord(llvm::codeview::UnionRecord &&u);
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index 20d8c1acf9c42..7af53e16ce9d5 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -1720,18 +1720,22 @@ void SymbolFileNativePDB::FindTypes(const lldb_private::TypeQuery &query,
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
- std::vector<TypeIndex> matches =
- m_index->tpi().findRecordsByName(query.GetTypeBasename().GetStringRef());
-
- for (TypeIndex type_idx : matches) {
- TypeSP type_sp = GetOrCreateType(type_idx);
- if (!type_sp)
+ // We can't query for the full name because the type might reside
+ // in an anonymous namespace. Search for the basename in our map and check the
+ // matching types afterwards.
+ std::vector<uint32_t> matches;
+ m_type_base_names.GetValues(query.GetTypeBasename(), matches);
+
+ for (uint32_t match_idx : matches) {
+ std::vector context = GetContextForType(TypeIndex(match_idx));
+ if (context.empty())
continue;
- // We resolved a type. Get the fully qualified name to ensure it matches.
- ConstString name = type_sp->GetQualifiedName();
- TypeQuery type_match(name.GetStringRef(), TypeQueryOptions::e_exact_match);
- if (query.ContextMatches(type_match.GetContextRef())) {
+ if (query.ContextMatches(context)) {
+ TypeSP type_sp = GetOrCreateType(TypeIndex(match_idx));
+ if (!type_sp)
+ continue;
+
results.InsertUnique(type_sp);
if (results.Done(query))
return;
@@ -2201,11 +2205,15 @@ void SymbolFileNativePDB::BuildParentMap() {
CVTagRecord tag = CVTagRecord::create(type);
RecordIndices &indices = record_indices[tag.asTag().getUniqueName()];
- if (tag.asTag().isForwardRef())
+ if (tag.asTag().isForwardRef()) {
indices.forward = *ti;
- else
+ } else {
indices.full = *ti;
+ auto base_name = MSVCUndecoratedNameParser::DropScope(tag.name());
+ m_type_base_names.Append(ConstString(base_name), ti->getIndex());
+ }
+
if (indices.full != TypeIndex::None() &&
indices.forward != TypeIndex::None()) {
forward_to_full[indices.forward] = indices.full;
@@ -2261,6 +2269,10 @@ void SymbolFileNativePDB::BuildParentMap() {
llvm::consumeError(std::move(error));
}
+ // After calling Append(), the type-name map needs to be sorted again to be
+ // able to look up a type by its name.
+ m_type_base_names.Sort();
+
// Now that we know the forward -> full mapping of all type indices, we can
// re-write all the indices. At the end of this process, we want a mapping
// consisting of fwd -> full and full -> full for all child -> parent indices.
@@ -2353,3 +2365,52 @@ SymbolFileNativePDB::GetParentType(llvm::codeview::TypeIndex ti) {
return std::nullopt;
return parent_iter->second;
}
+
+std::vector<CompilerContext>
+SymbolFileNativePDB::GetContextForType(TypeIndex ti) {
+ CVType type = m_index->tpi().getType(ti);
+ if (!IsTagRecord(type))
+ return {};
+
+ CVTagRecord tag = CVTagRecord::create(type);
+
+ std::optional<Type::ParsedName> parsed_name =
+ Type::GetTypeScopeAndBasename(tag.name());
+ if (!parsed_name)
+ return {{tag.contextKind(), ConstString(tag.name())}};
+
+ std::vector<CompilerContext> ctx;
+ // assume everything is a namespace at first
+ for (llvm::StringRef scope : parsed_name->scope) {
+ ctx.emplace_back(CompilerContextKind::Namespace, ConstString(scope));
+ }
+ // we know the kind of our own type
+ ctx.emplace_back(tag.contextKind(), ConstString(parsed_name->basename));
+
+ // try to find the kind of parents
+ for (auto &el : llvm::reverse(llvm::drop_end(ctx))) {
+ std::optional<TypeIndex> parent = GetParentType(ti);
+ if (!parent)
+ break;
+
+ ti = *parent;
+ type = m_index->tpi().getType(ti);
+ switch (type.kind()) {
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ case LF_INTERFACE:
+ el.kind = CompilerContextKind::ClassOrStruct;
+ continue;
+ case LF_UNION:
+ el.kind = CompilerContextKind::Union;
+ continue;
+ case LF_ENUM:
+ el.kind = CompilerContextKind::Enum;
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ return ctx;
+}
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
index 9891313f11d0b..eda375d4cebe7 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -258,6 +258,8 @@ class SymbolFileNativePDB : public SymbolFileCommon {
void ParseInlineSite(PdbCompilandSymId inline_site_id, Address func_addr);
+ std::vector<CompilerContext> GetContextForType(llvm::codeview::TypeIndex ti);
+
llvm::BumpPtrAllocator m_allocator;
lldb::addr_t m_obj_load_address = 0;
@@ -278,6 +280,8 @@ class SymbolFileNativePDB : public SymbolFileCommon {
llvm::DenseMap<lldb::user_id_t, std::shared_ptr<InlineSite>> m_inline_sites;
llvm::DenseMap<llvm::codeview::TypeIndex, llvm::codeview::TypeIndex>
m_parent_types;
+
+ lldb_private::UniqueCStringMap<uint32_t> m_type_base_names;
};
} // namespace npdb
diff --git a/lldb/source/Symbol/Type.cpp b/lldb/source/Symbol/Type.cpp
index b7adae41b5190..952b2bdee1886 100644
--- a/lldb/source/Symbol/Type.cpp
+++ b/lldb/source/Symbol/Type.cpp
@@ -817,10 +817,12 @@ Type::GetTypeScopeAndBasename(llvm::StringRef name) {
case ':':
if (prev_is_colon && template_depth == 0) {
llvm::StringRef scope_name = name.slice(name_begin, pos.index() - 1);
- // The itanium demangler uses this string to represent anonymous
+ // The demanglers use these strings to represent anonymous
// namespaces. Convert it to a more language-agnostic form (which is
// also used in DWARF).
- if (scope_name == "(anonymous namespace)")
+ if (scope_name == "(anonymous namespace)" ||
+ scope_name == "`anonymous namespace'" ||
+ scope_name == "`anonymous-namespace'")
scope_name = "";
result.scope.push_back(scope_name);
name_begin = pos.index() + 1;
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/namespace-access.test b/lldb/test/Shell/SymbolFile/NativePDB/namespace-access.test
new file mode 100644
index 0000000000000..f6c1ccf22fa18
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/namespace-access.test
@@ -0,0 +1,135 @@
+# REQUIRES: target-windows
+
+# Test namespace lookup.
+# RUN: split-file %s %t
+# RUN: %build --nodefaultlib -o %t.exe -- %t/main.cpp
+# RUN: %lldb -f %t.exe -s \
+# RUN: %t/commands.input 2>&1 | FileCheck %s
+
+#--- main.cpp
+
+struct S {
+ char a[1];
+};
+
+namespace Outer {
+
+ struct S {
+ char a[2];
+ };
+
+ namespace Inner1 {
+ struct S {
+ char a[3];
+ };
+
+ namespace Inner2 {
+ struct S {
+ char a[4];
+ };
+ } // namespace Inner2
+ } // namespace Inner1
+
+ namespace Inner2 {
+ struct S {
+ char a[5];
+ };
+ } // namespace Inner2
+
+ namespace {
+ struct A {
+ char a[6];
+ };
+ } // namespace
+
+} // namespace Outer
+
+namespace {
+ struct A {
+ char a[7];
+ };
+} // namespace
+
+int main(int argc, char **argv) {
+ S s;
+ Outer::S os;
+ Outer::Inner1::S oi1s;
+ Outer::Inner1::Inner2::S oi1i2s;
+ Outer::Inner2::S oi2s;
+ A a1;
+ Outer::A a2;
+ return sizeof(s) + sizeof(os) + sizeof(oi1s) + sizeof(oi1i2s) + sizeof(oi2s) + sizeof(a1) + sizeof(a2);
+}
+
+#--- commands.input
+
+b main
+r
+
+type lookup S
+type lookup ::S
+type lookup Outer::S
+type lookup Outer::Inner1::S
+type lookup Inner1::S
+type lookup Outer::Inner1::Inner2::S
+type lookup Inner2::S
+type lookup Outer::Inner2::S
+type lookup Outer::A
+type lookup A
+type lookup ::A
+expr sizeof(S)
+expr sizeof(A)
+
+quit
+
+# CHECK: (lldb) type lookup S
+# CHECK: struct S {
+# CHECK: struct S {
+# CHECK: struct S {
+# CHECK: struct S {
+# CHECK: struct S {
+# CHECK: }
+# CHECK-NEXT: (lldb) type lookup ::S
+# CHECK-NEXT: struct S {
+# CHECK-NEXT: char a[1];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup Outer::S
+# CHECK-NEXT: struct S {
+# CHECK-NEXT: char a[2];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup Outer::Inner1::S
+# CHECK-NEXT: struct S {
+# CHECK-NEXT: char a[3];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup Inner1::S
+# CHECK-NEXT: struct S {
+# CHECK-NEXT: char a[3];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup Outer::Inner1::Inner2::S
+# CHECK-NEXT: struct S {
+# CHECK-NEXT: char a[4];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup Inner2::S
+# CHECK-NEXT: struct S {
+# CHECK: struct S {
+# CHECK: }
+# CHECK-NEXT: (lldb) type lookup Outer::Inner2::S
+# CHECK-NEXT: struct S {
+# CHECK-NEXT: char a[5];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup Outer::A
+# CHECK-NEXT: struct A {
+# CHECK-NEXT: char a[6];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) type lookup A
+# CHECK-NEXT: struct A {
+# CHECK: struct A {
+# CHECK: }
+# CHECK-NEXT: (lldb) type lookup ::A
+# CHECK-NEXT: struct A {
+# CHECK-NEXT: char a[7];
+# CHECK-NEXT: }
+# CHECK-NEXT: (lldb) expr sizeof(S)
+# CHECK-NEXT: (__size_t) $0 = 1
+# CHECK-NEXT: (lldb) expr sizeof(A)
+# CHECK-NEXT: (__size_t) $1 = 7
More information about the lldb-commits
mailing list