[Lldb-commits] [lldb] [lldb][FrameRecognizer] Display the first non-std frame on verbose_trap (PR #108825)

Michael Buch via lldb-commits lldb-commits at lists.llvm.org
Thu Sep 19 01:52:52 PDT 2024


https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/108825

>From 729a0f69583c2c8ac597e68622231e4b719735b8 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Mon, 16 Sep 2024 14:10:46 +0100
Subject: [PATCH 1/3] [lldb][FrameRecognizer] Display the first non-std frame
 on verbose_trap

---
 .../Target/VerboseTrapFrameRecognizer.cpp     | 27 ++++++++++++++++++-
 .../Inputs/verbose_trap-in-stl-callback.cpp   | 22 +++++++++++++++
 .../Inputs/verbose_trap-in-stl-nested.cpp     | 21 +++++++++++++++
 .../Recognizer/Inputs/verbose_trap-in-stl.cpp | 17 ++++++++++++
 .../verbose_trap-in-stl-callback.test         | 13 +++++++++
 .../verbose_trap-in-stl-nested.test           | 13 +++++++++
 .../Shell/Recognizer/verbose_trap-in-stl.test | 13 +++++++++
 7 files changed, 125 insertions(+), 1 deletion(-)
 create mode 100644 lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-callback.cpp
 create mode 100644 lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-nested.cpp
 create mode 100644 lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl.cpp
 create mode 100644 lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback.test
 create mode 100644 lldb/test/Shell/Recognizer/verbose_trap-in-stl-nested.test
 create mode 100644 lldb/test/Shell/Recognizer/verbose_trap-in-stl.test

diff --git a/lldb/source/Target/VerboseTrapFrameRecognizer.cpp b/lldb/source/Target/VerboseTrapFrameRecognizer.cpp
index de710fcda54064..530a8941c8d504 100644
--- a/lldb/source/Target/VerboseTrapFrameRecognizer.cpp
+++ b/lldb/source/Target/VerboseTrapFrameRecognizer.cpp
@@ -16,6 +16,31 @@ 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) {
+  StackFrameSP most_relevant_frame_sp = selected_thread.GetStackFrameAtIndex(1);
+  while (most_relevant_frame_sp) {
+    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;
+
+    most_relevant_frame_sp = selected_thread.GetStackFrameAtIndex(
+        most_relevant_frame_sp->GetFrameIndex() + 1);
+  }
+
+  return nullptr;
+}
+
 VerboseTrapRecognizedStackFrame::VerboseTrapRecognizedStackFrame(
     StackFrameSP most_relevant_frame_sp, std::string stop_desc)
     : m_most_relevant_frame(most_relevant_frame_sp) {
@@ -30,7 +55,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.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-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.test b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback.test
new file mode 100644
index 00000000000000..6ea397d3ee700b
--- /dev/null
+++ b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback.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-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-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

>From 05f3c3b45a64cbc1aa92bb3925ec16e071cbf6c5 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Tue, 17 Sep 2024 17:43:18 +0100
Subject: [PATCH 2/3] fixup! add defensive stack walking threshold; add more
 tests

---
 .../Target/VerboseTrapFrameRecognizer.cpp     | 16 ++++++++++----
 ...verbose_trap-in-stl-callback-user-leaf.cpp | 22 +++++++++++++++++++
 .../Inputs/verbose_trap-in-stl-max-depth.cpp  | 13 +++++++++++
 ...erbose_trap-in-stl-callback-user-leaf.test | 22 +++++++++++++++++++
 .../verbose_trap-in-stl-callback.test         |  8 +++++++
 .../verbose_trap-in-stl-max-depth.test        | 16 ++++++++++++++
 6 files changed, 93 insertions(+), 4 deletions(-)
 create mode 100644 lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-callback-user-leaf.cpp
 create mode 100644 lldb/test/Shell/Recognizer/Inputs/verbose_trap-in-stl-max-depth.cpp
 create mode 100644 lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback-user-leaf.test
 create mode 100644 lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test

diff --git a/lldb/source/Target/VerboseTrapFrameRecognizer.cpp b/lldb/source/Target/VerboseTrapFrameRecognizer.cpp
index 530a8941c8d504..03ab58b8c59a9b 100644
--- a/lldb/source/Target/VerboseTrapFrameRecognizer.cpp
+++ b/lldb/source/Target/VerboseTrapFrameRecognizer.cpp
@@ -20,8 +20,16 @@ using namespace lldb_private;
 /// 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) {
-  StackFrameSP most_relevant_frame_sp = selected_thread.GetStackFrameAtIndex(1);
-  while (most_relevant_frame_sp) {
+  // 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();
@@ -34,8 +42,8 @@ static StackFrameSP FindMostRelevantFrame(Thread &selected_thread) {
     if (!frame_name.GetStringRef().starts_with("std::"))
       return most_relevant_frame_sp;
 
-    most_relevant_frame_sp = selected_thread.GetStackFrameAtIndex(
-        most_relevant_frame_sp->GetFrameIndex() + 1);
+    ++stack_idx;
+    most_relevant_frame_sp = selected_thread.GetStackFrameAtIndex(stack_idx);
   }
 
   return nullptr;
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-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/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
index 6ea397d3ee700b..b15bcb3a384f98 100644
--- a/lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback.test
+++ b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-callback.test
@@ -1,5 +1,13 @@
 # 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
 #
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..2a72b23297d2b4
--- /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 = EXC_BREAKPOINT
+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

>From 8a124e1436ef6a0d97c71dde8c64f18218eb40ef Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Thu, 19 Sep 2024 09:52:22 +0100
Subject: [PATCH 3/3] fixup! don't match stop reason

---
 lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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
index 2a72b23297d2b4..0c3275c571b3d9 100644
--- a/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test
+++ b/lldb/test/Shell/Recognizer/verbose_trap-in-stl-max-depth.test
@@ -8,7 +8,7 @@
 # RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK
 
 run
-# CHECK: thread #{{.*}}stop reason = EXC_BREAKPOINT
+# CHECK: thread #{{.*}}stop reason =
 frame recognizer info 0
 # CHECK: frame 0 is recognized by Verbose Trap StackFrame Recognizer
 frame info



More information about the lldb-commits mailing list