[Lldb-commits] [lldb] [lldb] Broadcast `eBroadcastBitStackChanged` when frame providers change (PR #171482)
Adrian Vogelsgesang via lldb-commits
lldb-commits at lists.llvm.org
Thu Feb 5 09:07:19 PST 2026
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 <avogelsgesang at salesforce.com>
Date: Tue, 9 Dec 2025 17:46:05 +0000
Subject: [PATCH 1/5] [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 <avogelsgesang at salesforce.com>
Date: Fri, 30 Jan 2026 01:42:25 +0000
Subject: [PATCH 2/5] 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 <avogelsgesang at salesforce.com>
Date: Thu, 5 Feb 2026 17:06:01 +0000
Subject: [PATCH 3/5] 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 <avogelsgesang at salesforce.com>
Date: Fri, 30 Jan 2026 02:02:13 +0000
Subject: [PATCH 4/5] 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 <avogelsgesang at salesforce.com>
Date: Thu, 5 Feb 2026 14:59:48 +0000
Subject: [PATCH 5/5] 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);
}
More information about the lldb-commits
mailing list