Skip to content

Commit a4561d9

Browse files
committed
[lldb][CPlusPlusLanguage] Respect the step-avoid-regex for functions with auto return types
**Summary** The primary motivation for this patch is to make sure we handle the step-in behaviour for functions in the `std` namespace which have an `auto` return type. Currently the default `step-avoid-regex` setting is `^std::` but LLDB will still step into template functions with `auto` return types in the `std` namespace. **Details** When we hit a breakpoint and check whether we should stop, we call into `ThreadPlanStepInRange::FrameMatchesAvoidCriteria`. We then ask for the frame function name via `SymbolContext::GetFunctionName(Mangled::ePreferDemangledWithoutArguments)`. This ends up trying to parse the function name using `CPlusPlusLanguage::MethodName::GetBasename` which parses the raw demangled name string. `CPlusPlusNameParser::ParseFunctionImpl` calls `ConsumeTypename` to skip the (in our case auto) return type of the demangled name (according to the Itanium ABI this is a valid thing to encode into the mangled name). However, `ConsumeTypename` doesn't strip out a plain `auto` identifier (it will strip a `decltype(auto) return type though). So we are now left with a basename that still has the return type in it, thus failing to match the `^std::` regex. Example frame where the return type is still part of the function name: ``` Process 1234 stopped * thread #1, stop reason = step in frame #0: 0x12345678 repro`auto std::test_return_auto<int>() at main.cpp:12:5 9 10 template <class> 11 auto test_return_auto() { -> 12 return 42; 13 } ``` This is another case where the `CPlusPlusNameParser` breaks us in subtle ways due to evolving C++ syntax. There are longer-term plans of replacing the hand-rolled C++ parser with an alternative that uses the mangle tree API to do the parsing for us. **Testing** * Added API and unit-tests * Adding support for ABI tags into the parser is a larger undertaking which we would rather solve properly by using libcxxabi's mangle tree parser Differential Revision: https://reviews.llvm.org/D135413
1 parent b3d4d9c commit a4561d9

File tree

5 files changed

+82
-5
lines changed

5 files changed

+82
-5
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "CPlusPlusNameParser.h"
1010

1111
#include "clang/Basic/IdentifierTable.h"
12+
#include "clang/Basic/TokenKinds.h"
1213
#include "llvm/ADT/StringMap.h"
1314
#include "llvm/Support/Threading.h"
1415

@@ -106,7 +107,7 @@ CPlusPlusNameParser::ParseFunctionImpl(bool expect_return_type) {
106107
Bookmark start_position = SetBookmark();
107108
if (expect_return_type) {
108109
// Consume return type if it's expected.
109-
if (!ConsumeTypename())
110+
if (!ConsumeToken(tok::kw_auto) && !ConsumeTypename())
110111
return None;
111112
}
112113

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
CXX_SOURCES := main.cpp
2+
CXXFLAGS_EXTRAS := -std=c++20
3+
4+
include Makefile.rules
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
"""
2+
Test thread step-in ignores frames according to the "Avoid regexp" option.
3+
"""
4+
5+
import lldb
6+
from lldbsuite.test.decorators import *
7+
from lldbsuite.test.lldbtest import *
8+
from lldbsuite.test import lldbutil
9+
10+
class StepAvoidsRegexTestCase(TestBase):
11+
def hit_correct_function(self, pattern):
12+
name = self.thread.frames[0].GetFunctionName()
13+
self.assertTrue(
14+
pattern in name, "Got to '%s' not the expected function '%s'." %
15+
(name, pattern))
16+
17+
def setUp(self):
18+
TestBase.setUp(self)
19+
self.dbg.HandleCommand(
20+
"settings set target.process.thread.step-avoid-regexp ^ignore::")
21+
22+
def test_step_avoid_regex(self):
23+
"""Tests stepping into a function which matches the avoid regex"""
24+
self.build()
25+
(_, _, self.thread, _) = lldbutil.run_to_source_breakpoint(self, "main", lldb.SBFileSpec('main.cpp'))
26+
27+
# Try to step into ignore::auto_ret
28+
self.thread.StepInto()
29+
self.hit_correct_function("main")
30+
31+
# Try to step into ignore::with_tag
32+
self.thread.StepInto()
33+
self.hit_correct_function("main")
34+
35+
# Try to step into ignore::decltype_auto_ret
36+
self.thread.StepInto()
37+
self.hit_correct_function("main")
38+
39+
@expectedFailureAll(bugnumber="rdar://100645742")
40+
def test_step_avoid_regex_abi_tagged_template(self):
41+
"""Tests stepping into an ABI tagged function that matches the avoid regex"""
42+
self.build()
43+
(_, _, self.thread, _) = lldbutil.run_to_source_breakpoint(self, "with_tag_template", lldb.SBFileSpec('main.cpp'))
44+
45+
# Try to step into ignore::with_tag_template
46+
self.thread.StepInto()
47+
self.hit_correct_function("main")
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace ignore {
2+
template <typename T> auto auto_ret(T x) { return 0; }
3+
[[gnu::abi_tag("test")]] int with_tag() { return 0; }
4+
template <typename T> [[gnu::abi_tag("test")]] int with_tag_template() {
5+
return 0;
6+
}
7+
8+
template <typename T> decltype(auto) decltype_auto_ret(T x) { return 0; }
9+
} // namespace ignore
10+
11+
int main() {
12+
auto v1 = ignore::auto_ret<int>(5);
13+
auto v2 = ignore::with_tag();
14+
auto v3 = ignore::decltype_auto_ret<int>(5);
15+
auto v4 = ignore::with_tag_template<int>();
16+
}

lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ TEST(CPlusPlusLanguage, MethodNameParsing) {
6565
"XX::(anonymous namespace)::anon_class::anon_func"},
6666

6767
// Lambda
68-
{"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const",
69-
"main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()", "()", "const",
68+
{"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() "
69+
"const",
70+
"main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()",
71+
"()", "const",
7072
"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()"},
7173

7274
// Function pointers
@@ -110,8 +112,15 @@ TEST(CPlusPlusLanguage, MethodNameParsing) {
110112
"f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>", "()", "",
111113
"f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>"},
112114
{"llvm::Optional<llvm::MCFixupKind>::operator*() const volatile &&",
113-
"llvm::Optional<llvm::MCFixupKind>", "operator*", "()", "const volatile &&",
114-
"llvm::Optional<llvm::MCFixupKind>::operator*"}};
115+
"llvm::Optional<llvm::MCFixupKind>", "operator*", "()",
116+
"const volatile &&", "llvm::Optional<llvm::MCFixupKind>::operator*"},
117+
118+
// auto return type
119+
{"auto std::test_return_auto<int>() const", "std",
120+
"test_return_auto<int>", "()", "const", "std::test_return_auto<int>"},
121+
{"decltype(auto) std::test_return_auto<int>(int) const", "std",
122+
"test_return_auto<int>", "(int)", "const",
123+
"std::test_return_auto<int>"}};
115124

116125
for (const auto &test : test_cases) {
117126
CPlusPlusLanguage::MethodName method(ConstString(test.input));

0 commit comments

Comments
 (0)