Skip to content

Commit d95dadf

Browse files
authored
[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.
1 parent df71243 commit d95dadf

File tree

5 files changed

+226
-14
lines changed

5 files changed

+226
-14
lines changed

lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ struct CVTagRecord {
7272
return cvunion.Name;
7373
}
7474

75+
CompilerContextKind contextKind() const {
76+
if (m_kind == Struct || m_kind == Class)
77+
return CompilerContextKind::ClassOrStruct;
78+
if (m_kind == Enum)
79+
return CompilerContextKind::Enum;
80+
81+
assert(m_kind == Union);
82+
return CompilerContextKind::Union;
83+
}
84+
7585
private:
7686
CVTagRecord(llvm::codeview::ClassRecord &&c);
7787
CVTagRecord(llvm::codeview::UnionRecord &&u);

lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1720,18 +1720,22 @@ void SymbolFileNativePDB::FindTypes(const lldb_private::TypeQuery &query,
17201720

17211721
std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
17221722

1723-
std::vector<TypeIndex> matches =
1724-
m_index->tpi().findRecordsByName(query.GetTypeBasename().GetStringRef());
1725-
1726-
for (TypeIndex type_idx : matches) {
1727-
TypeSP type_sp = GetOrCreateType(type_idx);
1728-
if (!type_sp)
1723+
// We can't query for the full name because the type might reside
1724+
// in an anonymous namespace. Search for the basename in our map and check the
1725+
// matching types afterwards.
1726+
std::vector<uint32_t> matches;
1727+
m_type_base_names.GetValues(query.GetTypeBasename(), matches);
1728+
1729+
for (uint32_t match_idx : matches) {
1730+
std::vector context = GetContextForType(TypeIndex(match_idx));
1731+
if (context.empty())
17291732
continue;
17301733

1731-
// We resolved a type. Get the fully qualified name to ensure it matches.
1732-
ConstString name = type_sp->GetQualifiedName();
1733-
TypeQuery type_match(name.GetStringRef(), TypeQueryOptions::e_exact_match);
1734-
if (query.ContextMatches(type_match.GetContextRef())) {
1734+
if (query.ContextMatches(context)) {
1735+
TypeSP type_sp = GetOrCreateType(TypeIndex(match_idx));
1736+
if (!type_sp)
1737+
continue;
1738+
17351739
results.InsertUnique(type_sp);
17361740
if (results.Done(query))
17371741
return;
@@ -2201,11 +2205,15 @@ void SymbolFileNativePDB::BuildParentMap() {
22012205
CVTagRecord tag = CVTagRecord::create(type);
22022206

22032207
RecordIndices &indices = record_indices[tag.asTag().getUniqueName()];
2204-
if (tag.asTag().isForwardRef())
2208+
if (tag.asTag().isForwardRef()) {
22052209
indices.forward = *ti;
2206-
else
2210+
} else {
22072211
indices.full = *ti;
22082212

2213+
auto base_name = MSVCUndecoratedNameParser::DropScope(tag.name());
2214+
m_type_base_names.Append(ConstString(base_name), ti->getIndex());
2215+
}
2216+
22092217
if (indices.full != TypeIndex::None() &&
22102218
indices.forward != TypeIndex::None()) {
22112219
forward_to_full[indices.forward] = indices.full;
@@ -2261,6 +2269,10 @@ void SymbolFileNativePDB::BuildParentMap() {
22612269
llvm::consumeError(std::move(error));
22622270
}
22632271

2272+
// After calling Append(), the type-name map needs to be sorted again to be
2273+
// able to look up a type by its name.
2274+
m_type_base_names.Sort();
2275+
22642276
// Now that we know the forward -> full mapping of all type indices, we can
22652277
// re-write all the indices. At the end of this process, we want a mapping
22662278
// consisting of fwd -> full and full -> full for all child -> parent indices.
@@ -2353,3 +2365,52 @@ SymbolFileNativePDB::GetParentType(llvm::codeview::TypeIndex ti) {
23532365
return std::nullopt;
23542366
return parent_iter->second;
23552367
}
2368+
2369+
std::vector<CompilerContext>
2370+
SymbolFileNativePDB::GetContextForType(TypeIndex ti) {
2371+
CVType type = m_index->tpi().getType(ti);
2372+
if (!IsTagRecord(type))
2373+
return {};
2374+
2375+
CVTagRecord tag = CVTagRecord::create(type);
2376+
2377+
std::optional<Type::ParsedName> parsed_name =
2378+
Type::GetTypeScopeAndBasename(tag.name());
2379+
if (!parsed_name)
2380+
return {{tag.contextKind(), ConstString(tag.name())}};
2381+
2382+
std::vector<CompilerContext> ctx;
2383+
// assume everything is a namespace at first
2384+
for (llvm::StringRef scope : parsed_name->scope) {
2385+
ctx.emplace_back(CompilerContextKind::Namespace, ConstString(scope));
2386+
}
2387+
// we know the kind of our own type
2388+
ctx.emplace_back(tag.contextKind(), ConstString(parsed_name->basename));
2389+
2390+
// try to find the kind of parents
2391+
for (auto &el : llvm::reverse(llvm::drop_end(ctx))) {
2392+
std::optional<TypeIndex> parent = GetParentType(ti);
2393+
if (!parent)
2394+
break;
2395+
2396+
ti = *parent;
2397+
type = m_index->tpi().getType(ti);
2398+
switch (type.kind()) {
2399+
case LF_CLASS:
2400+
case LF_STRUCTURE:
2401+
case LF_INTERFACE:
2402+
el.kind = CompilerContextKind::ClassOrStruct;
2403+
continue;
2404+
case LF_UNION:
2405+
el.kind = CompilerContextKind::Union;
2406+
continue;
2407+
case LF_ENUM:
2408+
el.kind = CompilerContextKind::Enum;
2409+
continue;
2410+
default:
2411+
break;
2412+
}
2413+
break;
2414+
}
2415+
return ctx;
2416+
}

lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ class SymbolFileNativePDB : public SymbolFileCommon {
258258

259259
void ParseInlineSite(PdbCompilandSymId inline_site_id, Address func_addr);
260260

261+
std::vector<CompilerContext> GetContextForType(llvm::codeview::TypeIndex ti);
262+
261263
llvm::BumpPtrAllocator m_allocator;
262264

263265
lldb::addr_t m_obj_load_address = 0;
@@ -278,6 +280,8 @@ class SymbolFileNativePDB : public SymbolFileCommon {
278280
llvm::DenseMap<lldb::user_id_t, std::shared_ptr<InlineSite>> m_inline_sites;
279281
llvm::DenseMap<llvm::codeview::TypeIndex, llvm::codeview::TypeIndex>
280282
m_parent_types;
283+
284+
lldb_private::UniqueCStringMap<uint32_t> m_type_base_names;
281285
};
282286

283287
} // namespace npdb

lldb/source/Symbol/Type.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -817,10 +817,12 @@ Type::GetTypeScopeAndBasename(llvm::StringRef name) {
817817
case ':':
818818
if (prev_is_colon && template_depth == 0) {
819819
llvm::StringRef scope_name = name.slice(name_begin, pos.index() - 1);
820-
// The itanium demangler uses this string to represent anonymous
820+
// The demanglers use these strings to represent anonymous
821821
// namespaces. Convert it to a more language-agnostic form (which is
822822
// also used in DWARF).
823-
if (scope_name == "(anonymous namespace)")
823+
if (scope_name == "(anonymous namespace)" ||
824+
scope_name == "`anonymous namespace'" ||
825+
scope_name == "`anonymous-namespace'")
824826
scope_name = "";
825827
result.scope.push_back(scope_name);
826828
name_begin = pos.index() + 1;
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# REQUIRES: target-windows
2+
3+
# Test namespace lookup.
4+
# RUN: split-file %s %t
5+
# RUN: %build --nodefaultlib -o %t.exe -- %t/main.cpp
6+
# RUN: %lldb -f %t.exe -s \
7+
# RUN: %t/commands.input 2>&1 | FileCheck %s
8+
9+
#--- main.cpp
10+
11+
struct S {
12+
char a[1];
13+
};
14+
15+
namespace Outer {
16+
17+
struct S {
18+
char a[2];
19+
};
20+
21+
namespace Inner1 {
22+
struct S {
23+
char a[3];
24+
};
25+
26+
namespace Inner2 {
27+
struct S {
28+
char a[4];
29+
};
30+
} // namespace Inner2
31+
} // namespace Inner1
32+
33+
namespace Inner2 {
34+
struct S {
35+
char a[5];
36+
};
37+
} // namespace Inner2
38+
39+
namespace {
40+
struct A {
41+
char a[6];
42+
};
43+
} // namespace
44+
45+
} // namespace Outer
46+
47+
namespace {
48+
struct A {
49+
char a[7];
50+
};
51+
} // namespace
52+
53+
int main(int argc, char **argv) {
54+
S s;
55+
Outer::S os;
56+
Outer::Inner1::S oi1s;
57+
Outer::Inner1::Inner2::S oi1i2s;
58+
Outer::Inner2::S oi2s;
59+
A a1;
60+
Outer::A a2;
61+
return sizeof(s) + sizeof(os) + sizeof(oi1s) + sizeof(oi1i2s) + sizeof(oi2s) + sizeof(a1) + sizeof(a2);
62+
}
63+
64+
#--- commands.input
65+
66+
b main
67+
r
68+
69+
type lookup S
70+
type lookup ::S
71+
type lookup Outer::S
72+
type lookup Outer::Inner1::S
73+
type lookup Inner1::S
74+
type lookup Outer::Inner1::Inner2::S
75+
type lookup Inner2::S
76+
type lookup Outer::Inner2::S
77+
type lookup Outer::A
78+
type lookup A
79+
type lookup ::A
80+
expr sizeof(S)
81+
expr sizeof(A)
82+
83+
quit
84+
85+
# CHECK: (lldb) type lookup S
86+
# CHECK: struct S {
87+
# CHECK: struct S {
88+
# CHECK: struct S {
89+
# CHECK: struct S {
90+
# CHECK: struct S {
91+
# CHECK: }
92+
# CHECK-NEXT: (lldb) type lookup ::S
93+
# CHECK-NEXT: struct S {
94+
# CHECK-NEXT: char a[1];
95+
# CHECK-NEXT: }
96+
# CHECK-NEXT: (lldb) type lookup Outer::S
97+
# CHECK-NEXT: struct S {
98+
# CHECK-NEXT: char a[2];
99+
# CHECK-NEXT: }
100+
# CHECK-NEXT: (lldb) type lookup Outer::Inner1::S
101+
# CHECK-NEXT: struct S {
102+
# CHECK-NEXT: char a[3];
103+
# CHECK-NEXT: }
104+
# CHECK-NEXT: (lldb) type lookup Inner1::S
105+
# CHECK-NEXT: struct S {
106+
# CHECK-NEXT: char a[3];
107+
# CHECK-NEXT: }
108+
# CHECK-NEXT: (lldb) type lookup Outer::Inner1::Inner2::S
109+
# CHECK-NEXT: struct S {
110+
# CHECK-NEXT: char a[4];
111+
# CHECK-NEXT: }
112+
# CHECK-NEXT: (lldb) type lookup Inner2::S
113+
# CHECK-NEXT: struct S {
114+
# CHECK: struct S {
115+
# CHECK: }
116+
# CHECK-NEXT: (lldb) type lookup Outer::Inner2::S
117+
# CHECK-NEXT: struct S {
118+
# CHECK-NEXT: char a[5];
119+
# CHECK-NEXT: }
120+
# CHECK-NEXT: (lldb) type lookup Outer::A
121+
# CHECK-NEXT: struct A {
122+
# CHECK-NEXT: char a[6];
123+
# CHECK-NEXT: }
124+
# CHECK-NEXT: (lldb) type lookup A
125+
# CHECK-NEXT: struct A {
126+
# CHECK: struct A {
127+
# CHECK: }
128+
# CHECK-NEXT: (lldb) type lookup ::A
129+
# CHECK-NEXT: struct A {
130+
# CHECK-NEXT: char a[7];
131+
# CHECK-NEXT: }
132+
# CHECK-NEXT: (lldb) expr sizeof(S)
133+
# CHECK-NEXT: (__size_t) $0 = 1
134+
# CHECK-NEXT: (lldb) expr sizeof(A)
135+
# CHECK-NEXT: (__size_t) $1 = 7

0 commit comments

Comments
 (0)