https://github.com/vogelsgesang updated 
https://github.com/llvm/llvm-project/pull/171482

>From 87356100a83ecf488fa6726257975a7e4af02ffa Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <[email protected]>
Date: Tue, 9 Dec 2025 17:46:05 +0000
Subject: [PATCH 1/6] [lldb] Broadcast `eBroadcastBitStackChanged` when frame
 providers change

We want to reload the call stack whenever the frame providers were
updated. To do so, we now emit a `eBroadcastBitStackChanged` on all
threads whenever any changes to the frame providers take place.

I found this very useful while iterating on a frame provider using
lldb-dap. So far, the new frame provider only took effect after
continuing execution. Now the back trace in VS-Code gets refreshed
immediately upon running `target frame-provider add`
---
 lldb/include/lldb/Target/Target.h |  5 +++
 lldb/source/Target/Target.cpp     | 59 +++++++++++++++++++------------
 2 files changed, 41 insertions(+), 23 deletions(-)

diff --git a/lldb/include/lldb/Target/Target.h 
b/lldb/include/lldb/Target/Target.h
index f781c4dabdd9f..f76ee0a2e83fd 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -800,6 +800,11 @@ class Target : public std::enable_shared_from_this<Target>,
   const llvm::DenseMap<uint32_t, ScriptedFrameProviderDescriptor> &
   GetScriptedFrameProviderDescriptors() const;
 
+protected:
+  /// Notify all threads that the stack traces might have changed.
+  void InvalidateThreadFrameProviders();
+
+public:
   // This part handles the breakpoints.
 
   BreakpointList &GetBreakpointList(bool internal = false);
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index d9e72cd5453e4..ad962eb174c9d 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -3726,47 +3726,45 @@ llvm::Expected<uint32_t> 
Target::AddScriptedFrameProviderDescriptor(
   if (!descriptor.IsValid())
     return llvm::createStringError("invalid frame provider descriptor");
 
+  uint32_t descriptor_id = descriptor.GetID();
+
   llvm::StringRef name = descriptor.GetName();
   if (name.empty())
     return llvm::createStringError(
         "frame provider descriptor has no class name");
 
-  std::lock_guard<std::recursive_mutex> guard(
-      m_frame_provider_descriptors_mutex);
-
-  uint32_t descriptor_id = descriptor.GetID();
-  m_frame_provider_descriptors[descriptor_id] = descriptor;
+  {
+    std::unique_lock<std::recursive_mutex> guard(
+        m_frame_provider_descriptors_mutex);
+    m_frame_provider_descriptors[descriptor_id] = descriptor;
+  }
 
-  // Clear frame providers on existing threads so they reload with new config.
-  if (ProcessSP process_sp = GetProcessSP())
-    for (ThreadSP thread_sp : process_sp->Threads())
-      thread_sp->ClearScriptedFrameProvider();
+  InvalidateThreadFrameProviders();
 
   return descriptor_id;
 }
 
 bool Target::RemoveScriptedFrameProviderDescriptor(uint32_t id) {
-  std::lock_guard<std::recursive_mutex> guard(
-      m_frame_provider_descriptors_mutex);
-  bool removed = m_frame_provider_descriptors.erase(id);
+  bool removed;
+  {
+    std::lock_guard<std::recursive_mutex> guard(
+        m_frame_provider_descriptors_mutex);
+    removed = m_frame_provider_descriptors.erase(id);
+  }
 
   if (removed)
-    if (ProcessSP process_sp = GetProcessSP())
-      for (ThreadSP thread_sp : process_sp->Threads())
-        thread_sp->ClearScriptedFrameProvider();
-
+    InvalidateThreadFrameProviders();
   return removed;
 }
 
 void Target::ClearScriptedFrameProviderDescriptors() {
-  std::lock_guard<std::recursive_mutex> guard(
-      m_frame_provider_descriptors_mutex);
-
-  m_frame_provider_descriptors.clear();
+  {
+    std::lock_guard<std::recursive_mutex> guard(
+        m_frame_provider_descriptors_mutex);
+    m_frame_provider_descriptors.clear();
+  }
 
-  if (ProcessSP process_sp = GetProcessSP())
-    for (ThreadSP thread_sp : process_sp->Threads())
-      thread_sp->ClearScriptedFrameProvider();
+  InvalidateThreadFrameProviders();
 }
 
 const llvm::DenseMap<uint32_t, ScriptedFrameProviderDescriptor> &
