Skip to content

Commit 1836531

Browse files
committed
[lldb-dap] Add frame recognizers for libc++ std::invoke
With this commit, we also hide the implementation details of `std::invoke`. To do so, the `LibCXXFrameRecognizer` got a couple more regular expressions. The regular expression passed into the `AddRecognizer` became problematic, as it was evaluated on the demangled name. Those names also included result types for C++ symbols. For `std::__invoke` the return type is a huge `decltype(...)`, making the regular expresison really hard to write. Instead, I added support for `AddRecognizer` to match on the demangled names without result type and argument types. By hiding the implementation details of `invoke`, also the back traces for `std::function` become even nicer, because `std::function` is using `__invoke` internally.
1 parent b765fdd commit 1836531

File tree

8 files changed

+173
-21
lines changed

8 files changed

+173
-21
lines changed

lldb/include/lldb/Target/StackFrameRecognizer.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,14 @@ class StackFrameRecognizerManager {
107107
public:
108108
void AddRecognizer(lldb::StackFrameRecognizerSP recognizer,
109109
ConstString module, llvm::ArrayRef<ConstString> symbols,
110-
bool first_instruction_only = true);
110+
bool first_instruction_only = true,
111+
Mangled::NamePreference mangling_preference = Mangled::ePreferDemangled);
111112

112113
void AddRecognizer(lldb::StackFrameRecognizerSP recognizer,
113114
lldb::RegularExpressionSP module,
114115
lldb::RegularExpressionSP symbol,
115-
bool first_instruction_only = true);
116+
bool first_instruction_only = true,
117+
Mangled::NamePreference mangling_preference = Mangled::ePreferDemangled);
116118

117119
void ForEach(std::function<
118120
void(uint32_t recognizer_id, std::string recognizer_name,
@@ -143,10 +145,13 @@ class StackFrameRecognizerManager {
143145
std::vector<ConstString> symbols;
144146
lldb::RegularExpressionSP symbol_regexp;
145147
bool first_instruction_only;
148+
Mangled::NamePreference mangling_preference;
146149
};
147150

148151
std::deque<RegisteredEntry> m_recognizers;
149152
uint16_t m_generation;
153+
std::unordered_set<Mangled::NamePreference> m_used_manglings;
154+
150155
};
151156

152157
/// \class ValueObjectRecognizerSynthesizedValue

lldb/source/Commands/Options.td

+1-1
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,7 @@ let Command = "thread backtrace" in {
10491049
def thread_backtrace_extended : Option<"extended", "e">, Group<1>,
10501050
Arg<"Boolean">, Desc<"Show the extended backtrace, if available">;
10511051
def thread_backtrace_unfiltered : Option<"unfiltered", "u">, Group<1>,
1052-
Desc<"Filter out frames according to installed frame recognizers">;
1052+
Desc<"Do not filter out frames according to installed frame recognizers">;
10531053
}
10541054

10551055
let Command = "thread step scope" in {

lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp

+36-12
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include <cstring>
10+
#include <iostream>
1011

1112
#include <memory>
1213

@@ -44,7 +45,7 @@ char CPPLanguageRuntime::ID = 0;
4445
/// A frame recognizer that is installed to hide libc++ implementation
4546
/// details from the backtrace.
4647
class LibCXXFrameRecognizer : public StackFrameRecognizer {
47-
RegularExpression m_hidden_function_regex;
48+
std::array<RegularExpression, 3> m_hidden_regex;
4849
RecognizedStackFrameSP m_hidden_frame;
4950

5051
struct LibCXXHiddenFrame : public RecognizedStackFrame {
@@ -53,10 +54,32 @@ class LibCXXFrameRecognizer : public StackFrameRecognizer {
5354

5455
public:
5556
LibCXXFrameRecognizer()
56-
: m_hidden_function_regex(
57-
R"(^std::__1::(__function.*::operator\(\)|__invoke))"
58-
R"((\[.*\])?)" // ABI tag.
59-
R"(( const)?$)"), // const.
57+
: m_hidden_regex{
58+
// internal implementation details of std::function
59+
// std::__1::__function::__alloc_func<void (*)(), std::__1::allocator<void (*)()>, void ()>::operator()[abi:ne200000]
60+
// std::__1::__function::__func<void (*)(), std::__1::allocator<void (*)()>, void ()>::operator()
61+
// std::__1::__function::__value_func<void ()>::operator()[abi:ne200000]() const
62+
RegularExpression{""
63+
R"(^std::__[0-9]*::)" // Namespace.
64+
R"(__function::.*::operator\(\))"
65+
R"((\[.*\])?)" // ABI tag.
66+
R"(( const)?$)"}, // const.
67+
// internal implementation details of std::invoke
68+
// std::__1::__invoke[abi:ne200000]<void (*&)()>
69+
RegularExpression{
70+
R"(^std::__[0-9]*::)" // Namespace.
71+
R"(__invoke)"
72+
R"((\[.*\])?)" // ABI tag.
73+
R"(<.*>)"}, // template argument.
74+
// internal implementation details of std::invoke
75+
// std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ne200000]<void (*&)()>
76+
RegularExpression{
77+
R"(^std::__[0-9]*::)" // Namespace.
78+
R"(__invoke_void_return_wrapper<.*>::__call)"
79+
R"((\[.*\])?)" // ABI tag.
80+
R"(<.*>)"} // template argument.
81+
82+
},
6083
m_hidden_frame(new LibCXXHiddenFrame()) {}
6184

6285
std::string GetName() override { return "libc++ frame recognizer"; }
@@ -69,8 +92,9 @@ class LibCXXFrameRecognizer : public StackFrameRecognizer {
6992
if (!sc.function)
7093
return {};
7194

72-
if (m_hidden_function_regex.Execute(sc.function->GetNameNoArguments()))
73-
return m_hidden_frame;
95+
for (RegularExpression &r : m_hidden_regex)
96+
if (r.Execute(sc.function->GetNameNoArguments()))
97+
return m_hidden_frame;
7498

7599
return {};
76100
}
@@ -81,8 +105,9 @@ CPPLanguageRuntime::CPPLanguageRuntime(Process *process)
81105
if (process)
82106
process->GetTarget().GetFrameRecognizerManager().AddRecognizer(
83107
StackFrameRecognizerSP(new LibCXXFrameRecognizer()), {},
84-
std::make_shared<RegularExpression>("^std::__1::"),
85-
/*first_instruction_only*/ false);
108+
std::make_shared<RegularExpression>("std::__[0-9]*::"),
109+
/*first_instruction_only=*/ false,
110+
/*mangling_preference=*/ Mangled::ePreferDemangledWithoutArguments);
86111
}
87112

88113
bool CPPLanguageRuntime::IsAllowedRuntimeValue(ConstString name) {
@@ -108,8 +133,7 @@ bool contains_lambda_identifier(llvm::StringRef &str_ref) {
108133

109134
CPPLanguageRuntime::LibCppStdFunctionCallableInfo
110135
line_entry_helper(Target &target, const SymbolContext &sc, Symbol *symbol,
111-
llvm::StringRef first_template_param_sref,
112-
bool has_invoke) {
136+
llvm::StringRef first_template_param_sref, bool has_invoke) {
113137

114138
CPPLanguageRuntime::LibCppStdFunctionCallableInfo optional_info;
115139

@@ -190,7 +214,7 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
190214
ValueObjectSP sub_member_f_(member_f_->GetChildMemberWithName("__f_"));
191215

192216
if (sub_member_f_)
193-
member_f_ = sub_member_f_;
217+
member_f_ = sub_member_f_;
194218
}
195219

196220
if (!member_f_)

lldb/source/Target/StackFrameRecognizer.cpp

+37-4
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,24 @@ void StackFrameRecognizerManager::BumpGeneration() {
6262

6363
void StackFrameRecognizerManager::AddRecognizer(
6464
StackFrameRecognizerSP recognizer, ConstString module,
65-
llvm::ArrayRef<ConstString> symbols, bool first_instruction_only) {
65+
llvm::ArrayRef<ConstString> symbols, bool first_instruction_only,
66+
Mangled::NamePreference mangling_preference) {
6667
m_recognizers.push_front({(uint32_t)m_recognizers.size(), recognizer, false,
6768
module, RegularExpressionSP(), symbols,
6869
RegularExpressionSP(), first_instruction_only});
70+
m_used_manglings.insert(mangling_preference);
6971
BumpGeneration();
7072
}
7173

7274
void StackFrameRecognizerManager::AddRecognizer(
7375
StackFrameRecognizerSP recognizer, RegularExpressionSP module,
74-
RegularExpressionSP symbol, bool first_instruction_only) {
76+
RegularExpressionSP symbol, bool first_instruction_only,
77+
Mangled::NamePreference mangling_preference) {
7578
m_recognizers.push_front({(uint32_t)m_recognizers.size(), recognizer, true,
7679
ConstString(), module, std::vector<ConstString>(),
77-
symbol, first_instruction_only});
80+
symbol, first_instruction_only,
81+
mangling_preference});
82+
m_used_manglings.insert(mangling_preference);
7883
BumpGeneration();
7984
}
8085

@@ -119,13 +124,30 @@ bool StackFrameRecognizerManager::RemoveRecognizerWithID(
119124
void StackFrameRecognizerManager::RemoveAllRecognizers() {
120125
BumpGeneration();
121126
m_recognizers.clear();
127+
m_used_manglings.clear();
122128
}
123129

124130
StackFrameRecognizerSP
125131
StackFrameRecognizerManager::GetRecognizerForFrame(StackFrameSP frame) {
126132
const SymbolContext &symctx = frame->GetSymbolContext(
127133
eSymbolContextModule | eSymbolContextFunction | eSymbolContextSymbol);
128-
ConstString function_name = symctx.GetFunctionName();
134+
ConstString function_name_mangled;
135+
ConstString function_name_demangled;
136+
ConstString function_name_noargs;
137+
for (Mangled::NamePreference m : m_used_manglings) {
138+
switch (m) {
139+
case Mangled::ePreferMangled:
140+
function_name_mangled = symctx.GetFunctionName(m);
141+
break;
142+
case Mangled::ePreferDemangled:
143+
function_name_demangled = symctx.GetFunctionName(m);
144+
break;
145+
case Mangled::ePreferDemangledWithoutArguments:
146+
function_name_noargs = symctx.GetFunctionName(m);
147+
break;
148+
}
149+
}
150+
129151
ModuleSP module_sp = symctx.module_sp;
130152
if (!module_sp)
131153
return StackFrameRecognizerSP();
@@ -145,6 +167,17 @@ StackFrameRecognizerManager::GetRecognizerForFrame(StackFrameSP frame) {
145167
if (!entry.module_regexp->Execute(module_name.GetStringRef()))
146168
continue;
147169

170+
ConstString function_name = [&]() {
171+
switch (entry.mangling_preference) {
172+
case Mangled::ePreferMangled:
173+
return function_name_mangled;
174+
case Mangled::ePreferDemangled:
175+
return function_name_demangled;
176+
case Mangled::ePreferDemangledWithoutArguments:
177+
return function_name_noargs;
178+
}
179+
}();
180+
148181
if (!entry.symbols.empty())
149182
if (!llvm::is_contained(entry.symbols, function_name))
150183
continue;

lldb/test/API/lang/cpp/std-function-recognizer/TestStdFunctionRecognizer.py

+19-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,23 @@
77
class LibCxxStdFunctionRecognizerTestCase(TestBase):
88
NO_DEBUG_INFO_TESTCASE = True
99

10+
@add_test_categories(["libc++"])
11+
def test_frame_recognizer(self):
12+
"""Test that std::function all implementation details are hidden in SBFrame"""
13+
self.build()
14+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
15+
self, "// break here", lldb.SBFileSpec("main.cpp")
16+
)
17+
self.assertIn("foo", thread.GetFrameAtIndex(0).GetFunctionName())
18+
# Skip all hidden frames
19+
frame_id = 1
20+
while frame_id < thread.GetNumFrames() and thread.GetFrameAtIndex(frame_id).IsHidden():
21+
frame_id = frame_id + 1
22+
# Expect `std::function<...>::operator()` to be the direct parent of `foo`
23+
self.assertIn("::operator()", thread.GetFrameAtIndex(frame_id).GetFunctionName())
24+
# And right above that, there should be the `main` frame
25+
self.assertIn("main", thread.GetFrameAtIndex(frame_id + 1).GetFunctionName())
26+
1027
@add_test_categories(["libc++"])
1128
def test_backtrace(self):
1229
"""Test that std::function implementation details are hidden in bt"""
@@ -27,12 +44,12 @@ def test_backtrace(self):
2744
self.expect(
2845
"thread backtrace -u",
2946
ordered=True,
30-
patterns=["frame.*foo", "frame.*std::__1::__function", "frame.*main"],
47+
patterns=["frame.*foo", "frame.*std::__[0-9]*::__function", "frame.*main"],
3148
)
3249
self.expect(
3350
"thread backtrace --unfiltered",
3451
ordered=True,
35-
patterns=["frame.*foo", "frame.*std::__1::__function", "frame.*main"],
52+
patterns=["frame.*foo", "frame.*std::__[0-9]*::__function", "frame.*main"],
3653
)
3754

3855
@add_test_categories(["libc++"])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CXX_SOURCES := main.cpp
2+
USE_LIBCPP := 1
3+
CXXFLAGS_EXTRAS := -std=c++17
4+
5+
include Makefile.rules
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
from lldbsuite.test.lldbtest import *
4+
from lldbsuite.test import lldbutil
5+
6+
7+
class LibCxxStdFunctionRecognizerTestCase(TestBase):
8+
NO_DEBUG_INFO_TESTCASE = True
9+
10+
@add_test_categories(["libc++"])
11+
def test_frame_recognizer(self):
12+
"""Test that implementation details details of `std::invoke`"""
13+
self.build()
14+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
15+
self, "// break here", lldb.SBFileSpec("main.cpp")
16+
)
17+
18+
while process.GetState() != lldb.eStateExited:
19+
self.assertIn("print_num", thread.GetFrameAtIndex(0).GetFunctionName())
20+
self.process.Continue()
21+
# # Skip all hidden frames
22+
# frame_id = 1
23+
# while frame_id < thread.GetNumFrames() and thread.GetFrameAtIndex(frame_id).IsHidden():
24+
# frame_id = frame_id + 1
25+
# # Expect `std::function<...>::operator()` to be the direct parent of `foo`
26+
# self.assertIn("::operator()", thread.GetFrameAtIndex(frame_id).GetFunctionName())
27+
# # And right above that, there should be the `main` frame
28+
# self.assertIn("main", thread.GetFrameAtIndex(frame_id + 1).GetFunctionName())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include <functional>
2+
#include <iostream>
3+
4+
void print_num(int i) {
5+
// break here
6+
std::cout << i << '\n';
7+
}
8+
9+
int add(int i, int j) {
10+
// break here
11+
return i + j;
12+
}
13+
14+
struct PrintAdder {
15+
PrintAdder(int num) : num_(num) {}
16+
void operator()(int i) const {
17+
// break here
18+
std::cout << i << '\n';
19+
}
20+
void print_add(int i) const {
21+
// break here
22+
std::cout << num_ + i << '\n';
23+
}
24+
int num_;
25+
};
26+
27+
int main() {
28+
// Invoke a void-returning function
29+
std::invoke(print_num, -9);
30+
31+
// Invoke a non-void-returning function
32+
std::cout << std::invoke(add, 1, 10) << '\n';
33+
34+
// Invoke a member function
35+
const PrintAdder foo(314159);
36+
std::invoke(&PrintAdder::print_add, foo, 1);
37+
38+
// Invoke a function object
39+
std::invoke(PrintAdder(12), 18);
40+
}

0 commit comments

Comments
 (0)