[Lldb-commits] [lldb] [lldb] Add SBFunction::GetBaseName() & SBSymbol::GetBaseName() (PR #155939)

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Thu Aug 28 15:51:13 PDT 2025


https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/155939

When you are trying for instance to set a breakpoint on a function by name, but the SBFunction or SBSymbol are returning demangled names with argument lists, that match can be tedious to do.  Internally, the base name of a symbol is something we handle all the time, so it's reasonable that there should be a way to get that info from the API as well.

rdar://159318791

>From f3247a0ee7b3b29a2238cf1d6a14d303654e194e Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Thu, 28 Aug 2025 15:50:29 -0700
Subject: [PATCH] [lldb] Add SBFunction::GetBaseName() &
 SBSymbol::GetBaseName()

When you are trying for instance to set a breakpoint on a function by
name, but the SBFunction or SBSymbol are returning demangled names with
argument lists, that match can be tedious to do.  Internally, the base
name of a symbol is something we handle all the time, so it's reasonable
that there should be a way to get that info from the API as well.

rdar://159318791
---
 lldb/include/lldb/API/SBFunction.h            |   2 +
 lldb/include/lldb/API/SBSymbol.h              |   2 +
 lldb/include/lldb/Core/Mangled.h              |  12 ++
 lldb/source/API/SBFunction.cpp                |   9 +
 lldb/source/API/SBSymbol.cpp                  |   9 +
 lldb/source/Core/Mangled.cpp                  |  18 ++
 lldb/test/API/python_api/basename/Makefile    |   3 +
 .../python_api/basename/TestGetBaseName.py    | 172 ++++++++++++++++++
 lldb/test/API/python_api/basename/main.cpp    |  17 ++
 9 files changed, 244 insertions(+)
 create mode 100644 lldb/test/API/python_api/basename/Makefile
 create mode 100644 lldb/test/API/python_api/basename/TestGetBaseName.py
 create mode 100644 lldb/test/API/python_api/basename/main.cpp

diff --git a/lldb/include/lldb/API/SBFunction.h b/lldb/include/lldb/API/SBFunction.h
index 0a8aeeff1ea5a..e703ae5dd63c1 100644
--- a/lldb/include/lldb/API/SBFunction.h
+++ b/lldb/include/lldb/API/SBFunction.h
@@ -36,6 +36,8 @@ class LLDB_API SBFunction {
 
   const char *GetMangledName() const;
 
+  const char *GetBaseName() const;
+
   lldb::SBInstructionList GetInstructions(lldb::SBTarget target);
 
   lldb::SBInstructionList GetInstructions(lldb::SBTarget target,
diff --git a/lldb/include/lldb/API/SBSymbol.h b/lldb/include/lldb/API/SBSymbol.h
index a93bc7a7ae074..580458ede212d 100644
--- a/lldb/include/lldb/API/SBSymbol.h
+++ b/lldb/include/lldb/API/SBSymbol.h
@@ -36,6 +36,8 @@ class LLDB_API SBSymbol {
 
   const char *GetMangledName() const;
 
+  const char *GetBaseName() const;
+
   lldb::SBInstructionList GetInstructions(lldb::SBTarget target);
 
   lldb::SBInstructionList GetInstructions(lldb::SBTarget target,
diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h
index eb9a58c568896..99a0d7c98543c 100644
--- a/lldb/include/lldb/Core/Mangled.h
+++ b/lldb/include/lldb/Core/Mangled.h
@@ -287,6 +287,18 @@ class Mangled {
   /// Retrieve \c DemangledNameInfo of the demangled name held by this object.
   const std::optional<DemangledNameInfo> &GetDemangledInfo() const;
 
+  /// Compute the base name (without namespace/class qualifiers) from the
+  /// demangled name.
+  ///
+  /// For a demangled name like "ns::MyClass<int>::templateFunc", this returns
+  /// just "templateFunc". If the demangled name is not available or the
+  /// basename range is invalid, this falls back to GetDisplayDemangledName().
+  ///
+  /// \return
+  ///     A ConstString containing the basename, or nullptr if computation
+  ///     fails.
+  ConstString GetBaseName() const;
+
 private:
   /// If \c force is \c false, this function will re-use the previously
   /// demangled name (if any). If \c force is \c true (or the mangled name
diff --git a/lldb/source/API/SBFunction.cpp b/lldb/source/API/SBFunction.cpp
index 19861f6af3645..65b02d6b309ca 100644
--- a/lldb/source/API/SBFunction.cpp
+++ b/lldb/source/API/SBFunction.cpp
@@ -79,6 +79,15 @@ const char *SBFunction::GetMangledName() const {
   return nullptr;
 }
 
+const char *SBFunction::GetBaseName() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  if (!m_opaque_ptr)
+    return nullptr;
+
+  return m_opaque_ptr->GetMangled().GetBaseName().AsCString();
+}
+
 bool SBFunction::operator==(const SBFunction &rhs) const {
   LLDB_INSTRUMENT_VA(this, rhs);
 
diff --git a/lldb/source/API/SBSymbol.cpp b/lldb/source/API/SBSymbol.cpp
index 3b59119494f37..3030c83292127 100644
--- a/lldb/source/API/SBSymbol.cpp
+++ b/lldb/source/API/SBSymbol.cpp
@@ -79,6 +79,15 @@ const char *SBSymbol::GetMangledName() const {
   return name;
 }
 
+const char *SBSymbol::GetBaseName() const {
+  LLDB_INSTRUMENT_VA(this);
+
+  if (!m_opaque_ptr)
+    return nullptr;
+
+  return m_opaque_ptr->GetMangled().GetBaseName().AsCString();
+}
+
 bool SBSymbol::operator==(const SBSymbol &rhs) const {
   LLDB_INSTRUMENT_VA(this, rhs);
 
diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp
index ce4db4e0daa8b..6e3c36f72155f 100644
--- a/lldb/source/Core/Mangled.cpp
+++ b/lldb/source/Core/Mangled.cpp
@@ -556,3 +556,21 @@ void Mangled::Encode(DataEncoder &file, ConstStringTable &strtab) const {
       break;
   }
 }
+
+ConstString Mangled::GetBaseName() const {
+  const auto &demangled_info = GetDemangledInfo();
+  if (!demangled_info.has_value())
+    return GetDisplayDemangledName();
+
+  ConstString demangled_name = GetDemangledName();
+  if (!demangled_name)
+    return GetDisplayDemangledName();
+
+  const char *name_str = demangled_name.AsCString();
+  const auto &range = demangled_info->BasenameRange;
+  if (range.first >= range.second || range.second > strlen(name_str))
+    return ConstString();
+
+  return ConstString(
+      llvm::StringRef(name_str + range.first, range.second - range.first));
+}
diff --git a/lldb/test/API/python_api/basename/Makefile b/lldb/test/API/python_api/basename/Makefile
new file mode 100644
index 0000000000000..2bb9ce046a907
--- /dev/null
+++ b/lldb/test/API/python_api/basename/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
\ No newline at end of file
diff --git a/lldb/test/API/python_api/basename/TestGetBaseName.py b/lldb/test/API/python_api/basename/TestGetBaseName.py
new file mode 100644
index 0000000000000..e546c99e98323
--- /dev/null
+++ b/lldb/test/API/python_api/basename/TestGetBaseName.py
@@ -0,0 +1,172 @@
+"""
+Test SBFunction::GetBaseName() and SBSymbol::GetBaseName() APIs.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class GetBaseNameTestCase(TestBase):
+    def setUp(self):
+        # Call super's setUp().
+        TestBase.setUp(self)
+        # Find the line number to break on.
+        self.line1 = line_number(
+            "main.cpp", "// Find the line number for breakpoint 1 here."
+        )
+
+    def test_function_basename(self):
+        """Test SBFunction.GetBaseName() API."""
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+
+        # Create a target by the debugger.
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        # Create a breakpoint inside the C++ namespaced function.
+        breakpoint1 = target.BreakpointCreateByLocation("main.cpp", self.line1)
+        self.trace("breakpoint1:", breakpoint1)
+        self.assertTrue(
+            breakpoint1 and breakpoint1.GetNumLocations() == 1, VALID_BREAKPOINT
+        )
+
+        # Now launch the process, and do not stop at entry point.
+        process = target.LaunchSimple(None, None, self.get_process_working_directory())
+        self.assertTrue(process, PROCESS_IS_VALID)
+
+        # Frame #0 should be on self.line1.
+        self.assertState(process.GetState(), lldb.eStateStopped)
+        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
+        self.assertTrue(
+            thread.IsValid(),
+            "There should be a thread stopped due to breakpoint condition",
+        )
+        frame0 = thread.GetFrameAtIndex(0)
+        function = frame0.GetFunction()
+
+        # Test the function name methods
+        full_name = function.GetName()
+        display_name = function.GetDisplayName()
+        basename = function.GetBaseName()
+
+        self.trace("Full name:", full_name)
+        self.trace("Display name:", display_name)
+        self.trace("Base name:", basename)
+
+        # For a C++ function like "ns::MyClass<int>::templateFunc",
+        # the basename should be just "templateFunc"
+        self.assertTrue(basename is not None, "GetBaseName() should not return None")
+        self.assertNotEqual(
+            basename, "", "GetBaseName() should not return empty string"
+        )
+
+        # The basename should not contain namespace qualifiers
+        self.assertNotIn(
+            "::", basename, "Basename should not contain namespace qualifiers"
+        )
+
+        # The basename should be shorter than or equal to the full name
+        if full_name:
+            self.assertLessEqual(
+                len(basename),
+                len(full_name),
+                "Basename should be shorter than or equal to full name",
+            )
+
+    def test_symbol_basename(self):
+        """Test SBSymbol.GetBaseName() API."""
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+
+        # Create a target by the debugger.
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        # Create a breakpoint inside the C++ namespaced function.
+        breakpoint1 = target.BreakpointCreateByLocation("main.cpp", self.line1)
+        self.trace("breakpoint1:", breakpoint1)
+        self.assertTrue(
+            breakpoint1 and breakpoint1.GetNumLocations() == 1, VALID_BREAKPOINT
+        )
+
+        # Now launch the process, and do not stop at entry point.
+        process = target.LaunchSimple(None, None, self.get_process_working_directory())
+        self.assertTrue(process, PROCESS_IS_VALID)
+
+        # Frame #0 should be on self.line1.
+        self.assertState(process.GetState(), lldb.eStateStopped)
+        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
+        self.assertTrue(
+            thread.IsValid(),
+            "There should be a thread stopped due to breakpoint condition",
+        )
+        frame0 = thread.GetFrameAtIndex(0)
+        symbol = frame0.GetSymbol()
+
+        # Test the symbol name methods
+        full_name = symbol.GetName()
+        display_name = symbol.GetDisplayName()
+        basename = symbol.GetBaseName()
+
+        self.trace("Symbol full name:", full_name)
+        self.trace("Symbol display name:", display_name)
+        self.trace("Symbol base name:", basename)
+
+        # For a C++ symbol like "ns::MyClass<int>::templateFunc",
+        # the basename should be just "templateFunc"
+        self.assertTrue(basename is not None, "GetBaseName() should not return None")
+        self.assertNotEqual(
+            basename, "", "GetBaseName() should not return empty string"
+        )
+
+        # The basename should not contain namespace qualifiers
+        self.assertNotIn(
+            "::", basename, "Basename should not contain namespace qualifiers"
+        )
+
+        # The basename should be shorter than or equal to the full name
+        if full_name:
+            self.assertLessEqual(
+                len(basename),
+                len(full_name),
+                "Basename should be shorter than or equal to full name",
+            )
+
+    def test_basename_consistency(self):
+        """Test that SBFunction.GetBaseName() and SBSymbol.GetBaseName() return consistent results."""
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+
+        # Create a target by the debugger.
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        # Create a breakpoint inside the C++ namespaced function.
+        breakpoint1 = target.BreakpointCreateByLocation("main.cpp", self.line1)
+
+        # Now launch the process, and do not stop at entry point.
+        process = target.LaunchSimple(None, None, self.get_process_working_directory())
+
+        # Get stopped thread and frame
+        thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
+        frame0 = thread.GetFrameAtIndex(0)
+
+        # Get both function and symbol
+        function = frame0.GetFunction()
+        symbol = frame0.GetSymbol()
+
+        # Test consistency between function and symbol basename
+        function_basename = function.GetBaseName()
+        symbol_basename = symbol.GetBaseName()
+
+        self.trace("Function basename:", function_basename)
+        self.trace("Symbol basename:", symbol_basename)
+
+        # Both should return valid strings
+        self.assertTrue(function_basename is not None)
+        self.assertTrue(symbol_basename is not None)
+        self.assertNotEqual(function_basename, "")
+        self.assertNotEqual(symbol_basename, "")
diff --git a/lldb/test/API/python_api/basename/main.cpp b/lldb/test/API/python_api/basename/main.cpp
new file mode 100644
index 0000000000000..17df50b4852e2
--- /dev/null
+++ b/lldb/test/API/python_api/basename/main.cpp
@@ -0,0 +1,17 @@
+#include <iostream>
+
+namespace ns {
+template <typename T> class MyClass {
+public:
+  void templateFunc() {
+    std::cout << "In templateFunc"
+              << std::endl; // Find the line number for breakpoint 1 here.
+  }
+};
+} // namespace ns
+
+int main() {
+  ns::MyClass<int> obj;
+  obj.templateFunc();
+  return 0;
+}
\ No newline at end of file



More information about the lldb-commits mailing list