@@ -3776,6 +3774,21 @@ Target::GetScriptedFrameProviderDescriptors() const {
   return m_frame_provider_descriptors;
 }
 
+void Target::InvalidateThreadFrameProviders() {
+  ProcessSP process_sp = GetProcessSP();
+  if (!process_sp)
+    return;
+  for (ThreadSP thread_sp : process_sp->Threads()) {
+    // Clear frame providers on existing threads so they reload with new 
config.
+    thread_sp->ClearScriptedFrameProvider();
+    // Notify threads that the stack traces might have changed.
+    if (EventTypeHasListeners(Thread::eBroadcastBitStackChanged)) {
+      auto data_sp = std::make_shared<Thread::ThreadEventData>(thread_sp);
+      thread_sp->BroadcastEvent(Thread::eBroadcastBitStackChanged, data_sp);
+    }
+  }
+}
+
 void Target::FinalizeFileActions(ProcessLaunchInfo &info) {
   Log *log = GetLog(LLDBLog::Process);
 

>From 1c865d90372a8d0498545424e6344bf61b854bd2 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <[email protected]>
Date: Fri, 30 Jan 2026 01:42:25 +0000
Subject: [PATCH 2/6] Update comment

---
 lldb/include/lldb/Target/Target.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lldb/include/lldb/Target/Target.h 
b/lldb/include/lldb/Target/Target.h
index f76ee0a2e83fd..4f5b022765f9e 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -801,7 +801,8 @@ class Target : public std::enable_shared_from_this<Target>,
   GetScriptedFrameProviderDescriptors() const;
 
 protected:
-  /// Notify all threads that the stack traces might have changed.
+  /// Invalidate all potentially cached frame providers for all threads
+  /// and trigger a stack changed event for all threads.
   void InvalidateThreadFrameProviders();
 
 public:

>From cf40625a3c26c9a669a56f355a00fcb0e95f12fa Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <[email protected]>
Date: Thu, 5 Feb 2026 17:06:01 +0000
Subject: [PATCH 3/6] Add test case

---
 .../TestScriptedFrameProvider.py              | 76 +++++++++++++++++++
 1 file changed, 76 insertions(+)

diff --git 
a/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
 
b/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
index 8397d60eedbb5..1392ea59b406b 100644
--- 
a/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
+++ 
b/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
@@ -1110,3 +1110,79 @@ def test_provider_lifecycle_with_frame_validity(self):
             _ = saved_frames[1].IsValid()
         except Exception as e:
             self.fail(f"Accessing invalidated frames should not crash: {e}")
+
+    def test_event_broadcasting(self):
+        """Test that adding/removing frame providers broadcasts 
eBroadcastBitStackChanged."""
+        self.build()
+
+        listener = lldb.SBListener("stack_changed_listener")
+        listener.StartListeningForEventClass(
+            self.dbg,
+            lldb.SBThread.GetBroadcasterClassName(),
+            lldb.SBThread.eBroadcastBitStackChanged,
+        )
+
+        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+            self, "Break here", lldb.SBFileSpec(self.source), 
only_one_thread=False
+        )
+
+        expected_thread_ids = {
+            process.GetThreadAtIndex(i).GetIndexID()
+            for i in range(process.GetNumThreads())
+        }
+
+        def collect_stack_changed_thread_ids(count):
+            event = lldb.SBEvent()
+            thread_ids = set()
+            for _ in range(count):
+                if not listener.WaitForEvent(5, event):
+                    break
+                self.assertEqual(
+                    event.GetType(),
+                    lldb.SBThread.eBroadcastBitStackChanged,
+                    "Event should be stack changed",
+                )
+                
thread_ids.add(lldb.SBThread.GetThreadFromEvent(event).GetIndexID())
+            return thread_ids
+
+        # Import the test frame provider.
+        script_path = os.path.join(self.getSourceDir(), 
"test_frame_providers.py")
+        self.runCmd("command script import " + script_path)
+
+        # 1. Test registration.
+        error = lldb.SBError()
+        provider_id = target.RegisterScriptedFrameProvider(
+            "test_frame_providers.ReplaceFrameProvider",
+            lldb.SBStructuredData(),
+            error,
+        )
+        self.assertSuccess(error, f"Failed to register provider: {error}")
+        self.assertEqual(
+            collect_stack_changed_thread_ids(len(expected_thread_ids)),
+            expected_thread_ids,
+            "All threads should broadcast eBroadcastBitStackChanged on 
registration",
+        )
+
+        # 2. Test removal.
+        result = target.RemoveScriptedFrameProvider(provider_id)
+        self.assertSuccess(result, f"Failed to remove provider: {result}")
+        self.assertEqual(
+            collect_stack_changed_thread_ids(len(expected_thread_ids)),
+            expected_thread_ids,
+            "All threads should broadcast eBroadcastBitStackChanged on 
removal",
+        )
+
+        # 3. Test clear.
+        target.RegisterScriptedFrameProvider(
+            "test_frame_providers.ReplaceFrameProvider",
+            lldb.SBStructuredData(),
+            error,
+        )
+        collect_stack_changed_thread_ids(len(expected_thread_ids))  # Consume 
registration
+
+        self.runCmd("target frame-provider clear")
+        self.assertEqual(
+            collect_stack_changed_thread_ids(len(expected_thread_ids)),
+            expected_thread_ids,
+            "All threads should broadcast eBroadcastBitStackChanged on clear",
+        )
\ No newline at end of file

