Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ class InstrumentationRuntimeStopInfo : public StopInfo {
return lldb::eStopReasonInstrumentation;
}

std::optional<uint32_t>
GetSuggestedStackFrameIndex(bool inlined_stack) override;

const char *GetDescription() override;

bool DoShouldNotify(Event *event_ptr) override { return true; }
Expand Down
42 changes: 42 additions & 0 deletions lldb/source/Target/InstrumentationRuntimeStopInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@

#include "lldb/Target/InstrumentationRuntimeStopInfo.h"

#include "lldb/Core/Module.h"
#include "lldb/Target/InstrumentationRuntime.h"
#include "lldb/Target/Process.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-private.h"

using namespace lldb;
using namespace lldb_private;

static bool IsStoppedInDarwinSanitizer(Thread &thread, Module &module) {
return module.GetFileSpec().GetFilename().GetStringRef().starts_with(
"libclang_rt.");
}

InstrumentationRuntimeStopInfo::InstrumentationRuntimeStopInfo(
Thread &thread, std::string description,
StructuredData::ObjectSP additional_data)
Expand All @@ -34,3 +41,38 @@ InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
return StopInfoSP(
new InstrumentationRuntimeStopInfo(thread, description, additionalData));
}

std::optional<uint32_t>
InstrumentationRuntimeStopInfo::GetSuggestedStackFrameIndex(
bool inlined_stack) {
ThreadSP thread_sp = GetThread();
if (!thread_sp)
return std::nullopt;

// Defensive upper-bound of when we stop walking up the frames in
// case we somehow ended up looking at an infinite recursion.
constexpr size_t max_stack_depth = 128;

// Start at parent frame.
size_t stack_idx = 1;
StackFrameSP most_relevant_frame_sp =
thread_sp->GetStackFrameAtIndex(stack_idx);

while (most_relevant_frame_sp && stack_idx <= max_stack_depth) {
auto const &sc =
most_relevant_frame_sp->GetSymbolContext(lldb::eSymbolContextModule);

if (!sc.module_sp)
return std::nullopt;

// Found a frame outside of the sanitizer runtime libraries.
// That's the one we want to display.
if (!IsStoppedInDarwinSanitizer(*thread_sp, *sc.module_sp))
return stack_idx;

++stack_idx;
most_relevant_frame_sp = thread_sp->GetStackFrameAtIndex(stack_idx);
}

return stack_idx;
}
8 changes: 8 additions & 0 deletions lldb/test/API/functionalities/asan/TestMemoryHistory.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ def libsanitizers_asan_tests(self):
)
self.check_traces()

# Make sure we're not stopped in the sanitizer library but instead at the
# point of failure in the user-code.
self.assertEqual(self.frame().GetFunctionName(), "main")

# do the same using SB API
process = self.dbg.GetSelectedTarget().process
val = (
Expand Down Expand Up @@ -218,6 +222,10 @@ def compiler_rt_asan_tests(self):

self.check_traces()

# Make sure we're not stopped in the sanitizer library but instead at the
Copy link
Member

@DataCorrupted DataCorrupted Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Michael137 Is this correct? This test compiler_rt_asan_tests runs on all platforms, but your heuristics in IsStoppedInDarwinSanitizer is for Darwin only?

In most CI this test is somehow disabled (maybe this is an issue as well?), but in my (totally not related) PR #157529 it keeps failing because the frame is __sanitizer::Die() not main

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yea that should only apply on Darwin. Will correct that

Would be nice if we made it work on non-Darwin too but that's for later

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing, the two tests in question are:

    lldb-api :: functionalities/asan/TestMemoryHistory.py
    lldb-api :: functionalities/asan/TestReportData.py

Randomly grabbed some jobs, seems like other PRs (e.g. #157534, cc @Michael137) are also affected by these two.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed now. Let me know if you're still seeing issues

# point of failure in the user-code.
self.assertEqual(self.frame().GetFunctionName(), "main")

# make sure the 'memory history' command still works even when we're
# generating a report now
self.expect(
Expand Down
4 changes: 4 additions & 0 deletions lldb/test/API/functionalities/asan/TestReportData.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ def asan_tests(self, libsanitizers=False):
lldb.eStopReasonInstrumentation,
)

# Make sure we're not stopped in the sanitizer library but instead at the
# point of failure in the user-code.
self.assertEqual(self.frame().GetFunctionName(), "main")

self.expect(
"bt",
"The backtrace should show the crashing line",
Expand Down
4 changes: 2 additions & 2 deletions lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ def ubsan_tests(self):
substrs=["1 match found"],
)

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

# The stopped thread backtrace should contain either 'align line'
found = False
Expand Down
Loading