[Lldb-commits] [lldb] d555b9f - [lldb][CPlusPlus] Add plugin.cplusplus.display.function-name-format setting (#131836)
Michael Buch via lldb-commits
lldb-commits at lists.llvm.org
Fri Apr 25 02:05:09 PDT 2025
Author: Michael Buch
Date: 2025-04-25T10:04:27+01:00
New Revision: d555b9f9a01705097edf2434cf897e351095e5c9
URL: https://github.com/llvm/llvm-project/commit/d555b9f9a01705097edf2434cf897e351095e5c9
DIFF: https://github.com/llvm/llvm-project/commit/d555b9f9a01705097edf2434cf897e351095e5c9.diff
LOG: [lldb][CPlusPlus] Add plugin.cplusplus.display.function-name-format setting (#131836)
Adds the new `plugin.cplusplus.display.function-name-format` setting and makes the `${function.name-with-args}` query it for formatting the function name.
One caveat is that the setting can't itself be set to `${function.name-with-args}` because that would cause infinite recursion and blow the stack. I added an XFAILed test-case for it and will address it in a follow-up patch.
https://github.com/llvm/llvm-project/pull/131836
Added:
lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td
lldb/test/Shell/Settings/TestCxxFrameFormat.test
lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test
lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test
lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test
lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test
Modified:
lldb/include/lldb/Core/PluginManager.h
lldb/include/lldb/Target/Language.h
lldb/source/Core/FormatEntity.cpp
lldb/source/Core/PluginManager.cpp
lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
Removed:
################################################################################
diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h
index a6dab045adf27..d73dd71d833f3 100644
--- a/lldb/include/lldb/Core/PluginManager.h
+++ b/lldb/include/lldb/Core/PluginManager.h
@@ -141,8 +141,10 @@ class PluginManager {
GetOperatingSystemCreateCallbackForPluginName(llvm::StringRef name);
// Language
- static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
- LanguageCreateInstance create_callback);
+ static bool
+ RegisterPlugin(llvm::StringRef name, llvm::StringRef description,
+ LanguageCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback = nullptr);
static bool UnregisterPlugin(LanguageCreateInstance create_callback);
@@ -613,6 +615,14 @@ class PluginManager {
static bool CreateSettingForStructuredDataPlugin(
Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
llvm::StringRef description, bool is_global_property);
+
+ static lldb::OptionValuePropertiesSP
+ GetSettingForCPlusPlusLanguagePlugin(Debugger &debugger,
+ llvm::StringRef setting_name);
+
+ static bool CreateSettingForCPlusPlusLanguagePlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ llvm::StringRef description, bool is_global_property);
};
} // namespace lldb_private
diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h
index 94ace6433df2f..d62871bd7ed70 100644
--- a/lldb/include/lldb/Target/Language.h
+++ b/lldb/include/lldb/Target/Language.h
@@ -495,6 +495,10 @@ class Language : public PluginInterface {
/// Python uses \b except. Defaults to \b catch.
virtual llvm::StringRef GetCatchKeyword() const { return "catch"; }
+ virtual const FormatEntity::Entry *GetFunctionNameFormat() const {
+ return nullptr;
+ }
+
protected:
// Classes that inherit from Language can see and modify these
diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp
index eafc8c932208c..e352d07fe487d 100644
--- a/lldb/source/Core/FormatEntity.cpp
+++ b/lldb/source/Core/FormatEntity.cpp
@@ -1237,6 +1237,35 @@ static bool HandleFunctionNameWithArgs(Stream &s,
return true;
}
+static bool FormatFunctionNameForLanguage(Stream &s,
+ const ExecutionContext *exe_ctx,
+ const SymbolContext *sc) {
+ assert(sc);
+
+ Language *language_plugin = nullptr;
+ if (sc->function)
+ language_plugin = Language::FindPlugin(sc->function->GetLanguage());
+ else if (sc->symbol)
+ language_plugin = Language::FindPlugin(sc->symbol->GetLanguage());
+
+ if (!language_plugin)
+ return false;
+
+ const auto *format = language_plugin->GetFunctionNameFormat();
+ if (!format)
+ return false;
+
+ StreamString name_stream;
+ const bool success =
+ FormatEntity::Format(*format, name_stream, sc, exe_ctx, /*addr=*/nullptr,
+ /*valobj=*/nullptr, /*function_changed=*/false,
+ /*initial_function=*/false);
+ if (success)
+ s << name_stream.GetString();
+
+ return success;
+}
+
bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s,
const SymbolContext *sc,
const ExecutionContext *exe_ctx,
@@ -1796,6 +1825,9 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
if (!sc)
return false;
+ if (FormatFunctionNameForLanguage(s, exe_ctx, sc))
+ return true;
+
return HandleFunctionNameWithArgs(s, exe_ctx, *sc);
}
case Entry::Type::FunctionMangledName: {
diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp
index e6cb248ef31ce..73c018330a24e 100644
--- a/lldb/source/Core/PluginManager.cpp
+++ b/lldb/source/Core/PluginManager.cpp
@@ -564,11 +564,12 @@ static LanguageInstances &GetLanguageInstances() {
return g_instances;
}
-bool PluginManager::RegisterPlugin(llvm::StringRef name,
- llvm::StringRef description,
- LanguageCreateInstance create_callback) {
- return GetLanguageInstances().RegisterPlugin(name, description,
- create_callback);
+bool PluginManager::RegisterPlugin(
+ llvm::StringRef name, llvm::StringRef description,
+ LanguageCreateInstance create_callback,
+ DebuggerInitializeCallback debugger_init_callback) {
+ return GetLanguageInstances().RegisterPlugin(
+ name, description, create_callback, debugger_init_callback);
}
bool PluginManager::UnregisterPlugin(LanguageCreateInstance create_callback) {
@@ -1682,6 +1683,7 @@ void PluginManager::DebuggerInitialize(Debugger &debugger) {
GetStructuredDataPluginInstances().PerformDebuggerCallback(debugger);
GetTracePluginInstances().PerformDebuggerCallback(debugger);
GetScriptedInterfaceInstances().PerformDebuggerCallback(debugger);
+ GetLanguageInstances().PerformDebuggerCallback(debugger);
}
// This is the preferred new way to register plugin specific settings. e.g.
@@ -1810,6 +1812,7 @@ static constexpr llvm::StringLiteral kSymbolLocatorPluginName("symbol-locator");
static constexpr llvm::StringLiteral kJITLoaderPluginName("jit-loader");
static constexpr llvm::StringLiteral
kStructuredDataPluginName("structured-data");
+static constexpr llvm::StringLiteral kCPlusPlusLanguagePlugin("cplusplus");
lldb::OptionValuePropertiesSP
PluginManager::GetSettingForDynamicLoaderPlugin(Debugger &debugger,
@@ -1967,3 +1970,17 @@ bool PluginManager::CreateSettingForStructuredDataPlugin(
"Settings for structured data plug-ins",
properties_sp, description, is_global_property);
}
+
+lldb::OptionValuePropertiesSP
+PluginManager::GetSettingForCPlusPlusLanguagePlugin(
+ Debugger &debugger, llvm::StringRef setting_name) {
+ return GetSettingForPlugin(debugger, setting_name, kCPlusPlusLanguagePlugin);
+}
+
+bool PluginManager::CreateSettingForCPlusPlusLanguagePlugin(
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
+ llvm::StringRef description, bool is_global_property) {
+ return CreateSettingForPlugin(debugger, kCPlusPlusLanguagePlugin,
+ "Settings for CPlusPlus language plug-ins",
+ properties_sp, description, is_global_property);
+}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
index ccdc4d0ae99b3..9bb10c2a792a9 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
+++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt
@@ -1,3 +1,11 @@
+lldb_tablegen(LanguageCPlusPlusProperties.inc -gen-lldb-property-defs
+ SOURCE LanguageCPlusPlusProperties.td
+ TARGET LLDBPluginLanguageCPlusPlusPropertiesGen)
+
+lldb_tablegen(LanguageCPlusPlusPropertiesEnum.inc -gen-lldb-property-enum-defs
+ SOURCE LanguageCPlusPlusProperties.td
+ TARGET LLDBPluginLanguageCPlusPlusPropertiesEnumGen)
+
add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
BlockPointer.cpp
Coroutines.cpp
@@ -41,3 +49,7 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
LINK_COMPONENTS
Support
)
+
+add_dependencies(lldbPluginCPlusPlusLanguage
+ LLDBPluginLanguageCPlusPlusPropertiesGen
+ LLDBPluginLanguageCPlusPlusPropertiesEnumGen)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index cf425fcc81c2f..943bc93348942 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -27,6 +27,7 @@
#include "lldb/DataFormatters/DataVisualization.h"
#include "lldb/DataFormatters/FormattersHelpers.h"
#include "lldb/DataFormatters/VectorType.h"
+#include "lldb/Interpreter/OptionValueProperties.h"
#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Utility/ConstString.h"
@@ -55,7 +56,7 @@ LLDB_PLUGIN_DEFINE(CPlusPlusLanguage)
void CPlusPlusLanguage::Initialize() {
PluginManager::RegisterPlugin(GetPluginNameStatic(), "C++ Language",
- CreateInstance);
+ CreateInstance, &DebuggerInitialize);
}
void CPlusPlusLanguage::Terminate() {
@@ -1968,3 +1969,47 @@ bool CPlusPlusLanguage::HandleFrameFormatVariable(
return false;
}
}
+
+#define LLDB_PROPERTIES_language_cplusplus
+#include "LanguageCPlusPlusProperties.inc"
+
+enum {
+#define LLDB_PROPERTIES_language_cplusplus
+#include "LanguageCPlusPlusPropertiesEnum.inc"
+};
+
+namespace {
+class PluginProperties : public Properties {
+public:
+ static llvm::StringRef GetSettingName() { return "display"; }
+
+ PluginProperties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_language_cplusplus_properties);
+ }
+
+ const FormatEntity::Entry *GetFunctionNameFormat() const {
+ return GetPropertyAtIndexAs<const FormatEntity::Entry *>(
+ ePropertyFunctionNameFormat);
+ }
+};
+} // namespace
+
+static PluginProperties &GetGlobalPluginProperties() {
+ static PluginProperties g_settings;
+ return g_settings;
+}
+
+const FormatEntity::Entry *CPlusPlusLanguage::GetFunctionNameFormat() const {
+ return GetGlobalPluginProperties().GetFunctionNameFormat();
+}
+
+void CPlusPlusLanguage::DebuggerInitialize(Debugger &debugger) {
+ if (!PluginManager::GetSettingForCPlusPlusLanguagePlugin(
+ debugger, PluginProperties::GetSettingName())) {
+ PluginManager::CreateSettingForCPlusPlusLanguagePlugin(
+ debugger, GetGlobalPluginProperties().GetValueProperties(),
+ "Properties for the CPlusPlus language plug-in.",
+ /*is_global_property=*/true);
+ }
+}
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
index 6192ff702773a..575f76c3101ed 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
@@ -136,8 +136,13 @@ class CPlusPlusLanguage : public Language {
llvm::StringRef GetInstanceVariableName() override { return "this"; }
+ const FormatEntity::Entry *GetFunctionNameFormat() const override;
+
// PluginInterface protocol
llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+private:
+ static void DebuggerInitialize(Debugger &);
};
} // namespace lldb_private
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td b/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td
new file mode 100644
index 0000000000000..6ec87afe25758
--- /dev/null
+++ b/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td
@@ -0,0 +1,8 @@
+include "../../../../include/lldb/Core/PropertiesBase.td"
+
+let Definition = "language_cplusplus" in {
+ def FunctionNameFormat: Property<"function-name-format", "FormatEntity">,
+ Global,
+ DefaultStringValue<"${function.return-left}${function.scope}${function.basename}${function.template-arguments}${function.formatted-arguments}${function.return-right}${function.qualifiers}">,
+ Desc<"C++ specific frame format string to use when displaying stack frame information for threads.">;
+}
diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormat.test b/lldb/test/Shell/Settings/TestCxxFrameFormat.test
new file mode 100644
index 0000000000000..c26d339f57130
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestCxxFrameFormat.test
@@ -0,0 +1,32 @@
+# Test the plugin.cplusplus.display.function-name-format setting.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.cpp -o %t.out
+# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
+# RUN: | FileCheck %s
+
+#--- main.cpp
+namespace ns::ns2 {
+void custom(int x) asm("_Zinvalid_mangling");
+void custom(int x) {}
+
+void bar() { custom(5); }
+void foo() { bar(); }
+}
+
+int main(int argc, char const *argv[]) {
+ ns::ns2::foo();
+ return 0;
+}
+
+#--- commands.input
+settings set plugin.cplusplus.display.function-name-format "${function.scope}${function.basename}"
+settings set -f frame-format "custom-frame '${function.name-with-args}'\n"
+break set -l 3
+
+run
+bt
+
+# CHECK: custom-frame '_Zinvalid_mangling(x=5)'
+# CHECK: custom-frame 'ns::ns2::bar'
+# CHECK: custom-frame 'ns::ns2::foo'
diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test b/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test
new file mode 100644
index 0000000000000..854cf6384d8ee
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test
@@ -0,0 +1,51 @@
+# Test the plugin.cplusplus.display.function-name-format setting
+# when interoperating multiple languages.
+
+# RUN: split-file %s %t
+# RUN: %clangxx_host -x c -c -g %t/lib.c -o %t.clib.o
+# RUN: %clangxx_host -c -g %t/lib.cpp -o %t.cxxlib.o
+# RUN: %clangxx_host %t/main.m %t.cxxlib.o %t.clib.o -o %t.out
+# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 | FileCheck %s
+
+#--- lib.c
+
+void foo();
+
+void func() {
+ foo();
+}
+
+#--- lib.cpp
+
+namespace ns {
+struct Foo {
+ void method() {}
+};
+}
+
+extern "C" {
+void foo() {
+ ns::Foo{}.method();
+}
+}
+
+#--- main.m
+
+void func();
+
+int main() {
+ func();
+}
+
+#--- commands.input
+settings set plugin.cplusplus.display.function-name-format "this affects C++ only"
+settings set -f frame-format "custom-frame '${function.name-with-args}'\n"
+break set -n method
+
+run
+bt
+
+# CHECK: custom-frame 'this affects C++ only'
+# CHECK: custom-frame 'this affects C++ only'
+# CHECK: custom-frame 'func'
+# CHECK: custom-frame 'main'
diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test b/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test
new file mode 100644
index 0000000000000..525ada2afe99c
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test
@@ -0,0 +1,24 @@
+# Test the plugin.cplusplus.display.function-name-format setting.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.m -o %t.objc.out
+# RUN: %lldb -x -b -s %t/commands.input %t.objc.out -o exit 2>&1 \
+# RUN: | FileCheck %s
+
+#--- main.m
+
+int func(int x) {}
+int bar(int y) { func(y); }
+
+int main() { return bar(10); }
+
+#--- commands.input
+settings set plugin.cplusplus.display.function-name-format "this affects C++ only"
+settings set -f frame-format "custom-frame '${function.name-with-args}'\n"
+break set -l 3
+run
+
+bt
+
+# CHECK: bt
+# CHECK-NOT: this affects C++ only
diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test b/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test
new file mode 100644
index 0000000000000..73564ae41837b
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test
@@ -0,0 +1,29 @@
+# Test that the plugin.cplusplus.display.function-name-format setting
+# doesn't print into the frame-format setting unless all its format variables
+# were successful.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.cpp -o %t.out
+# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \
+# RUN: | FileCheck %s
+
+#--- main.cpp
+template<typename T> T gunc(int x = 10) {
+ return T{};
+}
+
+int main(int argc, const char *argv[]) {
+ gunc<int>();
+ return 0;
+}
+
+#--- commands.input
+settings set plugin.cplusplus.display.function-name-format "${function.basename}${script.target:invalid_func}"
+settings set -f frame-format "custom-frame '${function.name-with-args}'\n"
+break set -n gunc
+
+run
+bt
+
+# CHECK: custom-frame 'int gunc<int>(x=10)'
+# CHECK: custom-frame 'main(argc=1, argv={{.*}})'
diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test b/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test
new file mode 100644
index 0000000000000..90cd2d3e327c9
--- /dev/null
+++ b/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test
@@ -0,0 +1,25 @@
+# XFAIL: *
+
+# Test disallowed variables inside the
+# plugin.cplusplus.display.function-name-format setting.
+
+# RUN: split-file %s %t
+# RUN: %build %t/main.cpp -o %t.out
+# RUN: %lldb -o "settings set interpreter.stop-command-source-on-error false" \
+# RUN: -x -b -s %t/commands.input %t.out -o exit 2>&1 \
+# RUN: | FileCheck %s
+
+#--- main.cpp
+int main(int argc, char const *argv[]) { return 0; }
+
+#--- commands.input
+settings set plugin.cplusplus.display.function-name-format "${function.name-with-args}"
+settings set -f frame-format "custom-frame '${function.name-with-args}'\n"
+b main
+run
+
+bt
+
+# CHECK: bt
+# CHECK-NOT: custom-frame
+# CHECK: main
More information about the lldb-commits
mailing list