>From 40ab0a3923d222ebc9aeabc23a83f3e2752ae610 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <[email protected]>
Date: Fri, 30 Jan 2026 02:02:13 +0000
Subject: [PATCH 4/6] Initialize variable

---
 lldb/source/Target/Target.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index ad962eb174c9d..6155a62fa9077 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -3745,7 +3745,7 @@ llvm::Expected<uint32_t> 
Target::AddScriptedFrameProviderDescriptor(
 }
 
 bool Target::RemoveScriptedFrameProviderDescriptor(uint32_t id) {
-  bool removed;
+  bool removed = false;
   {
     std::lock_guard<std::recursive_mutex> guard(
         m_frame_provider_descriptors_mutex);

>From 6e93631355396688562b86e75e1dc9cdf0daf497 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <[email protected]>
Date: Thu, 5 Feb 2026 14:59:48 +0000
Subject: [PATCH 5/6] Fix bug

---
 lldb/source/Target/Target.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 6155a62fa9077..787e66bfaf7a8 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -3782,7 +3782,7 @@ void Target::InvalidateThreadFrameProviders() {
     // Clear frame providers on existing threads so they reload with new 
config.
     thread_sp->ClearScriptedFrameProvider();
     // Notify threads that the stack traces might have changed.
-    if (EventTypeHasListeners(Thread::eBroadcastBitStackChanged)) {
+    if (thread_sp->EventTypeHasListeners(Thread::eBroadcastBitStackChanged)) {
       auto data_sp = std::make_shared<Thread::ThreadEventData>(thread_sp);
       thread_sp->BroadcastEvent(Thread::eBroadcastBitStackChanged, data_sp);
     }

>From e0739c3cf011fa602890aa2116cccac9a5067f11 Mon Sep 17 00:00:00 2001
From: Adrian Vogelsgesang <[email protected]>
Date: Thu, 5 Feb 2026 17:59:44 +0000
Subject: [PATCH 6/6] Fix formatting

---
 .../scripted_frame_provider/TestScriptedFrameProvider.py       | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git 
a/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
 
b/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
index 1392ea59b406b..8c2d8f0d5ad52 100644
--- 
a/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
+++ 
b/lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py
@@ -1178,7 +1178,8 @@ def collect_stack_changed_thread_ids(count):
             lldb.SBStructuredData(),
             error,
         )
-        collect_stack_changed_thread_ids(len(expected_thread_ids))  # Consume 
registration
+        # Consume registration
+        collect_stack_changed_thread_ids(len(expected_thread_ids))
 
         self.runCmd("target frame-provider clear")
         self.assertEqual(

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to