Author: nerix
Date: 2025-08-11T20:51:37-05:00
New Revision: 7e2cc725dbe00a9c99b0d58df020ab970e53dd51

URL: 
https://github.com/llvm/llvm-project/commit/7e2cc725dbe00a9c99b0d58df020ab970e53dd51
DIFF: 
https://github.com/llvm/llvm-project/commit/7e2cc725dbe00a9c99b0d58df020ab970e53dd51.diff

LOG: [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 <jo...@devlieghere.com>

Added: 
    

Modified: 
    lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
    lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
    lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp 
b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index dcea33dd9f854..986d647b4de2d 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -39,6 +39,7 @@
 #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h"
 #include "llvm/DebugInfo/PDB/Native/NativeSession.h"
 #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PublicsStream.h"
 #include "llvm/DebugInfo/PDB/Native/SymbolStream.h"
 #include "llvm/DebugInfo/PDB/Native/TpiStream.h"
 #include "llvm/DebugInfo/PDB/PDB.h"
@@ -1641,6 +1642,94 @@ void SymbolFileNativePDB::DumpClangAST(Stream &s, 
llvm::StringRef filter) {
   clang->GetNativePDBParser()->Dump(s, filter);
 }
 
+void SymbolFileNativePDB::CacheFunctionNames() {
+  if (!m_func_full_names.IsEmpty())
+    return;
+
+  // (segment, code offset) -> gid
+  std::map<std::pair<uint16_t, uint32_t>, uint32_t> addr_ids;
+
+  // First, find all function references in the globals table.
+  for (const uint32_t gid : m_index->globals().getGlobalsTable()) {
+    CVSymbol ref_sym = m_index->symrecords().readRecord(gid);
+    auto kind = ref_sym.kind();
+    if (kind != S_PROCREF && kind != S_LPROCREF)
+      continue;
+
+    ProcRefSym ref =
+        llvm::cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(ref_sym));
+    if (ref.Name.empty())
+      continue;
+
+    // Find the function this is referencing.
+    CompilandIndexItem &cci =
+        m_index->compilands().GetOrCreateCompiland(ref.modi());
+    auto iter = cci.m_debug_stream.getSymbolArray().at(ref.SymOffset);
+    if (iter == cci.m_debug_stream.getSymbolArray().end())
+      continue;
+    kind = iter->kind();
+    if (kind != S_GPROC32 && kind != S_LPROC32)
+      continue;
+
+    ProcSym proc =
+        llvm::cantFail(SymbolDeserializer::deserializeAs<ProcSym>(*iter));
+    if ((proc.Flags & ProcSymFlags::IsUnreachable) != ProcSymFlags::None)
+      continue;
+    if (proc.Name.empty())
+      continue;
+
+    // The function/procedure symbol only contains the demangled name.
+    // The mangled names are in the publics table. Save the address of this
+    // function to lookup the mangled name later.
+    addr_ids.emplace(std::make_pair(proc.Segment, proc.CodeOffset), gid);
+
+    llvm::StringRef basename = MSVCUndecoratedNameParser::DropScope(proc.Name);
+    if (basename.empty())
+      basename = proc.Name;
+
+    m_func_base_names.Append(ConstString(basename), gid);
+    m_func_full_names.Append(ConstString(proc.Name), gid);
+
+    // To see if this is a member function, check the type.
+    auto type = m_index->tpi().getType(proc.FunctionType);
+    if (type.kind() == LF_MFUNCTION) {
+      MemberFunctionRecord mfr;
+      llvm::cantFail(
+          TypeDeserializer::deserializeAs<MemberFunctionRecord>(type, mfr));
+      if (!mfr.getThisType().isNoneType())
+        m_func_method_names.Append(ConstString(basename), gid);
+    }
+  }
+
+  // The publics stream contains all mangled function names and their address.
+  for (auto pid : m_index->publics().getPublicsTable()) {
+    PdbGlobalSymId global{pid, true};
+    CVSymbol sym = m_index->ReadSymbolRecord(global);
+    auto kind = sym.kind();
+    if (kind != S_PUB32)
+      continue;
+    PublicSym32 pub =
+        llvm::cantFail(SymbolDeserializer::deserializeAs<PublicSym32>(sym));
+    // We only care about mangled names - if the name isn't mangled, it's
+    // already in the full name map.
+    if (!Mangled::IsMangledName(pub.Name))
+      continue;
+
+    // Check if this symbol is for one of our functions.
+    auto it = addr_ids.find({pub.Segment, pub.Offset});
+    if (it != addr_ids.end())
+      m_func_full_names.Append(ConstString(pub.Name), it->second);
+  }
+
+  // Sort them before value searching is working properly.
+  m_func_full_names.Sort();
+  m_func_full_names.SizeToFit();
+  m_func_method_names.Sort();
+  m_func_method_names.SizeToFit();
+  m_func_base_names.Sort();
+  m_func_base_names.SizeToFit();
+}
+
 void SymbolFileNativePDB::FindGlobalVariables(
     ConstString name, const CompilerDeclContext &parent_decl_ctx,
     uint32_t max_matches, VariableList &variables) {
@@ -1677,34 +1766,60 @@ void SymbolFileNativePDB::FindFunctions(
   if (name_type_mask & eFunctionNameTypeFull)
     name = lookup_info.GetName();
 
-  // For now we only support lookup by method name or full name.
   if (!(name_type_mask & eFunctionNameTypeFull ||
+        name_type_mask & eFunctionNameTypeBase ||
         name_type_mask & eFunctionNameTypeMethod))
     return;
+  CacheFunctionNames();
 
-  using SymbolAndOffset = std::pair<uint32_t, llvm::codeview::CVSymbol>;
+  std::set<uint32_t> resolved_ids; // avoid duplicate lookups
+  auto resolve_from = [&](UniqueCStringMap<uint32_t> &Names) {
+    std::vector<uint32_t> ids;
+    if (!Names.GetValues(name, ids))
+      return;
 
-  std::vector<SymbolAndOffset> matches = m_index->globals().findRecordsByName(
-      name.GetStringRef(), m_index->symrecords());
-  for (const SymbolAndOffset &match : matches) {
-    if (match.second.kind() != S_PROCREF && match.second.kind() != S_LPROCREF)
-      continue;
-    ProcRefSym proc(match.second.kind());
-    cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(match.second, 
proc));
+    for (uint32_t id : ids) {
+      if (!resolved_ids.insert(id).second)
+        continue;
 
-    if (!IsValidRecord(proc))
-      continue;
+      PdbGlobalSymId global{id, false};
+      if (parent_decl_ctx.IsValid() &&
+          GetDeclContextContainingUID(toOpaqueUid(global)) != parent_decl_ctx)
+        continue;
 
-    CompilandIndexItem &cci =
-        m_index->compilands().GetOrCreateCompiland(proc.modi());
-    SymbolContext sc;
+      CVSymbol sym = m_index->ReadSymbolRecord(global);
+      auto kind = sym.kind();
+      lldbassert(kind == S_PROCREF || kind == S_LPROCREF);
 
-    sc.comp_unit = GetOrCreateCompileUnit(cci).get();
-    PdbCompilandSymId func_id(proc.modi(), proc.SymOffset);
-    sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get();
+      ProcRefSym proc =
+          cantFail(SymbolDeserializer::deserializeAs<ProcRefSym>(sym));
 
-    sc_list.Append(sc);
-  }
+      if (!IsValidRecord(proc))
+        continue;
+
+      CompilandIndexItem &cci =
+          m_index->compilands().GetOrCreateCompiland(proc.modi());
+      SymbolContext sc;
+
+      sc.comp_unit = GetOrCreateCompileUnit(cci).get();
+      if (!sc.comp_unit)
+        continue;
+
+      PdbCompilandSymId func_id(proc.modi(), proc.SymOffset);
+      sc.function = GetOrCreateFunction(func_id, *sc.comp_unit).get();
+      if (!sc.function)
+        continue;
+
+      sc_list.Append(sc);
+    }
+  };
+
+  if (name_type_mask & eFunctionNameTypeFull)
+    resolve_from(m_func_full_names);
+  if (name_type_mask & eFunctionNameTypeBase)
+    resolve_from(m_func_base_names);
+  if (name_type_mask & eFunctionNameTypeMethod)
+    resolve_from(m_func_method_names);
 }
 
 void SymbolFileNativePDB::FindFunctions(const RegularExpression &regex,

diff  --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h 
b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
index eda375d4cebe7..6bbeb8bb14428 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h
@@ -260,6 +260,8 @@ class SymbolFileNativePDB : public SymbolFileCommon {
 
   std::vector<CompilerContext> GetContextForType(llvm::codeview::TypeIndex ti);
 
+  void CacheFunctionNames();
+
   llvm::BumpPtrAllocator m_allocator;
 
   lldb::addr_t m_obj_load_address = 0;
@@ -282,6 +284,13 @@ class SymbolFileNativePDB : public SymbolFileCommon {
       m_parent_types;
 
   lldb_private::UniqueCStringMap<uint32_t> m_type_base_names;
+
+  /// mangled name/full function name -> Global ID(s)
+  lldb_private::UniqueCStringMap<uint32_t> m_func_full_names;
+  /// basename -> Global ID(s)
+  lldb_private::UniqueCStringMap<uint32_t> m_func_base_names;
+  /// method basename -> Global ID(s)
+  lldb_private::UniqueCStringMap<uint32_t> m_func_method_names;
 };
 
 } // namespace npdb

diff  --git a/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp 
b/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp
index 5ebef61bdbfef..37a0b10d445f3 100644
--- a/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp
+++ b/lldb/test/Shell/SymbolFile/NativePDB/find-functions.cpp
@@ -6,24 +6,52 @@
 
 // RUN: lldb-test symbols --find=function --name=main --function-flags=full 
%t.exe \
 // RUN:     | FileCheck %s --check-prefix=FIND-MAIN
+// RUN: lldb-test symbols --find=function --name=main --function-flags=method 
%t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-NO-FUNCTION
+// RUN: lldb-test symbols --find=function --name=main --function-flags=base 
%t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-MAIN
 
 // RUN: lldb-test symbols --find=function --name=static_fn 
--function-flags=full %t.exe \
 // RUN:     | FileCheck %s --check-prefix=FIND-STATIC
+// RUN: lldb-test symbols --find=function --name=static_fn 
--function-flags=method %t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-NO-FUNCTION
+// RUN: lldb-test symbols --find=function --name=static_fn 
--function-flags=base %t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-STATIC
 
 // RUN: lldb-test symbols --find=function --name=varargs_fn 
--function-flags=full %t.exe \
 // RUN:     | FileCheck %s --check-prefix=FIND-VAR
+// RUN: lldb-test symbols --find=function --name=varargs_fn 
--function-flags=method %t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-NO-FUNCTION
+// RUN: lldb-test symbols --find=function --name=varargs_fn 
--function-flags=base %t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-VAR
 
 // RUN: lldb-test symbols --find=function --name=Struct::simple_method 
--function-flags=full %t.exe \
 // RUN:     | FileCheck %s --check-prefix=FIND-SIMPLE
+// RUN: lldb-test symbols --find=function --name=Struct::simple_method 
--function-flags=method %t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-SIMPLE
+// RUN: lldb-test symbols --find=function --name=Struct::simple_method 
--function-flags=base %t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-SIMPLE
 
 // RUN: lldb-test symbols --find=function --name=Struct::virtual_method 
--function-flags=full %t.exe \
 // RUN:     | FileCheck %s --check-prefix=FIND-VIRTUAL
+// RUN: lldb-test symbols --find=function --name=Struct::virtual_method 
--function-flags=method %t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-VIRTUAL
+// RUN: lldb-test symbols --find=function --name=Struct::virtual_method 
--function-flags=base %t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-VIRTUAL
 
 // RUN: lldb-test symbols --find=function --name=Struct::static_method 
--function-flags=full %t.exe \
 // RUN:     | FileCheck %s --check-prefix=FIND-STATIC-METHOD
+// RUN: lldb-test symbols --find=function --name=Struct::static_method 
--function-flags=method %t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-NO-FUNCTION
+// RUN: lldb-test symbols --find=function --name=Struct::static_method 
--function-flags=base %t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-STATIC-METHOD
 
 // RUN: lldb-test symbols --find=function --name=Struct::overloaded_method 
--function-flags=full %t.exe \
-// RUN:     | FileCheck %s --check-prefix=FIND-OVERLOAD
+// RUN:     | FileCheck %s --check-prefix=FIND-OVERLOAD-FULL
+// RUN: lldb-test symbols --find=function --name=Struct::overloaded_method 
--function-flags=method %t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-OVERLOAD-METHOD
+// RUN: lldb-test symbols --find=function --name=Struct::overloaded_method 
--function-flags=base %t.exe \
+// RUN:     | FileCheck %s --check-prefix=FIND-OVERLOAD-BASE
 
 struct Struct {
   int simple_method() {
@@ -51,7 +79,28 @@ struct Struct {
   }
 };
 
+class Class {
+public:
+  bool overloaded_method() {
+    return false;
+  }
+  bool overloaded_method(int i) {
+    return i > 0;
+  }
+  static int overloaded_method(bool b) {
+    return b ? 1 : 2;
+  }
+};
+
+char overloaded_method() {
+  return 0;
+}
+char overloaded_method(int i) {
+  return 0;
+}
+
 Struct s;
+Class c;
 
 static int static_fn() {
   return 42;
@@ -63,12 +112,16 @@ int varargs_fn(int x, int y, ...) {
 
 int main(int argc, char **argv) {
   return static_fn() + varargs_fn(argc, argc) + s.simple_method() +
-  Struct::static_method() + s.virtual_method() + s.overloaded_method();
+  Struct::static_method() + s.virtual_method() + s.overloaded_method() + 
+  Class::overloaded_method(false) + c.overloaded_method(1) + 
c.overloaded_method() 
+  + overloaded_method() + overloaded_method(1);
 }
 
 // FIND-MAIN:      Function: id = {{.*}}, name = "main"
 // FIND-MAIN-NEXT: FuncType: id = {{.*}}, compiler_type = "int (int, char **)"
 
+// FIND-NO-FUNCTION: Found 0 functions
+
 // FIND-STATIC:      Function: id = {{.*}}, name = "{{.*}}static_fn{{.*}}"
 // FIND-STATIC-NEXT: FuncType: id = {{.*}}, compiler_type = "int (void)"
 
@@ -84,7 +137,29 @@ int main(int argc, char **argv) {
 // FIND-STATIC-METHOD:      Function: id = {{.*}}, name = 
"{{.*}}Struct::static_method{{.*}}"
 // FIND-STATIC-METHOD-NEXT: FuncType: id = {{.*}}, compiler_type = "int (void)"
 
-// FIND-OVERLOAD: Function: id = {{.*}}, name = 
"{{.*}}Struct::overloaded_method{{.*}}"
-// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (void)"
-// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (char)"
-// FIND-OVERLOAD: FuncType: id = {{.*}}, compiler_type = "int (char, int, ...)"
+// FIND-OVERLOAD-FULL-NOT: "Class::overloaded_method"
+// FIND-OVERLOAD-FULL-NOT: "overloaded_method"
+// FIND-OVERLOAD-FULL: Function: id = {{.*}}, name = 
"{{.*}}Struct::overloaded_method{{.*}}"
+// FIND-OVERLOAD-FULL: FuncType: id = {{.*}}, compiler_type = "int (void)"
+// FIND-OVERLOAD-FULL: FuncType: id = {{.*}}, compiler_type = "int (char)"
+// FIND-OVERLOAD-FULL: FuncType: id = {{.*}}, compiler_type = "int (char, int, 
...)"
+
+// FIND-OVERLOAD-BASE-DAG: Function: id = {{.*}}, name = 
"{{.*}}Struct::overloaded_method{{.*}}"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "int (void)"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "int (char)"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "int (char, 
int, ...)"
+// FIND-OVERLOAD-BASE-DAG: Function: id = {{.*}}, name = 
"Class::overloaded_method"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "_Bool 
(void)"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "_Bool (int)"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "int (_Bool)"
+// FIND-OVERLOAD-BASE-DAG: Function: id = {{.*}}, name = "overloaded_method"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "char (void)"
+// FIND-OVERLOAD-BASE-DAG: FuncType: id = {{.*}}, compiler_type = "char (int)"
+
+// FIND-OVERLOAD-METHOD-NOT: "overloaded_method"
+// FIND-OVERLOAD-METHOD-DAG: Function: id = {{.*}}, name = 
"{{.*}}Struct::overloaded_method{{.*}}"
+// FIND-OVERLOAD-METHOD-DAG: FuncType: id = {{.*}}, compiler_type = "int 
(void)"
+// FIND-OVERLOAD-METHOD-DAG: FuncType: id = {{.*}}, compiler_type = "int 
(char)"
+// FIND-OVERLOAD-METHOD-DAG: Function: id = {{.*}}, name = 
"Class::overloaded_method"
+// FIND-OVERLOAD-METHOD-DAG: FuncType: id = {{.*}}, compiler_type = "_Bool 
(void)"
+// FIND-OVERLOAD-METHOD-DAG: FuncType: id = {{.*}}, compiler_type = "_Bool 
(int)"


        
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to