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 + +namespace detail { +template struct Quux {}; +} // namespace detail + +using FuncPtr = detail::Quux (*(*)(int))(float); + +struct Foo { + template void foo(T const &t) const noexcept(true) {} + + template void operator<<(size_t) {} + + template FuncPtr returns_func_ptr(detail::Quux &&) const noexcept(false) { return nullptr; } +}; + +namespace ns { +template int foo(T const &t) noexcept(false) { return 0; } + +template FuncPtr returns_func_ptr(detail::Quux &&) { return nullptr; } +} // namespace ns + +int bar() { return 1; } + +namespace { +int anon_bar() { return 1; } +auto anon_lambda = [](std::function) 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>); + ns::returns_func_ptr(detail::Quux{}); + Foo f; + f.foo(std::function{bar}); + f.foo(std::function{anon_bar}); + f.operator<< <(2 > 1)>(0); + f.returns_func_ptr(detail::Quux{}); + 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(t={{.*}}) +c +# CHECK: frame int ns::foo>(t= Function = bar() ) +c +# CHECK: frame int ns::foo<(anonymous namespace)::$_0>(t={{.*}}) +c +# CHECK: frame int ns::foo>(t= Function = (anonymous namespace)::anon_bar() ) +c +# CHECK: frame int ns::foo const&) const noexcept>(t={{.*}}) +c +# CHECK: frame ns::returns_func_ptr((null)={{.*}}) +c +# CHECK: frame void Foo::foo>(this={{.*}}, t= Function = bar() ) const +c +# CHECK: frame void Foo::foo>(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(this={{.*}}, (null)={{.*}}) +q