Author: Michael Buch Date: 2022-10-10T12:50:15+01:00 New Revision: a4561d934877fbba5cfb3cac3195a41707ba6043
URL: https://github.com/llvm/llvm-project/commit/a4561d934877fbba5cfb3cac3195a41707ba6043 DIFF: https://github.com/llvm/llvm-project/commit/a4561d934877fbba5cfb3cac3195a41707ba6043.diff LOG: [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 Added: lldb/test/API/functionalities/step-avoids-regexp/Makefile lldb/test/API/functionalities/step-avoids-regexp/TestStepAvoidsRegexp.py lldb/test/API/functionalities/step-avoids-regexp/main.cpp Modified: lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp Removed: ################################################################################ diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp index bc3b42a4ef07a..ac70226ca2a4d 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusNameParser.cpp @@ -9,6 +9,7 @@ #include "CPlusPlusNameParser.h" #include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/TokenKinds.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/Threading.h" @@ -106,7 +107,7 @@ CPlusPlusNameParser::ParseFunctionImpl(bool expect_return_type) { Bookmark start_position = SetBookmark(); if (expect_return_type) { // Consume return type if it's expected. - if (!ConsumeTypename()) + if (!ConsumeToken(tok::kw_auto) && !ConsumeTypename()) return None; } diff --git a/lldb/test/API/functionalities/step-avoids-regexp/Makefile b/lldb/test/API/functionalities/step-avoids-regexp/Makefile new file mode 100644 index 0000000000000..4f79c0a900c3a --- /dev/null +++ b/lldb/test/API/functionalities/step-avoids-regexp/Makefile @@ -0,0 +1,4 @@ +CXX_SOURCES := main.cpp +CXXFLAGS_EXTRAS := -std=c++20 + +include Makefile.rules diff --git a/lldb/test/API/functionalities/step-avoids-regexp/TestStepAvoidsRegexp.py b/lldb/test/API/functionalities/step-avoids-regexp/TestStepAvoidsRegexp.py new file mode 100644 index 0000000000000..a3de53e3c4b91 --- /dev/null +++ b/lldb/test/API/functionalities/step-avoids-regexp/TestStepAvoidsRegexp.py @@ -0,0 +1,47 @@ +""" +Test thread step-in ignores frames according to the "Avoid regexp" option. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class StepAvoidsRegexTestCase(TestBase): + def hit_correct_function(self, pattern): + name = self.thread.frames[0].GetFunctionName() + self.assertTrue( + pattern in name, "Got to '%s' not the expected function '%s'." % + (name, pattern)) + + def setUp(self): + TestBase.setUp(self) + self.dbg.HandleCommand( + "settings set target.process.thread.step-avoid-regexp ^ignore::") + + def test_step_avoid_regex(self): + """Tests stepping into a function which matches the avoid regex""" + self.build() + (_, _, self.thread, _) = lldbutil.run_to_source_breakpoint(self, "main", lldb.SBFileSpec('main.cpp')) + + # Try to step into ignore::auto_ret + self.thread.StepInto() + self.hit_correct_function("main") + + # Try to step into ignore::with_tag + self.thread.StepInto() + self.hit_correct_function("main") + + # Try to step into ignore::decltype_auto_ret + self.thread.StepInto() + self.hit_correct_function("main") + + @expectedFailureAll(bugnumber="rdar://100645742") + def test_step_avoid_regex_abi_tagged_template(self): + """Tests stepping into an ABI tagged function that matches the avoid regex""" + self.build() + (_, _, self.thread, _) = lldbutil.run_to_source_breakpoint(self, "with_tag_template", lldb.SBFileSpec('main.cpp')) + + # Try to step into ignore::with_tag_template + self.thread.StepInto() + self.hit_correct_function("main") diff --git a/lldb/test/API/functionalities/step-avoids-regexp/main.cpp b/lldb/test/API/functionalities/step-avoids-regexp/main.cpp new file mode 100644 index 0000000000000..20671188bd58e --- /dev/null +++ b/lldb/test/API/functionalities/step-avoids-regexp/main.cpp @@ -0,0 +1,16 @@ +namespace ignore { +template <typename T> auto auto_ret(T x) { return 0; } +[[gnu::abi_tag("test")]] int with_tag() { return 0; } +template <typename T> [[gnu::abi_tag("test")]] int with_tag_template() { + return 0; +} + +template <typename T> decltype(auto) decltype_auto_ret(T x) { return 0; } +} // namespace ignore + +int main() { + auto v1 = ignore::auto_ret<int>(5); + auto v2 = ignore::with_tag(); + auto v3 = ignore::decltype_auto_ret<int>(5); + auto v4 = ignore::with_tag_template<int>(); +} diff --git a/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp b/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp index 8ec4b5492fa5c..9a7e9a792f5b9 100644 --- a/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp +++ b/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp @@ -65,8 +65,10 @@ TEST(CPlusPlusLanguage, MethodNameParsing) { "XX::(anonymous namespace)::anon_class::anon_func"}, // Lambda - {"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const", - "main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()", "()", "const", + {"main::{lambda()#1}::operator()() const::{lambda()#1}::operator()() " + "const", + "main::{lambda()#1}::operator()() const::{lambda()#1}", "operator()", + "()", "const", "main::{lambda()#1}::operator()() const::{lambda()#1}::operator()"}, // Function pointers @@ -110,8 +112,15 @@ TEST(CPlusPlusLanguage, MethodNameParsing) { "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>", "()", "", "f<A<operator<(X,Y)::Subclass>, sizeof(B)<sizeof(C)>"}, {"llvm::Optional<llvm::MCFixupKind>::operator*() const volatile &&", - "llvm::Optional<llvm::MCFixupKind>", "operator*", "()", "const volatile &&", - "llvm::Optional<llvm::MCFixupKind>::operator*"}}; + "llvm::Optional<llvm::MCFixupKind>", "operator*", "()", + "const volatile &&", "llvm::Optional<llvm::MCFixupKind>::operator*"}, + + // auto return type + {"auto std::test_return_auto<int>() const", "std", + "test_return_auto<int>", "()", "const", "std::test_return_auto<int>"}, + {"decltype(auto) std::test_return_auto<int>(int) const", "std", + "test_return_auto<int>", "(int)", "const", + "std::test_return_auto<int>"}}; for (const auto &test : test_cases) { CPlusPlusLanguage::MethodName method(ConstString(test.input)); _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits