-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[lldb] Add SBFunction::GetBaseName() & SBSymbol::GetBaseName() #155939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-lldb Author: Jonas Devlieghere (JDevlieghere) ChangesWhen 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 Full diff: https://github.com/llvm/llvm-project/pull/155939.diff 9 Files Affected:
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
|
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
f3247a0
to
87e6eee
Compare
✅ With the latest revision this PR passed the Python code formatter. |
2256e55
to
aff73cd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
const char *name_str = demangled_name.AsCString(); | ||
const auto &range = demangled_info->BasenameRange; | ||
return ConstString( | ||
llvm::StringRef(name_str + range.first, range.second - range.first)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const char *name_str = demangled_name.AsCString(); | |
const auto &range = demangled_info->BasenameRange; | |
return ConstString( | |
llvm::StringRef(name_str + range.first, range.second - range.first)); | |
llvm::StringRef name_str = demangled_name.GetStringRef(); | |
const auto &range = demangled_info->BasenameRange; | |
return ConstString(name_str.splice(range.first, range.second)); |
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