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 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits