[Lldb-commits] [lldb] 031096d - [lldb][CPlusPlus] Implement CPlusPlusLanguage::GetFunctionDisplayName
Michael Buch via lldb-commits
lldb-commits at lists.llvm.org
Mon Oct 31 05:25:42 PDT 2022
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
More information about the lldb-commits
mailing list