[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