From a1663c37e5670daafd0666ba5c8852eaf7de02a4 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Thu, 10 Apr 2025 09:39:58 +0100 Subject: [PATCH 01/28] [lldb][FormatEntity][NFCI] Refactor FunctionNameWithArgs into helper functions and use LLVM style (#135031) I've always found this hard to read. Some upcoming changes make similar computations, so I thought it's a good time to factor out this logic into re-usable helpers and clean it up using LLVM's preferred early-return style. (cherry picked from commit f030f6f3c5ad44dca5c9f66c24e168a716fa1ece) --- lldb/source/Core/FormatEntity.cpp | 118 +++++++++++++++++------------- 1 file changed, 69 insertions(+), 49 deletions(-) diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 1ca34aaa222a3..a866b25614461 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1162,6 +1162,64 @@ static void FormatInlinedBlock(Stream &out_stream, Block *block) { } } +static VariableListSP GetFunctionVariableList(const SymbolContext &sc) { + assert(sc.function); + + if (sc.block) + if (Block *inline_block = sc.block->GetContainingInlinedBlock()) + return inline_block->GetBlockVariableList(true); + + return sc.function->GetBlock(true).GetBlockVariableList(true); +} + +static char const *GetInlinedFunctionName(const SymbolContext &sc) { + if (!sc.block) + return nullptr; + + const Block *inline_block = sc.block->GetContainingInlinedBlock(); + if (!inline_block) + return nullptr; + + const InlineFunctionInfo *inline_info = + inline_block->GetInlinedFunctionInfo(); + if (!inline_info) + return nullptr; + + return inline_info->GetName().AsCString(nullptr); +} + +static bool PrintFunctionNameWithArgs(Stream &s, + const ExecutionContext *exe_ctx, + const SymbolContext &sc) { + assert(sc.function); + + ExecutionContextScope *exe_scope = + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; + + const char *cstr = sc.function->GetName().AsCString(nullptr); + if (!cstr) + return false; + + if (const char *inlined_name = GetInlinedFunctionName(sc)) { + s.PutCString(cstr); + s.PutCString(" [inlined] "); + cstr = inlined_name; + } + + VariableList args; + if (auto variable_list_sp = GetFunctionVariableList(sc)) + variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, + args); + + if (args.GetSize() > 0) { + PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args); + } else { + s.PutCString(cstr); + } + + return true; +} + bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, @@ -1746,59 +1804,21 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin_handled) { s << ss.GetString(); return true; - } else { - // Print the function name with arguments in it - if (sc->function) { - ExecutionContextScope *exe_scope = - exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; - const char *cstr = sc->function->GetName().AsCString(nullptr); - if (cstr) { - const InlineFunctionInfo *inline_info = nullptr; - VariableListSP variable_list_sp; - bool get_function_vars = true; - if (sc->block) { - Block *inline_block = sc->block->GetContainingInlinedBlock(); - - if (inline_block) { - get_function_vars = false; - inline_info = inline_block->GetInlinedFunctionInfo(); - if (inline_info) - variable_list_sp = inline_block->GetBlockVariableList(true); - } - } + } - if (get_function_vars) { - variable_list_sp = - sc->function->GetBlock(true).GetBlockVariableList(true); - } + if (sc->function) + return PrintFunctionNameWithArgs(s, exe_ctx, *sc); - if (inline_info) { - s.PutCString(cstr); - s.PutCString(" [inlined] "); - cstr = inline_info->GetName().GetCString(); - } + if (!sc->symbol) + return false; - VariableList args; - if (variable_list_sp) - variable_list_sp->AppendVariablesWithScope( - eValueTypeVariableArgument, args); - if (args.GetSize() > 0) { - PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args); - } else { - s.PutCString(cstr); - } - return true; - } - } else if (sc->symbol) { - const char *cstr = sc->symbol->GetName().AsCString(nullptr); - if (cstr) { - s.PutCString(cstr); - return true; - } - } - } + const char *cstr = sc->symbol->GetName().AsCString(nullptr); + if (!cstr) + return false; + + s.PutCString(cstr); + return true; } - return false; case Entry::Type::FunctionMangledName: { if (!sc) From e16cdd30d030809d1a022a6e86a5fc61a330d2b7 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Fri, 11 Apr 2025 11:01:27 +0100 Subject: [PATCH 02/28] [lldb][Format][NFCI] Refactor CPlusPlusLanguage::GetFunctionDisplayName into helpers and use LLVM style (#135331) Same cleanup as in https://github.com/llvm/llvm-project/pull/135031. It pretty much is the same code that we had to duplicate in the language plugin. Maybe eventually we'll find a way of getting rid of the duplication. (cherry picked from commit b656915d5a4cbb35392d340142340bf9bef8d8d1) --- .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 130 +++++++++++------- 1 file changed, 77 insertions(+), 53 deletions(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 0198ceb8149e1..90a18a6ffdc63 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1699,65 +1699,89 @@ bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const { return file_path.contains("/usr/include/c++/"); } +static VariableListSP GetFunctionVariableList(const SymbolContext &sc) { + assert(sc.function); + + if (sc.block) + if (Block *inline_block = sc.block->GetContainingInlinedBlock()) + return inline_block->GetBlockVariableList(true); + + return sc.function->GetBlock(true).GetBlockVariableList(true); +} + +static char const *GetInlinedFunctionName(const SymbolContext &sc) { + if (!sc.block) + return nullptr; + + const Block *inline_block = sc.block->GetContainingInlinedBlock(); + if (!inline_block) + return nullptr; + + const InlineFunctionInfo *inline_info = + inline_block->GetInlinedFunctionInfo(); + if (!inline_info) + return nullptr; + + return inline_info->GetName().AsCString(nullptr); +} + +static bool PrintFunctionNameWithArgs(Stream &s, + const ExecutionContext *exe_ctx, + const SymbolContext &sc) { + assert(sc.function); + + ExecutionContextScope *exe_scope = + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; + + const char *cstr = sc.function->GetName().AsCString(nullptr); + if (!cstr) + return false; + + if (const char *inlined_name = GetInlinedFunctionName(sc)) { + s.PutCString(cstr); + s.PutCString(" [inlined] "); + cstr = inlined_name; + } + + VariableList args; + if (auto variable_list_sp = GetFunctionVariableList(sc)) + variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, + args); + + if (args.GetSize() > 0) + return PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args); + + // FIXME: can we just unconditionally call PrettyPrintFunctionNameWithArgs? + // It should be able to handle the "no arguments" case. + s.PutCString(cstr); + + return true; +} + bool CPlusPlusLanguage::GetFunctionDisplayName( const SymbolContext *sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s) { switch (representation) { case FunctionNameRepresentation::eNameWithArgs: { + assert(sc); + // Print the function name with arguments in it - if (sc->function) { - ExecutionContextScope *exe_scope = - exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; - const char *cstr = sc->function->GetName().AsCString(nullptr); - if (cstr) { - const InlineFunctionInfo *inline_info = nullptr; - VariableListSP variable_list_sp; - bool get_function_vars = true; - if (sc->block) { - Block *inline_block = sc->block->GetContainingInlinedBlock(); - - if (inline_block) { - get_function_vars = false; - inline_info = inline_block->GetInlinedFunctionInfo(); - if (inline_info) - variable_list_sp = inline_block->GetBlockVariableList(true); - } - } - - if (get_function_vars) { - variable_list_sp = - sc->function->GetBlock(true).GetBlockVariableList(true); - } - - if (inline_info) { - s.PutCString(cstr); - s.PutCString(" [inlined] "); - cstr = inline_info->GetName().GetCString(); - } - - VariableList args; - if (variable_list_sp) - variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, - args); - if (args.GetSize() > 0) { - if (!PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args)) - return false; - } else { - s.PutCString(cstr); - } - return true; - } - } else if (sc->symbol) { - const char *cstr = sc->symbol->GetName().AsCString(nullptr); - if (cstr) { - s.PutCString(cstr); - return true; - } - } - } break; - default: + if (sc->function) + return PrintFunctionNameWithArgs(s, exe_ctx, *sc); + + if (!sc->symbol) + return false; + + const char *cstr = sc->symbol->GetName().AsCString(nullptr); + if (!cstr) + return false; + + s.PutCString(cstr); + + return true; + } + case FunctionNameRepresentation::eNameWithNoArgs: + case FunctionNameRepresentation::eName: return false; } - - return false; } From a719c490f4ee730fd9f8aa57e49c0f11d628ed39 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Sun, 13 Apr 2025 23:19:26 +0100 Subject: [PATCH 03/28] [lldb][Language] Change GetFunctionDisplayName to take SymbolContext by reference (#135536) Both the `CPlusPlusLanguage` plugins and the Swift language plugin already assume the `sc != nullptr`. And all `FormatEntity` callsites of `GetFunctionDisplayName` already check for nullptr before passing `sc`. This patch makes this pre-condition explicit by changing the parameter to `const SymbolContext &`. This will help with some upcoming changes in this area. (cherry picked from commit 52e45a79ad24f8a2347a5566e6abaa207918df62) --- lldb/include/lldb/Target/Language.h | 2 +- lldb/source/Core/FormatEntity.cpp | 7 ++++--- .../Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp | 12 +++++------- .../Plugins/Language/CPlusPlus/CPlusPlusLanguage.h | 2 +- lldb/source/Target/Language.cpp | 2 +- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h index 6a1487a251aa1..63511e82f2977 100644 --- a/lldb/include/lldb/Target/Language.h +++ b/lldb/include/lldb/Target/Language.h @@ -270,7 +270,7 @@ class Language : public PluginInterface { // the reference has never been assigned virtual bool IsUninitializedReference(ValueObject &valobj); - virtual bool GetFunctionDisplayName(const SymbolContext *sc, + virtual bool GetFunctionDisplayName(const SymbolContext &sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s); diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index a866b25614461..d2e064666cd9b 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1729,7 +1729,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin) language_plugin_handled = language_plugin->GetFunctionDisplayName( - sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss); + *sc, exe_ctx, Language::FunctionNameRepresentation::eName, ss); if (language_plugin_handled) { s << ss.GetString(); @@ -1764,7 +1764,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin) language_plugin_handled = language_plugin->GetFunctionDisplayName( - sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs, + *sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithNoArgs, ss); if (language_plugin_handled) { @@ -1799,7 +1799,8 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin) language_plugin_handled = language_plugin->GetFunctionDisplayName( - sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss); + *sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, + ss); if (language_plugin_handled) { s << ss.GetString(); diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 90a18a6ffdc63..701f61f260720 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1759,20 +1759,18 @@ static bool PrintFunctionNameWithArgs(Stream &s, } bool CPlusPlusLanguage::GetFunctionDisplayName( - const SymbolContext *sc, const ExecutionContext *exe_ctx, + const SymbolContext &sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s) { switch (representation) { case FunctionNameRepresentation::eNameWithArgs: { - assert(sc); - // Print the function name with arguments in it - if (sc->function) - return PrintFunctionNameWithArgs(s, exe_ctx, *sc); + if (sc.function) + return PrintFunctionNameWithArgs(s, exe_ctx, sc); - if (!sc->symbol) + if (!sc.symbol) return false; - const char *cstr = sc->symbol->GetName().AsCString(nullptr); + const char *cstr = sc.symbol->GetName().AsCString(nullptr); if (!cstr) return false; diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h index 623d481bf117f..54f5a94388b92 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -138,7 +138,7 @@ class CPlusPlusLanguage : public Language { ConstString GetDemangledFunctionNameWithoutArguments(Mangled mangled) const override; - bool GetFunctionDisplayName(const SymbolContext *sc, + bool GetFunctionDisplayName(const SymbolContext &sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s) override; diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp index a75894ffa4b3b..86754c251cd93 100644 --- a/lldb/source/Target/Language.cpp +++ b/lldb/source/Target/Language.cpp @@ -510,7 +510,7 @@ bool Language::IsNilReference(ValueObject &valobj) { return false; } bool Language::IsUninitializedReference(ValueObject &valobj) { return false; } -bool Language::GetFunctionDisplayName(const SymbolContext *sc, +bool Language::GetFunctionDisplayName(const SymbolContext &sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s) { From 34bc8beb0e5b78cf6497aa486f488ff974ee74e8 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Sun, 13 Apr 2025 23:21:52 +0100 Subject: [PATCH 04/28] [lldb][Format] Display only the inlined frame name in backtraces if available (#135343) When a frame is inlined, LLDB will display its name in backtraces as follows: ``` * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3 * frame #0: 0x0000000100000398 a.out`func() [inlined] baz(x=10) at inline.cpp:1:42 frame #1: 0x0000000100000398 a.out`func() [inlined] bar() at inline.cpp:2:37 frame #2: 0x0000000100000398 a.out`func() at inline.cpp:4:15 frame #3: 0x00000001000003c0 a.out`main at inline.cpp:7:5 frame #4: 0x000000026eb29ab8 dyld`start + 6812 ``` The longer the names get the more confusing this gets because the first function name that appears is the parent frame. My assumption (which may need some more surveying) is that for the majority of cases we only care about the actual frame name (not the parent). So this patch removes all the special logic that prints the parent frame. Another quirk of the current format is that the inlined frame name does not abide by the `${function.name-XXX}` format variables. We always just print the raw demangled name. With this patch, we would format the inlined frame name according to the `frame-format` setting (see the test-cases). If we really want to have the `parentFrame [inlined] inlinedFrame` format, we could expose it through a new `frame-format` variable (e..g., `${function.inlined-at-name}` and let the user decide where to place things. (cherry picked from commit 1e153b782ea3054c02dd0016314fca11a5d781da) --- lldb/include/lldb/Symbol/SymbolContext.h | 7 ++ lldb/source/Core/FormatEntity.cpp | 95 +++++-------------- .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 25 +---- lldb/source/Symbol/SymbolContext.cpp | 30 ++++++ .../basic_entry_values/main.cpp | 4 +- .../inlining_and_tail_calls/main.cpp | 10 +- .../verbose_trap-in-stl-max-depth.test | 2 +- .../Shell/Settings/TestFrameFormatName.test | 8 +- 8 files changed, 76 insertions(+), 105 deletions(-) diff --git a/lldb/include/lldb/Symbol/SymbolContext.h b/lldb/include/lldb/Symbol/SymbolContext.h index 0bc707070f850..68143006c9cd5 100644 --- a/lldb/include/lldb/Symbol/SymbolContext.h +++ b/lldb/include/lldb/Symbol/SymbolContext.h @@ -309,6 +309,13 @@ class SymbolContext { SymbolContext &next_frame_sc, Address &inlined_frame_addr) const; + /// If available, will return the function name according to the specified + /// mangling preference. If this object represents an inlined function, + /// returns the name of the inlined function. Returns nullptr if no function + /// name could be determined. + const char *GetPossiblyInlinedFunctionName( + Mangled::NamePreference mangling_preference) const; + // Member variables lldb::TargetSP target_sp; ///< The Target for a given query lldb::ModuleSP module_sp; ///< The Module for a given query diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index d2e064666cd9b..7d4cca958595c 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1149,19 +1149,6 @@ static void PrettyPrintFunctionNameWithArgs(Stream &out_stream, out_stream.PutChar(')'); } -static void FormatInlinedBlock(Stream &out_stream, Block *block) { - if (!block) - return; - Block *inline_block = block->GetContainingInlinedBlock(); - if (inline_block) { - if (const InlineFunctionInfo *inline_info = - inline_block->GetInlinedFunctionInfo()) { - out_stream.PutCString(" [inlined] "); - inline_info->GetName().Dump(&out_stream); - } - } -} - static VariableListSP GetFunctionVariableList(const SymbolContext &sc) { assert(sc.function); @@ -1172,22 +1159,6 @@ static VariableListSP GetFunctionVariableList(const SymbolContext &sc) { return sc.function->GetBlock(true).GetBlockVariableList(true); } -static char const *GetInlinedFunctionName(const SymbolContext &sc) { - if (!sc.block) - return nullptr; - - const Block *inline_block = sc.block->GetContainingInlinedBlock(); - if (!inline_block) - return nullptr; - - const InlineFunctionInfo *inline_info = - inline_block->GetInlinedFunctionInfo(); - if (!inline_info) - return nullptr; - - return inline_info->GetName().AsCString(nullptr); -} - static bool PrintFunctionNameWithArgs(Stream &s, const ExecutionContext *exe_ctx, const SymbolContext &sc) { @@ -1196,16 +1167,11 @@ static bool PrintFunctionNameWithArgs(Stream &s, ExecutionContextScope *exe_scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; - const char *cstr = sc.function->GetName().AsCString(nullptr); + const char *cstr = + sc.GetPossiblyInlinedFunctionName(Mangled::ePreferDemangled); if (!cstr) return false; - if (const char *inlined_name = GetInlinedFunctionName(sc)) { - s.PutCString(cstr); - s.PutCString(" [inlined] "); - cstr = inlined_name; - } - VariableList args; if (auto variable_list_sp = GetFunctionVariableList(sc)) variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, @@ -1734,21 +1700,17 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin_handled) { s << ss.GetString(); return true; - } else { - const char *name = nullptr; - if (sc->function) - name = sc->function->GetName().AsCString(nullptr); - else if (sc->symbol) - name = sc->symbol->GetName().AsCString(nullptr); - - if (name) { - s.PutCString(name); - FormatInlinedBlock(s, sc->block); - return true; - } } + + const char *name = sc->GetPossiblyInlinedFunctionName( + Mangled::NamePreference::ePreferDemangled); + if (!name) + return false; + + s.PutCString(name); + + return true; } - return false; case Entry::Type::FunctionNameNoArgs: { if (!sc) @@ -1770,20 +1732,17 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (language_plugin_handled) { s << ss.GetString(); return true; - } else { - ConstString name; - if (sc->function) - name = sc->function->GetNameNoArguments(); - else if (sc->symbol) - name = sc->symbol->GetNameNoArguments(); - if (name) { - s.PutCString(name.GetCString()); - FormatInlinedBlock(s, sc->block); - return true; - } } + + const char *name = sc->GetPossiblyInlinedFunctionName( + Mangled::NamePreference::ePreferDemangledWithoutArguments); + if (!name) + return false; + + s.PutCString(name); + + return true; } - return false; case Entry::Type::FunctionNameWithArgs: { if (!sc) @@ -1825,19 +1784,13 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (!sc) return false; - const char *name = nullptr; - if (sc->symbol) - name = - sc->symbol->GetMangled().GetName(Mangled::ePreferMangled).AsCString(); - else if (sc->function) - name = sc->function->GetMangled() - .GetName(Mangled::ePreferMangled) - .AsCString(); - + const char *name = sc->GetPossiblyInlinedFunctionName( + Mangled::NamePreference::ePreferMangled); if (!name) return false; + s.PutCString(name); - FormatInlinedBlock(s, sc->block); + return true; } case Entry::Type::FunctionAddrOffset: diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 701f61f260720..d2c2c4ea40df6 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1709,22 +1709,6 @@ static VariableListSP GetFunctionVariableList(const SymbolContext &sc) { return sc.function->GetBlock(true).GetBlockVariableList(true); } -static char const *GetInlinedFunctionName(const SymbolContext &sc) { - if (!sc.block) - return nullptr; - - const Block *inline_block = sc.block->GetContainingInlinedBlock(); - if (!inline_block) - return nullptr; - - const InlineFunctionInfo *inline_info = - inline_block->GetInlinedFunctionInfo(); - if (!inline_info) - return nullptr; - - return inline_info->GetName().AsCString(nullptr); -} - static bool PrintFunctionNameWithArgs(Stream &s, const ExecutionContext *exe_ctx, const SymbolContext &sc) { @@ -1733,16 +1717,11 @@ static bool PrintFunctionNameWithArgs(Stream &s, ExecutionContextScope *exe_scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; - const char *cstr = sc.function->GetName().AsCString(nullptr); + const char *cstr = sc.GetPossiblyInlinedFunctionName( + Mangled::NamePreference::ePreferDemangled); if (!cstr) return false; - if (const char *inlined_name = GetInlinedFunctionName(sc)) { - s.PutCString(cstr); - s.PutCString(" [inlined] "); - cstr = inlined_name; - } - VariableList args; if (auto variable_list_sp = GetFunctionVariableList(sc)) variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, diff --git a/lldb/source/Symbol/SymbolContext.cpp b/lldb/source/Symbol/SymbolContext.cpp index 29b0b3c177ec1..13f609e75541f 100644 --- a/lldb/source/Symbol/SymbolContext.cpp +++ b/lldb/source/Symbol/SymbolContext.cpp @@ -925,6 +925,36 @@ const Symbol *SymbolContext::FindBestGlobalDataSymbol(ConstString name, return nullptr; // no error; we just didn't find anything } +char const *SymbolContext::GetPossiblyInlinedFunctionName( + Mangled::NamePreference mangling_preference) const { + const char *name = nullptr; + if (function) + name = function->GetMangled().GetName(mangling_preference).AsCString(); + else if (symbol) + name = symbol->GetMangled().GetName(mangling_preference).AsCString(); + + if (!block) + return name; + + const Block *inline_block = block->GetContainingInlinedBlock(); + if (!inline_block) + return name; + + const InlineFunctionInfo *inline_info = + inline_block->GetInlinedFunctionInfo(); + if (!inline_info) + return name; + + // If we do have an inlined frame name, return that. + if (char const *inline_name = + inline_info->GetMangled().GetName(mangling_preference).AsCString()) + return inline_name; + + // Sometimes an inline frame may not have mangling information, + // but does have a valid name. + return inline_info->GetName().AsCString(); +} + // // SymbolContextSpecifier // diff --git a/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp b/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp index 7ad72b4880d79..64e2a5b479675 100644 --- a/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp +++ b/lldb/test/API/functionalities/param_entry_vals/basic_entry_values/main.cpp @@ -70,8 +70,8 @@ __attribute__((noinline)) void func6(int &sink, int x) { __attribute__((noinline)) void func7(int &sink, int x) { //% self.filecheck("bt", "main.cpp", "-check-prefix=FUNC7-BT") // FUNC7-BT: func7 - // FUNC7-BT-NEXT: [inlined] func8_inlined - // FUNC7-BT-NEXT: [inlined] func9_inlined + // FUNC7-BT-NEXT: func8_inlined + // FUNC7-BT-NEXT: func9_inlined // FUNC7-BT-NEXT: func10 use(sink, x); use(dummy, 0); diff --git a/lldb/test/API/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp b/lldb/test/API/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp index 9829e0246fc2b..0a7d365d776c6 100644 --- a/lldb/test/API/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp +++ b/lldb/test/API/functionalities/tail_call_frames/inlining_and_tail_calls/main.cpp @@ -1,13 +1,13 @@ volatile int x; +// clang-format off void __attribute__((noinline)) tail_call_sink() { x++; //% self.filecheck("bt", "main.cpp", "-check-prefix=TAIL-CALL-SINK") // TAIL-CALL-SINK: frame #0: 0x{{[0-9a-f]+}} a.out`tail_call_sink() at main.cpp:[[@LINE-1]]:4 - // TAIL-CALL-SINK-NEXT: func3{{.*}} [artificial] + // TAIL-CALL-SINK-NEXT: inlinable_function_which_tail_calls() at main.cpp{{.*}} [artificial] // TAIL-CALL-SINK-NEXT: main{{.*}} - - // TODO: The backtrace should include inlinable_function_which_tail_calls. } +// clang-format on void __attribute__((always_inline)) inlinable_function_which_tail_calls() { tail_call_sink(); @@ -17,13 +17,15 @@ void __attribute__((noinline)) func3() { inlinable_function_which_tail_calls(); } +// clang-format off void __attribute__((always_inline)) inline_sink() { x++; //% self.filecheck("bt", "main.cpp", "-check-prefix=INLINE-SINK") - // INLINE-SINK: frame #0: 0x{{[0-9a-f]+}} a.out`func2() [inlined] inline_sink() at main.cpp:[[@LINE-1]]:4 + // INLINE-SINK: frame #0: 0x{{[0-9a-f]+}} a.out`inline_sink() at main.cpp:[[@LINE-1]]:4 // INLINE-SINK-NEXT: func2{{.*}} // INLINE-SINK-NEXT: func1{{.*}} [artificial] // INLINE-SINK-NEXT: main{{.*}} } +// clang-format on void __attribute__((noinline)) func2() { inline_sink(); /* inlined */ } diff --git a/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test index 0c3275c571b3d..2ea6594643c9c 100644 --- a/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test +++ b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test @@ -12,5 +12,5 @@ run frame recognizer info 0 # CHECK: frame 0 is recognized by Verbose Trap StackFrame Recognizer frame info -# CHECK: frame #0: {{.*}}`std::recursively_aborts(int) {{.*}} at verbose_trap-in-stl-max-depth.cpp +# CHECK: frame #0: {{.*}}`__clang_trap_msg$Error$max depth at verbose_trap-in-stl-max-depth.cpp q diff --git a/lldb/test/Shell/Settings/TestFrameFormatName.test b/lldb/test/Shell/Settings/TestFrameFormatName.test index caa3242527c6e..110daceb47b40 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatName.test +++ b/lldb/test/Shell/Settings/TestFrameFormatName.test @@ -30,7 +30,7 @@ c c # NAME_WITH_ARGS: frame Foo::returns_func_ptr(this={{.*}}, (null)={{.*}}) c -# NAME_WITH_ARGS: frame main [inlined] inlined_foo(str="bar") +# NAME_WITH_ARGS: frame inlined_foo(str="bar") q #--- name.input @@ -38,18 +38,18 @@ q settings set -f frame-format "frame ${function.name}\n" break set -n inlined_foo run -# NAME: frame main [inlined] inlined_foo(char const*) +# NAME: frame inlined_foo(char const*) #--- name_without_args.input # RUN: %lldb -b -s %t/name_without_args.input %t.out | FileCheck %s --check-prefix=NAME_WITHOUT_ARGS settings set -f frame-format "frame ${function.name-without-args}\n" break set -n inlined_foo run -# NAME_WITHOUT_ARGS: frame main [inlined] inlined_foo(char const*) +# NAME_WITHOUT_ARGS: frame inlined_foo #--- mangled_name.input # RUN: %lldb -b -s %t/mangled_name.input %t.out | FileCheck %s --check-prefix=MANGLED_NAME settings set -f frame-format "frame ${function.mangled-name}\n" break set -n inlined_foo run -# MANGLED_NAME: frame main [inlined] inlined_foo(char const*) +# MANGLED_NAME: frame _Z11inlined_fooPKc From 1a2f2d5d41cc95d934dcd8a4a16214fe5ccf2cf1 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Sun, 13 Apr 2025 23:22:02 +0100 Subject: [PATCH 05/28] [lldb][Format][NFC] Factor FunctionNameWithArgs case out into helper function (cherry picked from commit af7a7ba4aadea3600e78a5f522b72e5413c8e595) --- lldb/source/Core/FormatEntity.cpp | 65 +++++++++++++++++-------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 7d4cca958595c..4094f6249de62 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1186,6 +1186,40 @@ static bool PrintFunctionNameWithArgs(Stream &s, return true; } +static bool HandleFunctionNameWithArgs(Stream &s,const ExecutionContext *exe_ctx, + const SymbolContext &sc) { + Language *language_plugin = nullptr; + bool language_plugin_handled = false; + StreamString ss; + if (sc.function) + language_plugin = Language::FindPlugin(sc.function->GetLanguage()); + else if (sc.symbol) + language_plugin = Language::FindPlugin(sc.symbol->GetLanguage()); + + if (language_plugin) + language_plugin_handled = language_plugin->GetFunctionDisplayName( + sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, ss); + + if (language_plugin_handled) { + s << ss.GetString(); + return true; + } + + if (sc.function) + return PrintFunctionNameWithArgs(s, exe_ctx, sc); + + if (!sc.symbol) + return false; + + const char *cstr = sc.symbol->GetName().AsCString(nullptr); + if (!cstr) + return false; + + s.PutCString(cstr); + + return true; +} + bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, @@ -1748,36 +1782,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, if (!sc) return false; - Language *language_plugin = nullptr; - bool language_plugin_handled = false; - StreamString ss; - if (sc->function) - language_plugin = Language::FindPlugin(sc->function->GetLanguage()); - else if (sc->symbol) - language_plugin = Language::FindPlugin(sc->symbol->GetLanguage()); - - if (language_plugin) - language_plugin_handled = language_plugin->GetFunctionDisplayName( - *sc, exe_ctx, Language::FunctionNameRepresentation::eNameWithArgs, - ss); - - if (language_plugin_handled) { - s << ss.GetString(); - return true; - } - - if (sc->function) - return PrintFunctionNameWithArgs(s, exe_ctx, *sc); - - if (!sc->symbol) - return false; - - const char *cstr = sc->symbol->GetName().AsCString(nullptr); - if (!cstr) - return false; - - s.PutCString(cstr); - return true; + return HandleFunctionNameWithArgs(s, exe_ctx, *sc); } case Entry::Type::FunctionMangledName: { From 7f1e9886b5223dd882118a6498d60117dcfbc143 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Mon, 14 Apr 2025 06:31:26 +0100 Subject: [PATCH 06/28] [lldb][test] Fix NativePDB/inline_sites_live.cpp inlined frame format Adjust after https://github.com/llvm/llvm-project/pull/135343 (cherry picked from commit a3f8359410eb7e14c4a52b47f36e433af40c05e9) --- .../test/Shell/SymbolFile/NativePDB/inline_sites_live.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lldb/test/Shell/SymbolFile/NativePDB/inline_sites_live.cpp b/lldb/test/Shell/SymbolFile/NativePDB/inline_sites_live.cpp index 767149ea18c46..6b66e5cbceafa 100644 --- a/lldb/test/Shell/SymbolFile/NativePDB/inline_sites_live.cpp +++ b/lldb/test/Shell/SymbolFile/NativePDB/inline_sites_live.cpp @@ -22,12 +22,12 @@ int main(int argc, char** argv) { foo(argc); } -// CHECK: * thread #1, stop reason = breakpoint 1 -// CHECK-NEXT: frame #0: {{.*}}`main [inlined] bar(param=2) +// CHECK: * thread #1, {{.*}}stop reason = breakpoint 1 +// CHECK-NEXT: frame #0: {{.*}}`bar(param=2) // CHECK: (lldb) expression param // CHECK-NEXT: (int) $0 = 2 -// CHECK: * thread #1, stop reason = breakpoint 2 -// CHECK-NEXT: frame #0: {{.*}}`main [inlined] foo(param=1) +// CHECK: * thread #1, {{.*}}stop reason = breakpoint 2 +// CHECK-NEXT: frame #0: {{.*}}`foo(param=1) // CHECK: (lldb) expression param // CHECK-NEXT: (int) $1 = 1 // CHECK-NEXT: (lldb) expression local From d2ebe63feb2348ddf27bda8c07a05b1df525416b Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Mon, 14 Apr 2025 13:41:53 +0100 Subject: [PATCH 07/28] [lldb][Format][NFC] Remove unused FormatEntity::FormatCString One can use `FormatStringRef` instead anyway (cherry picked from commit cbbf562d1c2a076de83d50fedfee78acfb4d8003) --- lldb/include/lldb/Core/FormatEntity.h | 5 ----- lldb/source/Core/FormatEntity.cpp | 17 ----------------- 2 files changed, 22 deletions(-) diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h index bc51df726fc7a..f6c3bd981e03a 100644 --- a/lldb/include/lldb/Core/FormatEntity.h +++ b/lldb/include/lldb/Core/FormatEntity.h @@ -218,11 +218,6 @@ bool FormatStringRef(const llvm::StringRef &format, Stream &s, const Address *addr, ValueObject *valobj, bool function_changed, bool initial_function); -bool FormatCString(const char *format, Stream &s, const SymbolContext *sc, - const ExecutionContext *exe_ctx, const Address *addr, - ValueObject *valobj, bool function_changed, - bool initial_function); - Status Parse(const llvm::StringRef &format, Entry &entry); Status ExtractVariableInfo(llvm::StringRef &format_str, diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 4094f6249de62..f091ae58cdbb4 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1237,23 +1237,6 @@ bool FormatEntity::FormatStringRef(const llvm::StringRef &format_str, Stream &s, return false; } -bool FormatEntity::FormatCString(const char *format, Stream &s, - const SymbolContext *sc, - const ExecutionContext *exe_ctx, - const Address *addr, ValueObject *valobj, - bool function_changed, bool initial_function) { - if (format && format[0]) { - FormatEntity::Entry root; - llvm::StringRef format_str(format); - Status error = FormatEntity::Parse(format_str, root); - if (error.Success()) { - return FormatEntity::Format(root, s, sc, exe_ctx, addr, valobj, - function_changed, initial_function); - } - } - return false; -} - bool FormatEntity::Format(const Entry &entry, Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const Address *addr, From 2b60e9cf6947190990f6f96f5549cd4348bbf7ad Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Thu, 17 Apr 2025 21:53:32 +0100 Subject: [PATCH 08/28] [ItaniumDemangle] Add customizable printLeft/printRight APIs to OutputBuffer (#133249) This patch includes the necessary changes for the LLDB feature proposed in https://discourse.llvm.org/t/rfc-lldb-highlighting-function-names-in-lldb-backtraces/85309. The TL;DR is that we want to track where certain parts of a demangled name begin/end so we can highlight them in backtraces. We introduce a new `printLeft`/`printRight` API on `OutputBuffer` that a client (in our case LLDB) can implement to track state while printing the demangle tree. This requires redirecting all calls to to `printLeft`/`printRight` to the `OutputBuffer`. One quirk with the new API is that `Utility.h` would now depend on `ItaniumDemangle.h` and vice-versa. To keep these files header-only I made the definitions `inline` and implement the new APIs in `ItaniumDemangle.h` (so the definition of `Node` is available to them). Also introduces `notifyInsertion`/`notifyDeletion` APIs that a client can override to respond to cases where the `OutputBuffer` changes arbitrary parts of the name. (cherry picked from commit 889dad7f40932ea68c9e287e62441507f4f0f261) --- libcxxabi/src/demangle/ItaniumDemangle.h | 91 +++++++++++-------- libcxxabi/src/demangle/Utility.h | 26 +++++- llvm/include/llvm/Demangle/ItaniumDemangle.h | 91 +++++++++++-------- llvm/include/llvm/Demangle/Utility.h | 26 +++++- .../Demangle/ItaniumDemangleTest.cpp | 2 +- llvm/unittests/Demangle/OutputBufferTest.cpp | 37 ++++++++ 6 files changed, 196 insertions(+), 77 deletions(-) diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h index 36bf454636366..35197f59b148b 100644 --- a/libcxxabi/src/demangle/ItaniumDemangle.h +++ b/libcxxabi/src/demangle/ItaniumDemangle.h @@ -278,20 +278,11 @@ class Node { } void print(OutputBuffer &OB) const { - printLeft(OB); + OB.printLeft(*this); if (RHSComponentCache != Cache::No) - printRight(OB); + OB.printRight(*this); } - // Print the "left" side of this Node into OutputBuffer. - virtual void printLeft(OutputBuffer &) const = 0; - - // Print the "right". This distinction is necessary to represent C++ types - // that appear on the RHS of their subtype, such as arrays or functions. - // Since most types don't have such a component, provide a default - // implementation. - virtual void printRight(OutputBuffer &) const {} - virtual std::string_view getBaseName() const { return {}; } // Silence compiler warnings, this dtor will never be called. @@ -300,6 +291,24 @@ class Node { #ifndef NDEBUG DEMANGLE_DUMP_METHOD void dump() const; #endif + +private: + friend class OutputBuffer; + + // Print the "left" side of this Node into OutputBuffer. + // + // Note, should only be called from OutputBuffer implementations. + // Call \ref OutputBuffer::printLeft instead. + virtual void printLeft(OutputBuffer &) const = 0; + + // Print the "right". This distinction is necessary to represent C++ types + // that appear on the RHS of their subtype, such as arrays or functions. + // Since most types don't have such a component, provide a default + // implementation. + // + // Note, should only be called from OutputBuffer implementations. + // Call \ref OutputBuffer::printRight instead. + virtual void printRight(OutputBuffer &) const {} }; class NodeArray { @@ -444,11 +453,11 @@ class QualType final : public Node { } void printLeft(OutputBuffer &OB) const override { - Child->printLeft(OB); + OB.printLeft(*Child); printQuals(OB); } - void printRight(OutputBuffer &OB) const override { Child->printRight(OB); } + void printRight(OutputBuffer &OB) const override { OB.printRight(*Child); } }; class ConversionOperatorType final : public Node { @@ -477,7 +486,7 @@ class PostfixQualifiedType final : public Node { template void match(Fn F) const { F(Ty, Postfix); } void printLeft(OutputBuffer &OB) const override { - Ty->printLeft(OB); + OB.printLeft(*Ty); OB += Postfix; } }; @@ -563,7 +572,7 @@ struct AbiTagAttr : Node { std::string_view getBaseName() const override { return Base->getBaseName(); } void printLeft(OutputBuffer &OB) const override { - Base->printLeft(OB); + OB.printLeft(*Base); OB += "[abi:"; OB += Tag; OB += "]"; @@ -630,7 +639,7 @@ class PointerType final : public Node { // We rewrite objc_object* into id. if (Pointee->getKind() != KObjCProtoName || !static_cast(Pointee)->isObjCObject()) { - Pointee->printLeft(OB); + OB.printLeft(*Pointee); if (Pointee->hasArray(OB)) OB += " "; if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) @@ -649,7 +658,7 @@ class PointerType final : public Node { !static_cast(Pointee)->isObjCObject()) { if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) OB += ")"; - Pointee->printRight(OB); + OB.printRight(*Pointee); } } }; @@ -715,7 +724,7 @@ class ReferenceType : public Node { std::pair Collapsed = collapse(OB); if (!Collapsed.second) return; - Collapsed.second->printLeft(OB); + OB.printLeft(*Collapsed.second); if (Collapsed.second->hasArray(OB)) OB += " "; if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) @@ -732,7 +741,7 @@ class ReferenceType : public Node { return; if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) OB += ")"; - Collapsed.second->printRight(OB); + OB.printRight(*Collapsed.second); } }; @@ -752,7 +761,7 @@ class PointerToMemberType final : public Node { } void printLeft(OutputBuffer &OB) const override { - MemberType->printLeft(OB); + OB.printLeft(*MemberType); if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) OB += "("; else @@ -764,7 +773,7 @@ class PointerToMemberType final : public Node { void printRight(OutputBuffer &OB) const override { if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) OB += ")"; - MemberType->printRight(OB); + OB.printRight(*MemberType); } }; @@ -784,7 +793,7 @@ class ArrayType final : public Node { bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } bool hasArraySlow(OutputBuffer &) const override { return true; } - void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); } + void printLeft(OutputBuffer &OB) const override { OB.printLeft(*Base); } void printRight(OutputBuffer &OB) const override { if (OB.back() != ']') @@ -793,7 +802,7 @@ class ArrayType final : public Node { if (Dimension) Dimension->print(OB); OB += "]"; - Base->printRight(OB); + OB.printRight(*Base); } }; @@ -828,7 +837,7 @@ class FunctionType final : public Node { // by printing out the return types's left, then print our parameters, then // finally print right of the return type. void printLeft(OutputBuffer &OB) const override { - Ret->printLeft(OB); + OB.printLeft(*Ret); OB += " "; } @@ -836,7 +845,7 @@ class FunctionType final : public Node { OB.printOpen(); Params.printWithComma(OB); OB.printClose(); - Ret->printRight(OB); + OB.printRight(*Ret); if (CVQuals & QualConst) OB += " const"; @@ -941,6 +950,8 @@ class FunctionEncoding final : public Node { FunctionRefQual getRefQual() const { return RefQual; } NodeArray getParams() const { return Params; } const Node *getReturnType() const { return Ret; } + const Node *getAttrs() const { return Attrs; } + const Node *getRequires() const { return Requires; } bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } bool hasFunctionSlow(OutputBuffer &) const override { return true; } @@ -949,10 +960,11 @@ class FunctionEncoding final : public Node { void printLeft(OutputBuffer &OB) const override { if (Ret) { - Ret->printLeft(OB); + OB.printLeft(*Ret); if (!Ret->hasRHSComponent(OB)) OB += " "; } + Name->print(OB); } @@ -960,8 +972,9 @@ class FunctionEncoding final : public Node { OB.printOpen(); Params.printWithComma(OB); OB.printClose(); + if (Ret) - Ret->printRight(OB); + OB.printRight(*Ret); if (CVQuals & QualConst) OB += " const"; @@ -1301,14 +1314,14 @@ class NonTypeTemplateParamDecl final : public Node { template void match(Fn F) const { F(Name, Type); } void printLeft(OutputBuffer &OB) const override { - Type->printLeft(OB); + OB.printLeft(*Type); if (!Type->hasRHSComponent(OB)) OB += " "; } void printRight(OutputBuffer &OB) const override { Name->print(OB); - Type->printRight(OB); + OB.printRight(*Type); } }; @@ -1353,11 +1366,11 @@ class TemplateParamPackDecl final : public Node { template void match(Fn F) const { F(Param); } void printLeft(OutputBuffer &OB) const override { - Param->printLeft(OB); + OB.printLeft(*Param); OB += "..."; } - void printRight(OutputBuffer &OB) const override { Param->printRight(OB); } + void printRight(OutputBuffer &OB) const override { OB.printRight(*Param); } }; /// An unexpanded parameter pack (either in the expression or type context). If @@ -1424,13 +1437,13 @@ class ParameterPack final : public Node { initializePackExpansion(OB); size_t Idx = OB.CurrentPackIndex; if (Idx < Data.size()) - Data[Idx]->printLeft(OB); + OB.printLeft(*Data[Idx]); } void printRight(OutputBuffer &OB) const override { initializePackExpansion(OB); size_t Idx = OB.CurrentPackIndex; if (Idx < Data.size()) - Data[Idx]->printRight(OB); + OB.printRight(*Data[Idx]); } }; @@ -1588,13 +1601,13 @@ struct ForwardTemplateReference : Node { if (Printing) return; ScopedOverride SavePrinting(Printing, true); - Ref->printLeft(OB); + OB.printLeft(*Ref); } void printRight(OutputBuffer &OB) const override { if (Printing) return; ScopedOverride SavePrinting(Printing, true); - Ref->printRight(OB); + OB.printRight(*Ref); } }; @@ -1746,7 +1759,7 @@ class DtorName : public Node { void printLeft(OutputBuffer &OB) const override { OB += "~"; - Base->printLeft(OB); + OB.printLeft(*Base); } }; @@ -2026,7 +2039,7 @@ class CastExpr : public Node { { ScopedOverride LT(OB.GtIsGt, 0); OB += "<"; - To->printLeft(OB); + OB.printLeft(*To); OB += ">"; } OB.printOpen(); @@ -5946,6 +5959,10 @@ struct ManglingParser : AbstractManglingParser, Alloc> { Alloc>::AbstractManglingParser; }; +inline void OutputBuffer::printLeft(const Node &N) { N.printLeft(*this); } + +inline void OutputBuffer::printRight(const Node &N) { N.printRight(*this); } + DEMANGLE_NAMESPACE_END #ifdef _LIBCXXABI_COMPILER_CLANG diff --git a/libcxxabi/src/demangle/Utility.h b/libcxxabi/src/demangle/Utility.h index f1fad35d60d98..511983ad40f7a 100644 --- a/libcxxabi/src/demangle/Utility.h +++ b/libcxxabi/src/demangle/Utility.h @@ -27,6 +27,8 @@ DEMANGLE_NAMESPACE_BEGIN +class Node; + // Stream that AST nodes write their string representation into after the AST // has been parsed. class OutputBuffer { @@ -79,10 +81,24 @@ class OutputBuffer { OutputBuffer(const OutputBuffer &) = delete; OutputBuffer &operator=(const OutputBuffer &) = delete; + virtual ~OutputBuffer() {} + operator std::string_view() const { return std::string_view(Buffer, CurrentPosition); } + /// Called by the demangler when printing the demangle tree. By + /// default calls into \c Node::print{Left|Right} but can be overriden + /// by clients to track additional state when printing the demangled name. + virtual void printLeft(const Node &N); + virtual void printRight(const Node &N); + + /// Called when we write to this object anywhere other than the end. + virtual void notifyInsertion(size_t /*Position*/, size_t /*Count*/) {} + + /// Called when we make the \c CurrentPosition of this object smaller. + virtual void notifyDeletion(size_t /*OldPos*/, size_t /*NewPos*/) {} + /// If a ParameterPackExpansion (or similar type) is encountered, the offset /// into the pack that we're currently printing. unsigned CurrentPackIndex = std::numeric_limits::max(); @@ -126,6 +142,8 @@ class OutputBuffer { std::memcpy(Buffer, &*R.begin(), Size); CurrentPosition += Size; + notifyInsertion(/*Position=*/0, /*Count=*/Size); + return *this; } @@ -161,14 +179,20 @@ class OutputBuffer { DEMANGLE_ASSERT(Pos <= CurrentPosition, ""); if (N == 0) return; + grow(N); std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos); std::memcpy(Buffer + Pos, S, N); CurrentPosition += N; + + notifyInsertion(Pos, N); } size_t getCurrentPosition() const { return CurrentPosition; } - void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; } + void setCurrentPosition(size_t NewPos) { + notifyDeletion(CurrentPosition, NewPos); + CurrentPosition = NewPos; + } char back() const { DEMANGLE_ASSERT(CurrentPosition, ""); diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h index e7c008be32f9e..7de43205a7953 100644 --- a/llvm/include/llvm/Demangle/ItaniumDemangle.h +++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h @@ -277,20 +277,11 @@ class Node { } void print(OutputBuffer &OB) const { - printLeft(OB); + OB.printLeft(*this); if (RHSComponentCache != Cache::No) - printRight(OB); + OB.printRight(*this); } - // Print the "left" side of this Node into OutputBuffer. - virtual void printLeft(OutputBuffer &) const = 0; - - // Print the "right". This distinction is necessary to represent C++ types - // that appear on the RHS of their subtype, such as arrays or functions. - // Since most types don't have such a component, provide a default - // implementation. - virtual void printRight(OutputBuffer &) const {} - virtual std::string_view getBaseName() const { return {}; } // Silence compiler warnings, this dtor will never be called. @@ -299,6 +290,24 @@ class Node { #ifndef NDEBUG DEMANGLE_DUMP_METHOD void dump() const; #endif + +private: + friend class OutputBuffer; + + // Print the "left" side of this Node into OutputBuffer. + // + // Note, should only be called from OutputBuffer implementations. + // Call \ref OutputBuffer::printLeft instead. + virtual void printLeft(OutputBuffer &) const = 0; + + // Print the "right". This distinction is necessary to represent C++ types + // that appear on the RHS of their subtype, such as arrays or functions. + // Since most types don't have such a component, provide a default + // implementation. + // + // Note, should only be called from OutputBuffer implementations. + // Call \ref OutputBuffer::printRight instead. + virtual void printRight(OutputBuffer &) const {} }; class NodeArray { @@ -443,11 +452,11 @@ class QualType final : public Node { } void printLeft(OutputBuffer &OB) const override { - Child->printLeft(OB); + OB.printLeft(*Child); printQuals(OB); } - void printRight(OutputBuffer &OB) const override { Child->printRight(OB); } + void printRight(OutputBuffer &OB) const override { OB.printRight(*Child); } }; class ConversionOperatorType final : public Node { @@ -476,7 +485,7 @@ class PostfixQualifiedType final : public Node { template void match(Fn F) const { F(Ty, Postfix); } void printLeft(OutputBuffer &OB) const override { - Ty->printLeft(OB); + OB.printLeft(*Ty); OB += Postfix; } }; @@ -562,7 +571,7 @@ struct AbiTagAttr : Node { std::string_view getBaseName() const override { return Base->getBaseName(); } void printLeft(OutputBuffer &OB) const override { - Base->printLeft(OB); + OB.printLeft(*Base); OB += "[abi:"; OB += Tag; OB += "]"; @@ -629,7 +638,7 @@ class PointerType final : public Node { // We rewrite objc_object* into id. if (Pointee->getKind() != KObjCProtoName || !static_cast(Pointee)->isObjCObject()) { - Pointee->printLeft(OB); + OB.printLeft(*Pointee); if (Pointee->hasArray(OB)) OB += " "; if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) @@ -648,7 +657,7 @@ class PointerType final : public Node { !static_cast(Pointee)->isObjCObject()) { if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) OB += ")"; - Pointee->printRight(OB); + OB.printRight(*Pointee); } } }; @@ -714,7 +723,7 @@ class ReferenceType : public Node { std::pair Collapsed = collapse(OB); if (!Collapsed.second) return; - Collapsed.second->printLeft(OB); + OB.printLeft(*Collapsed.second); if (Collapsed.second->hasArray(OB)) OB += " "; if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) @@ -731,7 +740,7 @@ class ReferenceType : public Node { return; if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) OB += ")"; - Collapsed.second->printRight(OB); + OB.printRight(*Collapsed.second); } }; @@ -751,7 +760,7 @@ class PointerToMemberType final : public Node { } void printLeft(OutputBuffer &OB) const override { - MemberType->printLeft(OB); + OB.printLeft(*MemberType); if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) OB += "("; else @@ -763,7 +772,7 @@ class PointerToMemberType final : public Node { void printRight(OutputBuffer &OB) const override { if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) OB += ")"; - MemberType->printRight(OB); + OB.printRight(*MemberType); } }; @@ -783,7 +792,7 @@ class ArrayType final : public Node { bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } bool hasArraySlow(OutputBuffer &) const override { return true; } - void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); } + void printLeft(OutputBuffer &OB) const override { OB.printLeft(*Base); } void printRight(OutputBuffer &OB) const override { if (OB.back() != ']') @@ -792,7 +801,7 @@ class ArrayType final : public Node { if (Dimension) Dimension->print(OB); OB += "]"; - Base->printRight(OB); + OB.printRight(*Base); } }; @@ -827,7 +836,7 @@ class FunctionType final : public Node { // by printing out the return types's left, then print our parameters, then // finally print right of the return type. void printLeft(OutputBuffer &OB) const override { - Ret->printLeft(OB); + OB.printLeft(*Ret); OB += " "; } @@ -835,7 +844,7 @@ class FunctionType final : public Node { OB.printOpen(); Params.printWithComma(OB); OB.printClose(); - Ret->printRight(OB); + OB.printRight(*Ret); if (CVQuals & QualConst) OB += " const"; @@ -940,6 +949,8 @@ class FunctionEncoding final : public Node { FunctionRefQual getRefQual() const { return RefQual; } NodeArray getParams() const { return Params; } const Node *getReturnType() const { return Ret; } + const Node *getAttrs() const { return Attrs; } + const Node *getRequires() const { return Requires; } bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } bool hasFunctionSlow(OutputBuffer &) const override { return true; } @@ -948,10 +959,11 @@ class FunctionEncoding final : public Node { void printLeft(OutputBuffer &OB) const override { if (Ret) { - Ret->printLeft(OB); + OB.printLeft(*Ret); if (!Ret->hasRHSComponent(OB)) OB += " "; } + Name->print(OB); } @@ -959,8 +971,9 @@ class FunctionEncoding final : public Node { OB.printOpen(); Params.printWithComma(OB); OB.printClose(); + if (Ret) - Ret->printRight(OB); + OB.printRight(*Ret); if (CVQuals & QualConst) OB += " const"; @@ -1300,14 +1313,14 @@ class NonTypeTemplateParamDecl final : public Node { template void match(Fn F) const { F(Name, Type); } void printLeft(OutputBuffer &OB) const override { - Type->printLeft(OB); + OB.printLeft(*Type); if (!Type->hasRHSComponent(OB)) OB += " "; } void printRight(OutputBuffer &OB) const override { Name->print(OB); - Type->printRight(OB); + OB.printRight(*Type); } }; @@ -1352,11 +1365,11 @@ class TemplateParamPackDecl final : public Node { template void match(Fn F) const { F(Param); } void printLeft(OutputBuffer &OB) const override { - Param->printLeft(OB); + OB.printLeft(*Param); OB += "..."; } - void printRight(OutputBuffer &OB) const override { Param->printRight(OB); } + void printRight(OutputBuffer &OB) const override { OB.printRight(*Param); } }; /// An unexpanded parameter pack (either in the expression or type context). If @@ -1423,13 +1436,13 @@ class ParameterPack final : public Node { initializePackExpansion(OB); size_t Idx = OB.CurrentPackIndex; if (Idx < Data.size()) - Data[Idx]->printLeft(OB); + OB.printLeft(*Data[Idx]); } void printRight(OutputBuffer &OB) const override { initializePackExpansion(OB); size_t Idx = OB.CurrentPackIndex; if (Idx < Data.size()) - Data[Idx]->printRight(OB); + OB.printRight(*Data[Idx]); } }; @@ -1587,13 +1600,13 @@ struct ForwardTemplateReference : Node { if (Printing) return; ScopedOverride SavePrinting(Printing, true); - Ref->printLeft(OB); + OB.printLeft(*Ref); } void printRight(OutputBuffer &OB) const override { if (Printing) return; ScopedOverride SavePrinting(Printing, true); - Ref->printRight(OB); + OB.printRight(*Ref); } }; @@ -1745,7 +1758,7 @@ class DtorName : public Node { void printLeft(OutputBuffer &OB) const override { OB += "~"; - Base->printLeft(OB); + OB.printLeft(*Base); } }; @@ -2025,7 +2038,7 @@ class CastExpr : public Node { { ScopedOverride LT(OB.GtIsGt, 0); OB += "<"; - To->printLeft(OB); + OB.printLeft(*To); OB += ">"; } OB.printOpen(); @@ -5945,6 +5958,10 @@ struct ManglingParser : AbstractManglingParser, Alloc> { Alloc>::AbstractManglingParser; }; +inline void OutputBuffer::printLeft(const Node &N) { N.printLeft(*this); } + +inline void OutputBuffer::printRight(const Node &N) { N.printRight(*this); } + DEMANGLE_NAMESPACE_END #ifdef _LIBCXXABI_COMPILER_CLANG diff --git a/llvm/include/llvm/Demangle/Utility.h b/llvm/include/llvm/Demangle/Utility.h index e893cceea2cdc..d59d74511dd4f 100644 --- a/llvm/include/llvm/Demangle/Utility.h +++ b/llvm/include/llvm/Demangle/Utility.h @@ -27,6 +27,8 @@ DEMANGLE_NAMESPACE_BEGIN +class Node; + // Stream that AST nodes write their string representation into after the AST // has been parsed. class OutputBuffer { @@ -79,10 +81,24 @@ class OutputBuffer { OutputBuffer(const OutputBuffer &) = delete; OutputBuffer &operator=(const OutputBuffer &) = delete; + virtual ~OutputBuffer() {} + operator std::string_view() const { return std::string_view(Buffer, CurrentPosition); } + /// Called by the demangler when printing the demangle tree. By + /// default calls into \c Node::print{Left|Right} but can be overriden + /// by clients to track additional state when printing the demangled name. + virtual void printLeft(const Node &N); + virtual void printRight(const Node &N); + + /// Called when we write to this object anywhere other than the end. + virtual void notifyInsertion(size_t /*Position*/, size_t /*Count*/) {} + + /// Called when we make the \c CurrentPosition of this object smaller. + virtual void notifyDeletion(size_t /*OldPos*/, size_t /*NewPos*/) {} + /// If a ParameterPackExpansion (or similar type) is encountered, the offset /// into the pack that we're currently printing. unsigned CurrentPackIndex = std::numeric_limits::max(); @@ -126,6 +142,8 @@ class OutputBuffer { std::memcpy(Buffer, &*R.begin(), Size); CurrentPosition += Size; + notifyInsertion(/*Position=*/0, /*Count=*/Size); + return *this; } @@ -161,14 +179,20 @@ class OutputBuffer { DEMANGLE_ASSERT(Pos <= CurrentPosition, ""); if (N == 0) return; + grow(N); std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos); std::memcpy(Buffer + Pos, S, N); CurrentPosition += N; + + notifyInsertion(Pos, N); } size_t getCurrentPosition() const { return CurrentPosition; } - void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; } + void setCurrentPosition(size_t NewPos) { + notifyDeletion(CurrentPosition, NewPos); + CurrentPosition = NewPos; + } char back() const { DEMANGLE_ASSERT(CurrentPosition, ""); diff --git a/llvm/unittests/Demangle/ItaniumDemangleTest.cpp b/llvm/unittests/Demangle/ItaniumDemangleTest.cpp index bc6ccc2e16e65..329f33215817a 100644 --- a/llvm/unittests/Demangle/ItaniumDemangleTest.cpp +++ b/llvm/unittests/Demangle/ItaniumDemangleTest.cpp @@ -98,7 +98,7 @@ TEST(ItaniumDemangle, HalfType) { Node *parseType() { OutputBuffer OB; Node *N = AbstractManglingParser::parseType(); - N->printLeft(OB); + OB.printLeft(*N); std::string_view Name = N->getBaseName(); if (!Name.empty()) Types.push_back(std::string(Name.begin(), Name.end())); diff --git a/llvm/unittests/Demangle/OutputBufferTest.cpp b/llvm/unittests/Demangle/OutputBufferTest.cpp index 76031e523d781..4a30e66eee48e 100644 --- a/llvm/unittests/Demangle/OutputBufferTest.cpp +++ b/llvm/unittests/Demangle/OutputBufferTest.cpp @@ -93,3 +93,40 @@ TEST(OutputBufferTest, Extend) { std::free(OB.getBuffer()); } + +TEST(OutputBufferTest, Notifications) { + struct MyOutputBuffer : public OutputBuffer { + size_t Inserted = 0; + size_t LatestPos = 0; + + void notifyDeletion(size_t OldPos, size_t NewPos) override { + LatestPos = NewPos; + } + + void notifyInsertion(size_t Position, size_t Count) override { + Inserted += Count; + LatestPos = Position; + } + } OB; + + OB.prepend("n"); + EXPECT_EQ(OB.Inserted, 1U); + EXPECT_EQ(OB.LatestPos, 0U); + + OB.prepend(""); + EXPECT_EQ(OB.Inserted, 1U); + EXPECT_EQ(OB.LatestPos, 0U); + + OB.prepend("abc"); + EXPECT_EQ(OB.Inserted, 4U); + EXPECT_EQ(OB.LatestPos, 0U); + + OB.insert(2, "abc", 3U); + EXPECT_EQ(OB.Inserted, 7U); + EXPECT_EQ(OB.LatestPos, 2U); + + OB.setCurrentPosition(3U); + EXPECT_EQ(OB.LatestPos, 3U); + + std::free(OB.getBuffer()); +} From 1025be09bdd3e8e452fc4d956836c7ddf2b8eed3 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Wed, 23 Apr 2025 15:40:04 +0100 Subject: [PATCH 09/28] [ItaniumDemangle][test] Add test-cases for ref-qualified member pointer parameters I noticed that there are test-cases that are commented out. But the manglings for them seem to be impossible to generate from valid C++. I added two test-cases generated from following C++ program: ``` struct X { int func() const && { return 5; } const int &&func2() { return 5; } const int &&func3(const int &x) volatile { return 5; } }; void f(int (X::*)() const &&, int const && (X::*)(), int const && (X::*)(const int &) volatile) {} int main() { f(&X::func, &X::func2, &X::func3); return 0; } ``` (cherry picked from commit 46f18b7c6febe75b2cc0095f2227d935c14f70f2) --- libcxxabi/test/test_demangle.pass.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libcxxabi/test/test_demangle.pass.cpp b/libcxxabi/test/test_demangle.pass.cpp index fe5598991b831..546082c7b8668 100644 --- a/libcxxabi/test/test_demangle.pass.cpp +++ b/libcxxabi/test/test_demangle.pass.cpp @@ -29662,6 +29662,8 @@ const char* cases[][2] = {"_ZNKO1X1hEv", "X::h() const &&"}, // {"_Z1fM1XVKFivEMS_VFivEMS_KOFivE", "f(int (X::*)() const volatile, int (X::*)() volatile, int (X::*)() const &&)"}, // {"_Z1fM1XRFivEMS_OFivEMS_KOFivE", "f(int (X::*)() &, int (X::*)() &&, int (X::*)() const &&)"}, + {"_Z1fM1XKFivOE", "f(int (X::*)() const &&)"}, + {"_Z1fM1XKFivOEMS_FOKivEMS_VFS3_RS2_E", "f(int (X::*)() const &&, int const&& (X::*)(), int const&& (X::*)(int const&) volatile)"}, {"_ZN5test12f0ENS_1TILZNS_1xEEEE", "test1::f0(test1::T)"}, {"_ZN5test12f1ENS_2t1ILZNS_2f0EfEEE", "test1::f1(test1::t1)"}, {"_ZN5test22f1ENS_2t1IXadL_ZNS_2f0EfEEEE", "test2::f1(test2::t1<&test2::f0(float)>)"}, From 0226820778aa7241197ca557ab841cce530da064 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Mon, 7 Apr 2025 13:22:27 +0100 Subject: [PATCH 10/28] [lldb] Implement TrackingOutputBuffer to track demangled name information (#131836) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch implements a new `TrackingOutputBuffer` which tracks where the scope/basename/arguments begin and end in the demangled string. The idea that a function name can be decomposed into . The assumption is that given the ranges of those three elements and the demangled name, LLDB will be able to to reconstruct the full demangled name. The tracking of those ranges is pretty simple. We don’t ever deal with nesting, so whenever we recurse into a template argument list or another function type, we just stop tracking any positions. Once we recursed out of those, and are back to printing the top-level function name, we continue tracking the positions. We introduce a new structure `FunctionNameInfo` that holds all this information and is stored in the new `TrackingOutputBuffer` class. Tests are in `ItaniumDemangleTest.cpp`. https://github.com/llvm/llvm-project/pull/131836 (cherry picked from commit 9c830cef3d7c2f1adfadcc0026a73ba2cdbeef05) --- lldb/include/lldb/Core/DemangledNameInfo.h | 153 +++++++++++++++ lldb/source/Core/CMakeLists.txt | 1 + lldb/source/Core/DemangledNameInfo.cpp | 213 +++++++++++++++++++++ lldb/unittests/Core/MangledTest.cpp | 143 ++++++++++++++ 4 files changed, 510 insertions(+) create mode 100644 lldb/include/lldb/Core/DemangledNameInfo.h create mode 100644 lldb/source/Core/DemangledNameInfo.cpp diff --git a/lldb/include/lldb/Core/DemangledNameInfo.h b/lldb/include/lldb/Core/DemangledNameInfo.h new file mode 100644 index 0000000000000..51cd152bf79d8 --- /dev/null +++ b/lldb/include/lldb/Core/DemangledNameInfo.h @@ -0,0 +1,153 @@ +//===-- DemangledNameInfo.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_CORE_DEMANGLEDNAMEINFO_H +#define LLDB_CORE_DEMANGLEDNAMEINFO_H + +#include "llvm/Demangle/ItaniumDemangle.h" +#include "llvm/Demangle/Utility.h" + +#include +#include + +namespace lldb_private { + +/// Stores information about where certain portions of a demangled +/// function name begin and end. +struct DemangledNameInfo { + /// A [start, end) pair for the function basename. + /// The basename is the name without scope qualifiers + /// and without template parameters. E.g., + /// \code{.cpp} + /// void foo::bar::someFunc(int) const && + /// ^ ^ + /// start end + /// \endcode + std::pair BasenameRange; + + /// A [start, end) pair for the function scope qualifiers. + /// E.g., for + /// \code{.cpp} + /// void foo::bar::qux(int) const && + /// ^ ^ + /// start end + /// \endcode + std::pair ScopeRange; + + /// Indicates the [start, end) of the function argument lits. + /// E.g., + /// \code{.cpp} + /// int (*getFunc(float, double))(int, int) + /// ^ ^ + /// start end + /// \endcode + std::pair ArgumentsRange; + + /// Returns \c true if this object holds a valid basename range. + bool hasBasename() const { + return BasenameRange.first != BasenameRange.second && + BasenameRange.second > 0; + } + + friend bool operator==(const DemangledNameInfo &lhs, + const DemangledNameInfo &rhs) { + return std::tie(lhs.BasenameRange, lhs.ArgumentsRange, lhs.ScopeRange, + lhs.QualifiersRange) == + std::tie(rhs.BasenameRange, rhs.ArgumentsRange, rhs.ScopeRange, + lhs.QualifiersRange); + } + + friend bool operator!=(const DemangledNameInfo &lhs, + const DemangledNameInfo &rhs) { + return !(lhs == rhs); + } +}; + +/// An OutputBuffer which keeps a record of where certain parts of a +/// demangled name begin/end (e.g., basename, scope, argument list, etc.). +/// The tracking occurs during printing of the Itanium demangle tree. +/// +/// Usage: +/// \code{.cpp} +/// +/// Node *N = mangling_parser.parseType(); +/// +/// TrackingOutputBuffer buffer; +/// N->printLeft(OB); +/// +/// assert (buffer.NameInfo.hasBasename()); +/// +/// \endcode +struct TrackingOutputBuffer : public llvm::itanium_demangle::OutputBuffer { + using OutputBuffer::OutputBuffer; + + /// Holds information about the demangled name that is + /// being printed into this buffer. + DemangledNameInfo NameInfo; + + void printLeft(const llvm::itanium_demangle::Node &N) override; + void printRight(const llvm::itanium_demangle::Node &N) override; + +private: + void printLeftImpl(const llvm::itanium_demangle::FunctionType &N); + void printRightImpl(const llvm::itanium_demangle::FunctionType &N); + + void printLeftImpl(const llvm::itanium_demangle::FunctionEncoding &N); + void printRightImpl(const llvm::itanium_demangle::FunctionEncoding &N); + + void printLeftImpl(const llvm::itanium_demangle::NestedName &N); + void printLeftImpl(const llvm::itanium_demangle::NameWithTemplateArgs &N); + + /// Called whenever we start printing a function type in the Itanium + /// mangling scheme. Examples include \ref FunctionEncoding, \ref + /// FunctionType, etc. + /// + /// \returns A ScopedOverride which will update the nesting depth of + /// currently printed function types on destruction. + [[nodiscard]] llvm::itanium_demangle::ScopedOverride + enterFunctionTypePrinting(); + + /// Returns \c true if we're not printing any nested function types, + /// just a \ref FunctionEncoding in the Itanium mangling scheme. + bool isPrintingTopLevelFunctionType() const; + + /// If this object \ref shouldTrack, then update the end of + /// the basename range to the current \c OB position. + void updateBasenameEnd(); + + /// If this object \ref shouldTrack, then update the beginning + /// of the scope range to the current \c OB position. + void updateScopeStart(); + + /// If this object \ref shouldTrack, then update the end of + /// the scope range to the current \c OB position. + void updateScopeEnd(); + + /// Returns \c true if the members of this object can be + /// updated. E.g., when we're printing nested template + /// arguments, we don't need to be tracking basename + /// locations. + bool shouldTrack() const; + + /// Helpers called to track beginning and end of the function + /// arguments. + void finalizeArgumentEnd(); + void finalizeStart(); + void finalizeEnd(); + + /// Helper used in the finalize APIs. + bool canFinalize() const; + + /// Incremented each time we start printing a function type node + /// in the Itanium mangling scheme (e.g., \ref FunctionEncoding + /// or \ref FunctionType). + unsigned FunctionPrintingDepth = 0; +}; +} // namespace lldb_private + +#endif // LLDB_CORE_DEMANGLEDNAMEINFO_H diff --git a/lldb/source/Core/CMakeLists.txt b/lldb/source/Core/CMakeLists.txt index 00b9f2390c15f..c6bb3cded801a 100644 --- a/lldb/source/Core/CMakeLists.txt +++ b/lldb/source/Core/CMakeLists.txt @@ -28,6 +28,7 @@ add_lldb_library(lldbCore Debugger.cpp DebuggerEvents.cpp Declaration.cpp + DemangledNameInfo.cpp Disassembler.cpp DumpDataExtractor.cpp DumpRegisterValue.cpp diff --git a/lldb/source/Core/DemangledNameInfo.cpp b/lldb/source/Core/DemangledNameInfo.cpp new file mode 100644 index 0000000000000..89757032409c2 --- /dev/null +++ b/lldb/source/Core/DemangledNameInfo.cpp @@ -0,0 +1,213 @@ +//===-- DemangledNameInfo.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DemangledNameInfo.h" + +using namespace llvm::itanium_demangle; + +namespace lldb_private { + +bool TrackingOutputBuffer::shouldTrack() const { + if (!isPrintingTopLevelFunctionType()) + return false; + + if (isGtInsideTemplateArgs()) + return false; + + if (NameInfo.ArgumentsRange.first > 0) + return false; + + return true; +} + +bool TrackingOutputBuffer::canFinalize() const { + if (!isPrintingTopLevelFunctionType()) + return false; + + if (isGtInsideTemplateArgs()) + return false; + + if (NameInfo.ArgumentsRange.first == 0) + return false; + + return true; +} + +void TrackingOutputBuffer::updateBasenameEnd() { + if (!shouldTrack()) + return; + + NameInfo.BasenameRange.second = getCurrentPosition(); +} + +void TrackingOutputBuffer::updateScopeStart() { + if (!shouldTrack()) + return; + + NameInfo.ScopeRange.first = getCurrentPosition(); +} + +void TrackingOutputBuffer::updateScopeEnd() { + if (!shouldTrack()) + return; + + NameInfo.ScopeRange.second = getCurrentPosition(); +} + +void TrackingOutputBuffer::finalizeArgumentEnd() { + if (!canFinalize()) + return; + + NameInfo.ArgumentsRange.second = getCurrentPosition(); +} + +void TrackingOutputBuffer::finalizeStart() { + if (!shouldTrack()) + return; + + NameInfo.ArgumentsRange.first = getCurrentPosition(); + + // If nothing has set the end of the basename yet (for example when + // printing templates), then the beginning of the arguments is the end of + // the basename. + if (NameInfo.BasenameRange.second == 0) + NameInfo.BasenameRange.second = getCurrentPosition(); + + assert(!shouldTrack()); + assert(canFinalize()); +} + +void TrackingOutputBuffer::finalizeEnd() { + if (!canFinalize()) + return; + + if (NameInfo.ScopeRange.first > NameInfo.ScopeRange.second) + NameInfo.ScopeRange.second = NameInfo.ScopeRange.first; + NameInfo.BasenameRange.first = NameInfo.ScopeRange.second; +} + +ScopedOverride TrackingOutputBuffer::enterFunctionTypePrinting() { + return {FunctionPrintingDepth, FunctionPrintingDepth + 1}; +} + +bool TrackingOutputBuffer::isPrintingTopLevelFunctionType() const { + return FunctionPrintingDepth == 1; +} + +void TrackingOutputBuffer::printLeft(const Node &N) { + switch (N.getKind()) { + case Node::KFunctionType: + printLeftImpl(static_cast(N)); + break; + case Node::KFunctionEncoding: + printLeftImpl(static_cast(N)); + break; + case Node::KNestedName: + printLeftImpl(static_cast(N)); + break; + case Node::KNameWithTemplateArgs: + printLeftImpl(static_cast(N)); + break; + default: + OutputBuffer::printLeft(N); + } +} + +void TrackingOutputBuffer::printRight(const Node &N) { + switch (N.getKind()) { + case Node::KFunctionType: + printRightImpl(static_cast(N)); + break; + case Node::KFunctionEncoding: + printRightImpl(static_cast(N)); + break; + default: + OutputBuffer::printRight(N); + } +} + +void TrackingOutputBuffer::printLeftImpl(const FunctionType &N) { + auto Scoped = enterFunctionTypePrinting(); + OutputBuffer::printLeft(N); +} + +void TrackingOutputBuffer::printRightImpl(const FunctionType &N) { + auto Scoped = enterFunctionTypePrinting(); + OutputBuffer::printRight(N); +} + +void TrackingOutputBuffer::printLeftImpl(const FunctionEncoding &N) { + auto Scoped = enterFunctionTypePrinting(); + + const Node *Ret = N.getReturnType(); + if (Ret) { + printLeft(*Ret); + if (!Ret->hasRHSComponent(*this)) + *this += " "; + } + + updateScopeStart(); + + N.getName()->print(*this); +} + +void TrackingOutputBuffer::printRightImpl(const FunctionEncoding &N) { + auto Scoped = enterFunctionTypePrinting(); + finalizeStart(); + + printOpen(); + N.getParams().printWithComma(*this); + printClose(); + + finalizeArgumentEnd(); + + const Node *Ret = N.getReturnType(); + + if (Ret) + printRight(*Ret); + + auto CVQuals = N.getCVQuals(); + auto RefQual = N.getRefQual(); + auto *Attrs = N.getAttrs(); + auto *Requires = N.getRequires(); + + if (CVQuals & QualConst) + *this += " const"; + if (CVQuals & QualVolatile) + *this += " volatile"; + if (CVQuals & QualRestrict) + *this += " restrict"; + if (RefQual == FrefQualLValue) + *this += " &"; + else if (RefQual == FrefQualRValue) + *this += " &&"; + if (Attrs != nullptr) + Attrs->print(*this); + if (Requires != nullptr) { + *this += " requires "; + Requires->print(*this); + } + + finalizeEnd(); +} + +void TrackingOutputBuffer::printLeftImpl(const NestedName &N) { + N.Qual->print(*this); + *this += "::"; + updateScopeEnd(); + N.Name->print(*this); + updateBasenameEnd(); +} + +void TrackingOutputBuffer::printLeftImpl(const NameWithTemplateArgs &N) { + N.Name->print(*this); + updateBasenameEnd(); + N.TemplateArgs->print(*this); +} + +} // namespace lldb_private diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp index a3760ba43b3c9..b039299d032dd 100644 --- a/lldb/unittests/Core/MangledTest.cpp +++ b/lldb/unittests/Core/MangledTest.cpp @@ -11,6 +11,7 @@ #include "TestingSupport/SubsystemRAII.h" #include "TestingSupport/TestUtilities.h" +#include "lldb/Core/DemangledNameInfo.h" #include "lldb/Core/Mangled.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" @@ -319,3 +320,145 @@ TEST(MangledTest, NameIndexes_FindFunctionSymbols) { EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeBase)); EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeMethod)); } + +struct DemanglingPartsTestCase { + const char *mangled; + DemangledNameInfo expected_info; + std::string_view basename; + std::string_view scope; + bool valid_basename = true; +}; + +DemanglingPartsTestCase g_demangling_parts_test_cases[] = { + // clang-format off + { "_ZNVKO3BarIN2ns3QuxIiEEE1CIPFi3FooIS_IiES6_EEE6methodIS6_EENS5_IT_SC_E5InnerIiEESD_SD_", + { .BasenameRange = {92, 98}, .ScopeRange = {36, 92}, .ArgumentsRange = { 108, 158 } }, + .basename = "method", + .scope = "Bar>::C, Bar>)>::" + }, + { "_Z7getFuncIfEPFiiiET_", + { .BasenameRange = {6, 13}, .ScopeRange = {6, 6}, .ArgumentsRange = { 20, 27 } }, + .basename = "getFunc", + .scope = "" + }, + { "_ZN1f1b1c1gEv", + { .BasenameRange = {9, 10}, .ScopeRange = {0, 9}, .ArgumentsRange = { 10, 12 } }, + .basename = "g", + .scope = "f::b::c::" + }, + { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bEEEcvT__EES2_", + { .BasenameRange = {45, 48}, .ScopeRange = {38, 45}, .ArgumentsRange = { 53, 58 } }, + .basename = "fD1", + .scope = "test7::" + }, + { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_", + { .BasenameRange = {61, 64}, .ScopeRange = {54, 61}, .ArgumentsRange = { 69, 79 } }, + .basename = "fD1", + .scope = "test7::" + }, + { "_ZN5test7INDT1cE1dINDT1cE1dEEEE3fD1INDT1cE1dINDT1cE1dEEEEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_", + { .BasenameRange = {120, 123}, .ScopeRange = {81, 120}, .ArgumentsRange = { 155, 168 } }, + .basename = "fD1", + .scope = "test7>::" + }, + { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvE5parseIRA29_KcEESE_OT_NS2_8functionIFbiNS0_6detail13parse_event_tERSE_EEEbb", + { .BasenameRange = {687, 692}, .ScopeRange = {343, 687}, .ArgumentsRange = { 713, 1174 } }, + .basename = "parse", + .scope = "nlohmann::json_abi_v3_11_3::basic_json, std::__1::allocator>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector>, void>::" + }, + { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvEC1EDn", + { .BasenameRange = {344, 354}, .ScopeRange = {0, 344}, .ArgumentsRange = { 354, 370 } }, + .basename = "basic_json", + .scope = "nlohmann::json_abi_v3_11_3::basic_json, std::__1::allocator>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector>, void>::" + }, + { "_Z3fppIiEPFPFvvEiEf", + { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 18, 25 } }, + .basename = "fpp", + .scope = "" + }, + { "_Z3fppIiEPFPFvvEN2ns3FooIiEEEf", + { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 18, 25 } }, + .basename = "fpp", + .scope = "" + }, + { "_Z3fppIiEPFPFvPFN2ns3FooIiEENS2_3BarIfE3QuxEEEPFS2_S2_EEf", + { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 18, 25 } }, + .basename = "fpp", + .scope = "" + }, + { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvEiEf", + { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 72, 79 } }, + .basename = "fpp", + .scope = "ns::HasFuncs::Bar::Qux>>::" + }, + { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvES2_Ef", + { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 72, 79 } }, + .basename = "fpp", + .scope = "ns::HasFuncs::Bar::Qux>>::" + }, + { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvPFS2_S5_EEPFS2_S2_EEf", + { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 72, 79 } }, + .basename = "fpp", + .scope = "ns::HasFuncs::Bar::Qux>>::" + }, + { "_ZTV11ImageLoader", + { .BasenameRange = {0, 0}, .ScopeRange = {0, 0}, .ArgumentsRange = { 0, 0 } }, + .basename = "", + .scope = "", + .valid_basename = false + } + // clang-format on +}; + +struct DemanglingPartsTestFixture + : public ::testing::TestWithParam {}; + +namespace { +class TestAllocator { + llvm::BumpPtrAllocator Alloc; + +public: + void reset() { Alloc.Reset(); } + + template T *makeNode(Args &&...args) { + return new (Alloc.Allocate(sizeof(T), alignof(T))) + T(std::forward(args)...); + } + + void *allocateNodeArray(size_t sz) { + return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz, + alignof(llvm::itanium_demangle::Node *)); + } +}; +} // namespace + +TEST_P(DemanglingPartsTestFixture, DemanglingParts) { + const auto &[mangled, info, basename, scope, valid_basename] = GetParam(); + + llvm::itanium_demangle::ManglingParser Parser( + mangled, mangled + ::strlen(mangled)); + + const auto *Root = Parser.parse(); + + ASSERT_NE(nullptr, Root); + + TrackingOutputBuffer OB; + Root->print(OB); + auto demangled = std::string_view(OB); + + ASSERT_EQ(OB.NameInfo.hasBasename(), valid_basename); + + EXPECT_EQ(OB.NameInfo.BasenameRange, info.BasenameRange); + EXPECT_EQ(OB.NameInfo.ScopeRange, info.ScopeRange); + EXPECT_EQ(OB.NameInfo.ArgumentsRange, info.ArgumentsRange); + + auto get_part = [&](const std::pair &loc) { + return demangled.substr(loc.first, loc.second - loc.first); + }; + + EXPECT_EQ(get_part(OB.NameInfo.BasenameRange), basename); + EXPECT_EQ(get_part(OB.NameInfo.ScopeRange), scope); +} + +INSTANTIATE_TEST_SUITE_P(DemanglingPartsTests, DemanglingPartsTestFixture, + ::testing::ValuesIn(g_demangling_parts_test_cases)); From 8e5e1b2d0207b56280738093d2e8e74e3a903ab5 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Mon, 10 Mar 2025 15:39:10 +0000 Subject: [PATCH 11/28] [lldb][Mangled] Add API to force re-demangling a Mangled object (#131836) Add version of GetDemangledName that will force re-demangling. This is required because LLDB will SetDemangledName without going through the demangler. So we need a way to force demangling to set the m_demangled_info member when we need it. https://github.com/llvm/llvm-project/pull/131836 (cherry picked from commit f220ea2947b9c8b1e33db65b008086d47fa72af3) --- lldb/include/lldb/Core/Mangled.h | 17 +++- lldb/source/Core/Mangled.cpp | 146 +++++++++++++++++-------------- 2 files changed, 91 insertions(+), 72 deletions(-) diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h index 11f2ac08da675..3bc65b3468c5a 100644 --- a/lldb/include/lldb/Core/Mangled.h +++ b/lldb/include/lldb/Core/Mangled.h @@ -277,10 +277,19 @@ class Mangled { void Encode(DataEncoder &encoder, ConstStringTable &strtab) const; private: - /// Mangled member variables. - ConstString m_mangled; ///< The mangled version of the name - mutable ConstString m_demangled; ///< Mutable so we can get it on demand with - ///a const version of this object + /// 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 + /// on this object was not previously demangled), demangle and cache the + /// name. + ConstString GetDemangledNameImpl(bool force, + const SymbolContext *sc = nullptr) const; + + /// The mangled version of the name. + ConstString m_mangled; + + /// Mutable so we can get it on demand with + /// a const version of this object. + mutable ConstString m_demangled; }; Stream &operator<<(Stream &s, const Mangled &obj); diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index ccc6af2a83e4a..23c7663166812 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -274,81 +274,91 @@ bool Mangled::GetRichManglingInfo(RichManglingContext &context, llvm_unreachable("Fully covered switch above!"); } +ConstString Mangled::GetDemangledName( // BEGIN SWIFT + const SymbolContext *sc + // END SWIFT +) const { + return GetDemangledNameImpl(/*force=*/false, sc); +} + // Generate the demangled name on demand using this accessor. Code in this // class will need to use this accessor if it wishes to decode the demangled // name. The result is cached and will be kept until a new string value is // supplied to this object, or until the end of the object's lifetime. -ConstString Mangled::GetDemangledName(// BEGIN SWIFT - const SymbolContext *sc - // END SWIFT - ) const { - // Check to make sure we have a valid mangled name and that we haven't - // already decoded our mangled name. - if (m_mangled && m_demangled.IsNull()) { - // Don't bother running anything that isn't mangled - const char *mangled_name = m_mangled.GetCString(); - ManglingScheme mangling_scheme = - GetManglingScheme(m_mangled.GetStringRef()); - if (mangling_scheme != eManglingSchemeNone && - !m_mangled.GetMangledCounterpart(m_demangled)) { - // We didn't already mangle this name, demangle it and if all goes well - // add it to our map. - char *demangled_name = nullptr; - switch (mangling_scheme) { - case eManglingSchemeMSVC: - demangled_name = GetMSVCDemangledStr(mangled_name); - break; - case eManglingSchemeItanium: { - demangled_name = GetItaniumDemangledStr(mangled_name); - break; - } - case eManglingSchemeRustV0: - demangled_name = GetRustV0DemangledStr(m_mangled); - break; - case eManglingSchemeD: - demangled_name = GetDLangDemangledStr(m_mangled); - break; - case eManglingSchemeSwift: - // Demangling a swift name requires the swift compiler. This is - // explicitly unsupported on llvm.org. +ConstString Mangled::GetDemangledNameImpl(bool force, // BEGIN SWIFT + const SymbolContext *sc + // END SWIFT +) const { + if (!m_mangled) + return m_demangled; + + // Re-use previously demangled names. + if (!force && !m_demangled.IsNull()) + return m_demangled; + + if (!force && m_mangled.GetMangledCounterpart(m_demangled) && + !m_demangled.IsNull()) + return m_demangled; + + // We didn't already mangle this name, demangle it and if all goes well + // add it to our map. + char *demangled_name = nullptr; + switch (GetManglingScheme(m_mangled.GetStringRef())) { + case eManglingSchemeMSVC: + demangled_name = GetMSVCDemangledStr(m_mangled); + break; + case eManglingSchemeItanium: { + demangled_name = GetItaniumDemangledStr(m_mangled.GetCString()); + break; + } + case eManglingSchemeRustV0: + demangled_name = GetRustV0DemangledStr(m_mangled); + break; + case eManglingSchemeD: + demangled_name = GetDLangDemangledStr(m_mangled); + break; + case eManglingSchemeSwift: + // Demangling a swift name requires the swift compiler. This is + // explicitly unsupported on llvm.org. #ifdef LLDB_ENABLE_SWIFT - { - Log *log = GetLog(LLDBLog::Demangle); - LLDB_LOGF(log, "demangle swift: %s", mangled_name); - std::string demangled(SwiftLanguageRuntime::DemangleSymbolAsString( - mangled_name, SwiftLanguageRuntime::eTypeName, sc)); - // Don't cache the demangled name the function isn't available yet. - if (!sc || !sc->function) { - LLDB_LOGF(log, "demangle swift: %s -> \"%s\" (not cached)", - mangled_name, demangled.c_str()); - return ConstString(demangled); - } - if (demangled.empty()) { - LLDB_LOGF(log, "demangle swift: %s -> error: failed to demangle", - mangled_name); - } else { - LLDB_LOGF(log, "demangle swift: %s -> \"%s\"", mangled_name, - demangled.c_str()); - m_demangled.SetStringWithMangledCounterpart(demangled, m_mangled); - } - return m_demangled; - } -#endif // LLDB_ENABLE_SWIFT - break; - case eManglingSchemeNone: - llvm_unreachable("eManglingSchemeNone was handled already"); - } - if (demangled_name) { - m_demangled.SetStringWithMangledCounterpart( - llvm::StringRef(demangled_name), m_mangled); - free(demangled_name); - } + { + const char *mangled_name = m_mangled.GetCString(); + Log *log = GetLog(LLDBLog::Demangle); + LLDB_LOGF(log, "demangle swift: %s", mangled_name); + std::string demangled(SwiftLanguageRuntime::DemangleSymbolAsString( + mangled_name, SwiftLanguageRuntime::eTypeName, sc)); + // Don't cache the demangled name the function isn't available yet. + if (!sc || !sc->function) { + LLDB_LOGF(log, "demangle swift: %s -> \"%s\" (not cached)", mangled_name, + demangled.c_str()); + return ConstString(demangled); } - if (m_demangled.IsNull()) { - // Set the demangled string to the empty string to indicate we tried to - // parse it once and failed. - m_demangled.SetCString(""); + if (demangled.empty()) { + LLDB_LOGF(log, "demangle swift: %s -> error: failed to demangle", + mangled_name); + } else { + LLDB_LOGF(log, "demangle swift: %s -> \"%s\"", mangled_name, + demangled.c_str()); + m_demangled.SetStringWithMangledCounterpart(demangled, m_mangled); } + return m_demangled; + } +#endif // LLDB_ENABLE_SWIFT + break; + case eManglingSchemeNone: + // Don't bother demangling anything that isn't mangled. + break; + } + + if (demangled_name) { + m_demangled.SetStringWithMangledCounterpart(demangled_name, m_mangled); + free(demangled_name); + } + + if (m_demangled.IsNull()) { + // Set the demangled string to the empty string to indicate we tried to + // parse it once and failed. + m_demangled.SetCString(""); } return m_demangled; From 268253b3ce01bd0336cea3d58553c34d3c994207 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Mon, 7 Apr 2025 13:54:27 +0100 Subject: [PATCH 12/28] [lldb][Mangled] Retrieve and cache demangled name info (#131836) Uses the `TrackingOutputBuffer` to populate the new member `Mangled::m_demangled_info`. `m_demangled_info` is lazily popluated by `GetDemangledInfo`. To ensure `m_demangled` and `m_demangled_info` are in-sync we clear `m_demangled_info` anytime `m_demangled` is set/cleared. https://github.com/llvm/llvm-project/pull/131836 (cherry picked from commit a2672250be871bdac18c1a955265a98704434218) --- lldb/include/lldb/Core/DemangledNameInfo.h | 15 +- lldb/include/lldb/Core/Mangled.h | 20 ++- lldb/source/Core/DemangledNameInfo.cpp | 17 +++ lldb/source/Core/Mangled.cpp | 41 ++++- lldb/unittests/Core/MangledTest.cpp | 170 +++++++++++++++++---- llvm/include/llvm/Demangle/Demangle.h | 7 + llvm/lib/Demangle/ItaniumDemangle.cpp | 17 ++- 7 files changed, 243 insertions(+), 44 deletions(-) diff --git a/lldb/include/lldb/Core/DemangledNameInfo.h b/lldb/include/lldb/Core/DemangledNameInfo.h index 51cd152bf79d8..3926b45a96f3e 100644 --- a/lldb/include/lldb/Core/DemangledNameInfo.h +++ b/lldb/include/lldb/Core/DemangledNameInfo.h @@ -48,9 +48,20 @@ struct DemangledNameInfo { /// \endcode std::pair ArgumentsRange; + /// Indicates the [start, end) of the function qualifiers + /// (e.g., CV-qualifiers, reference qualifiers, requires clauses). + /// + /// E.g., + /// \code{.cpp} + /// void foo::bar::qux(int) const && + /// ^ ^ + /// start end + /// \endcode + std::pair QualifiersRange; + /// Returns \c true if this object holds a valid basename range. bool hasBasename() const { - return BasenameRange.first != BasenameRange.second && + return BasenameRange.second > BasenameRange.first && BasenameRange.second > 0; } @@ -139,6 +150,8 @@ struct TrackingOutputBuffer : public llvm::itanium_demangle::OutputBuffer { void finalizeArgumentEnd(); void finalizeStart(); void finalizeEnd(); + void finalizeQualifiersStart(); + void finalizeQualifiersEnd(); /// Helper used in the finalize APIs. bool canFinalize() const; diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h index 3bc65b3468c5a..b65583ce23cb0 100644 --- a/lldb/include/lldb/Core/Mangled.h +++ b/lldb/include/lldb/Core/Mangled.h @@ -9,10 +9,11 @@ #ifndef LLDB_CORE_MANGLED_H #define LLDB_CORE_MANGLED_H +#include "lldb/Core/DemangledNameInfo.h" +#include "lldb/Utility/ConstString.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-types.h" -#include "lldb/Utility/ConstString.h" #include "llvm/ADT/StringRef.h" #include @@ -134,9 +135,15 @@ class Mangled { /// A const reference to the display demangled name string object. ConstString GetDisplayDemangledName(const SymbolContext *sc = nullptr) const; - void SetDemangledName(ConstString name) { m_demangled = name; } + void SetDemangledName(ConstString name) { + m_demangled = name; + m_demangled_info.reset(); + } - void SetMangledName(ConstString name) { m_mangled = name; } + void SetMangledName(ConstString name) { + m_mangled = name; + m_demangled_info.reset(); + } /// Mangled name get accessor. /// @@ -276,6 +283,9 @@ class Mangled { /// table offsets in the cache data. void Encode(DataEncoder &encoder, ConstStringTable &strtab) const; + /// Retrieve \c DemangledNameInfo of the demangled name held by this object. + const std::optional &GetDemangledInfo() 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 @@ -290,6 +300,10 @@ class Mangled { /// Mutable so we can get it on demand with /// a const version of this object. mutable ConstString m_demangled; + + /// If available, holds information about where in \c m_demangled certain + /// parts of the name (e.g., basename, arguments, etc.) begin and end. + mutable std::optional m_demangled_info = std::nullopt; }; Stream &operator<<(Stream &s, const Mangled &obj); diff --git a/lldb/source/Core/DemangledNameInfo.cpp b/lldb/source/Core/DemangledNameInfo.cpp index 89757032409c2..54a06edc5ec1d 100644 --- a/lldb/source/Core/DemangledNameInfo.cpp +++ b/lldb/source/Core/DemangledNameInfo.cpp @@ -66,6 +66,20 @@ void TrackingOutputBuffer::finalizeArgumentEnd() { NameInfo.ArgumentsRange.second = getCurrentPosition(); } +void TrackingOutputBuffer::finalizeQualifiersStart() { + if (!canFinalize()) + return; + + NameInfo.QualifiersRange.first = getCurrentPosition(); +} + +void TrackingOutputBuffer::finalizeQualifiersEnd() { + if (!canFinalize()) + return; + + NameInfo.QualifiersRange.second = getCurrentPosition(); +} + void TrackingOutputBuffer::finalizeStart() { if (!shouldTrack()) return; @@ -171,6 +185,8 @@ void TrackingOutputBuffer::printRightImpl(const FunctionEncoding &N) { if (Ret) printRight(*Ret); + finalizeQualifiersStart(); + auto CVQuals = N.getCVQuals(); auto RefQual = N.getRefQual(); auto *Attrs = N.getAttrs(); @@ -193,6 +209,7 @@ void TrackingOutputBuffer::printRightImpl(const FunctionEncoding &N) { Requires->print(*this); } + finalizeQualifiersEnd(); finalizeEnd(); } diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index 23c7663166812..093451f070e70 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -9,6 +9,7 @@ #include "lldb/Core/Mangled.h" #include "lldb/Core/DataFileCache.h" +#include "lldb/Core/DemangledNameInfo.h" #include "lldb/Core/RichManglingContext.h" #include "lldb/Target/Language.h" #include "lldb/Utility/ConstString.h" @@ -120,6 +121,7 @@ Mangled::operator bool() const { return m_mangled || m_demangled; } void Mangled::Clear() { m_mangled.Clear(); m_demangled.Clear(); + m_demangled_info.reset(); } // Compare the string values. @@ -133,13 +135,16 @@ void Mangled::SetValue(ConstString name) { if (cstring_is_mangled(name.GetStringRef())) { m_demangled.Clear(); m_mangled = name; + m_demangled_info.reset(); } else { m_demangled = name; m_mangled.Clear(); + m_demangled_info.reset(); } } else { m_demangled.Clear(); m_mangled.Clear(); + m_demangled_info.reset(); } } @@ -161,20 +166,26 @@ static char *GetMSVCDemangledStr(llvm::StringRef M) { return demangled_cstr; } -static char *GetItaniumDemangledStr(const char *M) { +static std::pair +GetItaniumDemangledStr(const char *M) { char *demangled_cstr = nullptr; + DemangledNameInfo info; llvm::ItaniumPartialDemangler ipd; bool err = ipd.partialDemangle(M); if (!err) { - // Default buffer and size (will realloc in case it's too small). + // Default buffer and size (OutputBuffer will realloc in case it's too + // small). size_t demangled_size = 80; - demangled_cstr = static_cast(std::malloc(demangled_size)); - demangled_cstr = ipd.finishDemangle(demangled_cstr, &demangled_size); + demangled_cstr = static_cast(std::malloc(80)); + + TrackingOutputBuffer OB(demangled_cstr, demangled_size); + demangled_cstr = ipd.finishDemangle(&OB); + info = std::move(OB.NameInfo); assert(demangled_cstr && "finishDemangle must always succeed if partialDemangle did"); - assert(demangled_cstr[demangled_size - 1] == '\0' && + assert(demangled_cstr[OB.getCurrentPosition() - 1] == '\0' && "Expected demangled_size to return length including trailing null"); } @@ -183,9 +194,14 @@ static char *GetItaniumDemangledStr(const char *M) { LLDB_LOGF(log, "demangled itanium: %s -> \"%s\"", M, demangled_cstr); else LLDB_LOGF(log, "demangled itanium: %s -> error: failed to demangle", M); + + if (!info.hasBasename()) + LLDB_LOGF(log, + "demangled itanium: %s -> error: failed to retrieve name info", + M); } - return demangled_cstr; + return {demangled_cstr, std::move(info)}; } static char *GetRustV0DemangledStr(llvm::StringRef M) { @@ -281,6 +297,13 @@ ConstString Mangled::GetDemangledName( // BEGIN SWIFT return GetDemangledNameImpl(/*force=*/false, sc); } +std::optional const &Mangled::GetDemangledInfo() const { + if (!m_demangled_info) + GetDemangledNameImpl(/*force=*/true); + + return m_demangled_info; +} + // Generate the demangled name on demand using this accessor. Code in this // class will need to use this accessor if it wishes to decode the demangled // name. The result is cached and will be kept until a new string value is @@ -308,7 +331,10 @@ ConstString Mangled::GetDemangledNameImpl(bool force, // BEGIN SWIFT demangled_name = GetMSVCDemangledStr(m_mangled); break; case eManglingSchemeItanium: { - demangled_name = GetItaniumDemangledStr(m_mangled.GetCString()); + std::pair demangled = + GetItaniumDemangledStr(m_mangled.GetCString()); + demangled_name = demangled.first; + m_demangled_info.emplace(std::move(demangled.second)); break; } case eManglingSchemeRustV0: @@ -506,6 +532,7 @@ bool Mangled::Decode(const DataExtractor &data, lldb::offset_t *offset_ptr, const StringTableReader &strtab) { m_mangled.Clear(); m_demangled.Clear(); + m_demangled_info.reset(); MangledEncoding encoding = (MangledEncoding)data.GetU8(offset_ptr); switch (encoding) { case Empty: diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp index b039299d032dd..e903cadd410f3 100644 --- a/lldb/unittests/Core/MangledTest.cpp +++ b/lldb/unittests/Core/MangledTest.cpp @@ -321,90 +321,197 @@ TEST(MangledTest, NameIndexes_FindFunctionSymbols) { EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeMethod)); } +TEST(MangledTest, DemangledNameInfo_SetMangledResets) { + Mangled mangled; + EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt); + + mangled.SetMangledName(ConstString("_Z3foov")); + ASSERT_TRUE(mangled); + + auto info1 = mangled.GetDemangledInfo(); + EXPECT_NE(info1, std::nullopt); + EXPECT_TRUE(info1->hasBasename()); + + mangled.SetMangledName(ConstString("_Z4funcv")); + + // Should have re-calculated demangled-info since mangled name changed. + auto info2 = mangled.GetDemangledInfo(); + ASSERT_NE(info2, std::nullopt); + EXPECT_TRUE(info2->hasBasename()); + + EXPECT_NE(info1.value(), info2.value()); + EXPECT_EQ(mangled.GetDemangledName(), "func()"); +} + +TEST(MangledTest, DemangledNameInfo_SetDemangledResets) { + Mangled mangled("_Z3foov"); + ASSERT_TRUE(mangled); + + mangled.SetDemangledName(ConstString("")); + + // Mangled name hasn't changed, so GetDemangledInfo causes re-demangling + // of previously set mangled name. + EXPECT_NE(mangled.GetDemangledInfo(), std::nullopt); + EXPECT_EQ(mangled.GetDemangledName(), "foo()"); +} + +TEST(MangledTest, DemangledNameInfo_Clear) { + Mangled mangled("_Z3foov"); + ASSERT_TRUE(mangled); + EXPECT_NE(mangled.GetDemangledInfo(), std::nullopt); + + mangled.Clear(); + + EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt); +} + +TEST(MangledTest, DemangledNameInfo_SetValue) { + Mangled mangled("_Z4funcv"); + ASSERT_TRUE(mangled); + + auto demangled_func = mangled.GetDemangledInfo(); + + // SetValue(mangled) resets demangled-info + mangled.SetValue(ConstString("_Z3foov")); + auto demangled_foo = mangled.GetDemangledInfo(); + EXPECT_NE(demangled_foo, std::nullopt); + EXPECT_NE(demangled_foo, demangled_func); + + // SetValue(demangled) resets demangled-info + mangled.SetValue(ConstString("_Z4funcv")); + EXPECT_EQ(mangled.GetDemangledInfo(), demangled_func); + + // SetValue(empty) resets demangled-info + mangled.SetValue(ConstString()); + EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt); + + // Demangling invalid mangled name will set demangled-info + // (without a valid basename). + mangled.SetValue(ConstString("_Zinvalid")); + ASSERT_NE(mangled.GetDemangledInfo(), std::nullopt); + EXPECT_FALSE(mangled.GetDemangledInfo()->hasBasename()); +} + struct DemanglingPartsTestCase { const char *mangled; DemangledNameInfo expected_info; std::string_view basename; std::string_view scope; + std::string_view qualifiers; bool valid_basename = true; }; DemanglingPartsTestCase g_demangling_parts_test_cases[] = { // clang-format off { "_ZNVKO3BarIN2ns3QuxIiEEE1CIPFi3FooIS_IiES6_EEE6methodIS6_EENS5_IT_SC_E5InnerIiEESD_SD_", - { .BasenameRange = {92, 98}, .ScopeRange = {36, 92}, .ArgumentsRange = { 108, 158 } }, + { .BasenameRange = {92, 98}, .ScopeRange = {36, 92}, .ArgumentsRange = { 108, 158 }, + .QualifiersRange = {158, 176} }, .basename = "method", - .scope = "Bar>::C, Bar>)>::" + .scope = "Bar>::C, Bar>)>::", + .qualifiers = " const volatile &&" }, { "_Z7getFuncIfEPFiiiET_", - { .BasenameRange = {6, 13}, .ScopeRange = {6, 6}, .ArgumentsRange = { 20, 27 } }, + { .BasenameRange = {6, 13}, .ScopeRange = {6, 6}, .ArgumentsRange = { 20, 27 }, .QualifiersRange = {38, 38} }, .basename = "getFunc", - .scope = "" + .scope = "", + .qualifiers = "" }, { "_ZN1f1b1c1gEv", - { .BasenameRange = {9, 10}, .ScopeRange = {0, 9}, .ArgumentsRange = { 10, 12 } }, + { .BasenameRange = {9, 10}, .ScopeRange = {0, 9}, .ArgumentsRange = { 10, 12 }, + .QualifiersRange = {12, 12} }, .basename = "g", - .scope = "f::b::c::" + .scope = "f::b::c::", + .qualifiers = "" }, { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bEEEcvT__EES2_", - { .BasenameRange = {45, 48}, .ScopeRange = {38, 45}, .ArgumentsRange = { 53, 58 } }, + { .BasenameRange = {45, 48}, .ScopeRange = {38, 45}, .ArgumentsRange = { 53, 58 }, + .QualifiersRange = {58, 58} }, .basename = "fD1", - .scope = "test7::" + .scope = "test7::", + .qualifiers = "" }, { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_", - { .BasenameRange = {61, 64}, .ScopeRange = {54, 61}, .ArgumentsRange = { 69, 79 } }, + { .BasenameRange = {61, 64}, .ScopeRange = {54, 61}, .ArgumentsRange = { 69, 79 }, + .QualifiersRange = {79, 79} }, .basename = "fD1", - .scope = "test7::" + .scope = "test7::", + .qualifiers = "" }, { "_ZN5test7INDT1cE1dINDT1cE1dEEEE3fD1INDT1cE1dINDT1cE1dEEEEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_", - { .BasenameRange = {120, 123}, .ScopeRange = {81, 120}, .ArgumentsRange = { 155, 168 } }, + { .BasenameRange = {120, 123}, .ScopeRange = {81, 120}, .ArgumentsRange = { 155, 168 }, + .QualifiersRange = {168, 168} }, .basename = "fD1", - .scope = "test7>::" + .scope = "test7>::", + .qualifiers = "" }, { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvE5parseIRA29_KcEESE_OT_NS2_8functionIFbiNS0_6detail13parse_event_tERSE_EEEbb", - { .BasenameRange = {687, 692}, .ScopeRange = {343, 687}, .ArgumentsRange = { 713, 1174 } }, + { .BasenameRange = {687, 692}, .ScopeRange = {343, 687}, .ArgumentsRange = { 713, 1174 }, + .QualifiersRange = {1174, 1174} }, .basename = "parse", - .scope = "nlohmann::json_abi_v3_11_3::basic_json, std::__1::allocator>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector>, void>::" + .scope = "nlohmann::json_abi_v3_11_3::basic_json, std::__1::allocator>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector>, void>::", + .qualifiers = "" }, { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvEC1EDn", - { .BasenameRange = {344, 354}, .ScopeRange = {0, 344}, .ArgumentsRange = { 354, 370 } }, + { .BasenameRange = {344, 354}, .ScopeRange = {0, 344}, .ArgumentsRange = { 354, 370 }, + .QualifiersRange = {370, 370} }, .basename = "basic_json", - .scope = "nlohmann::json_abi_v3_11_3::basic_json, std::__1::allocator>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector>, void>::" + .scope = "nlohmann::json_abi_v3_11_3::basic_json, std::__1::allocator>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector>, void>::", + .qualifiers = "" }, { "_Z3fppIiEPFPFvvEiEf", - { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 18, 25 } }, + { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 18, 25 }, .QualifiersRange = {34,34} }, .basename = "fpp", - .scope = "" + .scope = "", + .qualifiers = "" }, { "_Z3fppIiEPFPFvvEN2ns3FooIiEEEf", - { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 18, 25 } }, + { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 18, 25 }, + .QualifiersRange = {43, 43} }, .basename = "fpp", - .scope = "" + .scope = "", + .qualifiers = "" }, { "_Z3fppIiEPFPFvPFN2ns3FooIiEENS2_3BarIfE3QuxEEEPFS2_S2_EEf", - { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 18, 25 } }, + { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 18, 25 }, + .QualifiersRange = {108, 108} }, .basename = "fpp", - .scope = "" + .scope = "", + .qualifiers = "" }, { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvEiEf", - { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 72, 79 } }, + { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 72, 79 }, + .QualifiersRange = {88, 88} }, .basename = "fpp", - .scope = "ns::HasFuncs::Bar::Qux>>::" + .scope = "ns::HasFuncs::Bar::Qux>>::", + .qualifiers = "" }, { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvES2_Ef", - { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 72, 79 } }, + { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 72, 79 }, + .QualifiersRange = {97, 97} }, .basename = "fpp", - .scope = "ns::HasFuncs::Bar::Qux>>::" + .scope = "ns::HasFuncs::Bar::Qux>>::", + .qualifiers = "", }, { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvPFS2_S5_EEPFS2_S2_EEf", - { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 72, 79 } }, + { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 72, 79 }, + .QualifiersRange = {162, 162} }, .basename = "fpp", - .scope = "ns::HasFuncs::Bar::Qux>>::" + .scope = "ns::HasFuncs::Bar::Qux>>::", + .qualifiers = "", + }, + { "_ZNKO2ns3ns23Bar3fooIiEEPFPFNS0_3FooIiEEiENS3_IfEEEi", + { .BasenameRange = {37, 40}, .ScopeRange = {23, 37}, .ArgumentsRange = { 45, 50 }, + .QualifiersRange = {78, 87} }, + .basename = "foo", + .scope = "ns::ns2::Bar::", + .qualifiers = " const &&", }, { "_ZTV11ImageLoader", - { .BasenameRange = {0, 0}, .ScopeRange = {0, 0}, .ArgumentsRange = { 0, 0 } }, + { .BasenameRange = {0, 0}, .ScopeRange = {0, 0}, .ArgumentsRange = { 0, 0 }, + .QualifiersRange = {0, 0} }, .basename = "", .scope = "", + .qualifiers = "", .valid_basename = false } // clang-format on @@ -433,7 +540,8 @@ class TestAllocator { } // namespace TEST_P(DemanglingPartsTestFixture, DemanglingParts) { - const auto &[mangled, info, basename, scope, valid_basename] = GetParam(); + const auto &[mangled, info, basename, scope, qualifiers, valid_basename] = + GetParam(); llvm::itanium_demangle::ManglingParser Parser( mangled, mangled + ::strlen(mangled)); @@ -451,6 +559,7 @@ TEST_P(DemanglingPartsTestFixture, DemanglingParts) { EXPECT_EQ(OB.NameInfo.BasenameRange, info.BasenameRange); EXPECT_EQ(OB.NameInfo.ScopeRange, info.ScopeRange); EXPECT_EQ(OB.NameInfo.ArgumentsRange, info.ArgumentsRange); + EXPECT_EQ(OB.NameInfo.QualifiersRange, info.QualifiersRange); auto get_part = [&](const std::pair &loc) { return demangled.substr(loc.first, loc.second - loc.first); @@ -458,6 +567,7 @@ TEST_P(DemanglingPartsTestFixture, DemanglingParts) { EXPECT_EQ(get_part(OB.NameInfo.BasenameRange), basename); EXPECT_EQ(get_part(OB.NameInfo.ScopeRange), scope); + EXPECT_EQ(get_part(OB.NameInfo.QualifiersRange), qualifiers); } INSTANTIATE_TEST_SUITE_P(DemanglingPartsTests, DemanglingPartsTestFixture, diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h index 132e5088b5514..21e7457b6336f 100644 --- a/llvm/include/llvm/Demangle/Demangle.h +++ b/llvm/include/llvm/Demangle/Demangle.h @@ -93,6 +93,13 @@ struct ItaniumPartialDemangler { /// second and third parameters to __cxa_demangle. char *finishDemangle(char *Buf, size_t *N) const; + /// See \ref finishDemangle + /// + /// \param[in] OB A llvm::itanium_demangle::OutputBuffer that the demangled + /// name will be printed into. + /// + char *finishDemangle(void *OB) const; + /// Get the base name of a function. This doesn't include trailing template /// arguments, ie for "a::b" this function returns "b". char *getFunctionBaseName(char *Buf, size_t *N) const; diff --git a/llvm/lib/Demangle/ItaniumDemangle.cpp b/llvm/lib/Demangle/ItaniumDemangle.cpp index 5c21b06a1d095..1009cc91ca12a 100644 --- a/llvm/lib/Demangle/ItaniumDemangle.cpp +++ b/llvm/lib/Demangle/ItaniumDemangle.cpp @@ -411,9 +411,7 @@ bool ItaniumPartialDemangler::partialDemangle(const char *MangledName) { RootNode = Parser->parse(); return RootNode == nullptr; } - -static char *printNode(const Node *RootNode, char *Buf, size_t *N) { - OutputBuffer OB(Buf, N); +static char *printNode(const Node *RootNode, OutputBuffer &OB, size_t *N) { RootNode->print(OB); OB += '\0'; if (N != nullptr) @@ -421,6 +419,11 @@ static char *printNode(const Node *RootNode, char *Buf, size_t *N) { return OB.getBuffer(); } +static char *printNode(const Node *RootNode, char *Buf, size_t *N) { + OutputBuffer OB(Buf, N); + return printNode(RootNode, OB, N); +} + char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const { if (!isFunction()) return nullptr; @@ -540,6 +543,14 @@ char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const { return printNode(static_cast(RootNode), Buf, N); } +char *ItaniumPartialDemangler::finishDemangle(void *OB) const { + assert(RootNode != nullptr && "must call partialDemangle()"); + assert(OB != nullptr && "valid OutputBuffer argument required"); + return printNode(static_cast(RootNode), + *static_cast(OB), + /*N=*/nullptr); +} + bool ItaniumPartialDemangler::hasFunctionQualifiers() const { assert(RootNode != nullptr && "must call partialDemangle()"); if (!isFunction()) From 3ddde4c380d1a24d1a9c3ed266719271bb19d193 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Fri, 11 Apr 2025 11:02:19 +0100 Subject: [PATCH 13/28] [lldb][Format] Introduce new frame-format variables for function parts (#131836) Adds new frame-format variables and implements them in the CPlusPlusLanguage plugin. We use the `DemangledNameInfo` type to retrieve the necessary part of the demangled name. https://github.com/llvm/llvm-project/pull/131836 (cherry picked from commit 8b91b44a3be680788f914af72f38f90d35925c23) --- lldb/include/lldb/Core/FormatEntity.h | 7 + lldb/include/lldb/Symbol/SymbolContext.h | 3 +- lldb/include/lldb/Target/Language.h | 8 + lldb/source/Core/FormatEntity.cpp | 61 ++++- .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 233 +++++++++++++++++- .../Language/CPlusPlus/CPlusPlusLanguage.h | 5 + lldb/source/Symbol/SymbolContext.cpp | 29 ++- .../TestFrameFormatFunctionBasename.test | 46 ++++ .../TestFrameFormatFunctionBasenameObjC.test | 24 ++ ...FrameFormatFunctionFormattedArguments.test | 42 ++++ ...eFormatFunctionFormattedArgumentsObjC.test | 24 ++ .../TestFrameFormatFunctionQualifiers.test | 24 ++ ...TestFrameFormatFunctionQualifiersObjC.test | 25 ++ .../TestFrameFormatFunctionReturn.test | 55 +++++ .../TestFrameFormatFunctionReturnObjC.test | 31 +++ .../TestFrameFormatFunctionScope.test | 41 +++ .../TestFrameFormatFunctionScopeObjC.test | 24 ++ ...tFrameFormatFunctionTemplateArguments.test | 37 +++ ...meFormatFunctionTemplateArgumentsObjC.test | 24 ++ .../Shell/Settings/TestFrameFormatName.test | 4 +- 20 files changed, 717 insertions(+), 30 deletions(-) create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test create mode 100644 lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h index f6c3bd981e03a..31a08b3e4add0 100644 --- a/lldb/include/lldb/Core/FormatEntity.h +++ b/lldb/include/lldb/Core/FormatEntity.h @@ -88,6 +88,13 @@ struct Entry { FunctionNameWithArgs, FunctionNameNoArgs, FunctionMangledName, + FunctionScope, + FunctionBasename, + FunctionTemplateArguments, + FunctionFormattedArguments, + FunctionReturnLeft, + FunctionReturnRight, + FunctionQualifiers, FunctionAddrOffset, FunctionAddrOffsetConcrete, FunctionLineOffset, diff --git a/lldb/include/lldb/Symbol/SymbolContext.h b/lldb/include/lldb/Symbol/SymbolContext.h index 68143006c9cd5..0fa74f27faab9 100644 --- a/lldb/include/lldb/Symbol/SymbolContext.h +++ b/lldb/include/lldb/Symbol/SymbolContext.h @@ -313,8 +313,7 @@ class SymbolContext { /// mangling preference. If this object represents an inlined function, /// returns the name of the inlined function. Returns nullptr if no function /// name could be determined. - const char *GetPossiblyInlinedFunctionName( - Mangled::NamePreference mangling_preference) const; + Mangled GetPossiblyInlinedFunctionName() const; // Member variables lldb::TargetSP target_sp; ///< The Target for a given query diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h index 63511e82f2977..c1dad8ac1ecc2 100644 --- a/lldb/include/lldb/Target/Language.h +++ b/lldb/include/lldb/Target/Language.h @@ -15,6 +15,7 @@ #include #include +#include "lldb/Core/FormatEntity.h" #include "lldb/Core/Highlighter.h" #include "lldb/Core/PluginInterface.h" #include "lldb/DataFormatters/DumpValueObjectOptions.h" @@ -275,6 +276,13 @@ class Language : public PluginInterface { FunctionNameRepresentation representation, Stream &s); + virtual bool HandleFrameFormatVariable(const SymbolContext &sc, + const ExecutionContext *exe_ctx, + FormatEntity::Entry::Type type, + Stream &s) { + return false; + } + virtual ConstString GetDemangledFunctionNameWithoutArguments(Mangled mangled) const { if (ConstString demangled = mangled.GetDemangledName()) diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index f091ae58cdbb4..25df85951f498 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -122,7 +122,15 @@ constexpr Definition g_function_child_entries[] = { Definition("pc-offset", EntryType::FunctionPCOffset), Definition("initial-function", EntryType::FunctionInitial), Definition("changed", EntryType::FunctionChanged), - Definition("is-optimized", EntryType::FunctionIsOptimized)}; + Definition("is-optimized", EntryType::FunctionIsOptimized), + Definition("scope", EntryType::FunctionScope), + Definition("basename", EntryType::FunctionBasename), + Definition("template-arguments", EntryType::FunctionTemplateArguments), + Definition("formatted-arguments", EntryType::FunctionFormattedArguments), + Definition("return-left", EntryType::FunctionReturnLeft), + Definition("return-right", EntryType::FunctionReturnRight), + Definition("qualifiers", EntryType::FunctionQualifiers), +}; constexpr Definition g_line_child_entries[] = { Entry::DefinitionWithChildren("file", EntryType::LineEntryFile, @@ -353,6 +361,13 @@ const char *FormatEntity::Entry::TypeToCString(Type t) { ENUM_TO_CSTR(FunctionNameWithArgs); ENUM_TO_CSTR(FunctionNameNoArgs); ENUM_TO_CSTR(FunctionMangledName); + ENUM_TO_CSTR(FunctionScope); + ENUM_TO_CSTR(FunctionBasename); + ENUM_TO_CSTR(FunctionTemplateArguments); + ENUM_TO_CSTR(FunctionFormattedArguments); + ENUM_TO_CSTR(FunctionReturnLeft); + ENUM_TO_CSTR(FunctionReturnRight); + ENUM_TO_CSTR(FunctionQualifiers); ENUM_TO_CSTR(FunctionAddrOffset); ENUM_TO_CSTR(FunctionAddrOffsetConcrete); ENUM_TO_CSTR(FunctionLineOffset); @@ -1167,8 +1182,9 @@ static bool PrintFunctionNameWithArgs(Stream &s, ExecutionContextScope *exe_scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; - const char *cstr = - sc.GetPossiblyInlinedFunctionName(Mangled::ePreferDemangled); + const char *cstr = sc.GetPossiblyInlinedFunctionName() + .GetName(Mangled::ePreferDemangled) + .AsCString(); if (!cstr) return false; @@ -1186,7 +1202,8 @@ static bool PrintFunctionNameWithArgs(Stream &s, return true; } -static bool HandleFunctionNameWithArgs(Stream &s,const ExecutionContext *exe_ctx, +static bool HandleFunctionNameWithArgs(Stream &s, + const ExecutionContext *exe_ctx, const SymbolContext &sc) { Language *language_plugin = nullptr; bool language_plugin_handled = false; @@ -1719,8 +1736,9 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, return true; } - const char *name = sc->GetPossiblyInlinedFunctionName( - Mangled::NamePreference::ePreferDemangled); + const char *name = sc->GetPossiblyInlinedFunctionName() + .GetName(Mangled::NamePreference::ePreferDemangled) + .AsCString(); if (!name) return false; @@ -1751,8 +1769,10 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, return true; } - const char *name = sc->GetPossiblyInlinedFunctionName( - Mangled::NamePreference::ePreferDemangledWithoutArguments); + const char *name = + sc->GetPossiblyInlinedFunctionName() + .GetName(Mangled::NamePreference::ePreferDemangledWithoutArguments) + .AsCString(); if (!name) return false; @@ -1761,19 +1781,38 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, return true; } + case Entry::Type::FunctionScope: + case Entry::Type::FunctionBasename: + case Entry::Type::FunctionTemplateArguments: + case Entry::Type::FunctionFormattedArguments: + case Entry::Type::FunctionReturnRight: + case Entry::Type::FunctionReturnLeft: + case Entry::Type::FunctionQualifiers: { + if (!sc->function) + return false; + + Language *language_plugin = + Language::FindPlugin(sc->function->GetLanguage()); + if (!language_plugin) + return false; + + return language_plugin->HandleFrameFormatVariable(*sc, exe_ctx, entry.type, + s); + } + case Entry::Type::FunctionNameWithArgs: { if (!sc) return false; return HandleFunctionNameWithArgs(s, exe_ctx, *sc); } - case Entry::Type::FunctionMangledName: { if (!sc) return false; - const char *name = sc->GetPossiblyInlinedFunctionName( - Mangled::NamePreference::ePreferMangled); + const char *name = sc->GetPossiblyInlinedFunctionName() + .GetName(Mangled::NamePreference::ePreferMangled) + .AsCString(); if (!name) return false; diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index d2c2c4ea40df6..90d21df5ddb91 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -208,6 +208,151 @@ static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream, return true; } +static std::optional +GetDemangledBasename(const SymbolContext &sc) { + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return std::nullopt; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return std::nullopt; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return std::nullopt; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return std::nullopt; + + return demangled_name.slice(info->BasenameRange.first, + info->BasenameRange.second); +} + +static std::optional +GetDemangledTemplateArguments(const SymbolContext &sc) { + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return std::nullopt; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return std::nullopt; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return std::nullopt; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return std::nullopt; + + if (info->ArgumentsRange.first < info->BasenameRange.second) + return std::nullopt; + + return demangled_name.slice(info->BasenameRange.second, + info->ArgumentsRange.first); +} + +static std::optional +GetDemangledReturnTypeLHS(const SymbolContext &sc) { + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return std::nullopt; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return std::nullopt; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return std::nullopt; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return std::nullopt; + + if (info->ScopeRange.first >= demangled_name.size()) + return std::nullopt; + + return demangled_name.substr(0, info->ScopeRange.first); +} + +static std::optional +GetDemangledFunctionQualifiers(const SymbolContext &sc) { + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return std::nullopt; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return std::nullopt; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return std::nullopt; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return std::nullopt; + + if (info->QualifiersRange.second < info->QualifiersRange.first) + return std::nullopt; + + return demangled_name.slice(info->QualifiersRange.first, + info->QualifiersRange.second); +} + +static std::optional +GetDemangledReturnTypeRHS(const SymbolContext &sc) { + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return std::nullopt; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return std::nullopt; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return std::nullopt; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return std::nullopt; + + if (info->QualifiersRange.first < info->ArgumentsRange.second) + return std::nullopt; + + return demangled_name.slice(info->ArgumentsRange.second, + info->QualifiersRange.first); +} + +static std::optional +GetDemangledScope(const SymbolContext &sc) { + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return std::nullopt; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return std::nullopt; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return std::nullopt; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return std::nullopt; + + if (info->ScopeRange.second < info->ScopeRange.first) + return std::nullopt; + + return demangled_name.slice(info->ScopeRange.first, info->ScopeRange.second); +} + bool CPlusPlusLanguage::MethodName::TrySimplifiedParse() { // This method tries to parse simple method definitions which are presumably // most comman in user programs. Definitions that can be parsed by this @@ -1717,8 +1862,9 @@ static bool PrintFunctionNameWithArgs(Stream &s, ExecutionContextScope *exe_scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; - const char *cstr = sc.GetPossiblyInlinedFunctionName( - Mangled::NamePreference::ePreferDemangled); + const char *cstr = sc.GetPossiblyInlinedFunctionName() + .GetName(Mangled::NamePreference::ePreferDemangled) + .AsCString(); if (!cstr) return false; @@ -1762,3 +1908,86 @@ bool CPlusPlusLanguage::GetFunctionDisplayName( return false; } } +bool CPlusPlusLanguage::HandleFrameFormatVariable( + const SymbolContext &sc, const ExecutionContext *exe_ctx, + FormatEntity::Entry::Type type, Stream &s) { + assert(sc.function); + + switch (type) { + case FormatEntity::Entry::Type::FunctionScope: { + std::optional scope = GetDemangledScope(sc); + if (!scope) + return false; + + s << *scope; + + return true; + } + + case FormatEntity::Entry::Type::FunctionBasename: { + std::optional name = GetDemangledBasename(sc); + if (!name) + return false; + + s << *name; + + return true; + } + + case FormatEntity::Entry::Type::FunctionTemplateArguments: { + std::optional template_args = + GetDemangledTemplateArguments(sc); + if (!template_args) + return false; + + s << *template_args; + + return true; + } + + case FormatEntity::Entry::Type::FunctionFormattedArguments: { + VariableList args; + if (auto variable_list_sp = GetFunctionVariableList(sc)) + variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, + args); + + ExecutionContextScope *exe_scope = + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; + + s << '('; + FormatEntity::PrettyPrintFunctionArguments(s, args, exe_scope); + s << ')'; + + return true; + } + case FormatEntity::Entry::Type::FunctionReturnRight: { + std::optional return_rhs = GetDemangledReturnTypeRHS(sc); + if (!return_rhs) + return false; + + s << *return_rhs; + + return true; + } + case FormatEntity::Entry::Type::FunctionReturnLeft: { + std::optional return_lhs = GetDemangledReturnTypeLHS(sc); + if (!return_lhs) + return false; + + s << *return_lhs; + + return true; + } + case FormatEntity::Entry::Type::FunctionQualifiers: { + std::optional quals = GetDemangledFunctionQualifiers(sc); + if (!quals) + return false; + + s << *quals; + + return true; + } + default: + return false; + } +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h index 54f5a94388b92..e6ba422af1a87 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -143,6 +143,11 @@ class CPlusPlusLanguage : public Language { FunctionNameRepresentation representation, Stream &s) override; + bool HandleFrameFormatVariable(const SymbolContext &sc, + const ExecutionContext *exe_ctx, + FormatEntity::Entry::Type type, + Stream &s) override; + static bool IsCPPMangledName(llvm::StringRef name); // Extract C++ context and identifier from a string using heuristic matching diff --git a/lldb/source/Symbol/SymbolContext.cpp b/lldb/source/Symbol/SymbolContext.cpp index 13f609e75541f..afdd5699355e3 100644 --- a/lldb/source/Symbol/SymbolContext.cpp +++ b/lldb/source/Symbol/SymbolContext.cpp @@ -10,6 +10,7 @@ #include "lldb/Core/Address.h" #include "lldb/Core/Debugger.h" +#include "lldb/Core/DemangledNameInfo.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Host/Host.h" @@ -925,34 +926,36 @@ const Symbol *SymbolContext::FindBestGlobalDataSymbol(ConstString name, return nullptr; // no error; we just didn't find anything } -char const *SymbolContext::GetPossiblyInlinedFunctionName( - Mangled::NamePreference mangling_preference) const { - const char *name = nullptr; - if (function) - name = function->GetMangled().GetName(mangling_preference).AsCString(); - else if (symbol) - name = symbol->GetMangled().GetName(mangling_preference).AsCString(); +Mangled SymbolContext::GetPossiblyInlinedFunctionName() const { + auto get_mangled = [this]() { + if (function) + return function->GetMangled(); + + if (symbol) + return symbol->GetMangled(); + + return Mangled{}; + }; if (!block) - return name; + return get_mangled(); const Block *inline_block = block->GetContainingInlinedBlock(); if (!inline_block) - return name; + return get_mangled(); const InlineFunctionInfo *inline_info = inline_block->GetInlinedFunctionInfo(); if (!inline_info) - return name; + return get_mangled(); // If we do have an inlined frame name, return that. - if (char const *inline_name = - inline_info->GetMangled().GetName(mangling_preference).AsCString()) + if (const Mangled &inline_name = inline_info->GetMangled()) return inline_name; // Sometimes an inline frame may not have mangling information, // but does have a valid name. - return inline_info->GetName().AsCString(); + return Mangled{inline_info->GetName().AsCString()}; } // diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test new file mode 100644 index 0000000000000..249a5fac5b55e --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test @@ -0,0 +1,46 @@ +# Test the ${function.basename} frame-format variable. + +# 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 { +template +struct Bar { + template + T bar(K k) const & { return 1.0f; } +}; + +template +struct Foo { + template + [[gnu::abi_tag("Test")]] void foo() const volatile && { + Bar b; + b.bar(b); + } +}; + +template +T func() { + ns::Foo{}.foo(); + return T{}; +} +} // namespace ns + +int main() { + ns::func>(); + return 0; +} + +#--- commands.input +settings set -f frame-format "custom-frame '${function.basename}'\n" +break set -n bar + +run +bt + +# CHECK: custom-frame 'bar' +# CHECK: custom-frame 'foo[abi:Test]' +# CHECK: custom-frame 'func' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test new file mode 100644 index 0000000000000..1a36d049db06c --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test @@ -0,0 +1,24 @@ +# Check that we have an appropriate fallback for ${function.basename} in languages that +# don't implement this frame format variable (in this case Objective-C). +# +# 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 bar() { func(); } + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.basename}'\n" +break set -n bar + +run +bt + +# CHECK: bt +# CHECK-NOT: custom-frame diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test new file mode 100644 index 0000000000000..5554830d3a247 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test @@ -0,0 +1,42 @@ +# Test the ${function.formatted-arguments} frame-format variable. + +# 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 +struct Foo { + void func() {} +}; + +void bar() { + Foo{}.func(); +} + +void foo(int, int x) { + bar(); +} + +void myFunc(char const * str, + void (*fptr)(int, int)) { + fptr(5, 10); +} + +int main(int argc, char const *argv[]) { + myFunc("hello", &foo); + return 0; +} + +#--- commands.input +settings set -f frame-format "custom-frame '${function.formatted-arguments}'\n" +break set -n func + +run +bt + +# CHECK: custom-frame '(this={{.*}})' +# CHECK: custom-frame '()' +# CHECK: custom-frame '((null)=5, x=10)' +# CHECK: custom-frame '(str="hello", fptr=({{.*}}.out`foo(int, int) at main.cpp:{{[0-9]+}}))' +# CHECK: custom-frame '(argc=1, argv={{.*}})' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test new file mode 100644 index 0000000000000..fdafa65c6d05d --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test @@ -0,0 +1,24 @@ +# Check that we have an appropriate fallback for ${function.formatted-arguments} in languages that +# don't implement this frame format variable (in this case Objective-C). +# +# 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 bar() { func(); } + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.formatted-arguments}'\n" +break set -n func + +run +bt + +# CHECK: bt +# CHECK-NOT: custom-frame diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test new file mode 100644 index 0000000000000..95a3be3811d85 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test @@ -0,0 +1,24 @@ +# Test the ${function.qualifiers} frame-format variable. + +# 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 +struct Foo { + void foo() const volatile && {} + void bar() { Foo{}.foo(); } +}; + +int main() { Foo{}.bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.qualifiers}'\n" +break set -n foo + +run +bt + +# CHECK: custom-frame ' const volatile &&' +# CHECK: custom-frame '' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test new file mode 100644 index 0000000000000..eff1b581c15dc --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test @@ -0,0 +1,25 @@ +# Check that we have an appropriate fallback for ${function.qualifiers} in +# languages that don't implement this frame format variable (in this case Objective-C). + +# 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 foo() {} +int bar() { foo(); } + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.qualifiers}'\n" +break set -n foo + +run +bt + +# CHECK: bt +# CHECK-NOT: custom-frame diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test new file mode 100644 index 0000000000000..a5e49a1054c86 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test @@ -0,0 +1,55 @@ +# Test the ${function.return-left} and ${function.return-right} +# frame-format variables. + +# 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 { +template +struct Foo {}; + +template +Foo qux(int) { + return {}; +} + +template +Foo (*bar(Foo))(int) { + qux(5); + return qux; +} + +struct Bar { + template + Foo (* (*foo(int) const &&)(Foo))(int) { + bar(Foo{}); + return bar; + } +}; +} + +int main(int argc, char const *argv[]) { + ns::ns2::Bar{}.foo(5); + return 0; +} + +#--- commands.input +settings set -f frame-format "custom-frame '${function.return-left}'\n" +break set -n qux + +run +bt + +# CHECK: custom-frame 'ns::ns2::Foo ' +# CHECK: custom-frame 'ns::ns2::Foo (*' +# CHECK: custom-frame 'ns::ns2::Foo (* (*' + +settings set -f frame-format "other-frame '${function.return-right}'\n" +bt + +# CHECK: other-frame '' +# CHECK: other-frame ')(int)' +# CHECK: other-frame ')(ns::ns2::Foo))(int)' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test new file mode 100644 index 0000000000000..69dd0fdbb0c19 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test @@ -0,0 +1,31 @@ +# Check that we have an appropriate fallback for ${function.return-left} and +# ${function.return-right} in languages that don't implement this frame +# format variable (in this case Objective-C). +# +# 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 qux() {} +int bar() { qux(); } + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.return-left}'\n" +break set -n qux + +run +bt + +# CHECK: bt +# CHECK-NOT: custom-frame + +settings set -f frame-format "other-frame '${function.return-right}'\n" +bt + +# CHECK: bt +# CHECK-NOT: other-frame diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test new file mode 100644 index 0000000000000..28f0ab7ca39e3 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test @@ -0,0 +1,41 @@ +# Test the ${function.scope} frame-format variable. + +# 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 +namespace ns::ns2 { +inline namespace ins { +template +struct Foo { + void func() {} +}; + +int foo() { + Foo{}.func(); + return 5; +} +} // namespace ins +} // namespace ns::ns2 + +using namespace ns::ns2; + +int bar() { + return ns::ns2::foo(); +} + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.scope}'\n" +break set -n func + +run +bt + +# CHECK: frame 'ns::ns2::ins::Foo::' +# CHECK: frame 'ns::ns2::ins::' +# CHECK: frame '' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test new file mode 100644 index 0000000000000..310b5e1992ab8 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test @@ -0,0 +1,24 @@ +# Check that we have an appropriate fallback for ${function.scope} in languages that +# don't implement this frame format variable (in this case Objective-C). +# +# 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 bar() { func(); } + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.scope}'\n" +break set -n func + +run +bt + +# CHECK: bt +# CHECK-NOT: custom-frame diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test new file mode 100644 index 0000000000000..396dd29dfd02c --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test @@ -0,0 +1,37 @@ +# Test the ${function.template-arguments} frame-format variable. + +# RUN: split-file %s %t +# RUN: %build %t/main.cpp -o %t.cxx.out +# RUN: %lldb -x -b -s %t/commands.input %t.cxx.out -o exit 2>&1 \ +# RUN: | FileCheck %s + +#--- main.cpp +template +struct Foo { + template + void func() {} +}; + +template class K, + typename M> +int foo() { + Foo{}.func(); + return 5; +} + +int bar() { + return foo>(); +} + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.template-arguments}'\n" +break set -n func + +run +bt + +# CHECK: custom-frame '' +# CHECK: custom-frame '>' +# CHECK: custom-frame '' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test new file mode 100644 index 0000000000000..1726aebacb9eb --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test @@ -0,0 +1,24 @@ +# Check that we have an appropriate fallback for ${function.template-arguments} in +# languages that don't implement this frame format variable (in this case Objective-C). +# +# 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 bar() { func(); } + +int main() { return bar(); } + +#--- commands.input +settings set -f frame-format "custom-frame '${function.template-arguments}'\n" +break set -n func + +run +bt + +# CHECK: bt +# CHECK-NOT: custom-frame diff --git a/lldb/test/Shell/Settings/TestFrameFormatName.test b/lldb/test/Shell/Settings/TestFrameFormatName.test index 110daceb47b40..34eb2f4975315 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatName.test +++ b/lldb/test/Shell/Settings/TestFrameFormatName.test @@ -22,13 +22,13 @@ c c # NAME_WITH_ARGS: frame int ns::foo(str="method") c -# NAME_WITH_ARGS: frame ns::returns_func_ptr((null)={{.*}}) +# NAME_WITH_ARGS: frame detail::Quux (* (*ns::returns_func_ptr((null)={{.*}}))(int))(float) c # NAME_WITH_ARGS: frame void Foo::foo(this={{.*}}, arg=({{.*}}`(anonymous namespace)::anon_bar() at {{.*}})) c # NAME_WITH_ARGS: frame void Foo::operator<<<1>(this={{.*}}, (null)=0) c -# NAME_WITH_ARGS: frame Foo::returns_func_ptr(this={{.*}}, (null)={{.*}}) +# NAME_WITH_ARGS: frame detail::Quux (* (*Foo::returns_func_ptr(this={{.*}}, (null)={{.*}}))(int))(float) const c # NAME_WITH_ARGS: frame inlined_foo(str="bar") q From b4c1a1871bcaca7b7d559fbaa873f164922a422d Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Mon, 14 Apr 2025 09:31:05 +0100 Subject: [PATCH 14/28] [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 (cherry picked from commit d555b9f9a01705097edf2434cf897e351095e5c9) --- lldb/include/lldb/Core/PluginManager.h | 14 ++++- lldb/include/lldb/Target/Language.h | 4 ++ lldb/source/Core/FormatEntity.cpp | 32 ++++++++++++ lldb/source/Core/PluginManager.cpp | 27 ++++++++-- .../Plugins/Language/CPlusPlus/CMakeLists.txt | 12 +++++ .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 47 ++++++++++++++++- .../Language/CPlusPlus/CPlusPlusLanguage.h | 5 ++ .../CPlusPlus/LanguageCPlusPlusProperties.td | 8 +++ .../Shell/Settings/TestCxxFrameFormat.test | 32 ++++++++++++ .../TestCxxFrameFormatMixedLanguages.test | 51 +++++++++++++++++++ .../Settings/TestCxxFrameFormatObjC.test | 24 +++++++++ .../TestCxxFrameFormatPartialFailure.test | 29 +++++++++++ .../Settings/TestCxxFrameFormatRecursive.test | 25 +++++++++ 13 files changed, 302 insertions(+), 8 deletions(-) create mode 100644 lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td create mode 100644 lldb/test/Shell/Settings/TestCxxFrameFormat.test create mode 100644 lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test create mode 100644 lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test create mode 100644 lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test create mode 100644 lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h index e4e0c3eea67f8..c0213e3f8d3f6 100644 --- a/lldb/include/lldb/Core/PluginManager.h +++ b/lldb/include/lldb/Core/PluginManager.h @@ -134,8 +134,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); @@ -600,6 +602,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 c1dad8ac1ecc2..9533f9c99b0ea 100644 --- a/lldb/include/lldb/Target/Language.h +++ b/lldb/include/lldb/Target/Language.h @@ -399,6 +399,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 25df85951f498..25778afaf2ddc 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, @@ -1804,6 +1833,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 952b6ff3b9a3a..c7ef83d82d6c7 100644 --- a/lldb/source/Core/PluginManager.cpp +++ b/lldb/source/Core/PluginManager.cpp @@ -508,11 +508,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) { @@ -1635,6 +1636,7 @@ void PluginManager::DebuggerInitialize(Debugger &debugger) { GetTracePluginInstances().PerformDebuggerCallback(debugger); GetTypeSystemInstances().PerformDebuggerCallback(debugger); GetScriptedInterfaceInstances().PerformDebuggerCallback(debugger); + GetLanguageInstances().PerformDebuggerCallback(debugger); } // This is the preferred new way to register plugin specific settings. e.g. @@ -1763,6 +1765,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, @@ -1920,3 +1923,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 90d21df5ddb91..00d58c22e4ac8 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() { @@ -1991,3 +1992,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(GetSettingName()); + m_collection_sp->Initialize(g_language_cplusplus_properties); + } + + const FormatEntity::Entry *GetFunctionNameFormat() const { + return GetPropertyAtIndexAs( + 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 e6ba422af1a87..8dd487d54cc7d 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -174,8 +174,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 T gunc(int x = 10) { + return T{}; +} + +int main(int argc, const char *argv[]) { + gunc(); + 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(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 From 7c147e0047a2cb4d688d0a794e9c53e57508d4e2 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Fri, 25 Apr 2025 10:17:20 +0100 Subject: [PATCH 15/28] [lldb] Remove redundant DemangledNameInfo::operator== MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This wasn't defined correctly and was unused. And it was causing a CI build failure: ``` /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/lldb/include/lldb/Core/DemangledNameInfo.h: In function ‘bool lldb_private::operator==(const lldb_private::DemangledNameInfo&, const lldb_private::DemangledNameInfo&)’: /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/lldb/include/lldb/Core/DemangledNameInfo.h:60:25: error: ‘const struct lldb_private::DemangledNameInfo’ has no member named ‘QualifiersRange’ 60 | lhs.QualifiersRange) == | ^~~~~~~~~~~~~~~ /home/buildbot/buildbot-root/cross-project-tests-sie-ubuntu-dwarf5/llvm-project/lldb/include/lldb/Core/DemangledNameInfo.h:62:25: error: ‘const struct lldb_private::DemangledNameInfo’ has no member named ‘QualifiersRange’ 62 | lhs.QualifiersRange); | ^~~~~~~~~~~~~~~ 111.469 [1284/7/3896] Building CXX object tools/lldb/source/Expression/CMakeFiles/lldbExpression.dir/ObjectFileJIT.cpp.o 111.470 [1284/6/3897] Building CXX object tools/lldb/source/Expression/CMakeFiles/lldbExpression.dir/Materializer.cpp.o 111.492 [1284/5/3898] Building CXX object tools/lldb/source/Expression/CMakeFiles/lldbExpression.dir/REPL.cpp.o 111.496 [1284/4/3899] Building CXX object tools/lldb/source/Expression/CMakeFiles/lldbExpression.dir/UserExpression.cpp.o 111.695 [1284/3/3900] Building CXX object tools/lldb/source/Commands/CMakeFiles/lldbCommands.dir/CommandObjectTarget.cpp.o 112.944 [1284/2/3901] Linking CXX shared library lib/libclang.so.21.0.0git 113.098 [1284/1/3902] Linking CXX shared library lib/libclang-cpp.so.21.0git ``` (cherry picked from commit 5a645109c3a965dcaab08e3485f2fa505e44cde3) --- lldb/include/lldb/Core/DemangledNameInfo.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/lldb/include/lldb/Core/DemangledNameInfo.h b/lldb/include/lldb/Core/DemangledNameInfo.h index 3926b45a96f3e..11d3bb58871b8 100644 --- a/lldb/include/lldb/Core/DemangledNameInfo.h +++ b/lldb/include/lldb/Core/DemangledNameInfo.h @@ -64,19 +64,6 @@ struct DemangledNameInfo { return BasenameRange.second > BasenameRange.first && BasenameRange.second > 0; } - - friend bool operator==(const DemangledNameInfo &lhs, - const DemangledNameInfo &rhs) { - return std::tie(lhs.BasenameRange, lhs.ArgumentsRange, lhs.ScopeRange, - lhs.QualifiersRange) == - std::tie(rhs.BasenameRange, rhs.ArgumentsRange, rhs.ScopeRange, - lhs.QualifiersRange); - } - - friend bool operator!=(const DemangledNameInfo &lhs, - const DemangledNameInfo &rhs) { - return !(lhs == rhs); - } }; /// An OutputBuffer which keeps a record of where certain parts of a From 5fc9e5c3de9c69e8afec61ea63376ed5aced2032 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Fri, 25 Apr 2025 11:18:11 +0100 Subject: [PATCH 16/28] [lldb] Fix MangledTest build failure No equality operator was specified. Define one locally. (cherry picked from commit da14f6d4b9d8c2b1fde37cd766688ce5e26b2984) --- lldb/unittests/Core/MangledTest.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp index e903cadd410f3..7dfd4b2a81f9b 100644 --- a/lldb/unittests/Core/MangledTest.cpp +++ b/lldb/unittests/Core/MangledTest.cpp @@ -321,6 +321,14 @@ TEST(MangledTest, NameIndexes_FindFunctionSymbols) { EXPECT_EQ(0, Count("undemangable", eFunctionNameTypeMethod)); } +static bool NameInfoEquals(const DemangledNameInfo &lhs, + const DemangledNameInfo &rhs) { + return std::tie(lhs.BasenameRange, lhs.ArgumentsRange, lhs.ScopeRange, + lhs.QualifiersRange) == + std::tie(rhs.BasenameRange, rhs.ArgumentsRange, rhs.ScopeRange, + rhs.QualifiersRange); +} + TEST(MangledTest, DemangledNameInfo_SetMangledResets) { Mangled mangled; EXPECT_EQ(mangled.GetDemangledInfo(), std::nullopt); @@ -339,7 +347,7 @@ TEST(MangledTest, DemangledNameInfo_SetMangledResets) { ASSERT_NE(info2, std::nullopt); EXPECT_TRUE(info2->hasBasename()); - EXPECT_NE(info1.value(), info2.value()); + EXPECT_FALSE(NameInfoEquals(info1.value(), info2.value())); EXPECT_EQ(mangled.GetDemangledName(), "func()"); } @@ -375,11 +383,12 @@ TEST(MangledTest, DemangledNameInfo_SetValue) { mangled.SetValue(ConstString("_Z3foov")); auto demangled_foo = mangled.GetDemangledInfo(); EXPECT_NE(demangled_foo, std::nullopt); - EXPECT_NE(demangled_foo, demangled_func); + EXPECT_FALSE(NameInfoEquals(demangled_foo.value(), demangled_func.value())); // SetValue(demangled) resets demangled-info mangled.SetValue(ConstString("_Z4funcv")); - EXPECT_EQ(mangled.GetDemangledInfo(), demangled_func); + EXPECT_TRUE(NameInfoEquals(mangled.GetDemangledInfo().value(), + demangled_func.value())); // SetValue(empty) resets demangled-info mangled.SetValue(ConstString()); From 004abd1edda91540595215fc68a03b000566c644 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Fri, 25 Apr 2025 22:04:31 +0100 Subject: [PATCH 17/28] [ItaniumDemangle][NFC] Add getter to ObjCProtoName::getProtocol And remove now redunant friend declaration. For some reason this was failing to build on one of the MSVC bots after https://github.com/llvm/llvm-project/pull/131836: ``` FAILED: tools/lldb/source/Plugins/ExpressionParser/Clang/CMakeFiles/lldbPluginExpressionParserClang.dir/ClangExpressionParser.cpp.obj ccache C:\PROGRA~1\MICROS~1\2022\COMMUN~1\VC\Tools\MSVC\1441~1.341\bin\Hostx64\x64\cl.exe /nologo /TP -DCLANG_BUILD_STATIC -DGTEST_HAS_RTTI=0 -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_ENABLE_EXTENDED_ALIGNED_STORAGE -D_GLIBCXX_ASSERTIONS -D_HAS_EXCEPTIONS=0 -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\source\Plugins\ExpressionParser\Clang -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\ExpressionParser\Clang -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\include -IC:\Python312\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\..\clang\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\..\clang\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\source -D__OPTIMIZE__ /Zc:inline /Zc:preprocessor /Zc:__cplusplus /Oi /bigobj /permissive- /W4 -wd4141 -wd4146 -wd4244 -wd4267 -wd4291 -wd4351 -wd4456 -wd4457 -wd4458 -wd4459 -wd4503 -wd4624 -wd4722 -wd4100 -wd4127 -wd4512 -wd4505 -wd4610 -wd4510 -wd4702 -wd4245 -wd4706 -wd4310 -wd4701 -wd4703 -wd4389 -wd4611 -wd4805 -wd4204 -wd4577 -wd4091 -wd4592 -wd4319 -wd4709 -wd5105 -wd4324 -wd4251 -wd4275 -w14062 -we4238 /Gw /O2 /Ob2 -MD -wd4018 -wd4068 -wd4150 -wd4201 -wd4251 -wd4521 -wd4530 -wd4589 /EHs-c- /GR- -UNDEBUG -std:c++17 /showIncludes /Fotools\lldb\source\Plugins\ExpressionParser\Clang\CMakeFiles\lldbPluginExpressionParserClang.dir\ClangExpressionParser.cpp.obj /Fdtools\lldb\source\Plugins\ExpressionParser\Clang\CMakeFiles\lldbPluginExpressionParserClang.dir\lldbPluginExpressionParserClang.pdb /FS -c C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source\Plugins\ExpressionParser\Clang\ClangExpressionParser.cpp C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\include\llvm/Demangle/ItaniumDemangle.h(667): error C2248: 'llvm::itanium_demangle::ObjCProtoName::Protocol': cannot access private member declared in class 'llvm::itanium_demangle::ObjCProtoName' C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\include\llvm/Demangle/ItaniumDemangle.h(615): note: see declaration of 'llvm::itanium_demangle::ObjCProtoName::Protocol' C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\include\llvm/Demangle/ItaniumDemangle.h(613): note: see declaration of 'llvm::itanium_demangle::ObjCProtoName' ``` It's not quite clear to me why this wasn't compiling but either way this is cleaner. (cherry picked from commit bd96fa778809d70e688d27cae2f7667b2aed69c0) --- libcxxabi/src/demangle/ItaniumDemangle.h | 6 +++--- llvm/include/llvm/Demangle/ItaniumDemangle.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h index 35197f59b148b..eca8b88788643 100644 --- a/libcxxabi/src/demangle/ItaniumDemangle.h +++ b/libcxxabi/src/demangle/ItaniumDemangle.h @@ -598,8 +598,6 @@ class ObjCProtoName : public Node { const Node *Ty; std::string_view Protocol; - friend class PointerType; - public: ObjCProtoName(const Node *Ty_, std::string_view Protocol_) : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {} @@ -611,6 +609,8 @@ class ObjCProtoName : public Node { static_cast(Ty)->getName() == "objc_object"; } + std::string_view getProtocol() const { return Protocol; } + void printLeft(OutputBuffer &OB) const override { Ty->print(OB); OB += "<"; @@ -648,7 +648,7 @@ class PointerType final : public Node { } else { const auto *objcProto = static_cast(Pointee); OB += "id<"; - OB += objcProto->Protocol; + OB += objcProto->getProtocol(); OB += ">"; } } diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h index 7de43205a7953..a398e778b037a 100644 --- a/llvm/include/llvm/Demangle/ItaniumDemangle.h +++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h @@ -597,8 +597,6 @@ class ObjCProtoName : public Node { const Node *Ty; std::string_view Protocol; - friend class PointerType; - public: ObjCProtoName(const Node *Ty_, std::string_view Protocol_) : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {} @@ -610,6 +608,8 @@ class ObjCProtoName : public Node { static_cast(Ty)->getName() == "objc_object"; } + std::string_view getProtocol() const { return Protocol; } + void printLeft(OutputBuffer &OB) const override { Ty->print(OB); OB += "<"; @@ -647,7 +647,7 @@ class PointerType final : public Node { } else { const auto *objcProto = static_cast(Pointee); OB += "id<"; - OB += objcProto->Protocol; + OB += objcProto->getProtocol(); OB += ">"; } } From 265cbb394c97bf91fc9fffdeb5df270609734d8f Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Fri, 25 Apr 2025 22:51:57 +0100 Subject: [PATCH 18/28] [lldb][NFC] Add missing newline between function definitions (cherry picked from commit b571aa49b1e07908b22bba3494461799e2f5c493) --- lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 00d58c22e4ac8..0cf9df559934c 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -1909,6 +1909,7 @@ bool CPlusPlusLanguage::GetFunctionDisplayName( return false; } } + bool CPlusPlusLanguage::HandleFrameFormatVariable( const SymbolContext &sc, const ExecutionContext *exe_ctx, FormatEntity::Entry::Type type, Stream &s) { From a0cc26142c3a0393453e7975bbadcd1f33046775 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Sat, 26 Apr 2025 00:57:55 +0100 Subject: [PATCH 19/28] [lldb][test] MangledTest: don't use designated initializers It's a C++20 feature and is causing some buildbots to fail: ``` FAILED: tools/lldb/unittests/Core/CMakeFiles/LLDBCoreTests.dir/MangledTest.cpp.obj ccache C:\PROGRA~1\MICROS~1\2022\COMMUN~1\VC\Tools\MSVC\1443~1.348\bin\Hostx64\x64\cl.exe /nologo /TP -DGTEST_HAS_RTTI=0 -DLLVM_BUILD_STATIC -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_ENABLE_EXTENDED_ALIGNED_STORAGE -D_GLIBCXX_ASSERTIONS -D_HAS_EXCEPTIONS=0 -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\unittests\Core -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests\Core -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\include -IC:\Python312\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\llvm\..\clang\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\build\tools\lldb\..\clang\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\source -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\third-party\unittest\googletest\include -IC:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\third-party\unittest\googlemock\include -D__OPTIMIZE__ /Zc:inline /Zc:preprocessor /Zc:__cplusplus /Oi /bigobj /permissive- /W4 -wd4141 -wd4146 -wd4244 -wd4267 -wd4291 -wd4351 -wd4456 -wd4457 -wd4458 -wd4459 -wd4503 -wd4624 -wd4722 -wd4100 -wd4127 -wd4512 -wd4505 -wd4610 -wd4510 -wd4702 -wd4245 -wd4706 -wd4310 -wd4701 -wd4703 -wd4389 -wd4611 -wd4805 -wd4204 -wd4577 -wd4091 -wd4592 -wd4319 -wd4709 -wd5105 -wd4324 -wd4251 -wd4275 -w14062 -we4238 /Gw /O2 /Ob2 -MD -wd4018 -wd4068 -wd4150 -wd4201 -wd4251 -wd4521 -wd4530 -wd4589 /EHs-c- /GR- -UNDEBUG -std:c++17 /showIncludes /Fotools\lldb\unittests\Core\CMakeFiles\LLDBCoreTests.dir\MangledTest.cpp.obj /Fdtools\lldb\unittests\Core\CMakeFiles\LLDBCoreTests.dir\ /FS -c C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests\Core\MangledTest.cpp C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests\Core\MangledTest.cpp(416): error C7555: use of designated initializers requires at least '/std:c++20' C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests\Core\MangledTest.cpp(415): error C7556: cannot mix designated-initializers with non-designated-initializers C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests\Core\MangledTest.cpp(418): error C7555: use of designated initializers requires at least '/std:c++20' C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests\Core\MangledTest.cpp(423): error C7555: use of designated initializers requires at least '/std:c++20' C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests\Core\MangledTest.cpp(422): error C7556: cannot mix designated-initializers with non-designated-initializers C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests\Core\MangledTest.cpp(424): error C7555: use of designated initializers requires at least '/std:c++20' C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests\Core\MangledTest.cpp(429): error C7555: use of designated initializers requires at least '/std:c++20' C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests\Core\MangledTest.cpp(428): error C7556: cannot mix designated-initializers with non-designated-initializers C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests\Core\MangledTest.cpp(431): error C7555: use of designated initializers requires at least '/std:c++20' C:\buildbot\as-builder-10\lldb-x-aarch64\llvm-project\lldb\unittests\Core\MangledTest.cpp(436): error C7555: use of designated initializers requires at least '/std:c++20' ``` (cherry picked from commit 5137587fb1dc878e2dca31c4b929a3ca99f70ace) --- lldb/unittests/Core/MangledTest.cpp | 158 ++++++++++++++-------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/lldb/unittests/Core/MangledTest.cpp b/lldb/unittests/Core/MangledTest.cpp index 7dfd4b2a81f9b..8a452a84614ef 100644 --- a/lldb/unittests/Core/MangledTest.cpp +++ b/lldb/unittests/Core/MangledTest.cpp @@ -413,115 +413,115 @@ struct DemanglingPartsTestCase { DemanglingPartsTestCase g_demangling_parts_test_cases[] = { // clang-format off { "_ZNVKO3BarIN2ns3QuxIiEEE1CIPFi3FooIS_IiES6_EEE6methodIS6_EENS5_IT_SC_E5InnerIiEESD_SD_", - { .BasenameRange = {92, 98}, .ScopeRange = {36, 92}, .ArgumentsRange = { 108, 158 }, - .QualifiersRange = {158, 176} }, - .basename = "method", - .scope = "Bar>::C, Bar>)>::", - .qualifiers = " const volatile &&" + { /*.BasenameRange=*/{92, 98}, /*.ScopeRange=*/{36, 92}, /*.ArgumentsRange=*/{ 108, 158 }, + /*.QualifiersRange=*/{158, 176} }, + /*.basename=*/"method", + /*.scope=*/"Bar>::C, Bar>)>::", + /*.qualifiers=*/" const volatile &&" }, { "_Z7getFuncIfEPFiiiET_", - { .BasenameRange = {6, 13}, .ScopeRange = {6, 6}, .ArgumentsRange = { 20, 27 }, .QualifiersRange = {38, 38} }, - .basename = "getFunc", - .scope = "", - .qualifiers = "" + { /*.BasenameRange=*/{6, 13}, /*.ScopeRange=*/{6, 6}, /*.ArgumentsRange=*/{ 20, 27 }, /*.QualifiersRange=*/{38, 38} }, + /*.basename=*/"getFunc", + /*.scope=*/"", + /*.qualifiers=*/"" }, { "_ZN1f1b1c1gEv", - { .BasenameRange = {9, 10}, .ScopeRange = {0, 9}, .ArgumentsRange = { 10, 12 }, - .QualifiersRange = {12, 12} }, - .basename = "g", - .scope = "f::b::c::", - .qualifiers = "" + { /*.BasenameRange=*/{9, 10}, /*.ScopeRange=*/{0, 9}, /*.ArgumentsRange=*/{ 10, 12 }, + /*.QualifiersRange=*/{12, 12} }, + /*.basename=*/"g", + /*.scope=*/"f::b::c::", + /*.qualifiers=*/"" }, { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bEEEcvT__EES2_", - { .BasenameRange = {45, 48}, .ScopeRange = {38, 45}, .ArgumentsRange = { 53, 58 }, - .QualifiersRange = {58, 58} }, - .basename = "fD1", - .scope = "test7::", - .qualifiers = "" + { /*.BasenameRange=*/{45, 48}, /*.ScopeRange=*/{38, 45}, /*.ArgumentsRange=*/{ 53, 58 }, + /*.QualifiersRange=*/{58, 58} }, + /*.basename=*/"fD1", + /*.scope=*/"test7::", + /*.qualifiers=*/"" }, { "_ZN5test73fD1IiEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_", - { .BasenameRange = {61, 64}, .ScopeRange = {54, 61}, .ArgumentsRange = { 69, 79 }, - .QualifiersRange = {79, 79} }, - .basename = "fD1", - .scope = "test7::", - .qualifiers = "" + { /*.BasenameRange=*/{61, 64}, /*.ScopeRange=*/{54, 61}, /*.ArgumentsRange=*/{ 69, 79 }, + /*.QualifiersRange=*/{79, 79} }, + /*.basename=*/"fD1", + /*.scope=*/"test7::", + /*.qualifiers=*/"" }, { "_ZN5test7INDT1cE1dINDT1cE1dEEEE3fD1INDT1cE1dINDT1cE1dEEEEEDTcmtlNS_1DEL_ZNS_1bINDT1cE1dEEEEEcvT__EES2_", - { .BasenameRange = {120, 123}, .ScopeRange = {81, 120}, .ArgumentsRange = { 155, 168 }, - .QualifiersRange = {168, 168} }, - .basename = "fD1", - .scope = "test7>::", - .qualifiers = "" + { /*.BasenameRange=*/{120, 123}, /*.ScopeRange=*/{81, 120}, /*.ArgumentsRange=*/{ 155, 168 }, + /*.QualifiersRange=*/{168, 168} }, + /*.basename=*/"fD1", + /*.scope=*/"test7>::", + /*.qualifiers=*/"" }, { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvE5parseIRA29_KcEESE_OT_NS2_8functionIFbiNS0_6detail13parse_event_tERSE_EEEbb", - { .BasenameRange = {687, 692}, .ScopeRange = {343, 687}, .ArgumentsRange = { 713, 1174 }, - .QualifiersRange = {1174, 1174} }, - .basename = "parse", - .scope = "nlohmann::json_abi_v3_11_3::basic_json, std::__1::allocator>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector>, void>::", - .qualifiers = "" + { /*.BasenameRange=*/{687, 692}, /*.ScopeRange=*/{343, 687}, /*.ArgumentsRange=*/{ 713, 1174 }, + /*.QualifiersRange=*/{1174, 1174} }, + /*.basename=*/"parse", + /*.scope=*/"nlohmann::json_abi_v3_11_3::basic_json, std::__1::allocator>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector>, void>::", + /*.qualifiers=*/"" }, { "_ZN8nlohmann16json_abi_v3_11_310basic_jsonINSt3__13mapENS2_6vectorENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEbxydS8_NS0_14adl_serializerENS4_IhNS8_IhEEEEvEC1EDn", - { .BasenameRange = {344, 354}, .ScopeRange = {0, 344}, .ArgumentsRange = { 354, 370 }, - .QualifiersRange = {370, 370} }, - .basename = "basic_json", - .scope = "nlohmann::json_abi_v3_11_3::basic_json, std::__1::allocator>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector>, void>::", - .qualifiers = "" + { /*.BasenameRange=*/{344, 354}, /*.ScopeRange=*/{0, 344}, /*.ArgumentsRange=*/{ 354, 370 }, + /*.QualifiersRange=*/{370, 370} }, + /*.basename=*/"basic_json", + /*.scope=*/"nlohmann::json_abi_v3_11_3::basic_json, std::__1::allocator>, bool, long long, unsigned long long, double, std::__1::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::__1::vector>, void>::", + /*.qualifiers=*/"" }, { "_Z3fppIiEPFPFvvEiEf", - { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 18, 25 }, .QualifiersRange = {34,34} }, - .basename = "fpp", - .scope = "", - .qualifiers = "" + { /*.BasenameRange=*/{10, 13}, /*.ScopeRange=*/{10, 10}, /*.ArgumentsRange=*/{ 18, 25 }, /*.QualifiersRange=*/{34,34} }, + /*.basename=*/"fpp", + /*.scope=*/"", + /*.qualifiers=*/"" }, { "_Z3fppIiEPFPFvvEN2ns3FooIiEEEf", - { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 18, 25 }, - .QualifiersRange = {43, 43} }, - .basename = "fpp", - .scope = "", - .qualifiers = "" + { /*.BasenameRange=*/{10, 13}, /*.ScopeRange=*/{10, 10}, /*.ArgumentsRange=*/{ 18, 25 }, + /*.QualifiersRange=*/{43, 43} }, + /*.basename=*/"fpp", + /*.scope=*/"", + /*.qualifiers=*/"" }, { "_Z3fppIiEPFPFvPFN2ns3FooIiEENS2_3BarIfE3QuxEEEPFS2_S2_EEf", - { .BasenameRange = {10, 13}, .ScopeRange = {10, 10}, .ArgumentsRange = { 18, 25 }, - .QualifiersRange = {108, 108} }, - .basename = "fpp", - .scope = "", - .qualifiers = "" + { /*.BasenameRange=*/{10, 13}, /*.ScopeRange=*/{10, 10}, /*.ArgumentsRange=*/{ 18, 25 }, + /*.QualifiersRange=*/{108, 108} }, + /*.basename=*/"fpp", + /*.scope=*/"", + /*.qualifiers=*/"" }, { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvEiEf", - { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 72, 79 }, - .QualifiersRange = {88, 88} }, - .basename = "fpp", - .scope = "ns::HasFuncs::Bar::Qux>>::", - .qualifiers = "" + { /*.BasenameRange=*/{64, 67}, /*.ScopeRange=*/{10, 64}, /*.ArgumentsRange=*/{ 72, 79 }, + /*.QualifiersRange=*/{88, 88} }, + /*.basename=*/"fpp", + /*.scope=*/"ns::HasFuncs::Bar::Qux>>::", + /*.qualifiers=*/"" }, { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvvES2_Ef", - { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 72, 79 }, - .QualifiersRange = {97, 97} }, - .basename = "fpp", - .scope = "ns::HasFuncs::Bar::Qux>>::", - .qualifiers = "", + { /*.BasenameRange=*/{64, 67}, /*.ScopeRange=*/{10, 64}, /*.ArgumentsRange=*/{ 72, 79 }, + /*.QualifiersRange=*/{97, 97} }, + /*.basename=*/"fpp", + /*.scope=*/"ns::HasFuncs::Bar::Qux>>::", + /*.qualifiers=*/"", }, { "_ZN2ns8HasFuncsINS_3FooINS1_IiE3BarIfE3QuxEEEE3fppIiEEPFPFvPFS2_S5_EEPFS2_S2_EEf", - { .BasenameRange = {64, 67}, .ScopeRange = {10, 64}, .ArgumentsRange = { 72, 79 }, - .QualifiersRange = {162, 162} }, - .basename = "fpp", - .scope = "ns::HasFuncs::Bar::Qux>>::", - .qualifiers = "", + { /*.BasenameRange=*/{64, 67}, /*.ScopeRange=*/{10, 64}, /*.ArgumentsRange=*/{ 72, 79 }, + /*.QualifiersRange=*/{162, 162} }, + /*.basename=*/"fpp", + /*.scope=*/"ns::HasFuncs::Bar::Qux>>::", + /*.qualifiers=*/"", }, { "_ZNKO2ns3ns23Bar3fooIiEEPFPFNS0_3FooIiEEiENS3_IfEEEi", - { .BasenameRange = {37, 40}, .ScopeRange = {23, 37}, .ArgumentsRange = { 45, 50 }, - .QualifiersRange = {78, 87} }, - .basename = "foo", - .scope = "ns::ns2::Bar::", - .qualifiers = " const &&", + { /*.BasenameRange=*/{37, 40}, /*.ScopeRange=*/{23, 37}, /*.ArgumentsRange=*/{ 45, 50 }, + /*.QualifiersRange=*/{78, 87} }, + /*.basename=*/"foo", + /*.scope=*/"ns::ns2::Bar::", + /*.qualifiers=*/" const &&", }, { "_ZTV11ImageLoader", - { .BasenameRange = {0, 0}, .ScopeRange = {0, 0}, .ArgumentsRange = { 0, 0 }, - .QualifiersRange = {0, 0} }, - .basename = "", - .scope = "", - .qualifiers = "", - .valid_basename = false + { /*.BasenameRange=*/{0, 0}, /*.ScopeRange=*/{0, 0}, /*.ArgumentsRange=*/{ 0, 0 }, + /*.QualifiersRange=*/{0, 0} }, + /*.basename=*/"", + /*.scope=*/"", + /*.qualifiers=*/"", + /*.valid_basename=*/false } // clang-format on }; From 42e1c5c386eaee6d7035c6231b30ca5497990cd7 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Sat, 26 Apr 2025 08:40:27 +0100 Subject: [PATCH 20/28] [lldb][test] Skip Objective-C FrameFormat tests on Windows This were failing on Windows CI with errors like: ``` 22: (lldb) bt 23: * thread #1, stop reason = breakpoint 1.1 24: frame #0: 0x00007ff7c5e41000 TestFrameFormatFunctionFormattedArgumentsObjC.test.tmp.objc.out`func at main.m:2 25: frame #1: 0x00007ff7c5e4101c TestFrameFormatFunctionFormattedArgumentsObjC.test.tmp.objc.out`bar + 12 at main.m:3 26: frame #2: 0x00007ff7c5e4103c TestFrameFormatFunctionFormattedArgumentsObjC.test.tmp.objc.out`main + 16 at main.m:5 27: custom-frame '()' !~~~~~~~~~~~ error: no match expected 28: custom-frame '(__formal=)' ``` (cherry picked from commit cc0bdb38ee6976c24e5e3dcb2b3fd190a4ae7112) --- lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test | 2 ++ lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test | 2 ++ .../Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test | 2 ++ 3 files changed, 6 insertions(+) diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test b/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test index 854cf6384d8ee..bafd36f5ae177 100644 --- a/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test +++ b/lldb/test/Shell/Settings/TestCxxFrameFormatMixedLanguages.test @@ -1,3 +1,5 @@ +# UNSUPPORTED: system-windows + # Test the plugin.cplusplus.display.function-name-format setting # when interoperating multiple languages. diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test b/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test index 525ada2afe99c..ba574444bc8a8 100644 --- a/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test +++ b/lldb/test/Shell/Settings/TestCxxFrameFormatObjC.test @@ -1,3 +1,5 @@ +# UNSUPPORTED: system-windows + # Test the plugin.cplusplus.display.function-name-format setting. # RUN: split-file %s %t diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test index fdafa65c6d05d..61adb2ec8ac9a 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test @@ -1,3 +1,5 @@ +# UNSUPPORTED: system-windows + # Check that we have an appropriate fallback for ${function.formatted-arguments} in languages that # don't implement this frame format variable (in this case Objective-C). # From f3422924cd95e719ac07a669f666ef4c8c53fcf3 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Sat, 26 Apr 2025 08:56:16 +0100 Subject: [PATCH 21/28] [lldb][test] Fix/XFAIL FrameFormat tests on Windows All of these were failing on Windows CI. Some are failing because breakpoints on template functions can't be set by name. Others are failing because of slight textual differences. Most are failing because we can't track components of a mangled name from PDB, so XFAIL those. (cherry picked from commit e6f7e3418eb8519d6cf12da8576ad75aac6b307b) --- lldb/test/Shell/Settings/TestCxxFrameFormat.test | 2 ++ .../test/Shell/Settings/TestCxxFrameFormatPartialFailure.test | 2 +- lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test | 4 +++- .../Settings/TestFrameFormatFunctionFormattedArguments.test | 4 ++-- .../Shell/Settings/TestFrameFormatFunctionQualifiers.test | 2 ++ lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test | 2 ++ lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test | 2 ++ .../Settings/TestFrameFormatFunctionTemplateArguments.test | 4 +++- 8 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormat.test b/lldb/test/Shell/Settings/TestCxxFrameFormat.test index c26d339f57130..babb14bed4440 100644 --- a/lldb/test/Shell/Settings/TestCxxFrameFormat.test +++ b/lldb/test/Shell/Settings/TestCxxFrameFormat.test @@ -1,3 +1,5 @@ +# XFAIL: system-windows + # Test the plugin.cplusplus.display.function-name-format setting. # RUN: split-file %s %t diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test b/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test index 73564ae41837b..0ae48d4eb541e 100644 --- a/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test +++ b/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test @@ -20,7 +20,7 @@ int main(int argc, const char *argv[]) { #--- 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 +break set -l 2 run bt diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test index 249a5fac5b55e..ae405be44578d 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test @@ -1,3 +1,5 @@ +# XFAIL: system-windows + # Test the ${function.basename} frame-format variable. # RUN: split-file %s %t @@ -36,7 +38,7 @@ int main() { #--- commands.input settings set -f frame-format "custom-frame '${function.basename}'\n" -break set -n bar +break set -l 5 run bt diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test index 5554830d3a247..31602c83b17c9 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test @@ -37,6 +37,6 @@ bt # CHECK: custom-frame '(this={{.*}})' # CHECK: custom-frame '()' -# CHECK: custom-frame '((null)=5, x=10)' -# CHECK: custom-frame '(str="hello", fptr=({{.*}}.out`foo(int, int) at main.cpp:{{[0-9]+}}))' +# CHECK: custom-frame '({{.*}}=5, x=10)' +# CHECK: custom-frame '(str="hello", fptr=({{.*}}.out`{{.*}}foo(int,{{.*}}int) at main.cpp:{{[0-9]+}}))' # CHECK: custom-frame '(argc=1, argv={{.*}})' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test index 95a3be3811d85..3848f7d527f6b 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test @@ -1,3 +1,5 @@ +# XFAIL: system-windows + # Test the ${function.qualifiers} frame-format variable. # RUN: split-file %s %t diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test index a5e49a1054c86..f06753157d2c7 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test @@ -1,3 +1,5 @@ +# XFAIL: system-windows + # Test the ${function.return-left} and ${function.return-right} # frame-format variables. diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test index 28f0ab7ca39e3..a5da652372dbc 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test @@ -1,3 +1,5 @@ +# XFAIL: system-windows + # Test the ${function.scope} frame-format variable. # RUN: split-file %s %t diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test index 396dd29dfd02c..2c0bd9099eb1d 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test @@ -1,3 +1,5 @@ +# XFAIL: system-windows + # Test the ${function.template-arguments} frame-format variable. # RUN: split-file %s %t @@ -27,7 +29,7 @@ int main() { return bar(); } #--- commands.input settings set -f frame-format "custom-frame '${function.template-arguments}'\n" -break set -n func +break set -l 4 run bt From 166d3e0a3a12b7b453067a70ffa63e5848aa55a3 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Sat, 26 Apr 2025 11:27:58 +0100 Subject: [PATCH 22/28] [lldb][test] Make sure we compile FrameFormat tests with DWARF These don't make sense for PDB on Windows (cherry picked from commit 7581aa1d8e66453845aae584aeda99b3f5fc9646) --- lldb/test/Shell/Settings/TestCxxFrameFormat.test | 2 +- .../test/Shell/Settings/TestCxxFrameFormatPartialFailure.test | 2 +- lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test | 2 +- lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test | 4 +--- .../Shell/Settings/TestFrameFormatFunctionBasenameObjC.test | 2 +- .../Settings/TestFrameFormatFunctionFormattedArguments.test | 2 +- .../TestFrameFormatFunctionFormattedArgumentsObjC.test | 2 +- .../Shell/Settings/TestFrameFormatFunctionQualifiers.test | 4 +--- .../Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test | 3 +-- lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test | 4 +--- .../Shell/Settings/TestFrameFormatFunctionReturnObjC.test | 2 +- lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test | 4 +--- .../test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test | 2 +- .../Settings/TestFrameFormatFunctionTemplateArguments.test | 4 +--- .../TestFrameFormatFunctionTemplateArgumentsObjC.test | 2 +- 15 files changed, 15 insertions(+), 26 deletions(-) diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormat.test b/lldb/test/Shell/Settings/TestCxxFrameFormat.test index babb14bed4440..3751dfa737eae 100644 --- a/lldb/test/Shell/Settings/TestCxxFrameFormat.test +++ b/lldb/test/Shell/Settings/TestCxxFrameFormat.test @@ -3,7 +3,7 @@ # Test the plugin.cplusplus.display.function-name-format setting. # RUN: split-file %s %t -# RUN: %build %t/main.cpp -o %t.out +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out # RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ # RUN: | FileCheck %s diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test b/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test index 0ae48d4eb541e..249c25bab0f47 100644 --- a/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test +++ b/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test @@ -3,7 +3,7 @@ # were successful. # RUN: split-file %s %t -# RUN: %build %t/main.cpp -o %t.out +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out # RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ # RUN: | FileCheck %s diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test b/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test index 90cd2d3e327c9..887de882886ed 100644 --- a/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test +++ b/lldb/test/Shell/Settings/TestCxxFrameFormatRecursive.test @@ -4,7 +4,7 @@ # plugin.cplusplus.display.function-name-format setting. # RUN: split-file %s %t -# RUN: %build %t/main.cpp -o %t.out +# RUN: %clang_host -g -gdwarf %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 diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test index ae405be44578d..32e6dcec43ce6 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test @@ -1,9 +1,7 @@ -# XFAIL: system-windows - # Test the ${function.basename} frame-format variable. # RUN: split-file %s %t -# RUN: %build %t/main.cpp -o %t.out +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out # RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ # RUN: | FileCheck %s # diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test index 1a36d049db06c..e41ba49f112a9 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasenameObjC.test @@ -2,7 +2,7 @@ # don't implement this frame format variable (in this case Objective-C). # # RUN: split-file %s %t -# RUN: %build %t/main.m -o %t.objc.out +# RUN: %clang_host -g -gdwarf %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 diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test index 31602c83b17c9..2a7860fbf0e24 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test @@ -1,7 +1,7 @@ # Test the ${function.formatted-arguments} frame-format variable. # RUN: split-file %s %t -# RUN: %build %t/main.cpp -o %t.out +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out # RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ # RUN: | FileCheck %s diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test index 61adb2ec8ac9a..c9da1d4bd7772 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArgumentsObjC.test @@ -4,7 +4,7 @@ # don't implement this frame format variable (in this case Objective-C). # # RUN: split-file %s %t -# RUN: %build %t/main.m -o %t.objc.out +# RUN: %clang_host -g -gdwarf %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 diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test index 3848f7d527f6b..c53023732afcf 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test @@ -1,9 +1,7 @@ -# XFAIL: system-windows - # Test the ${function.qualifiers} frame-format variable. # RUN: split-file %s %t -# RUN: %build %t/main.cpp -o %t.out +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out # RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ # RUN: | FileCheck %s diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test index eff1b581c15dc..f3e2b8ea9a6b9 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiersObjC.test @@ -2,8 +2,7 @@ # languages that don't implement this frame format variable (in this case Objective-C). # RUN: split-file %s %t -# -# RUN: %build %t/main.m -o %t.objc.out +# RUN: %clang_host -g -gdwarf %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 diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test index f06753157d2c7..55ebcbfa8812c 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test @@ -1,10 +1,8 @@ -# XFAIL: system-windows - # Test the ${function.return-left} and ${function.return-right} # frame-format variables. # RUN: split-file %s %t -# RUN: %build %t/main.cpp -o %t.out +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out # RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ # RUN: | FileCheck %s diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test index 69dd0fdbb0c19..2692c3d9c3e70 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturnObjC.test @@ -3,7 +3,7 @@ # format variable (in this case Objective-C). # # RUN: split-file %s %t -# RUN: %build %t/main.m -o %t.objc.out +# RUN: %clang_host -g -gdwarf %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 diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test index a5da652372dbc..d59e154dafafd 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test @@ -1,9 +1,7 @@ -# XFAIL: system-windows - # Test the ${function.scope} frame-format variable. # RUN: split-file %s %t -# RUN: %build %t/main.cpp -o %t.out +# RUN: %clang_host -g -gdwarf %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 diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test index 310b5e1992ab8..be6ed7f1a3e04 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionScopeObjC.test @@ -2,7 +2,7 @@ # don't implement this frame format variable (in this case Objective-C). # # RUN: split-file %s %t -# RUN: %build %t/main.m -o %t.objc.out +# RUN: %clang_host -g -gdwarf %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 diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test index 2c0bd9099eb1d..d71d98765a012 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test @@ -1,9 +1,7 @@ -# XFAIL: system-windows - # Test the ${function.template-arguments} frame-format variable. # RUN: split-file %s %t -# RUN: %build %t/main.cpp -o %t.cxx.out +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.cxx.out # RUN: %lldb -x -b -s %t/commands.input %t.cxx.out -o exit 2>&1 \ # RUN: | FileCheck %s diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test index 1726aebacb9eb..3f9872965e8c2 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArgumentsObjC.test @@ -2,7 +2,7 @@ # languages that don't implement this frame format variable (in this case Objective-C). # # RUN: split-file %s %t -# RUN: %build %t/main.m -o %t.objc.out +# RUN: %clang_host -g -gdwarf %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 From 64688bb769cf321497115ce6d2fdb7860036d4cf Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Sat, 26 Apr 2025 13:04:20 +0100 Subject: [PATCH 23/28] [lldb][test] Un-XFAIL TestCxxFrameFormat.test for now This was XPASSing on the linux-win remote CI jobs. Since we're now compiling with DWARF (not PDB), lets see if these pass on Windows again. (cherry picked from commit 8a5bc9e340f88d2651fe743d6b7a820ecd839516) --- lldb/test/Shell/Settings/TestCxxFrameFormat.test | 2 -- 1 file changed, 2 deletions(-) diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormat.test b/lldb/test/Shell/Settings/TestCxxFrameFormat.test index 3751dfa737eae..d2d89b7014511 100644 --- a/lldb/test/Shell/Settings/TestCxxFrameFormat.test +++ b/lldb/test/Shell/Settings/TestCxxFrameFormat.test @@ -1,5 +1,3 @@ -# XFAIL: system-windows - # Test the plugin.cplusplus.display.function-name-format setting. # RUN: split-file %s %t From dc6e033953a34c4f77ac619fbb092e5d4cdfd310 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Sat, 26 Apr 2025 13:23:42 +0100 Subject: [PATCH 24/28] [lldb][test] XFAIL FrameFormat tests on Windows again These are failing for various reasons on CI, most likely due to us requiring the Microsoft mangler. So XFAIL these. (cherry picked from commit 28293ea023c1aeeed3bff0e2d06feacc9bc365cd) --- lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test | 2 ++ lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test | 2 ++ .../Settings/TestFrameFormatFunctionFormattedArguments.test | 2 ++ lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test | 2 ++ lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test | 2 ++ lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test | 2 ++ .../Settings/TestFrameFormatFunctionTemplateArguments.test | 2 ++ 7 files changed, 14 insertions(+) diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test b/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test index 249c25bab0f47..f10de878b8d88 100644 --- a/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test +++ b/lldb/test/Shell/Settings/TestCxxFrameFormatPartialFailure.test @@ -1,3 +1,5 @@ +# XFAIL: target-windows + # 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. diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test index 32e6dcec43ce6..a2cb1c6adf064 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test @@ -1,3 +1,5 @@ +# XFAIL: target-windows + # Test the ${function.basename} frame-format variable. # RUN: split-file %s %t diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test index 2a7860fbf0e24..c4c9062b640f1 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test @@ -1,3 +1,5 @@ +# XFAIL: target-windows + # Test the ${function.formatted-arguments} frame-format variable. # RUN: split-file %s %t diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test index c53023732afcf..c4b74b7d7defa 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test @@ -1,3 +1,5 @@ +# XFAIL: target-windows + # Test the ${function.qualifiers} frame-format variable. # RUN: split-file %s %t diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test index 55ebcbfa8812c..ce970d4d24d86 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test @@ -1,3 +1,5 @@ +# XFAIL: target-windows + # Test the ${function.return-left} and ${function.return-right} # frame-format variables. diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test index d59e154dafafd..d98893aa5e8b4 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test @@ -1,3 +1,5 @@ +# XFAIL: target-windows + # Test the ${function.scope} frame-format variable. # RUN: split-file %s %t diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test index d71d98765a012..f0c29bcee2ce5 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test @@ -1,3 +1,5 @@ +# XFAIL: target-windows + # Test the ${function.template-arguments} frame-format variable. # RUN: split-file %s %t From 91e761c822ee46c28383784f467b23e8e9cc9f1b Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Fri, 25 Apr 2025 22:49:36 +0100 Subject: [PATCH 25/28] [lldb][CPlusPLus] Make C++ frame-format work without debug-info (cherry picked from commit db417e84e944ee80f045414a4ce0f83a3e423e45) --- lldb/source/Core/FormatEntity.cpp | 9 +++-- .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 38 ++++++++++++++++++- .../TestFrameFormatFunctionBasename.test | 4 ++ ...FrameFormatFunctionFormattedArguments.test | 9 +++++ .../TestFrameFormatFunctionQualifiers.test | 4 ++ .../TestFrameFormatFunctionReturn.test | 4 ++ .../TestFrameFormatFunctionScope.test | 7 +++- ...tFrameFormatFunctionTemplateArguments.test | 8 +++- 8 files changed, 73 insertions(+), 10 deletions(-) diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index 25778afaf2ddc..f1470657b01ed 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -1817,11 +1817,12 @@ bool FormatEntity::Format(const Entry &entry, Stream &s, case Entry::Type::FunctionReturnRight: case Entry::Type::FunctionReturnLeft: case Entry::Type::FunctionQualifiers: { - if (!sc->function) - return false; + 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()); - Language *language_plugin = - Language::FindPlugin(sc->function->GetLanguage()); if (!language_plugin) return false; diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 0cf9df559934c..0d62d48f6995c 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -354,6 +354,34 @@ GetDemangledScope(const SymbolContext &sc) { return demangled_name.slice(info->ScopeRange.first, info->ScopeRange.second); } +static bool PrintDemangledArgumentList(Stream &s, const SymbolContext &sc) { + assert(sc.symbol); + + Mangled mangled = sc.GetPossiblyInlinedFunctionName(); + if (!mangled) + return false; + + auto demangled_name = mangled.GetDemangledName().GetStringRef(); + if (demangled_name.empty()) + return false; + + const std::optional &info = mangled.GetDemangledInfo(); + if (!info) + return false; + + // Function without a basename is nonsense. + if (!info->hasBasename()) + return false; + + if (info->ArgumentsRange.second < info->ArgumentsRange.first) + return false; + + s << demangled_name.slice(info->ArgumentsRange.first, + info->ArgumentsRange.second); + + return true; +} + bool CPlusPlusLanguage::MethodName::TrySimplifiedParse() { // This method tries to parse simple method definitions which are presumably // most comman in user programs. Definitions that can be parsed by this @@ -1913,8 +1941,6 @@ bool CPlusPlusLanguage::GetFunctionDisplayName( bool CPlusPlusLanguage::HandleFrameFormatVariable( const SymbolContext &sc, const ExecutionContext *exe_ctx, FormatEntity::Entry::Type type, Stream &s) { - assert(sc.function); - switch (type) { case FormatEntity::Entry::Type::FunctionScope: { std::optional scope = GetDemangledScope(sc); @@ -1948,6 +1974,14 @@ bool CPlusPlusLanguage::HandleFrameFormatVariable( } case FormatEntity::Entry::Type::FunctionFormattedArguments: { + // This ensures we print the arguments even when no debug-info is available. + // + // FIXME: we should have a Entry::Type::FunctionArguments and + // use it in the plugin.cplusplus.display.function-name-format + // once we have a "fallback operator" in the frame-format language. + if (!sc.function && sc.symbol) + return PrintDemangledArgumentList(s, sc); + VariableList args; if (auto variable_list_sp = GetFunctionVariableList(sc)) variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument, diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test index a2cb1c6adf064..7e34fbd3855d0 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionBasename.test @@ -7,6 +7,10 @@ # RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ # RUN: | FileCheck %s # +# RUN: %clang_host -O0 %t/main.cpp -o %t-nodebug.out +# RUN: %lldb -x -b -s %t/commands.input %t-nodebug.out -o exit 2>&1 \ +# RUN: | FileCheck %s + #--- main.cpp namespace ns { template diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test index c4c9062b640f1..04f51701a2a2d 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionFormattedArguments.test @@ -6,6 +6,10 @@ # RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out # RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ # RUN: | FileCheck %s +# +# RUN: %clang_host -O0 %t/main.cpp -o %t-nodebug.out +# RUN: %lldb -x -b -s %t/commands.input %t-nodebug.out -o exit 2>&1 \ +# RUN: | FileCheck %s --check-prefix=CHECK-NODEBUG #--- main.cpp struct Foo { @@ -42,3 +46,8 @@ bt # CHECK: custom-frame '({{.*}}=5, x=10)' # CHECK: custom-frame '(str="hello", fptr=({{.*}}.out`{{.*}}foo(int,{{.*}}int) at main.cpp:{{[0-9]+}}))' # CHECK: custom-frame '(argc=1, argv={{.*}})' + +# CHECK-NODEBUG: custom-frame '()' +# CHECK-NODEBUG: custom-frame '()' +# CHECK-NODEBUG: custom-frame '(int, int)' +# CHECK-NODEBUG: custom-frame '(char const*, void (*)(int, int))' diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test index c4b74b7d7defa..b1dfe834c1deb 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionQualifiers.test @@ -6,6 +6,10 @@ # RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out # RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ # RUN: | FileCheck %s +# +# RUN: %clang_host -O0 %t/main.cpp -o %t-nodebug.out +# RUN: %lldb -x -b -s %t/commands.input %t-nodebug.out -o exit 2>&1 \ +# RUN: | FileCheck %s #--- main.cpp struct Foo { diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test index ce970d4d24d86..f913162a1aa66 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionReturn.test @@ -7,6 +7,10 @@ # RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out # RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ # RUN: | FileCheck %s +# +# RUN: %clang_host -O0 %t/main.cpp -o %t-nodebug.out +# RUN: %lldb -x -b -s %t/commands.input %t-nodebug.out -o exit 2>&1 \ +# RUN: | FileCheck %s #--- main.cpp namespace ns::ns2 { diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test index d98893aa5e8b4..a28c16f95a9e2 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionScope.test @@ -4,8 +4,11 @@ # RUN: split-file %s %t # RUN: %clang_host -g -gdwarf %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: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ +# RUN: | FileCheck %s +# +# RUN: %clang_host -O0 %t/main.cpp -o %t-nodebug.out +# RUN: %lldb -x -b -s %t/commands.input %t-nodebug.out -o exit 2>&1 \ # RUN: | FileCheck %s #--- main.cpp diff --git a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test index f0c29bcee2ce5..1421c8e918a03 100644 --- a/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test +++ b/lldb/test/Shell/Settings/TestFrameFormatFunctionTemplateArguments.test @@ -3,8 +3,12 @@ # Test the ${function.template-arguments} frame-format variable. # RUN: split-file %s %t -# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.cxx.out -# RUN: %lldb -x -b -s %t/commands.input %t.cxx.out -o exit 2>&1 \ +# RUN: %clang_host -g -gdwarf %t/main.cpp -o %t.out +# RUN: %lldb -x -b -s %t/commands.input %t.out -o exit 2>&1 \ +# RUN: | FileCheck %s +# +# RUN: %clang_host -O0 %t/main.cpp -o %t-nodebug.out +# RUN: %lldb -x -b -s %t/commands.input %t-nodebug.out -o exit 2>&1 \ # RUN: | FileCheck %s #--- main.cpp From a06d36290170f6056738f89fa2d37ea5d00e172e Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Fri, 25 Apr 2025 10:25:51 +0100 Subject: [PATCH 26/28] [lldb] Highlight basenames in backtraces instead of PC value --- lldb/source/Core/CoreProperties.td | 4 ++-- .../Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lldb/source/Core/CoreProperties.td b/lldb/source/Core/CoreProperties.td index 2e941fc116807..b6ad8e9f0e0b9 100644 --- a/lldb/source/Core/CoreProperties.td +++ b/lldb/source/Core/CoreProperties.td @@ -112,7 +112,7 @@ let Definition = "debugger" in { Desc<"The default disassembly format string to use when disassembling instruction sequences.">; def FrameFormat: Property<"frame-format", "FormatEntity">, Global, - DefaultStringValue<"frame #${frame.index}: ${ansi.fg.yellow}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\\\\n">, + DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\\\\n">, Desc<"The default frame format string to use when displaying stack frame information for threads.">; def NotiftVoid: Property<"notify-void", "Boolean">, Global, @@ -268,7 +268,7 @@ let Definition = "debugger" in { Desc<"If true, LLDB will automatically escape non-printable and escape characters when formatting strings.">; def FrameFormatUnique: Property<"frame-format-unique", "FormatEntity">, Global, - DefaultStringValue<"frame #${frame.index}: ${ansi.fg.yellow}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\\\\n">, + DefaultStringValue<"frame #${frame.index}: ${ansi.fg.cyan}${frame.pc}${ansi.normal}{ ${module.file.basename}{`${function.name-without-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{${function.is-optimized} [opt]}{${frame.is-artificial} [artificial]}\\\\n">, Desc<"The default frame format string to use when displaying stack frame information for threads from thread backtrace unique.">; def ShowAutosuggestion: Property<"show-autosuggestion", "Boolean">, Global, diff --git a/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td b/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td index 6ec87afe25758..348de256b154a 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td +++ b/lldb/source/Plugins/Language/CPlusPlus/LanguageCPlusPlusProperties.td @@ -3,6 +3,6 @@ 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}">, + DefaultStringValue<"${function.return-left}${function.scope}${ansi.fg.yellow}${function.basename}${ansi.normal}${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.">; } From 885c46ed9fc0a51c57437171b948e422bbde14f8 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Sat, 26 Apr 2025 18:38:20 +0100 Subject: [PATCH 27/28] [lldb][test] XFAIL TestCxxFrameFormat.test for Windows target Fails on Windows CI: ``` | 10: (lldb) break set -l 3 | check:30'0 ~~~~~~~~~~~~~~~~~~~~~~ | 11: error: No selected frame to use to find the default file. | check:30'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 12: error: No file supplied and no default file available. | check:30'0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 13: (lldb) exit ``` This passes fine when compiling on Windows for Linux targets. --- lldb/test/Shell/Settings/TestCxxFrameFormat.test | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/test/Shell/Settings/TestCxxFrameFormat.test b/lldb/test/Shell/Settings/TestCxxFrameFormat.test index d2d89b7014511..0db3bfa1b4a10 100644 --- a/lldb/test/Shell/Settings/TestCxxFrameFormat.test +++ b/lldb/test/Shell/Settings/TestCxxFrameFormat.test @@ -1,3 +1,5 @@ +# XFAIL: target-windows + # Test the plugin.cplusplus.display.function-name-format setting. # RUN: split-file %s %t From 0685cec185cd9d18ac44c3b8d36fcdf9e0579418 Mon Sep 17 00:00:00 2001 From: Michael Buch Date: Sun, 27 Apr 2025 08:18:57 +0100 Subject: [PATCH 28/28] [lldb][Swift][NFC] Adjust GetFunctionDisplayName to take SymbolContext by reference The API was changed upstream for the C++ plugin. This patch adjusts the API implementation for Swift --- .../Plugins/Language/Swift/SwiftLanguage.cpp | 26 +++++++++---------- .../Plugins/Language/Swift/SwiftLanguage.h | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp b/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp index b793c41a6ac96..edfe3b07e30fc 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp +++ b/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp @@ -1699,33 +1699,33 @@ bool SwiftLanguage::IsUninitializedReference(ValueObject &valobj) { } bool SwiftLanguage::GetFunctionDisplayName( - const SymbolContext *sc, const ExecutionContext *exe_ctx, + const SymbolContext &sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s) { switch (representation) { case Language::FunctionNameRepresentation::eName: // No need to customize this. return false; case Language::FunctionNameRepresentation::eNameWithNoArgs: { - if (!sc->function) + if (!sc.function) return false; - if (sc->function->GetLanguage() != eLanguageTypeSwift) + if (sc.function->GetLanguage() != eLanguageTypeSwift) return false; std::string display_name = SwiftLanguageRuntime::DemangleSymbolAsString( - sc->function->GetMangled().GetMangledName().GetStringRef(), - SwiftLanguageRuntime::eSimplified, sc, exe_ctx); + sc.function->GetMangled().GetMangledName().GetStringRef(), + SwiftLanguageRuntime::eSimplified, &sc, exe_ctx); if (display_name.empty()) return false; s << display_name; return true; } case Language::FunctionNameRepresentation::eNameWithArgs: { - if (!sc->function) + if (!sc.function) return false; - if (sc->function->GetLanguage() != eLanguageTypeSwift) + if (sc.function->GetLanguage() != eLanguageTypeSwift) return false; std::string display_name = SwiftLanguageRuntime::DemangleSymbolAsString( - sc->function->GetMangled().GetMangledName().GetStringRef(), - SwiftLanguageRuntime::eSimplified, sc, exe_ctx); + sc.function->GetMangled().GetMangledName().GetStringRef(), + SwiftLanguageRuntime::eSimplified, &sc, exe_ctx); if (display_name.empty()) return false; ExecutionContextScope *exe_scope = @@ -1733,12 +1733,12 @@ bool SwiftLanguage::GetFunctionDisplayName( const InlineFunctionInfo *inline_info = NULL; VariableListSP variable_list_sp; bool get_function_vars = true; - if (sc->block) { - Block *inline_block = sc->block->GetContainingInlinedBlock(); + if (sc.block) { + Block *inline_block = sc.block->GetContainingInlinedBlock(); if (inline_block) { get_function_vars = false; - inline_info = sc->block->GetInlinedFunctionInfo(); + inline_info = sc.block->GetInlinedFunctionInfo(); if (inline_info) variable_list_sp = inline_block->GetBlockVariableList(true); } @@ -1746,7 +1746,7 @@ bool SwiftLanguage::GetFunctionDisplayName( if (get_function_vars) { variable_list_sp = - sc->function->GetBlock(true).GetBlockVariableList(true); + sc.function->GetBlock(true).GetBlockVariableList(true); } if (inline_info) { diff --git a/lldb/source/Plugins/Language/Swift/SwiftLanguage.h b/lldb/source/Plugins/Language/Swift/SwiftLanguage.h index b6da165402b5e..c93743f595661 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftLanguage.h +++ b/lldb/source/Plugins/Language/Swift/SwiftLanguage.h @@ -64,7 +64,7 @@ class SwiftLanguage : public Language { bool IsUninitializedReference(ValueObject &valobj) override; - bool GetFunctionDisplayName(const SymbolContext *sc, + bool GetFunctionDisplayName(const SymbolContext &sc, const ExecutionContext *exe_ctx, FunctionNameRepresentation representation, Stream &s) override;