[Lldb-commits] [lldb] bca5073 - [lldb][FrameRecognizer] Display the first non-std frame on verbose_trap (#108825)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Sep 19 02:06:32 PDT 2024
Author: Michael Buch
Date: 2024-09-19T10:06:28+01:00
New Revision: bca507387ae1945137214ec7fb80b709927ee6e8
URL: https://github.com/llvm/llvm-project/commit/bca507387ae1945137214ec7fb80b709927ee6e8
DIFF: https://github.com/llvm/llvm-project/commit/bca507387ae1945137214ec7fb80b709927ee6e8.diff
LOG: [lldb][FrameRecognizer] Display the first non-std frame on verbose_trap (#108825)
This attempts to improve user-experience when LLDB stops on a
verbose_trap. Currently if a `__builtin_verbose_trap` triggers, we
display the first frame above the call to the verbose_trap. So in the
newly added test case, we would've previously stopped here:
```
(lldb) run
Process 28095 launched: '/Users/michaelbuch/a.out' (arm64)
Process 28095 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = Bounds error: out-of-bounds access
frame #1: 0x0000000100003f5c a.out`std::__1::vector<int>::operator[](this=0x000000016fdfebef size=0, (null)=10) at verbose_trap.cpp:6:9
3 template <typename T>
4 struct vector {
5 void operator[](unsigned) {
-> 6 __builtin_verbose_trap("Bounds error", "out-of-bounds access");
7 }
8 };
```
After this patch, we would stop in the first non-`std` frame:
```
(lldb) run
Process 27843 launched: '/Users/michaelbuch/a.out' (arm64)
Process 27843 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = Bounds error: out-of-bounds access
frame #2: 0x0000000100003f44 a.out`g() at verbose_trap.cpp:14:5
11
12 void g() {
13 std::vector<int> v;
-> 14 v[10];
15 }
16
```
rdar://134490328
Added:
lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-callback-user-leaf.cpp
lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-callback.cpp
lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-max-depth.cpp
lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-nested.cpp
lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl.cpp
lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback-user-leaf.test
lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback.test
lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test
lldb/test/Shell/Recognizer/verbose_trap-in-stl-nested.test
lldb/test/Shell/Recognizer/verbose_trap-in-stl.test
Modified:
lldb/source/Target/VerboseTrapFrameRecognizer.cpp
Removed:
################################################################################
diff --git a/lldb/source/Target/VerboseTrapFrameRecognizer.cpp b/lldb/source/Target/VerboseTrapFrameRecognizer.cpp
index de710fcda54064..03ab58b8c59a9b 100644
--- a/lldb/source/Target/VerboseTrapFrameRecognizer.cpp
+++ b/lldb/source/Target/VerboseTrapFrameRecognizer.cpp
@@ -16,6 +16,39 @@ using namespace llvm;
using namespace lldb;
using namespace lldb_private;
+/// The 0th frame is the artificial inline frame generated to store
+/// the verbose_trap message. So, starting with the current parent frame,
+/// find the first frame that's not inside of the STL.
+static StackFrameSP FindMostRelevantFrame(Thread &selected_thread) {
+ // Defensive upper-bound of when we stop walking up the frames in
+ // case we somehow ended up looking at an infinite recursion.
+ const size_t max_stack_depth = 128;
+
+ // Start at parent frame.
+ size_t stack_idx = 1;
+ StackFrameSP most_relevant_frame_sp =
+ selected_thread.GetStackFrameAtIndex(stack_idx);
+
+ while (most_relevant_frame_sp && stack_idx <= max_stack_depth) {
+ auto const &sc =
+ most_relevant_frame_sp->GetSymbolContext(eSymbolContextEverything);
+ ConstString frame_name = sc.GetFunctionName();
+ if (!frame_name)
+ return nullptr;
+
+ // Found a frame outside of the `std` namespace. That's the
+ // first frame in user-code that ended up triggering the
+ // verbose_trap. Hence that's the one we want to display.
+ if (!frame_name.GetStringRef().starts_with("std::"))
+ return most_relevant_frame_sp;
+
+ ++stack_idx;
+ most_relevant_frame_sp = selected_thread.GetStackFrameAtIndex(stack_idx);
+ }
+
+ return nullptr;
+}
+
VerboseTrapRecognizedStackFrame::VerboseTrapRecognizedStackFrame(
StackFrameSP most_relevant_frame_sp, std::string stop_desc)
: m_most_relevant_frame(most_relevant_frame_sp) {
@@ -30,7 +63,7 @@ VerboseTrapFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) {
ThreadSP thread_sp = frame_sp->GetThread();
ProcessSP process_sp = thread_sp->GetProcess();
- StackFrameSP most_relevant_frame_sp = thread_sp->GetStackFrameAtIndex(1);
+ StackFrameSP most_relevant_frame_sp = FindMostRelevantFrame(*thread_sp);
if (!most_relevant_frame_sp) {
Log *log = GetLog(LLDBLog::Unwind);
diff --git a/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-callback-user-leaf.cpp b/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-callback-user-leaf.cpp
new file mode 100644
index 00000000000000..6c36682626a6ef
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-callback-user-leaf.cpp
@@ -0,0 +1,22 @@
+void definitely_aborts() { __builtin_verbose_trap("User", "Invariant violated"); }
+
+namespace std {
+void aborts_soon() { definitely_aborts(); }
+} // namespace std
+
+void g() { std::aborts_soon(); }
+
+namespace std {
+namespace detail {
+void eventually_aborts() { g(); }
+} // namespace detail
+
+inline namespace __1 {
+void eventually_aborts() { detail::eventually_aborts(); }
+} // namespace __1
+} // namespace std
+
+int main() {
+ std::eventually_aborts();
+ return 0;
+}
diff --git a/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-callback.cpp b/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-callback.cpp
new file mode 100644
index 00000000000000..23beed4c62c3b3
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-callback.cpp
@@ -0,0 +1,22 @@
+namespace std {
+void definitely_aborts() { __builtin_verbose_trap("Failed", "Invariant violated"); }
+
+void aborts_soon() { definitely_aborts(); }
+} // namespace std
+
+void g() { std::aborts_soon(); }
+
+namespace std {
+namespace detail {
+void eventually_aborts() { g(); }
+} // namespace detail
+
+inline namespace __1 {
+void eventually_aborts() { detail::eventually_aborts(); }
+} // namespace __1
+} // namespace std
+
+int main() {
+ std::eventually_aborts();
+ return 0;
+}
diff --git a/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-max-depth.cpp b/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-max-depth.cpp
new file mode 100644
index 00000000000000..48f564ce674e4d
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-max-depth.cpp
@@ -0,0 +1,13 @@
+namespace std {
+void recursively_aborts(int depth) {
+ if (depth == 0)
+ __builtin_verbose_trap("Error", "max depth");
+
+ recursively_aborts(--depth);
+}
+} // namespace std
+
+int main() {
+ std::recursively_aborts(256);
+ return 0;
+}
diff --git a/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-nested.cpp b/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-nested.cpp
new file mode 100644
index 00000000000000..67fa65c9ceae22
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-nested.cpp
@@ -0,0 +1,21 @@
+namespace std {
+namespace detail {
+void function_that_aborts() { __builtin_verbose_trap("Bounds error", "out-of-bounds access"); }
+} // namespace detail
+
+inline namespace __1 {
+template <typename T> struct vector {
+ void operator[](unsigned) { detail::function_that_aborts(); }
+};
+} // namespace __1
+} // namespace std
+
+void g() {
+ std::vector<int> v;
+ v[10];
+}
+
+int main() {
+ g();
+ return 0;
+}
diff --git a/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl.cpp b/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl.cpp
new file mode 100644
index 00000000000000..4f01827944e166
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl.cpp
@@ -0,0 +1,17 @@
+namespace std {
+inline namespace __1 {
+template <typename T> struct vector {
+ void operator[](unsigned) { __builtin_verbose_trap("Bounds error", "out-of-bounds access"); }
+};
+} // namespace __1
+} // namespace std
+
+void g() {
+ std::vector<int> v;
+ v[10];
+}
+
+int main() {
+ g();
+ return 0;
+}
diff --git a/lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback-user-leaf.test b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback-user-leaf.test
new file mode 100644
index 00000000000000..5a84c163453ccd
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback-user-leaf.test
@@ -0,0 +1,22 @@
+# Tests that we show the first non-STL frame when
+# a verbose_trap triggers from within the STL.
+#
+# Specifically tests that we correctly handle backtraces
+# of the form:
+# #0 __builtin_verbose_trap
+# #1 user-code
+# #2 STL
+# #3 user-code
+# #4 STL
+# #5 user-code
+
+# UNSUPPORTED: system-windows
+#
+# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap-in-stl-callback-user-leaf.cpp -o %t.out
+# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK
+
+run
+# CHECK: thread #{{.*}}stop reason = User: Invariant violated
+frame info
+# CHECK: frame #{{.*}}`definitely_aborts() at verbose_trap-in-stl-callback-user-leaf.cpp
+q
diff --git a/lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback.test b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback.test
new file mode 100644
index 00000000000000..b15bcb3a384f98
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback.test
@@ -0,0 +1,21 @@
+# Tests that we show the first non-STL frame when
+# a verbose_trap triggers from within the STL.
+#
+# Specifically tests that we correctly handle backtraces
+# of the form:
+# #0 __builtin_verbose_trap
+# #1 STL
+# #2 user-code
+# #3 STL
+# #4 user-code
+
+# UNSUPPORTED: system-windows
+#
+# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap-in-stl-callback.cpp -o %t.out
+# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK
+
+run
+# CHECK: thread #{{.*}}stop reason = Failed: Invariant violated
+frame info
+# CHECK: frame #{{.*}}`g() at verbose_trap-in-stl-callback.cpp
+q
diff --git a/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test
new file mode 100644
index 00000000000000..0c3275c571b3d9
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test
@@ -0,0 +1,16 @@
+# Tests that the VerboseTrapFrameRecognizer stops
+# walking the stack once a certain implementation-defined
+# threshold is reached.
+
+# UNSUPPORTED: system-windows
+#
+# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap-in-stl-max-depth.cpp -o %t.out
+# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK
+
+run
+# CHECK: thread #{{.*}}stop reason =
+frame recognizer info 0
+# CHECK: frame 0 is recognized by Verbose Trap StackFrame Recognizer
+frame info
+# CHECK: frame #0: {{.*}}`std::recursively_aborts(int) {{.*}} at verbose_trap-in-stl-max-depth.cpp
+q
diff --git a/lldb/test/Shell/Recognizer/verbose_trap-in-stl-nested.test b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-nested.test
new file mode 100644
index 00000000000000..81a492d1ed5791
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-nested.test
@@ -0,0 +1,13 @@
+# Tests that we show the first non-STL frame when
+# a verbose_trap triggers from within the STL.
+
+# UNSUPPORTED: system-windows
+#
+# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap-in-stl-nested.cpp -o %t.out
+# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK
+
+run
+# CHECK: thread #{{.*}}stop reason = Bounds error: out-of-bounds access
+frame info
+# CHECK: frame #{{.*}}`g() at verbose_trap-in-stl-nested.cpp
+q
diff --git a/lldb/test/Shell/Recognizer/verbose_trap-in-stl.test b/lldb/test/Shell/Recognizer/verbose_trap-in-stl.test
new file mode 100644
index 00000000000000..dd08290174e3af
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/verbose_trap-in-stl.test
@@ -0,0 +1,13 @@
+# Tests that we show the first non-STL frame when
+# a verbose_trap triggers from within the STL.
+
+# UNSUPPORTED: system-windows
+#
+# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap-in-stl.cpp -o %t.out
+# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK
+
+run
+# CHECK: thread #{{.*}}stop reason = Bounds error: out-of-bounds access
+frame info
+# CHECK: frame #{{.*}}`g() at verbose_trap-in-stl.cpp
+q
More information about the lldb-commits
mailing list