Skip to content

Commit 879f40a

Browse files
authored
[lldb][Instrumentation] Set selected frame to outside sanitizer libraries (#133079)
When hitting a sanitizer breakpoint, LLDB currently displays the frame in the sanitizer dylib (which we usually don't have debug-info for), which isn't very helpful to the user. A more helpful frame to display would be the first frame not in the sanitizer library (we have a [similar heuristic when we trap inside libc++](#108825)). This patch does just that, by implementing the `GetSuggestedStackFrameIndex` API Depends on #133078
1 parent 262e994 commit 879f40a

File tree

5 files changed

+59
-2
lines changed

5 files changed

+59
-2
lines changed

lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ class InstrumentationRuntimeStopInfo : public StopInfo {
2424
return lldb::eStopReasonInstrumentation;
2525
}
2626

27+
std::optional<uint32_t>
28+
GetSuggestedStackFrameIndex(bool inlined_stack) override;
29+
2730
const char *GetDescription() override;
2831

2932
bool DoShouldNotify(Event *event_ptr) override { return true; }

lldb/source/Target/InstrumentationRuntimeStopInfo.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,20 @@
88

99
#include "lldb/Target/InstrumentationRuntimeStopInfo.h"
1010

11+
#include "lldb/Core/Module.h"
1112
#include "lldb/Target/InstrumentationRuntime.h"
1213
#include "lldb/Target/Process.h"
14+
#include "lldb/lldb-enumerations.h"
1315
#include "lldb/lldb-private.h"
1416

1517
using namespace lldb;
1618
using namespace lldb_private;
1719

20+
static bool IsStoppedInDarwinSanitizer(Thread &thread, Module &module) {
21+
return module.GetFileSpec().GetFilename().GetStringRef().starts_with(
22+
"libclang_rt.");
23+
}
24+
1825
InstrumentationRuntimeStopInfo::InstrumentationRuntimeStopInfo(
1926
Thread &thread, std::string description,
2027
StructuredData::ObjectSP additional_data)
@@ -34,3 +41,38 @@ InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
3441
return StopInfoSP(
3542
new InstrumentationRuntimeStopInfo(thread, description, additionalData));
3643
}
44+
45+
std::optional<uint32_t>
46+
InstrumentationRuntimeStopInfo::GetSuggestedStackFrameIndex(
47+
bool inlined_stack) {
48+
ThreadSP thread_sp = GetThread();
49+
if (!thread_sp)
50+
return std::nullopt;
51+
52+
// Defensive upper-bound of when we stop walking up the frames in
53+
// case we somehow ended up looking at an infinite recursion.
54+
constexpr size_t max_stack_depth = 128;
55+
56+
// Start at parent frame.
57+
size_t stack_idx = 1;
58+
StackFrameSP most_relevant_frame_sp =
59+
thread_sp->GetStackFrameAtIndex(stack_idx);
60+
61+
while (most_relevant_frame_sp && stack_idx <= max_stack_depth) {
62+
auto const &sc =
63+
most_relevant_frame_sp->GetSymbolContext(lldb::eSymbolContextModule);
64+
65+
if (!sc.module_sp)
66+
return std::nullopt;
67+
68+
// Found a frame outside of the sanitizer runtime libraries.
69+
// That's the one we want to display.
70+
if (!IsStoppedInDarwinSanitizer(*thread_sp, *sc.module_sp))
71+
return stack_idx;
72+
73+
++stack_idx;
74+
most_relevant_frame_sp = thread_sp->GetStackFrameAtIndex(stack_idx);
75+
}
76+
77+
return stack_idx;
78+
}

lldb/test/API/functionalities/asan/TestMemoryHistory.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ def libsanitizers_asan_tests(self):
9494
)
9595
self.check_traces()
9696

97+
# Make sure we're not stopped in the sanitizer library but instead at the
98+
# point of failure in the user-code.
99+
self.assertEqual(self.frame().GetFunctionName(), "main")
100+
97101
# do the same using SB API
98102
process = self.dbg.GetSelectedTarget().process
99103
val = (
@@ -218,6 +222,10 @@ def compiler_rt_asan_tests(self):
218222

219223
self.check_traces()
220224

225+
# Make sure we're not stopped in the sanitizer library but instead at the
226+
# point of failure in the user-code.
227+
self.assertEqual(self.frame().GetFunctionName(), "main")
228+
221229
# make sure the 'memory history' command still works even when we're
222230
# generating a report now
223231
self.expect(

lldb/test/API/functionalities/asan/TestReportData.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ def asan_tests(self, libsanitizers=False):
6767
lldb.eStopReasonInstrumentation,
6868
)
6969

70+
# Make sure we're not stopped in the sanitizer library but instead at the
71+
# point of failure in the user-code.
72+
self.assertEqual(self.frame().GetFunctionName(), "main")
73+
7074
self.expect(
7175
"bt",
7276
"The backtrace should show the crashing line",

lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ def ubsan_tests(self):
5252
substrs=["1 match found"],
5353
)
5454

55-
# We should be stopped in __ubsan_on_report
56-
self.assertIn("__ubsan_on_report", frame.GetFunctionName())
55+
# We should not be stopped in the sanitizer library.
56+
self.assertIn("main", frame.GetFunctionName())
5757

5858
# The stopped thread backtrace should contain either 'align line'
5959
found = False

0 commit comments

Comments
 (0)