https://github.com/llvmbot updated 
https://github.com/llvm/llvm-project/pull/157568

>From 54896838ca5e1f69f0f3040fe6847546db0463d4 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Mon, 8 Sep 2025 17:18:25 +0100
Subject: [PATCH 1/4] [lldb][Target] Clear selected frame index after a
 StopInfo::PerformAction (#133078)

The motivation for this patch is that
`StopInfo::GetSuggestedStackFrameIndex` would not take effect for
`InstrumentationRuntimeStopInfo` (which we plan to implement in
https://github.com/llvm/llvm-project/pull/133079). This was happening
because the instrumentation runtime plugins would run utility
expressions as part of the stop that would set the
`m_selected_frame_idx`. This means `SelectMostRelevantFrame` was never
called, and we would not be able to report the selected frame via the
`StopInfo` object.

This patch makes sure we clear the `m_selected_frame_idx` to allow
`GetSuggestedStackFrameIndex` to take effect, regardless of what the
frame recognizers choose to do.

(cherry picked from commit 39572f5e9168b1b44c2f9078494616fed8752086)
---
 lldb/include/lldb/Target/StackFrameList.h | 12 ++++++++++++
 lldb/include/lldb/Target/Thread.h         |  5 +++++
 lldb/source/Target/Process.cpp            |  8 ++++++++
 lldb/source/Target/StackFrameList.cpp     |  2 ++
 4 files changed, 27 insertions(+)

diff --git a/lldb/include/lldb/Target/StackFrameList.h 
b/lldb/include/lldb/Target/StackFrameList.h
index 8a66296346f2d..8d455dc831df3 100644
--- a/lldb/include/lldb/Target/StackFrameList.h
+++ b/lldb/include/lldb/Target/StackFrameList.h
@@ -46,6 +46,9 @@ class StackFrameList {
   /// Mark a stack frame as the currently selected frame and return its index.
   uint32_t SetSelectedFrame(lldb_private::StackFrame *frame);
 
+  /// Resets the selected frame index of this object.
+  void ClearSelectedFrameIndex();
+
   /// Get the currently selected frame index.
   /// We should only call SelectMostRelevantFrame if (a) the user hasn't 
already
   /// selected a frame, and (b) if this really is a user facing
@@ -172,6 +175,15 @@ class StackFrameList {
   /// The currently selected frame. An optional is used to record whether 
anyone
   /// has set the selected frame on this stack yet. We only let recognizers
   /// change the frame if this is the first time GetSelectedFrame is called.
+  ///
+  /// Thread-safety:
+  /// This member is not protected by a mutex.
+  /// LLDB really only should have an opinion about the selected frame index
+  /// when a process stops, before control gets handed back to the user.
+  /// After that, it's up to them to change it whenever they feel like it.
+  /// If two parts of lldb decided they wanted to be in control of the selected
+  /// frame index on stop the right way to fix it would need to be some 
explicit
+  /// negotiation for who gets to control this.
   std::optional<uint32_t> m_selected_frame_idx;
 
   /// The number of concrete frames fetched while filling the frame list. This
diff --git a/lldb/include/lldb/Target/Thread.h 
b/lldb/include/lldb/Target/Thread.h
index 6ede7fa301a82..688c056da2633 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -479,6 +479,11 @@ class Thread : public std::enable_shared_from_this<Thread>,
   bool SetSelectedFrameByIndexNoisily(uint32_t frame_idx,
                                       Stream &output_stream);
 
+  /// Resets the selected frame index of this object.
+  void ClearSelectedFrameIndex() {
+    return GetStackFrameList()->ClearSelectedFrameIndex();
+  }
+
   void SetDefaultFileAndLineToSelectedFrame() {
     GetStackFrameList()->SetDefaultFileAndLineToSelectedFrame();
   }
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 2aa02fd58335e..a2fa88b569135 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -4257,6 +4257,14 @@ 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.
 
+        // Clear the selected frame which may have been set as part of utility
+        // expressions that have been run as part of this stop. If we didn't
+        // clear this, then StopInfo::GetSuggestedStackFrameIndex would not
+        // take affect when we next called SelectMostRelevantFrame.
+        // PerformAction should not be the one setting a selected frame, 
instead
+        // this should be done via GetSuggestedStackFrameIndex.
+        thread_sp->ClearSelectedFrameIndex();
+
         // FIXME: we might have run.
         if (stop_info_sp->HasTargetRunSinceMe()) {
           SetRestarted(true);
diff --git a/lldb/source/Target/StackFrameList.cpp 
b/lldb/source/Target/StackFrameList.cpp
index 16cd2548c2784..931b73b1e3633 100644
--- a/lldb/source/Target/StackFrameList.cpp
+++ b/lldb/source/Target/StackFrameList.cpp
@@ -936,3 +936,5 @@ size_t StackFrameList::GetStatus(Stream &strm, uint32_t 
first_frame,
   strm.IndentLess();
   return num_frames_displayed;
 }
+
+void StackFrameList::ClearSelectedFrameIndex() { m_selected_frame_idx.reset(); 
}

>From faedeb1a242079c7fdea59d8b8487ab9d4c31bb9 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Mon, 8 Sep 2025 18:03:31 +0100
Subject: [PATCH 2/4] [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++](https://github.com/llvm/llvm-project/pull/108825)). This patch
does just that, by implementing the `GetSuggestedStackFrameIndex` API

Depends on https://github.com/llvm/llvm-project/pull/133078

(cherry picked from commit 879f40ab041b31fa73b9b25e4ec9e06e810bc767)
---
 .../Target/InstrumentationRuntimeStopInfo.h   |  3 ++
 .../Target/InstrumentationRuntimeStopInfo.cpp | 42 +++++++++++++++++++
 .../functionalities/asan/TestMemoryHistory.py |  8 ++++
 .../functionalities/asan/TestReportData.py    |  4 ++
 .../ubsan/basic/TestUbsanBasic.py             |  4 +-
 5 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h 
b/lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h
index 5345160850914..dafa41c11327a 100644
--- a/lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h
+++ b/lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h
@@ -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; }
diff --git a/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp 
b/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp
index 7f82581cc601e..aef895def7939 100644
--- a/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp
+++ b/lldb/source/Target/InstrumentationRuntimeStopInfo.cpp
@@ -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) {
+  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;
+}
diff --git a/lldb/test/API/functionalities/asan/TestMemoryHistory.py 
b/lldb/test/API/functionalities/asan/TestMemoryHistory.py
index 1568140b355dc..0a2ae96a557ac 100644
--- a/lldb/test/API/functionalities/asan/TestMemoryHistory.py
+++ b/lldb/test/API/functionalities/asan/TestMemoryHistory.py
@@ -96,6 +96,10 @@ def libsanitizers_asan_tests(self):
         )
         self.check_traces(skip_line_numbers=True)
 
+        # 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 = (
@@ -220,6 +224,10 @@ def compiler_rt_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")
+
         # make sure the 'memory history' command still works even when we're
         # generating a report now
         self.expect(
diff --git a/lldb/test/API/functionalities/asan/TestReportData.py 
b/lldb/test/API/functionalities/asan/TestReportData.py
index dd6834a01b80c..ccc1b846d1607 100644
--- a/lldb/test/API/functionalities/asan/TestReportData.py
+++ b/lldb/test/API/functionalities/asan/TestReportData.py
@@ -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",
diff --git a/lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py 
b/lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py
index 868a2864d2b5e..f46d167d910ea 100644
--- a/lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py
+++ b/lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py
@@ -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

>From f5ba88341e7c4f7427e33c8237c07c3c5f1e189c Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Mon, 8 Sep 2025 19:25:20 +0100
Subject: [PATCH 3/4] [lldb][test] TestTsanBasic.py: fix function name
 assertion

Since https://github.com/llvm/llvm-project/pull/133079 we no longer stop
in the stanitizer library when hitting a sanitizer breakpoint. Adjust
the test accordingly.

(cherry picked from commit bdf645bb9b509b60bdb6a71d865b4f8999187977)
---
 lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py 
b/lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py
index ca8b74e35dff6..2747b2b442195 100644
--- a/lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py
+++ b/lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py
@@ -63,11 +63,11 @@ def tsan_tests(self):
             substrs=["1 match found"],
         )
 
-        # We should be stopped in __tsan_on_report
+        # We should not be stopped in the sanitizer library.
         process = self.dbg.GetSelectedTarget().process
         thread = process.GetSelectedThread()
         frame = thread.GetSelectedFrame()
-        self.assertIn("__tsan_on_report", frame.GetFunctionName())
+        self.assertIn("f2", frame.GetFunctionName())
 
         # The stopped thread backtrace should contain either line1 or line2
         # from main.c.

>From 2c8cb316b5d2b0121d7d364d30b86b4310fb0809 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Mon, 8 Sep 2025 22:32:49 +0100
Subject: [PATCH 4/4] [lldb][test] Only assert function name is in user-code on
 Darwin platforms

The frame recognizer for the instrumentation runtimes (added in
https://github.com/llvm/llvm-project/pull/133079) only triggers on Darwin
for `libclang_rt`. Adjust the tests accordingly.

(cherry picked from commit 5326b3b176e82191b18ffc368118b36e0103af3d)
---
 .../functionalities/asan/TestMemoryHistory.py    | 16 +++++++++-------
 .../API/functionalities/asan/TestReportData.py   |  9 +++++----
 .../functionalities/tsan/basic/TestTsanBasic.py  |  7 +++++--
 .../ubsan/basic/TestUbsanBasic.py                |  7 +++++--
 4 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/lldb/test/API/functionalities/asan/TestMemoryHistory.py 
b/lldb/test/API/functionalities/asan/TestMemoryHistory.py
index 0a2ae96a557ac..302a45c2368de 100644
--- a/lldb/test/API/functionalities/asan/TestMemoryHistory.py
+++ b/lldb/test/API/functionalities/asan/TestMemoryHistory.py
@@ -2,7 +2,6 @@
 Test that ASan memory history provider returns correct stack traces
 """
 
-
 import lldb
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
@@ -10,6 +9,7 @@
 from lldbsuite.test import lldbutil
 from lldbsuite.test_event.build_exception import BuildError
 
+
 class MemoryHistoryTestCase(TestBase):
     @skipIfFreeBSD  # llvm.org/pr21136 runtimes not yet available by default
     @expectedFailureNetBSD
@@ -96,9 +96,10 @@ def libsanitizers_asan_tests(self):
         )
         self.check_traces(skip_line_numbers=True)
 
-        # 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")
+        if self.platformIsDarwin():
+            # 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
@@ -224,9 +225,10 @@ def compiler_rt_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")
+        if self.platformIsDarwin():
+            # 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")
 
         # make sure the 'memory history' command still works even when we're
         # generating a report now
diff --git a/lldb/test/API/functionalities/asan/TestReportData.py 
b/lldb/test/API/functionalities/asan/TestReportData.py
index ccc1b846d1607..c832436b0f447 100644
--- a/lldb/test/API/functionalities/asan/TestReportData.py
+++ b/lldb/test/API/functionalities/asan/TestReportData.py
@@ -2,7 +2,6 @@
 Test the AddressSanitizer runtime support for report breakpoint and data 
extraction.
 """
 
-
 import json
 import lldb
 from lldbsuite.test.decorators import *
@@ -10,6 +9,7 @@
 from lldbsuite.test import lldbutil
 from lldbsuite.test_event.build_exception import BuildError
 
+
 class AsanTestReportDataCase(TestBase):
     @skipIfFreeBSD  # llvm.org/pr21136 runtimes not yet available by default
     @expectedFailureNetBSD
@@ -67,9 +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")
+        if self.platformIsDarwin():
+            # 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",
diff --git a/lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py 
b/lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py
index 2747b2b442195..51a28c5013071 100644
--- a/lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py
+++ b/lldb/test/API/functionalities/tsan/basic/TestTsanBasic.py
@@ -63,11 +63,14 @@ def tsan_tests(self):
             substrs=["1 match found"],
         )
 
-        # We should not be stopped in the sanitizer library.
         process = self.dbg.GetSelectedTarget().process
         thread = process.GetSelectedThread()
         frame = thread.GetSelectedFrame()
-        self.assertIn("f2", frame.GetFunctionName())
+        if self.platformIsDarwin():
+            # We should not be stopped in the sanitizer library.
+            self.assertIn("f2", frame.GetFunctionName())
+        else:
+            self.assertIn("__tsan_on_report", frame.GetFunctionName())
 
         # The stopped thread backtrace should contain either line1 or line2
         # from main.c.
diff --git a/lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py 
b/lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py
index f46d167d910ea..9e9ea2114196e 100644
--- a/lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py
+++ b/lldb/test/API/functionalities/ubsan/basic/TestUbsanBasic.py
@@ -52,8 +52,11 @@ def ubsan_tests(self):
             substrs=["1 match found"],
         )
 
-        # We should not be stopped in the sanitizer library.
-        self.assertIn("main", frame.GetFunctionName())
+        if self.platformIsDarwin():
+            # We should not be stopped in the sanitizer library.
+            self.assertIn("main", frame.GetFunctionName())
+        else:
+            self.assertIn("__ubsan_on_report", frame.GetFunctionName())
 
         # The stopped thread backtrace should contain either 'align line'
         found = False

_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to