Skip to content

[lldb][Instrumentation] Set selected frame to outside sanitizer libraries #133079

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
@@ -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; }
2 changes: 2 additions & 0 deletions lldb/include/lldb/Target/StackFrameList.h
Original file line number Diff line number Diff line change
@@ -36,6 +36,8 @@ class StackFrameList {
/// Get the frame at index \p idx. Invisible frames cannot be indexed.
lldb::StackFrameSP GetFrameAtIndex(uint32_t idx);

void ResetSuggestedStackFrameIndex() { m_selected_frame_idx.reset(); }

/// Get the first concrete frame with index greater than or equal to \p idx.
/// Unlike \ref GetFrameAtIndex, this cannot return a synthetic frame.
lldb::StackFrameSP GetFrameWithConcreteFrameIndex(uint32_t unwind_idx);
4 changes: 4 additions & 0 deletions lldb/include/lldb/Target/Thread.h
Original file line number Diff line number Diff line change
@@ -433,6 +433,10 @@ class Thread : public std::enable_shared_from_this<Thread>,
return GetStackFrameList()->GetFrameAtIndex(idx);
}

virtual void ResetSuggestedStackFrameIndex() {
return GetStackFrameList()->ResetSuggestedStackFrameIndex();
}

virtual lldb::StackFrameSP
GetFrameWithConcreteFrameIndex(uint32_t unwind_idx);

42 changes: 42 additions & 0 deletions lldb/source/Target/InstrumentationRuntimeStopInfo.cpp
Original file line number Diff line number Diff line change
@@ -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)
@@ -34,3 +41,38 @@ InstrumentationRuntimeStopInfo::CreateStopReasonWithInstrumentationData(
return StopInfoSP(
new InstrumentationRuntimeStopInfo(thread, description, additionalData));
}

std::optional<uint32_t>
InstrumentationRuntimeStopInfo::GetSuggestedStackFrameIndex(
bool inlined_stack) {
auto 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.
const 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;
}
2 changes: 2 additions & 0 deletions lldb/source/Target/Process.cpp
Original file line number Diff line number Diff line change
@@ -4257,6 +4257,8 @@ bool Process::ProcessEventData::ShouldStop(Event *event_ptr,
// appropriately. We also need to stop processing actions, since they
// aren't expecting the target to be running.

thread_sp->ResetSuggestedStackFrameIndex();

// FIXME: we might have run.
if (stop_info_sp->HasTargetRunSinceMe()) {
SetRestarted(true);
4 changes: 4 additions & 0 deletions lldb/test/API/functionalities/asan/TestMemoryHistory.py
Original file line number Diff line number Diff line change
@@ -52,6 +52,10 @@ def libsanitizer_tests(self):
substrs=["stopped", "stop reason = Use of deallocated memory"],
)

# 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")

# test the 'memory history' command
self.expect(
"memory history 'pointer'",
4 changes: 4 additions & 0 deletions lldb/test/API/functionalities/asan/TestReportData.py
Original file line number Diff line number Diff line change
@@ -69,6 +69,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",
4 changes: 2 additions & 2 deletions lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py
Original file line number Diff line number Diff line change
@@ -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