Skip to content

Commit bca5073

Browse files
authored
[lldb][FrameRecognizer] Display the first non-std frame on verbose_trap (llvm#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
1 parent 57777a5 commit bca5073

11 files changed

+214
-1
lines changed

lldb/source/Target/VerboseTrapFrameRecognizer.cpp

+34-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,39 @@ using namespace llvm;
1616
using namespace lldb;
1717
using namespace lldb_private;
1818

19+
/// The 0th frame is the artificial inline frame generated to store
20+
/// the verbose_trap message. So, starting with the current parent frame,
21+
/// find the first frame that's not inside of the STL.
22+
static StackFrameSP FindMostRelevantFrame(Thread &selected_thread) {
23+
// Defensive upper-bound of when we stop walking up the frames in
24+
// case we somehow ended up looking at an infinite recursion.
25+
const size_t max_stack_depth = 128;
26+
27+
// Start at parent frame.
28+
size_t stack_idx = 1;
29+
StackFrameSP most_relevant_frame_sp =
30+
selected_thread.GetStackFrameAtIndex(stack_idx);
31+
32+
while (most_relevant_frame_sp && stack_idx <= max_stack_depth) {
33+
auto const &sc =
34+
most_relevant_frame_sp->GetSymbolContext(eSymbolContextEverything);
35+
ConstString frame_name = sc.GetFunctionName();
36+
if (!frame_name)
37+
return nullptr;
38+
39+
// Found a frame outside of the `std` namespace. That's the
40+
// first frame in user-code that ended up triggering the
41+
// verbose_trap. Hence that's the one we want to display.
42+
if (!frame_name.GetStringRef().starts_with("std::"))
43+
return most_relevant_frame_sp;
44+
45+
++stack_idx;
46+
most_relevant_frame_sp = selected_thread.GetStackFrameAtIndex(stack_idx);
47+
}
48+
49+
return nullptr;
50+
}
51+
1952
VerboseTrapRecognizedStackFrame::VerboseTrapRecognizedStackFrame(
2053
StackFrameSP most_relevant_frame_sp, std::string stop_desc)
2154
: m_most_relevant_frame(most_relevant_frame_sp) {
@@ -30,7 +63,7 @@ VerboseTrapFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) {
3063
ThreadSP thread_sp = frame_sp->GetThread();
3164
ProcessSP process_sp = thread_sp->GetProcess();
3265

33-
StackFrameSP most_relevant_frame_sp = thread_sp->GetStackFrameAtIndex(1);
66+
StackFrameSP most_relevant_frame_sp = FindMostRelevantFrame(*thread_sp);
3467

3568
if (!most_relevant_frame_sp) {
3669
Log *log = GetLog(LLDBLog::Unwind);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
void definitely_aborts() { __builtin_verbose_trap("User", "Invariant violated"); }
2+
3+
namespace std {
4+
void aborts_soon() { definitely_aborts(); }
5+
} // namespace std
6+
7+
void g() { std::aborts_soon(); }
8+
9+
namespace std {
10+
namespace detail {
11+
void eventually_aborts() { g(); }
12+
} // namespace detail
13+
14+
inline namespace __1 {
15+
void eventually_aborts() { detail::eventually_aborts(); }
16+
} // namespace __1
17+
} // namespace std
18+
19+
int main() {
20+
std::eventually_aborts();
21+
return 0;
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace std {
2+
void definitely_aborts() { __builtin_verbose_trap("Failed", "Invariant violated"); }
3+
4+
void aborts_soon() { definitely_aborts(); }
5+
} // namespace std
6+
7+
void g() { std::aborts_soon(); }
8+
9+
namespace std {
10+
namespace detail {
11+
void eventually_aborts() { g(); }
12+
} // namespace detail
13+
14+
inline namespace __1 {
15+
void eventually_aborts() { detail::eventually_aborts(); }
16+
} // namespace __1
17+
} // namespace std
18+
19+
int main() {
20+
std::eventually_aborts();
21+
return 0;
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace std {
2+
void recursively_aborts(int depth) {
3+
if (depth == 0)
4+
__builtin_verbose_trap("Error", "max depth");
5+
6+
recursively_aborts(--depth);
7+
}
8+
} // namespace std
9+
10+
int main() {
11+
std::recursively_aborts(256);
12+
return 0;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace std {
2+
namespace detail {
3+
void function_that_aborts() { __builtin_verbose_trap("Bounds error", "out-of-bounds access"); }
4+
} // namespace detail
5+
6+
inline namespace __1 {
7+
template <typename T> struct vector {
8+
void operator[](unsigned) { detail::function_that_aborts(); }
9+
};
10+
} // namespace __1
11+
} // namespace std
12+
13+
void g() {
14+
std::vector<int> v;
15+
v[10];
16+
}
17+
18+
int main() {
19+
g();
20+
return 0;
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace std {
2+
inline namespace __1 {
3+
template <typename T> struct vector {
4+
void operator[](unsigned) { __builtin_verbose_trap("Bounds error", "out-of-bounds access"); }
5+
};
6+
} // namespace __1
7+
} // namespace std
8+
9+
void g() {
10+
std::vector<int> v;
11+
v[10];
12+
}
13+
14+
int main() {
15+
g();
16+
return 0;
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Tests that we show the first non-STL frame when
2+
# a verbose_trap triggers from within the STL.
3+
#
4+
# Specifically tests that we correctly handle backtraces
5+
# of the form:
6+
# #0 __builtin_verbose_trap
7+
# #1 user-code
8+
# #2 STL
9+
# #3 user-code
10+
# #4 STL
11+
# #5 user-code
12+
13+
# UNSUPPORTED: system-windows
14+
#
15+
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap-in-stl-callback-user-leaf.cpp -o %t.out
16+
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK
17+
18+
run
19+
# CHECK: thread #{{.*}}stop reason = User: Invariant violated
20+
frame info
21+
# CHECK: frame #{{.*}}`definitely_aborts() at verbose_trap-in-stl-callback-user-leaf.cpp
22+
q
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Tests that we show the first non-STL frame when
2+
# a verbose_trap triggers from within the STL.
3+
#
4+
# Specifically tests that we correctly handle backtraces
5+
# of the form:
6+
# #0 __builtin_verbose_trap
7+
# #1 STL
8+
# #2 user-code
9+
# #3 STL
10+
# #4 user-code
11+
12+
# UNSUPPORTED: system-windows
13+
#
14+
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap-in-stl-callback.cpp -o %t.out
15+
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK
16+
17+
run
18+
# CHECK: thread #{{.*}}stop reason = Failed: Invariant violated
19+
frame info
20+
# CHECK: frame #{{.*}}`g() at verbose_trap-in-stl-callback.cpp
21+
q
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Tests that the VerboseTrapFrameRecognizer stops
2+
# walking the stack once a certain implementation-defined
3+
# threshold is reached.
4+
5+
# UNSUPPORTED: system-windows
6+
#
7+
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap-in-stl-max-depth.cpp -o %t.out
8+
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK
9+
10+
run
11+
# CHECK: thread #{{.*}}stop reason =
12+
frame recognizer info 0
13+
# CHECK: frame 0 is recognized by Verbose Trap StackFrame Recognizer
14+
frame info
15+
# CHECK: frame #0: {{.*}}`std::recursively_aborts(int) {{.*}} at verbose_trap-in-stl-max-depth.cpp
16+
q
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Tests that we show the first non-STL frame when
2+
# a verbose_trap triggers from within the STL.
3+
4+
# UNSUPPORTED: system-windows
5+
#
6+
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap-in-stl-nested.cpp -o %t.out
7+
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK
8+
9+
run
10+
# CHECK: thread #{{.*}}stop reason = Bounds error: out-of-bounds access
11+
frame info
12+
# CHECK: frame #{{.*}}`g() at verbose_trap-in-stl-nested.cpp
13+
q
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Tests that we show the first non-STL frame when
2+
# a verbose_trap triggers from within the STL.
3+
4+
# UNSUPPORTED: system-windows
5+
#
6+
# RUN: %clang_host -g -O0 %S/Inputs/verbose_trap-in-stl.cpp -o %t.out
7+
# RUN: %lldb -b -s %s %t.out | FileCheck %s --check-prefixes=CHECK
8+
9+
run
10+
# CHECK: thread #{{.*}}stop reason = Bounds error: out-of-bounds access
11+
frame info
12+
# CHECK: frame #{{.*}}`g() at verbose_trap-in-stl.cpp
13+
q

0 commit comments

Comments
 (0)