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/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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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); } _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
