Skip to content

[lldb] Broadcast eBroadcastBitStackChanged when frame providers change#171482

Merged
vogelsgesang merged 6 commits intollvm:mainfrom
vogelsgesang:avogelsgesang-lldb-frameprovider-event
Feb 5, 2026
Merged

[lldb] Broadcast eBroadcastBitStackChanged when frame providers change#171482
vogelsgesang merged 6 commits intollvm:mainfrom
vogelsgesang:avogelsgesang-lldb-frameprovider-event

Conversation

@vogelsgesang
Copy link
Member

@vogelsgesang vogelsgesang commented Dec 9, 2025

We want to reload the call stack whenever the frame providers are 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 in lldb-dap. So far, the new frame provider only took effect after continuing execution. Now the backtrace in VS-Code gets refreshed immediately upon running target frame-provider add.

@llvmbot
Copy link
Member

llvmbot commented Dec 9, 2025

@llvm/pr-subscribers-lldb

Author: Adrian Vogelsgesang (vogelsgesang)

Changes

We want to reload the call stack whenever the frame providers are 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 in 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.


Full diff: https://github.com/llvm/llvm-project/pull/171482.diff

2 Files Affected:

  • (modified) lldb/include/lldb/Target/Target.h (+5)
  • (modified) lldb/source/Target/Target.cpp (+35-24)
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 812a638910b3b..6d1f5f83d153b 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -776,6 +776,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 NotifyThreadsOfChangedFrameProviders();
+
+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 2305f1019ea4f..fe8e41ec4d825 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -3725,47 +3725,43 @@ 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();
+  NotifyThreadsOfChangedFrameProviders();
 
   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);
-
-  if (removed)
-    if (ProcessSP process_sp = GetProcessSP())
-      for (ThreadSP thread_sp : process_sp->Threads())
-        thread_sp->ClearScriptedFrameProvider();
+  bool removed;
+  {
+    std::lock_guard<std::recursive_mutex> guard(
+        m_frame_provider_descriptors_mutex);
+    removed = m_frame_provider_descriptors.erase(id);
+  }
 
+  if (removed) NotifyThreadsOfChangedFrameProviders();
   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();
+  NotifyThreadsOfChangedFrameProviders();
 }
 
 const llvm::DenseMap<uint32_t, ScriptedFrameProviderDescriptor> &
@@ -3775,6 +3771,21 @@ Target::GetScriptedFrameProviderDescriptors() const {
   return m_frame_provider_descriptors;
 }
 
+void Target::NotifyThreadsOfChangedFrameProviders() {
+  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);
 

@github-actions
Copy link

github-actions bot commented Dec 9, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@jimingham
Copy link
Collaborator

This looks fine to me, except that it is a little odd to have a function called NotifyThreadsOfChangedFrameProviders... that does more than notify, it also clears the cached providers. A function called NotifyWhatever should just notify.

@vogelsgesang vogelsgesang force-pushed the avogelsgesang-lldb-frameprovider-event branch from b42d277 to 9468ba4 Compare December 9, 2025 18:21
@vogelsgesang
Copy link
Member Author

vogelsgesang commented Dec 9, 2025

except that it is a little odd to have a function called NotifyThreadsOfChangedFrameProviders... that does more than notify, it also clears the cached providers. A function called NotifyWhatever should just notify.

LLM-generated proposals:

  • InvalidateThreadFrameProviders() — "invalidate" implies clearing cache and forcing reload
  • RefreshThreadsForChangedFrameProviders() — "refresh" implies clearing and reloading
  • ReloadThreadFrameProviders() — "reload" implies clearing and reloading

Any preference? Otherwise, I would probably go with InvalidateThreadFrameProviders

Copy link
Member

@medismailben medismailben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with comments. Please write a test for this to make sure we receive the event as expected.

@vogelsgesang vogelsgesang force-pushed the avogelsgesang-lldb-frameprovider-event branch from d4ec168 to 2011d92 Compare January 30, 2026 02:02
@github-actions
Copy link

github-actions bot commented Jan 30, 2026

🐧 Linux x64 Test Results

  • 33302 tests passed
  • 512 tests skipped

✅ The build succeeded and all tests passed.

@vogelsgesang
Copy link
Member Author

ok, AI failed at generating a correct test case. I will need some time to understand how to use SBListeners from Python

@vogelsgesang vogelsgesang force-pushed the avogelsgesang-lldb-frameprovider-event branch from 2011d92 to 7dde47e Compare February 5, 2026 15:00
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`
@vogelsgesang vogelsgesang force-pushed the avogelsgesang-lldb-frameprovider-event branch from 7dde47e to 6e93631 Compare February 5, 2026 17:07
@github-actions
Copy link

github-actions bot commented Feb 5, 2026

✅ With the latest revision this PR passed the Python code formatter.

@vogelsgesang vogelsgesang merged commit 943782b into llvm:main Feb 5, 2026
10 checks passed
@vogelsgesang
Copy link
Member Author

/cherry-pick 943782b

@llvmbot
Copy link
Member

llvmbot commented Feb 5, 2026

/cherry-pick 943782b

Error: Command failed due to missing milestone.

@vogelsgesang
Copy link
Member Author

/cherry-pick 943782b

@llvmbot
Copy link
Member

llvmbot commented Feb 5, 2026

Failed to cherry-pick: 943782b

https://github.com/llvm/llvm-project/actions/runs/21727862504

Please manually backport the fix and push it to your github fork. Once this is done, please create a pull request

@vogelsgesang
Copy link
Member Author

vogelsgesang commented Feb 5, 2026

Ok, takes some rebasing effort to backport this.
@medismailben do you think we should even backport this?
If so, I could do that some time next week

AlexisPerry pushed a commit to llvm-project-tlp/llvm-project that referenced this pull request Feb 6, 2026
…nge (llvm#171482)

We want to reload the call stack whenever the frame providers are
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 in
lldb-dap. So far, the new frame provider only took effect after
continuing execution. Now the backtrace in VS-Code gets refreshed
immediately upon running `target frame-provider add`.
c-rhodes pushed a commit to llvmbot/llvm-project that referenced this pull request Feb 9, 2026
…nge (llvm#171482)

We want to reload the call stack whenever the frame providers are
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 in
lldb-dap. So far, the new frame provider only took effect after
continuing execution. Now the backtrace in VS-Code gets refreshed
immediately upon running `target frame-provider add`.

(cherry picked from commit 943782b)
rishabhmadan19 pushed a commit to rishabhmadan19/llvm-project that referenced this pull request Feb 9, 2026
…nge (llvm#171482)

We want to reload the call stack whenever the frame providers are
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 in
lldb-dap. So far, the new frame provider only took effect after
continuing execution. Now the backtrace in VS-Code gets refreshed
immediately upon running `target frame-provider add`.
Xinlong-Chen pushed a commit to Xinlong-Chen/llvm-project that referenced this pull request Feb 12, 2026
…nge (llvm#171482)

We want to reload the call stack whenever the frame providers are
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 in
lldb-dap. So far, the new frame provider only took effect after
continuing execution. Now the backtrace in VS-Code gets refreshed
immediately upon running `target frame-provider add`.
@vogelsgesang vogelsgesang deleted the avogelsgesang-lldb-frameprovider-event branch February 16, 2026 23:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Development

Successfully merging this pull request may close these issues.

4 participants