Skip to content

Commit 4aeda9f

Browse files
committed
[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 (cherry picked from commit 971407a)
1 parent 7bad7e7 commit 4aeda9f

File tree

5 files changed

+175
-1
lines changed

5 files changed

+175
-1
lines changed

lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
2323
#include "lldb/Core/Module.h"
2424
#include "lldb/Core/PluginManager.h"
2525
#include "lldb/Core/UniqueCStringMap.h"
26+
#include "lldb/Core/ValueObjectVariable.h"
2627
#include "lldb/DataFormatters/CXXFunctionPointer.h"
2728
#include "lldb/DataFormatters/DataVisualization.h"
2829
#include "lldb/DataFormatters/FormattersHelpers.h"
2930
#include "lldb/DataFormatters/VectorType.h"
3031
#include "lldb/Symbol/SymbolFile.h"
32+
#include "lldb/Symbol/VariableList.h"
3133
#include "lldb/Utility/ConstString.h"
3234
#include "lldb/Utility/LLDBLog.h"
3335
#include "lldb/Utility/Log.h"
@@ -171,6 +173,40 @@ static bool IsTrivialBasename(const llvm::StringRef &basename) {
171173
return idx == basename.size();
172174
}
173175

176+
/// Writes out the function name in 'full_name' to 'out_stream'
177+
/// but replaces each argument type with the variable name
178+
/// and the corresponding pretty-printed value
179+
static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream,
180+
char const *full_name,
181+
ExecutionContextScope *exe_scope,
182+
VariableList const &args) {
183+
CPlusPlusLanguage::MethodName cpp_method{ConstString(full_name)};
184+
185+
if (!cpp_method.IsValid())
186+
return false;
187+
188+
llvm::StringRef return_type = cpp_method.GetReturnType();
189+
if (!return_type.empty()) {
190+
out_stream.PutCString(return_type);
191+
out_stream.PutChar(' ');
192+
}
193+
194+
out_stream.PutCString(cpp_method.GetScopeQualifiedName());
195+
out_stream.PutChar('(');
196+
197+
FormatEntity::PrettyPrintFunctionArguments(out_stream, args, exe_scope);
198+
199+
out_stream.PutChar(')');
200+
201+
llvm::StringRef qualifiers = cpp_method.GetQualifiers();
202+
if (!qualifiers.empty()) {
203+
out_stream.PutChar(' ');
204+
out_stream.PutCString(qualifiers);
205+
}
206+
207+
return true;
208+
}
209+
174210
bool CPlusPlusLanguage::MethodName::TrySimplifiedParse() {
175211
// This method tries to parse simple method definitions which are presumably
176212
// most comman in user programs. Definitions that can be parsed by this
@@ -1470,3 +1506,66 @@ bool CPlusPlusLanguage::IsSourceFile(llvm::StringRef file_path) const {
14701506
// that we could check for.
14711507
return file_path.contains("/usr/include/c++/");
14721508
}
1509+
1510+
bool CPlusPlusLanguage::GetFunctionDisplayName(
1511+
const SymbolContext *sc, const ExecutionContext *exe_ctx,
1512+
FunctionNameRepresentation representation, Stream &s) {
1513+
switch (representation) {
1514+
case FunctionNameRepresentation::eNameWithArgs: {
1515+
// Print the function name with arguments in it
1516+
if (sc->function) {
1517+
ExecutionContextScope *exe_scope =
1518+
exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
1519+
const char *cstr = sc->function->GetName().AsCString(nullptr);
1520+
if (cstr) {
1521+
const InlineFunctionInfo *inline_info = nullptr;
1522+
VariableListSP variable_list_sp;
1523+
bool get_function_vars = true;
1524+
if (sc->block) {
1525+
Block *inline_block = sc->block->GetContainingInlinedBlock();
1526+
1527+
if (inline_block) {
1528+
get_function_vars = false;
1529+
inline_info = sc->block->GetInlinedFunctionInfo();
1530+
if (inline_info)
1531+
variable_list_sp = inline_block->GetBlockVariableList(true);
1532+
}
1533+
}
1534+
1535+
if (get_function_vars) {
1536+
variable_list_sp =
1537+
sc->function->GetBlock(true).GetBlockVariableList(true);
1538+
}
1539+
1540+
if (inline_info) {
1541+
s.PutCString(cstr);
1542+
s.PutCString(" [inlined] ");
1543+
cstr = inline_info->GetName().GetCString();
1544+
}
1545+
1546+
VariableList args;
1547+
if (variable_list_sp)
1548+
variable_list_sp->AppendVariablesWithScope(eValueTypeVariableArgument,
1549+
args);
1550+
if (args.GetSize() > 0) {
1551+
if (!PrettyPrintFunctionNameWithArgs(s, cstr, exe_scope, args))
1552+
return false;
1553+
} else {
1554+
s.PutCString(cstr);
1555+
}
1556+
return true;
1557+
}
1558+
} else if (sc->symbol) {
1559+
const char *cstr = sc->symbol->GetName().AsCString(nullptr);
1560+
if (cstr) {
1561+
s.PutCString(cstr);
1562+
return true;
1563+
}
1564+
}
1565+
} break;
1566+
default:
1567+
return false;
1568+
}
1569+
1570+
return false;
1571+
}

lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ class CPlusPlusLanguage : public Language {
136136
ConstString
137137
GetDemangledFunctionNameWithoutArguments(Mangled mangled) const override;
138138

139+
bool GetFunctionDisplayName(const SymbolContext *sc,
140+
const ExecutionContext *exe_ctx,
141+
FunctionNameRepresentation representation,
142+
Stream &s) override;
143+
139144
static bool IsCPPMangledName(llvm::StringRef name);
140145

141146
// Extract C++ context and identifier from a string using heuristic matching
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include <functional>
2+
3+
namespace detail {
4+
template <typename T> struct Quux {};
5+
} // namespace detail
6+
7+
using FuncPtr = detail::Quux<double> (*(*)(int))(float);
8+
9+
struct Foo {
10+
template <typename T> void foo(T const &t) const noexcept(true) {}
11+
12+
template <size_t T> void operator<<(size_t) {}
13+
14+
template <typename T> FuncPtr returns_func_ptr(detail::Quux<int> &&) const noexcept(false) { return nullptr; }
15+
};
16+
17+
namespace ns {
18+
template <typename T> int foo(T const &t) noexcept(false) { return 0; }
19+
20+
template <typename T> FuncPtr returns_func_ptr(detail::Quux<int> &&) { return nullptr; }
21+
} // namespace ns
22+
23+
int bar() { return 1; }
24+
25+
namespace {
26+
int anon_bar() { return 1; }
27+
auto anon_lambda = [](std::function<int(int (*)(int))>) mutable {};
28+
} // namespace
29+
30+
int main() {
31+
ns::foo(bar);
32+
ns::foo(std::function{bar});
33+
ns::foo(anon_lambda);
34+
ns::foo(std::function{anon_bar});
35+
ns::foo(&Foo::foo<std::function<int(int)>>);
36+
ns::returns_func_ptr<int>(detail::Quux<int>{});
37+
Foo f;
38+
f.foo(std::function{bar});
39+
f.foo(std::function{anon_bar});
40+
f.operator<< <(2 > 1)>(0);
41+
f.returns_func_ptr<int>(detail::Quux<int>{});
42+
return 0;
43+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# RUN: %clangxx_host -g -O0 %S/Inputs/names.cpp -std=c++17 -o %t.out
2+
# RUN: %lldb -b -s %s %t.out | FileCheck %s
3+
settings set -f frame-format "frame ${function.name-with-args}\n"
4+
break set -n foo
5+
break set -n operator<<
6+
break set -n returns_func_ptr
7+
run
8+
# CHECK: frame int ns::foo<int ()>(t={{.*}})
9+
c
10+
# CHECK: frame int ns::foo<std::__1::function<int ()>>(t= Function = bar() )
11+
c
12+
# CHECK: frame int ns::foo<(anonymous namespace)::$_0>(t={{.*}})
13+
c
14+
# CHECK: frame int ns::foo<std::__1::function<int ()>>(t= Function = (anonymous namespace)::anon_bar() )
15+
c
16+
# CHECK: frame int ns::foo<void (Foo::*)(std::__1::function<int (int)> const&) const noexcept>(t={{.*}})
17+
c
18+
# CHECK: frame ns::returns_func_ptr<int>((null)={{.*}})
19+
c
20+
# CHECK: frame void Foo::foo<std::__1::function<int ()>>(this={{.*}}, t= Function = bar() ) const
21+
c
22+
# CHECK: frame void Foo::foo<std::__1::function<int ()>>(this={{.*}}, t= Function = (anonymous namespace)::anon_bar() ) const
23+
c
24+
# CHECK: frame void Foo::operator<<<1ul>(this={{.*}}, (null)=0)
25+
c
26+
# CHECK: frame Foo::returns_func_ptr<int>(this={{.*}}, (null)={{.*}})
27+
q

lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ TEST(CPlusPlusLanguage, MethodNameParsing) {
206206
"Foo[abi:abc]<int>", "operator<<<Foo[abi:abc]<int>>", "(int)", "&",
207207
"Foo[abi:abc]<int>::operator<<<Foo[abi:abc]<int>>"},
208208

209-
{"auto A::operator<=>[abi:tag]<A::B>()", "A",
209+
{"auto A::operator<=>[abi:tag]<A::B>()", "auto", "A",
210210
"operator<=>[abi:tag]<A::B>", "()", "",
211211
"A::operator<=>[abi:tag]<A::B>"}};
212212

0 commit comments

Comments
 (0)