[libcxx-commits] [libcxx] [lldb] [lldb][libc++] Hide all libc++ implementation details from stacktraces (PR #108870)
Adrian Vogelsgesang via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Sep 23 13:47:55 PDT 2024
https://github.com/vogelsgesang updated https://github.com/llvm/llvm-project/pull/108870
>From 04daaac0eade25a439856bbb287e15860aba1dfd Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Tue, 27 Aug 2024 17:34:11 +0000
Subject: [PATCH 1/7] [lldb][libc++] Hide all libc++ implementation details
from stacktraces
This commit changes the libc++ frame recognizer to hide implementation
details of libc++ more aggressively. The applied heuristic is rather
straightforward: We consider every function name starting with `__` as
an implementation detail.
This works pretty neatly for `std::invoke`, `std::function`,
`std::sort`, `std::map::emplace` and many others. Also, this should
align quite nicely with libc++'s general coding convention of using the
`__` for their implementation details, thereby keeping the future
maintenance effort low.
However, it is noteworthy, that this does not work 100% in all cases:
E.g., for `std::ranges::sort`, itself is not really a function call, but
an object with an overloaded `operator()`, which means that there is no
actual call `std::ranges::sort` in the call stack.
---
libcxx/docs/UserDocumentation.rst | 26 ++++++
.../CPlusPlus/CPPLanguageRuntime.cpp | 27 ++----
.../cpp/libcxx-internals-recognizer/Makefile | 5 ++
.../TestLibcxxInternalsRecognizer.py | 56 ++++++++++++
.../cpp/libcxx-internals-recognizer/main.cpp | 86 +++++++++++++++++++
5 files changed, 181 insertions(+), 19 deletions(-)
create mode 100644 lldb/test/API/lang/cpp/libcxx-internals-recognizer/Makefile
create mode 100644 lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py
create mode 100644 lldb/test/API/lang/cpp/libcxx-internals-recognizer/main.cpp
diff --git a/libcxx/docs/UserDocumentation.rst b/libcxx/docs/UserDocumentation.rst
index 6659fa54f49df5..8999b4f23e91d2 100644
--- a/libcxx/docs/UserDocumentation.rst
+++ b/libcxx/docs/UserDocumentation.rst
@@ -346,6 +346,32 @@ Third-party Integrations
Libc++ provides integration with a few third-party tools.
+Debugging libc++ internals in LLDB
+----------------------------------
+
+LLDB hides the implementation details of libc++ by default.
+
+E.g., when setting a breakpoint in a comparator passed to ``std::sort``, the
+backtrace will read as
+
+.. code-block::
+
+ (lldb) thread backtrace
+ * thread #1, name = 'a.out', stop reason = breakpoint 3.1
+ * frame #0: 0x000055555555520e a.out`my_comparator(a=1, b=8) at test-std-sort.cpp:6:3
+ frame #7: 0x0000555555555615 a.out`void std::__1::sort[abi:ne200000]<std::__1::__wrap_iter<int*>, bool (*)(int, int)>(__first=(item = 8), __last=(item = 0), __comp=(a.out`my_less(int, int) at test-std-sort.cpp:5)) at sort.h:1003:3
+ frame #8: 0x000055555555531a a.out`main at test-std-sort.cpp:24:3
+
+Note how the caller of ``my_comparator`` is shown as ``std::sort``. Looking at
+the frame numbers, we can see that frames #1 until #6 were hidden. Those frames
+represent internal implementation details such as ``__sort4`` and similar
+utility functions.
+
+To also show those implementation details, use ``thread backtrace -u``.
+Alternatively, to disable those compact backtraces for good, use
+``frame recognizer list`` and ``frame recognizer delete`` to delete the libc++
+frame recognizer.
+
GDB Pretty printers for libc++
------------------------------
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
index faa05e8f834ea1..d0e84bdeb94f01 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
@@ -45,7 +45,7 @@ char CPPLanguageRuntime::ID = 0;
/// A frame recognizer that is installed to hide libc++ implementation
/// details from the backtrace.
class LibCXXFrameRecognizer : public StackFrameRecognizer {
- std::array<RegularExpression, 4> m_hidden_regex;
+ std::array<RegularExpression, 2> m_hidden_regex;
RecognizedStackFrameSP m_hidden_frame;
struct LibCXXHiddenFrame : public RecognizedStackFrame {
@@ -55,28 +55,17 @@ class LibCXXFrameRecognizer : public StackFrameRecognizer {
public:
LibCXXFrameRecognizer()
: m_hidden_regex{
- // internal implementation details of std::function
+ // internal implementation details in the `std::` namespace
// std::__1::__function::__alloc_func<void (*)(), std::__1::allocator<void (*)()>, void ()>::operator()[abi:ne200000]
// std::__1::__function::__func<void (*)(), std::__1::allocator<void (*)()>, void ()>::operator()
// std::__1::__function::__value_func<void ()>::operator()[abi:ne200000]() const
- RegularExpression{""
- R"(^std::__[^:]*::)" // Namespace.
- R"(__function::.*::operator\(\))"},
- // internal implementation details of std::function in ABI v2
// std::__2::__function::__policy_invoker<void (int, int)>::__call_impl[abi:ne200000]<std::__2::__function::__default_alloc_func<int (*)(int, int), int (int, int)>>
- RegularExpression{""
- R"(^std::__[^:]*::)" // Namespace.
- R"(__function::.*::__call_impl)"},
- // internal implementation details of std::invoke
- // std::__1::__invoke[abi:ne200000]<void (*&)()>
- RegularExpression{
- R"(^std::__[^:]*::)" // Namespace.
- R"(__invoke)"},
- // internal implementation details of std::invoke
- // std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ne200000]<void (*&)()>
- RegularExpression{
- R"(^std::__[^:]*::)" // Namespace.
- R"(__invoke_void_return_wrapper<.*>::__call)"}
+ // std::__1::__invoke[abi:ne200000]<void (*&)()>
+ // std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ne200000]<void (*&)()>
+ RegularExpression{R"(^std::__[^:]*::__)"},
+ // internal implementation details in the `std::ranges` namespace
+ // std::__1::ranges::__sort::__sort_fn_impl[abi:ne200000]<std::__1::__wrap_iter<int*>, std::__1::__wrap_iter<int*>, bool (*)(int, int), std::__1::identity>
+ RegularExpression{R"(^std::__[^:]*::ranges::__)"},
},
m_hidden_frame(new LibCXXHiddenFrame()) {}
diff --git a/lldb/test/API/lang/cpp/libcxx-internals-recognizer/Makefile b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/Makefile
new file mode 100644
index 00000000000000..bb571299664934
--- /dev/null
+++ b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/Makefile
@@ -0,0 +1,5 @@
+CXX_SOURCES := main.cpp
+USE_LIBCPP := 1
+CXXFLAGS_EXTRAS := -std=c++20
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py
new file mode 100644
index 00000000000000..a5b4e4fe995c38
--- /dev/null
+++ b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py
@@ -0,0 +1,56 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class LibCxxInternalsRecognizerTestCase(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ @add_test_categories(["libc++"])
+ def test_frame_recognizer(self):
+ """Test that implementation details of libc++ are hidden"""
+ self.build()
+ (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "break here", lldb.SBFileSpec("main.cpp")
+ )
+
+ expected_parents = {
+ "sort_less(int, int)": ["::sort", "test_algorithms"],
+ # `std::ranges::sort` is implemented as an object of types `__sort`,
+ # which unfortunately means that there is no `std::ranges::sort`
+ # stack frame, and `main` is the direct parent of `my_less_ranges`.
+ "ranges_sort_less(int, int)": ["test_algorithms"],
+ # `ranges::views::transform` internally uses `std::invoke`, and that
+ # call also shows up in the stack trace
+ "view_transform(int)": ["::invoke", "ranges::transform_view", "test_algorithms"],
+ # Various types of `invoke` calls
+ "consume_number(int)": ["::invoke", "test_invoke"],
+ "invoke_add(int, int)": ["::invoke", "test_invoke"],
+ "Callable::member_function(int) const": ["::invoke", "test_invoke"],
+ "Callable::operator()(int) const": ["::invoke", "test_invoke"],
+ # Containers
+ "MyKey::operator<(MyKey const&) const": ["less", "::emplace", "test_containers"],
+ }
+ stop_set = set()
+ while process.GetState() != lldb.eStateExited:
+ fn = thread.GetFrameAtIndex(0).GetFunctionName()
+ stop_set.add(fn)
+ self.assertIn(fn, expected_parents.keys())
+ frame_id = 1
+ for expected_parent in expected_parents[fn]:
+ # Skip all hidden frames
+ while (
+ frame_id < thread.GetNumFrames()
+ and thread.GetFrameAtIndex(frame_id).IsHidden()
+ ):
+ frame_id = frame_id + 1
+ # Expect the correct parent frame
+ self.assertIn(
+ expected_parent, thread.GetFrameAtIndex(frame_id).GetFunctionName()
+ )
+ frame_id = frame_id + 1
+ process.Continue()
+
+ # Make sure that we actually verified all intended scenarios
+ self.assertEqual(len(stop_set), len(expected_parents))
diff --git a/lldb/test/API/lang/cpp/libcxx-internals-recognizer/main.cpp b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/main.cpp
new file mode 100644
index 00000000000000..870301b0970439
--- /dev/null
+++ b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/main.cpp
@@ -0,0 +1,86 @@
+#include <algorithm>
+#include <functional>
+#include <map>
+#include <ranges>
+#include <vector>
+
+bool sort_less(int a, int b) {
+ __builtin_printf("break here");
+ return a < b;
+}
+
+bool ranges_sort_less(int a, int b) {
+ __builtin_printf("break here");
+ return a < b;
+}
+
+int view_transform(int a) {
+ __builtin_printf("break here");
+ return a * a;
+}
+
+void test_algorithms() {
+ std::vector<int> vec{8, 1, 3, 2};
+
+ // The internal frames for `std::sort` should be hidden
+ std::sort(vec.begin(), vec.end(), sort_less);
+
+ // The internal frames for `ranges::sort` should be hidden
+ std::ranges::sort(vec.begin(), vec.end(), ranges_sort_less);
+
+ // Same for views
+ for (auto x : vec | std::ranges::views::transform(view_transform)) {
+ // no-op
+ }
+}
+
+void consume_number(int i) { __builtin_printf("break here"); }
+
+int invoke_add(int i, int j) {
+ __builtin_printf("break here");
+ return i + j;
+}
+
+struct Callable {
+ Callable(int num) : num_(num) {}
+ void operator()(int i) const { __builtin_printf("break here"); }
+ void member_function(int i) const { __builtin_printf("break here"); }
+ int num_;
+};
+
+void test_invoke() {
+ // Invoke a void-returning function
+ std::invoke(consume_number, -9);
+
+ // Invoke a non-void-returning function
+ std::invoke(invoke_add, 1, 10);
+
+ // Invoke a member function
+ const Callable foo(314159);
+ std::invoke(&Callable::member_function, foo, 1);
+
+ // Invoke a function object
+ std::invoke(Callable(12), 18);
+}
+
+struct MyKey {
+ int x;
+ bool operator==(const MyKey &) const = default;
+ bool operator<(const MyKey &other) const {
+ __builtin_printf("break here");
+ return x < other.x;
+ }
+};
+
+void test_containers() {
+ std::map<MyKey, int> map;
+ map.emplace(MyKey{1}, 2);
+ map.emplace(MyKey{2}, 3);
+}
+
+int main() {
+ test_algorithms();
+ test_invoke();
+ test_containers();
+ return 0;
+}
>From d0cf329c94728a9cf0da97a2545a4a6c9e185e3f Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Tue, 17 Sep 2024 08:37:08 +0000
Subject: [PATCH 2/7] Remove superseded std::invoke tests
---
.../lang/cpp/std-invoke-recognizer/Makefile | 5 ---
.../TestStdInvokeRecognizer.py | 44 -------------------
.../lang/cpp/std-invoke-recognizer/main.cpp | 30 -------------
3 files changed, 79 deletions(-)
delete mode 100644 lldb/test/API/lang/cpp/std-invoke-recognizer/Makefile
delete mode 100644 lldb/test/API/lang/cpp/std-invoke-recognizer/TestStdInvokeRecognizer.py
delete mode 100644 lldb/test/API/lang/cpp/std-invoke-recognizer/main.cpp
diff --git a/lldb/test/API/lang/cpp/std-invoke-recognizer/Makefile b/lldb/test/API/lang/cpp/std-invoke-recognizer/Makefile
deleted file mode 100644
index 69014eb9c0f2eb..00000000000000
--- a/lldb/test/API/lang/cpp/std-invoke-recognizer/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-CXX_SOURCES := main.cpp
-USE_LIBCPP := 1
-CXXFLAGS_EXTRAS := -std=c++17
-
-include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/std-invoke-recognizer/TestStdInvokeRecognizer.py b/lldb/test/API/lang/cpp/std-invoke-recognizer/TestStdInvokeRecognizer.py
deleted file mode 100644
index dbe29610bf7982..00000000000000
--- a/lldb/test/API/lang/cpp/std-invoke-recognizer/TestStdInvokeRecognizer.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import lldb
-from lldbsuite.test.decorators import *
-from lldbsuite.test.lldbtest import *
-from lldbsuite.test import lldbutil
-
-
-class LibCxxStdFunctionRecognizerTestCase(TestBase):
- NO_DEBUG_INFO_TESTCASE = True
-
- @add_test_categories(["libc++"])
- def test_frame_recognizer(self):
- """Test that implementation details of `std::invoke` are hidden"""
- self.build()
- (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
- self, "break here", lldb.SBFileSpec("main.cpp")
- )
-
- stop_cnt = 0
- while process.GetState() != lldb.eStateExited:
- stop_cnt += 1
- self.assertTrue(
- any(
- f in thread.GetFrameAtIndex(0).GetFunctionName()
- for f in ["consume_number", "add", "Callable"]
- )
- )
- # Skip all hidden frames
- frame_id = 1
- while (
- frame_id < thread.GetNumFrames()
- and thread.GetFrameAtIndex(frame_id).IsHidden()
- ):
- frame_id = frame_id + 1
- # Expect `std::invoke` to be the direct parent
- self.assertIn(
- "::invoke", thread.GetFrameAtIndex(frame_id).GetFunctionName()
- )
- # And right above that, there should be the `main` frame
- self.assertIn(
- "main", thread.GetFrameAtIndex(frame_id + 1).GetFunctionName()
- )
- process.Continue()
-
- self.assertEqual(stop_cnt, 4)
diff --git a/lldb/test/API/lang/cpp/std-invoke-recognizer/main.cpp b/lldb/test/API/lang/cpp/std-invoke-recognizer/main.cpp
deleted file mode 100644
index bafbbd28386e8b..00000000000000
--- a/lldb/test/API/lang/cpp/std-invoke-recognizer/main.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include <functional>
-
-void consume_number(int i) { __builtin_printf("break here"); }
-
-int add(int i, int j) {
- // break here
- return i + j;
-}
-
-struct Callable {
- Callable(int num) : num_(num) {}
- void operator()(int i) const { __builtin_printf("break here"); }
- void member_function(int i) const { __builtin_printf("break here"); }
- int num_;
-};
-
-int main() {
- // Invoke a void-returning function
- std::invoke(consume_number, -9);
-
- // Invoke a non-void-returning function
- std::invoke(add, 1, 10);
-
- // Invoke a member function
- const Callable foo(314159);
- std::invoke(&Callable::member_function, foo, 1);
-
- // Invoke a function object
- std::invoke(Callable(12), 18);
-}
>From d31e3c541cab88c1b7589120a6eed88c667e1191 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Sat, 21 Sep 2024 08:50:14 +0000
Subject: [PATCH 3/7] Don't hide frame of entry point into libc++
---
.../CPlusPlus/CPPLanguageRuntime.cpp | 15 ++++++++++++++-
.../TestLibcxxInternalsRecognizer.py | 8 ++++----
2 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
index d0e84bdeb94f01..a3fc876476d0aa 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
@@ -79,9 +79,22 @@ class LibCXXFrameRecognizer : public StackFrameRecognizer {
if (!sc.function)
return {};
+ // Check if we have a regex match
+ bool matches_regex = false;
for (RegularExpression &r : m_hidden_regex)
- if (r.Execute(sc.function->GetNameNoArguments()))
+ if (r.Execute(sc.function->GetNameNoArguments())) {
+ matches_regex = true;
+ break;
+ }
+
+ if (matches_regex) {
+ // Only hide this frame if the immediate caller is also within libc++.
+ lldb::StackFrameSP parent_frame = frame_sp->GetThread()->GetStackFrameAtIndex(frame_sp->GetFrameIndex() + 1);
+ const auto& parent_sc = parent_frame->GetSymbolContext(lldb::eSymbolContextFunction);
+ if (parent_sc.function->GetNameNoArguments().GetStringRef().starts_with("std::")) {
return m_hidden_frame;
+ }
+ }
return {};
}
diff --git a/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py
index a5b4e4fe995c38..a751525f1a9298 100644
--- a/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py
+++ b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py
@@ -17,10 +17,10 @@ def test_frame_recognizer(self):
expected_parents = {
"sort_less(int, int)": ["::sort", "test_algorithms"],
- # `std::ranges::sort` is implemented as an object of types `__sort`,
- # which unfortunately means that there is no `std::ranges::sort`
- # stack frame, and `main` is the direct parent of `my_less_ranges`.
- "ranges_sort_less(int, int)": ["test_algorithms"],
+ # `std::ranges::sort` is implemented as an object of types `__sort`.
+ # We never hide the frame of the entry-point into the standard library, even
+ # if the name starts with `__` which usually indicates an internal function.
+ "ranges_sort_less(int, int)": ["ranges::__sort::operator()", "test_algorithms"],
# `ranges::views::transform` internally uses `std::invoke`, and that
# call also shows up in the stack trace
"view_transform(int)": ["::invoke", "ranges::transform_view", "test_algorithms"],
>From 79b591ce8d834b1037c51dd8305093d7356bcf97 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Sat, 21 Sep 2024 09:46:26 +0000
Subject: [PATCH 4/7] Fix code formatting
---
.../CPlusPlus/CPPLanguageRuntime.cpp | 16 ++++++++++------
.../TestLibcxxInternalsRecognizer.py | 17 ++++++++++++++---
2 files changed, 24 insertions(+), 9 deletions(-)
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
index a3fc876476d0aa..2a35182b42c945 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
@@ -80,18 +80,22 @@ class LibCXXFrameRecognizer : public StackFrameRecognizer {
return {};
// Check if we have a regex match
- bool matches_regex = false;
+ bool matches_regex = false;
for (RegularExpression &r : m_hidden_regex)
if (r.Execute(sc.function->GetNameNoArguments())) {
- matches_regex = true;
- break;
+ matches_regex = true;
+ break;
}
if (matches_regex) {
// Only hide this frame if the immediate caller is also within libc++.
- lldb::StackFrameSP parent_frame = frame_sp->GetThread()->GetStackFrameAtIndex(frame_sp->GetFrameIndex() + 1);
- const auto& parent_sc = parent_frame->GetSymbolContext(lldb::eSymbolContextFunction);
- if (parent_sc.function->GetNameNoArguments().GetStringRef().starts_with("std::")) {
+ lldb::StackFrameSP parent_frame =
+ frame_sp->GetThread()->GetStackFrameAtIndex(
+ frame_sp->GetFrameIndex() + 1);
+ const auto &parent_sc =
+ parent_frame->GetSymbolContext(lldb::eSymbolContextFunction);
+ if (parent_sc.function->GetNameNoArguments().GetStringRef().starts_with(
+ "std::")) {
return m_hidden_frame;
}
}
diff --git a/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py
index a751525f1a9298..ad48208f21e502 100644
--- a/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py
+++ b/lldb/test/API/lang/cpp/libcxx-internals-recognizer/TestLibcxxInternalsRecognizer.py
@@ -20,17 +20,28 @@ def test_frame_recognizer(self):
# `std::ranges::sort` is implemented as an object of types `__sort`.
# We never hide the frame of the entry-point into the standard library, even
# if the name starts with `__` which usually indicates an internal function.
- "ranges_sort_less(int, int)": ["ranges::__sort::operator()", "test_algorithms"],
+ "ranges_sort_less(int, int)": [
+ "ranges::__sort::operator()",
+ "test_algorithms",
+ ],
# `ranges::views::transform` internally uses `std::invoke`, and that
# call also shows up in the stack trace
- "view_transform(int)": ["::invoke", "ranges::transform_view", "test_algorithms"],
+ "view_transform(int)": [
+ "::invoke",
+ "ranges::transform_view",
+ "test_algorithms",
+ ],
# Various types of `invoke` calls
"consume_number(int)": ["::invoke", "test_invoke"],
"invoke_add(int, int)": ["::invoke", "test_invoke"],
"Callable::member_function(int) const": ["::invoke", "test_invoke"],
"Callable::operator()(int) const": ["::invoke", "test_invoke"],
# Containers
- "MyKey::operator<(MyKey const&) const": ["less", "::emplace", "test_containers"],
+ "MyKey::operator<(MyKey const&) const": [
+ "less",
+ "::emplace",
+ "test_containers",
+ ],
}
stop_set = set()
while process.GetState() != lldb.eStateExited:
>From c272d51457e22932ba6394526fb9c1415d0f4fb0 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Sat, 21 Sep 2024 19:47:43 +0000
Subject: [PATCH 5/7] Update documentation in libc++ to recommend `disable`
---
libcxx/docs/UserDocumentation.rst | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/libcxx/docs/UserDocumentation.rst b/libcxx/docs/UserDocumentation.rst
index 8999b4f23e91d2..3d6cdae7c55e74 100644
--- a/libcxx/docs/UserDocumentation.rst
+++ b/libcxx/docs/UserDocumentation.rst
@@ -368,9 +368,8 @@ represent internal implementation details such as ``__sort4`` and similar
utility functions.
To also show those implementation details, use ``thread backtrace -u``.
-Alternatively, to disable those compact backtraces for good, use
-``frame recognizer list`` and ``frame recognizer delete`` to delete the libc++
-frame recognizer.
+Alternatively, to disable those compact backtraces, use ``frame recognizer list``
+and ``frame recognizer disable`` on the "libc++ frame recognizer".
GDB Pretty printers for libc++
------------------------------
>From a02a62313206f6730c407260d747951119920a52 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Mon, 23 Sep 2024 18:38:11 +0000
Subject: [PATCH 6/7] Address comments
---
libcxx/docs/UserDocumentation.rst | 4 ++++
.../CPlusPlus/CPPLanguageRuntime.cpp | 15 +++++++++++----
2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/libcxx/docs/UserDocumentation.rst b/libcxx/docs/UserDocumentation.rst
index 3d6cdae7c55e74..10c0a50045626a 100644
--- a/libcxx/docs/UserDocumentation.rst
+++ b/libcxx/docs/UserDocumentation.rst
@@ -371,6 +371,10 @@ To also show those implementation details, use ``thread backtrace -u``.
Alternatively, to disable those compact backtraces, use ``frame recognizer list``
and ``frame recognizer disable`` on the "libc++ frame recognizer".
+Futhermore, stepping into libc++ functions is disabled by default. This is controlled via the
+setting ``target.process.thread.step-avoid-regexp`` which defaults to ``^std::`` and can be
+disabled using ``settings set target.process.thread.step-avoid-regexp ""``.
+
GDB Pretty printers for libc++
------------------------------
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
index 2a35182b42c945..565a51b8635d2f 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
@@ -81,19 +81,26 @@ class LibCXXFrameRecognizer : public StackFrameRecognizer {
// Check if we have a regex match
bool matches_regex = false;
- for (RegularExpression &r : m_hidden_regex)
+ for (RegularExpression &r : m_hidden_regex) {
if (r.Execute(sc.function->GetNameNoArguments())) {
matches_regex = true;
break;
}
+ }
if (matches_regex) {
// Only hide this frame if the immediate caller is also within libc++.
- lldb::StackFrameSP parent_frame =
- frame_sp->GetThread()->GetStackFrameAtIndex(
+ lldb::ThreadSP thread_sp = frame_sp->GetThread();
+ if (!thread_sp)
+ return {};
+ lldb::StackFrameSP parent_frame_sp = thread_sp->GetStackFrameAtIndex(
frame_sp->GetFrameIndex() + 1);
+ if (!parent_frame_sp)
+ return {};
const auto &parent_sc =
- parent_frame->GetSymbolContext(lldb::eSymbolContextFunction);
+ parent_frame_sp->GetSymbolContext(lldb::eSymbolContextFunction);
+ if (!parent_sc.function)
+ return {};
if (parent_sc.function->GetNameNoArguments().GetStringRef().starts_with(
"std::")) {
return m_hidden_frame;
>From 2c0ffe2738288f112635da906e61ed90cbaeccc7 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <avogelsgesang at salesforce.com>
Date: Mon, 23 Sep 2024 20:47:31 +0000
Subject: [PATCH 7/7] Fix code formatting
---
.../LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
index 565a51b8635d2f..48e80db90d1b1a 100644
--- a/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
+++ b/lldb/source/Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.cpp
@@ -92,15 +92,15 @@ class LibCXXFrameRecognizer : public StackFrameRecognizer {
// Only hide this frame if the immediate caller is also within libc++.
lldb::ThreadSP thread_sp = frame_sp->GetThread();
if (!thread_sp)
- return {};
- lldb::StackFrameSP parent_frame_sp = thread_sp->GetStackFrameAtIndex(
- frame_sp->GetFrameIndex() + 1);
+ return {};
+ lldb::StackFrameSP parent_frame_sp =
+ thread_sp->GetStackFrameAtIndex(frame_sp->GetFrameIndex() + 1);
if (!parent_frame_sp)
- return {};
+ return {};
const auto &parent_sc =
parent_frame_sp->GetSymbolContext(lldb::eSymbolContextFunction);
if (!parent_sc.function)
- return {};
+ return {};
if (parent_sc.function->GetNameNoArguments().GetStringRef().starts_with(
"std::")) {
return m_hidden_frame;
More information about the libcxx-commits
mailing list