Skip to content

Commit 7e2cc72

Browse files
[LLDB][NativePDB] Find functions by basename (#152295)
This adds the ability for functions to be matched by their basename. Before, the globals were searched for the name. This works if the full name is available but fails for basenames. PDB only includes the full names of functions, so we need to cache all basenames. This is (again) very similar to [SymbolFilePDB](https://github.com/llvm/llvm-project/blob/b242150b075a8a720b00821682a9469258bbcd30/lldb/source/Plugins/SymbolFile/PDB/SymbolFilePDB.cpp#L1291-L1383). There are two main differences: - We can't just access the parent of a function to determine that it's a member function - we need to check the type of the function, and its "this type". - SymbolFilePDB saved the full method name in the map. However, when searching for methods, only the basename is passed, so the function never found any methods. Fixes #51933. --------- Co-authored-by: Jonas Devlieghere <[email protected]>
1 parent 713ec58 commit 7e2cc72

File tree

3 files changed

+224
-25
lines changed

3 files changed

+224
-25
lines changed

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

Lines changed: 134 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
4040
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
4141
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
42+
#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
4243
#include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
4344
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
4445
#include "llvm/DebugInfo/PDB/PDB.h"
@@ -1641,6 +1642,94 @@ void SymbolFileNativePDB::DumpClangAST(Stream &s, llvm::StringRef filter) {
16411642
clang->GetNativePDBParser()->Dump(s, filter);
16421643
}
16431644

1645+
void SymbolFileNativePDB::CacheFunctionNames() {
1646+
if (!m_func_full_names.IsEmpty())
1647+
return;
1648+
1649+
// (segment, code offset) -> gid
1650+
std::map<std::pair<uint16_t, uint32_t>, uint32_t> addr_ids;
1651+
1652+
// First, find all function references in the globals table.
1653+
for (const uint32_t gid : m_index->globals().getGlobalsTable()) {
1654+
CVSymbol ref_sym = m_index->symrecords().readRecord(gid);
1655+
auto kind = ref_sym.kind();
1656+
if (kind != S_PROCREF && kind != S_LPROCREF)
1657+
continue;
1658+
1659+
ProcRefSym ref =
1660+
llvm::cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(ref_sym));
1661+
if (ref.Name.empty())
1662+
continue;
1663+
1664+
// Find the function this is referencing.
1665+
CompilandIndexItem &cci =
1666+
m_index->compilands().GetOrCreateCompiland(ref.modi());
1667+
auto iter = cci.m_debug_stream.getSymbolArray().at(ref.SymOffset);
1668+
if (iter == cci.m_debug_stream.getSymbolArray().end())
1669+
continue;
1670+
kind = iter->kind();
1671+
if (kind != S_GPROC32 && kind != S_LPROC32)
1672+
continue;
1673+
1674+
ProcSym proc =
1675+
llvm::cantFail(SymbolDeserializer::deserializeAs<ProcSym>(*iter));
1676+
if ((proc.Flags & ProcSymFlags::IsUnreachable) != ProcSymFlags::None)
1677+
continue;
1678+
if (proc.Name.empty())
1679+
continue;
1680+
1681+
// The function/procedure symbol only contains the demangled name.
1682+
// The mangled names are in the publics table. Save the address of this
1683+
// function to lookup the mangled name later.
1684+
addr_ids.emplace(std::make_pair(proc.Segment, proc.CodeOffset), gid);
1685+
1686+
llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(proc.Name);
1687+
if (basename.empty())
1688+
basename = proc.Name;
1689+
1690+
m_func_base_names.Append(ConstString(basename), gid);
1691+
m_func_full_names.Append(ConstString(proc.Name), gid);
1692+
1693+
// To see if this is a member function, check the type.
1694+
auto type = m_index->tpi().getType(proc.FunctionType);
1695+
if (type.kind() == LF_MFUNCTION) {
1696+
MemberFunctionRecord mfr;
1697+
llvm::cantFail(
1698+
TypeDeserializer::deserializeAs<MemberFunctionRecord>(type, mfr));
1699+
if (!mfr.getThisType().isNoneType())
1700+
m_func_method_names.Append(ConstString(basename), gid);
1701+
}
1702+
}
1703+
1704+
// The publics stream contains all mangled function names and their address.
1705+
for (auto pid : m_index->publics().getPublicsTable()) {
1706+
PdbGlobalSymId global{pid, true};
1707+
CVSymbol sym = m_index->ReadSymbolRecord(global);
1708+
auto kind = sym.kind();
1709+
if (kind != S_PUB32)
1710+
continue;
1711+
PublicSym32 pub =
1712+
llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(sym));
1713+
// We only care about mangled names - if the name isn't mangled, it's
1714+
// already in the full name map.
1715+
if (!Mangled::IsMangledName(pub.Name))
1716+
continue;
1717+
1718+
// Check if this symbol is for one of our functions.
1719+
auto it = addr_ids.find({pub.Segment, pub.Offset});
1720+
if (it != addr_ids.end())
1721+
m_func_full_names.Append(ConstString(pub.Name), it->second);
1722+
}
1723+
1724+
// Sort them before value searching is working properly.
1725+
m_func_full_names.Sort();
1726+
m_func_full_names.SizeToFit();
1727+
m_func_method_names.Sort();
1728+
m_func_method_names.SizeToFit();
1729+
m_func_base_names.Sort();
1730+
m_func_base_names.SizeToFit();
1731+
}
1732+
16441733
void SymbolFileNativePDB::FindGlobalVariables(
16451734
ConstString name, const CompilerDeclContext &parent_decl_ctx,
16461735
uint32_t max_matches, VariableList &variables) {
@@ -1677,34 +1766,60 @@ void SymbolFileNativePDB::FindFunctions(
16771766
if (name_type_mask & eFunctionNameTypeFull)
16781767
name = lookup_info.GetName();
16791768

1680-
// For now we only support lookup by method name or full name.
16811769
if (!(name_type_mask & eFunctionNameTypeFull ||
1770+
name_type_mask & eFunctionNameTypeBase ||
16821771
name_type_mask & eFunctionNameTypeMethod))
16831772
return;
1773+
CacheFunctionNames();
16841774

1685-
using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>;
1775+
std::set<uint32_t> resolved_ids; // avoid duplicate lookups
1776+
auto resolve_from = [&](UniqueCStringMap<uint32_t> &Names) {
1777+
std::vector<uint32_t> ids;
1778+
if (!Names.GetValues(name, ids))
1779+
return;
16861780

1687-
std::vector<SymbolAndOffset> matches = m_index->globals().findRecordsByName(
1688-
name.GetStringRef(), m_index->symrecords());
1689-
for (const SymbolAndOffset &match : matches) {
1690-
if (match.second.kind() != S_PROCREF && match.second.kind() != S_LPROCREF)
1691-
continue;
1692-
ProcRefSym proc(match.second.kind());
1693-
cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(match.second, proc));
1781+
for (uint32_t id : ids) {
1782+
if (!resolved_ids.insert(id).second)
1783+
continue;
16941784

1695-
if (!IsValidRecord(proc))
1696-
continue;
1785+
PdbGlobalSymId global{id, false};
1786+
if (parent_decl_ctx.IsValid() &&
1787+
GetDeclContextContainingUID(toOpaqueUid(global)) != parent_decl_ctx)
1788+
continue;
16971789

1698-
CompilandIndexItem &cci =
1699-
m_index->compilands().GetOrCreateCompiland(proc.modi());
1700-
SymbolContext sc;
1790+
CVSymbol sym = m_index->ReadSymbolRecord(global);
1791+
auto kind = sym.kind();
1792+
lldbassert(kind == S_PROCREF || kind == S_LPROCREF);
17011793

1702-
sc.comp_unit = GetOrCreateCompileUnit(cci).get();
1703-
PdbCompilandSymId func_id(proc.modi(), proc.SymOffset);
1704-
sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get();
1794+
ProcRefSym proc =
1795+
cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(sym));
17051796

1706-
sc_list.Append(sc);
1707-
}
1797+
if (!IsValidRecord(proc))
1798+
continue;
1799+
1800+
CompilandIndexItem &cci =
1801+
m_index->compilands().GetOrCreateCompiland(proc.modi());
1802+
SymbolContext sc;
1803+
1804+
sc.comp_unit = GetOrCreateCompileUnit(cci).get();
1805+
if (!sc.comp_unit)
1806+
continue;
1807+
1808+
PdbCompilandSymId func_id(proc.modi(), proc.SymOffset);
1809+
sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get();
1810+
if (!sc.function)
1811+
continue;
1812+
1813+
sc_list.Append(sc);
1814+
}
1815+
};
1816+
1817+
if (name_type_mask & eFunctionNameTypeFull)
1818+
resolve_from(m_func_full_names);
1819+
if (name_type_mask & eFunctionNameTypeBase)
1820+
resolve_from(m_func_base_names);
1821+
if (name_type_mask & eFunctionNameTypeMethod)
1822+
resolve_from(m_func_method_names);
17081823
}
17091824

17101825
void SymbolFileNativePDB::FindFunctions(const RegularExpression &regex,

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,8 @@ class SymbolFileNativePDB : public SymbolFileCommon {
260260

261261
std::vector<CompilerContext> GetContextForType(llvm::codeview::TypeIndex ti);
262262

263+
void CacheFunctionNames();
264+
263265
llvm::BumpPtrAllocator m_allocator;
264266

265267
lldb::addr_t m_obj_load_address = 0;
@@ -282,6 +284,13 @@ class SymbolFileNativePDB : public SymbolFileCommon {
282284
m_parent_types;
283285

284286
lldb_private::UniqueCStringMap<uint32_t> m_type_base_names;
287+
288+
/// mangled name/full function name -> Global ID(s)
289+
lldb_private::UniqueCStringMap<uint32_t> m_func_full_names;
290+
/// basename -> Global ID(s)
291+
lldb_private::UniqueCStringMap<uint32_t> m_func_base_names;
292+
/// method basename -> Global ID(s)
293+
lldb_private::UniqueCStringMap<uint32_t> m_func_method_names;
285294
};
286295

287296
} // namespace npdb

lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,52 @@
66

77
// RUN: lldb-test symbols --find=function --name=main --function-flags=full %t.exe \
88
// RUN: | FileCheck %s --check-prefix=FIND-MAIN
9+
// RUN: lldb-test symbols --find=function --name=main --function-flags=method %t.exe \
10+
// RUN: | FileCheck %s --check-prefix=FIND-NO-FUNCTION
11+
// RUN: lldb-test symbols --find=function --name=main --function-flags=base %t.exe \
12+
// RUN: | FileCheck %s --check-prefix=FIND-MAIN
913

1014
// RUN: lldb-test symbols --find=function --name=static_fn --function-flags=full %t.exe \
1115
// RUN: | FileCheck %s --check-prefix=FIND-STATIC
16+
// RUN: lldb-test symbols --find=function --name=static_fn --function-flags=method %t.exe \
17+
// RUN: | FileCheck %s --check-prefix=FIND-NO-FUNCTION
18+
// RUN: lldb-test symbols --find=function --name=static_fn --function-flags=base %t.exe \
19+
// RUN: | FileCheck %s --check-prefix=FIND-STATIC
1220

1321
// RUN: lldb-test symbols --find=function --name=varargs_fn --function-flags=full %t.exe \
1422
// RUN: | FileCheck %s --check-prefix=FIND-VAR
23+
// RUN: lldb-test symbols --find=function --name=varargs_fn --function-flags=method %t.exe \
24+
// RUN: | FileCheck %s --check-prefix=FIND-NO-FUNCTION
25+
// RUN: lldb-test symbols --find=function --name=varargs_fn --function-flags=base %t.exe \
26+
// RUN: | FileCheck %s --check-prefix=FIND-VAR
1527

1628
// RUN: lldb-test symbols --find=function --name=Struct::simple_method --function-flags=full %t.exe \
1729
// RUN: | FileCheck %s --check-prefix=FIND-SIMPLE
30+
// RUN: lldb-test symbols --find=function --name=Struct::simple_method --function-flags=method %t.exe \
31+
// RUN: | FileCheck %s --check-prefix=FIND-SIMPLE
32+
// RUN: lldb-test symbols --find=function --name=Struct::simple_method --function-flags=base %t.exe \
33+
// RUN: | FileCheck %s --check-prefix=FIND-SIMPLE
1834

1935
// RUN: lldb-test symbols --find=function --name=Struct::virtual_method --function-flags=full %t.exe \
2036
// RUN: | FileCheck %s --check-prefix=FIND-VIRTUAL
37+
// RUN: lldb-test symbols --find=function --name=Struct::virtual_method --function-flags=method %t.exe \
38+
// RUN: | FileCheck %s --check-prefix=FIND-VIRTUAL
39+
// RUN: lldb-test symbols --find=function --name=Struct::virtual_method --function-flags=base %t.exe \
40+
// RUN: | FileCheck %s --check-prefix=FIND-VIRTUAL
2141

2242
// RUN: lldb-test symbols --find=function --name=Struct::static_method --function-flags=full %t.exe \
2343
// RUN: | FileCheck %s --check-prefix=FIND-STATIC-METHOD
44+
// RUN: lldb-test symbols --find=function --name=Struct::static_method --function-flags=method %t.exe \
45+
// RUN: | FileCheck %s --check-prefix=FIND-NO-FUNCTION
46+
// RUN: lldb-test symbols --find=function --name=Struct::static_method --function-flags=base %t.exe \
47+
// RUN: | FileCheck %s --check-prefix=FIND-STATIC-METHOD
2448

2549
// RUN: lldb-test symbols --find=function --name=Struct::overloaded_method --function-flags=full %t.exe \
26-
// RUN: | FileCheck %s --check-prefix=FIND-OVERLOAD
50+
// RUN: | FileCheck %s --check-prefix=FIND-OVERLOAD-FULL
51+
// RUN: lldb-test symbols --find=function --name=Struct::overloaded_method --function-flags=method %t.exe \
52+
// RUN: | FileCheck %s --check-prefix=FIND-OVERLOAD-METHOD
53+
// RUN: lldb-test symbols --find=function --name=Struct::overloaded_method --function-flags=base %t.exe \
54+
// RUN: | FileCheck %s --check-prefix=FIND-OVERLOAD-BASE
2755

2856
struct Struct {
2957
int simple_method() {
@@ -51,7 +79,28 @@ struct Struct {
5179
}
5280
};
5381

82+
class Class {
83+
public:
84+
bool overloaded_method() {
85+
return false;
86+
}
87+
bool overloaded_method(int i) {
88+
return i > 0;
89+
}
90+
static int overloaded_method(bool b) {
91+
return b ? 1 : 2;
92+
}
93+
};
94+
95+
char overloaded_method() {
96+
return 0;
97+
}
98+
char overloaded_method(int i) {
99+
return 0;
100+
}
101+
54102
Struct s;
103+
Class c;
55104

56105
static int static_fn() {
57106
return 42;
@@ -63,12 +112,16 @@ int varargs_fn(int x, int y, ...) {
63112

64113
int main(int argc, char **argv) {
65114
return static_fn() + varargs_fn(argc, argc) + s.simple_method() +
66-
Struct::static_method() + s.virtual_method() + s.overloaded_method();
115+
Struct::static_method() + s.virtual_method() + s.overloaded_method() +
116+
Class::overloaded_method(false) + c.overloaded_method(1) + c.overloaded_method()
117+
+ overloaded_method() + overloaded_method(1);
67118
}
68119

69120
// FIND-MAIN: Function: id = {{.*}}, name = "main"
70121
// FIND-MAIN-NEXT: FuncType: id = {{.*}}, compiler_type = "int (int, char **)"
71122

123+
// FIND-NO-FUNCTION: Found 0 functions
124+
72125
// FIND-STATIC: Function: id = {{.*}}, name = "{{.*}}static_fn{{.*}}"
73126
// FIND-STATIC-NEXT: FuncType: id = {{.*}}, compiler_type = "int (void)"
74127

@@ -84,7 +137,29 @@ int main(int argc, char **argv) {
84137
// FIND-STATIC-METHOD: Function: id = {{.*}}, name = "{{.*}}Struct::static_method{{.*}}"
85138
// FIND-STATIC-METHOD-NEXT: FuncType: id = {{.*}}, compiler_type = "int (void)"
86139

87-
// FIND-OVERLOAD: Function: id = {{.*}}, name = "{{.*}}Struct::overloaded_method{{.*}}"
88-
// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (void)"
89-
// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (char)"
90-
// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (char, int, ...)"
140+
// FIND-OVERLOAD-FULL-NOT: "Class::overloaded_method"
141+
// FIND-OVERLOAD-FULL-NOT: "overloaded_method"
142+
// FIND-OVERLOAD-FULL: Function: id = {{.*}}, name = "{{.*}}Struct::overloaded_method{{.*}}"
143+
// FIND-OVERLOAD-FULL: FuncType: id = {{.*}}, compiler_type = "int (void)"
144+
// FIND-OVERLOAD-FULL: FuncType: id = {{.*}}, compiler_type = "int (char)"
145+
// FIND-OVERLOAD-FULL: FuncType: id = {{.*}}, compiler_type = "int (char, int, ...)"
146+
147+
// FIND-OVERLOAD-BASE-DAG: Function: id = {{.*}}, name = "{{.*}}Struct::overloaded_method{{.*}}"
148+
// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "int (void)"
149+
// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "int (char)"
150+
// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "int (char, int, ...)"
151+
// FIND-OVERLOAD-BASE-DAG: Function: id = {{.*}}, name = "Class::overloaded_method"
152+
// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "_Bool (void)"
153+
// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "_Bool (int)"
154+
// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "int (_Bool)"
155+
// FIND-OVERLOAD-BASE-DAG: Function: id = {{.*}}, name = "overloaded_method"
156+
// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "char (void)"
157+
// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "char (int)"
158+
159+
// FIND-OVERLOAD-METHOD-NOT: "overloaded_method"
160+
// FIND-OVERLOAD-METHOD-DAG: Function: id = {{.*}}, name = "{{.*}}Struct::overloaded_method{{.*}}"
161+
// FIND-OVERLOAD-METHOD-DAG: FuncType: id = {{.*}}, compiler_type = "int (void)"
162+
// FIND-OVERLOAD-METHOD-DAG: FuncType: id = {{.*}}, compiler_type = "int (char)"
163+
// FIND-OVERLOAD-METHOD-DAG: Function: id = {{.*}}, name = "Class::overloaded_method"
164+
// FIND-OVERLOAD-METHOD-DAG: FuncType: id = {{.*}}, compiler_type = "_Bool (void)"
165+
// FIND-OVERLOAD-METHOD-DAG: FuncType: id = {{.*}}, compiler_type = "_Bool (int)"

0 commit comments

Comments
 (0)