[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