Author: Michael Buch Date: 2022-10-31T12:25:19Z New Revision: 031096d04d09ac4a93859d161fb42d1a1b308253
URL: https://github.com/llvm/llvm-project/commit/031096d04d09ac4a93859d161fb42d1a1b308253 DIFF: https://github.com/llvm/llvm-project/commit/031096d04d09ac4a93859d161fb42d1a1b308253.diff LOG: [lldb][CPlusPlus] Implement CPlusPlusLanguage::GetFunctionDisplayName This patch implements the `GetFunctionDisplayName` API which gets used by the frame-formatting code to decide how to print a function name. Currently this API trivially returns `false`, so we try to parse the demangled function base-name by hand. We try find the closing parenthesis by doing a forward scan through the demangled name. However, for arguments that contain parenthesis (e.g., function pointers) this would leave garbage in the frame function name. By re-using the `CPlusPlusLanguage` parser for this we offload the need to parse function names to a component that knows how to do this already. We leave the existing parsing code in `FormatEntity` since it's used in cases where a language-plugin is not available (and is not necessarily C++ specific). **Example** For following function: ``` int foo(std::function<int(void)> const& func) { return 1; } ``` Before patch: ``` frame #0: 0x000000010000151c a.out`foo(func= Function = bar() )> const&) at sample.cpp:11:49 ``` After patch: ``` frame #0: 0x000000010000151c a.out`foo(func= Function = bar() ) at sample.cpp:11:49 ``` **Testing** * Added shell test Added: lldb/test/Shell/Settings/Inputs/names.cpp lldb/test/Shell/Settings/TestFrameFormatNameWithArgs.test Modified: lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h Removed: ################################################################################ diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 3e94f555d3cda..752e68c73f875 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -23,11 +23,13 @@ #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Core/ValueObjectVariable.h" #include "lldb/DataFormatters/CXXFunctionPointer.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/DataFormatters/FormattersHelpers.h" #include "lldb/DataFormatters/VectorType.h" #include "lldb/Symbol/SymbolFile.h" +#include "lldb/Symbol/VariableList.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" @@ -171,6 +173,40 @@ static bool IsTrivialBasename(const llvm::StringRef &basename) { return idx == basename.size(); } +/// Writes out the function name in 'full_name' to 'out_stream' +/// but replaces each argument type with the variable name +/// and the corresponding pretty-printed value +static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream, + char const *full_name, + ExecutionContextScope *exe_scope, + VariableList const &args) { + CPlusPlusLanguage::MethodName cpp_method{ConstString(full_name)}; + + if (!cpp_method.IsValid()) + return false; + + llvm::StringRef return_type = cpp_method.GetReturnType(); + if (!return_type.empty()) { + out_stream.PutCString(return_type); + out_stream.PutChar(' '); + } + + out_stream.PutCString(cpp_method.GetScopeQualifiedName()); + out_stream.PutChar('('); + + FormatEntity::PrettyPrintFunctionArguments(out_stream, args, exe_scope); + + out_stream.PutChar(')'); + + llvm::StringRef qualifiers = cpp_method.GetQualifiers(); + if (!qualifiers.empty()) { + out_stream.PutChar(' '); + out_stream.PutCString(qualifiers); + } + + 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 @@ -1465,3 +1501,66 @@ bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const { // that we could check for. return file_path.contains("/usr/include/c++/"); } + +bool CPlusPlusLanguage::GetFunctionDisplayName( + const SymbolContext *sc, const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, Stream &s) { + switch (representation) { + case FunctionNameRepresentation::eNameWithArgs: { + // 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 = sc->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: + return false; + } + + return false; +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h index f9b3251374d0e..809996497c11a 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h @@ -136,6 +136,11 @@ class CPlusPlusLanguage : public Language { ConstString GetDemangledFunctionNameWithoutArguments(Mangled mangled) const override; + bool GetFunctionDisplayName(const SymbolContext *sc, + const ExecutionContext *exe_ctx, + FunctionNameRepresentation representation, + Stream &s) override; + static bool IsCPPMangledName(llvm::StringRef name); // Extract C++ context and identifier from a string using heuristic matching diff --git a/lldb/test/Shell/Settings/Inputs/names.cpp b/lldb/test/Shell/Settings/Inputs/names.cpp new file mode 100644 index 0000000000000..461c6d091a0f4 --- /dev/null +++ b/lldb/test/Shell/Settings/Inputs/names.cpp @@ -0,0 +1,43 @@ +#include <functional> + +namespace detail { +template <typename T> struct Quux {}; +} // namespace detail + +using FuncPtr = detail::Quux<double> (*(*)(int))(float); + +struct Foo { + template <typename T> void foo(T const &t) const noexcept(true) {} + + template <size_t T> void operator<<(size_t) {} + + template <typename T> FuncPtr returns_func_ptr(detail::Quux<int> &&) const noexcept(false) { return nullptr; } +}; + +namespace ns { +template <typename T> int foo(T const &t) noexcept(false) { return 0; } + +template <typename T> FuncPtr returns_func_ptr(detail::Quux<int> &&) { return nullptr; } +} // namespace ns + +int bar() { return 1; } + +namespace { +int anon_bar() { return 1; } +auto anon_lambda = [](std::function<int(int (*)(int))>) mutable {}; +} // namespace + +int main() { + ns::foo(bar); + ns::foo(std::function{bar}); + ns::foo(anon_lambda); + ns::foo(std::function{anon_bar}); + ns::foo(&Foo::foo<std::function<int(int)>>); + ns::returns_func_ptr<int>(detail::Quux<int>{}); + Foo f; + f.foo(std::function{bar}); + f.foo(std::function{anon_bar}); + f.operator<< <(2 > 1)>(0); + f.returns_func_ptr<int>(detail::Quux<int>{}); + return 0; +} diff --git a/lldb/test/Shell/Settings/TestFrameFormatNameWithArgs.test b/lldb/test/Shell/Settings/TestFrameFormatNameWithArgs.test new file mode 100644 index 0000000000000..9cf3dba3d6590 --- /dev/null +++ b/lldb/test/Shell/Settings/TestFrameFormatNameWithArgs.test @@ -0,0 +1,27 @@ +# RUN: %clangxx_host -g -O0 %S/Inputs/names.cpp -std=c++17 -o %t.out +# RUN: %lldb -b -s %s %t.out | FileCheck %s +settings set -f frame-format "frame ${function.name-with-args}\n" +break set -n foo +break set -n operator<< +break set -n returns_func_ptr +run +# CHECK: frame int ns::foo<int ()>(t={{.*}}) +c +# CHECK: frame int ns::foo<std::__1::function<int ()>>(t= Function = bar() ) +c +# CHECK: frame int ns::foo<(anonymous namespace)::$_0>(t={{.*}}) +c +# CHECK: frame int ns::foo<std::__1::function<int ()>>(t= Function = (anonymous namespace)::anon_bar() ) +c +# CHECK: frame int ns::foo<void (Foo::*)(std::__1::function<int (int)> const&) const noexcept>(t={{.*}}) +c +# CHECK: frame ns::returns_func_ptr<int>((null)={{.*}}) +c +# CHECK: frame void Foo::foo<std::__1::function<int ()>>(this={{.*}}, t= Function = bar() ) const +c +# CHECK: frame void Foo::foo<std::__1::function<int ()>>(this={{.*}}, t= Function = (anonymous namespace)::anon_bar() ) const +c +# CHECK: frame void Foo::operator<<<1ul>(this={{.*}}, (null)=0) +c +# CHECK: frame Foo::returns_func_ptr<int>(this={{.*}}, (null)={{.*}}) +q _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits