Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bc604c7

Browse files
vogelsgesangc-rhodes
authored andcommitted
[lldb] Broadcast eBroadcastBitStackChanged when frame providers change (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)
1 parent 6fc6d00 commit bc604c7

3 files changed

Lines changed: 119 additions & 23 deletions

File tree

lldb/include/lldb/Target/Target.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,12 @@ class Target : public std::enable_shared_from_this<Target>,
776776
const llvm::DenseMap<uint32_t, ScriptedFrameProviderDescriptor> &
777777
GetScriptedFrameProviderDescriptors() const;
778778

779+
protected:
780+
/// Invalidate all potentially cached frame providers for all threads
781+
/// and trigger a stack changed event for all threads.
782+
void InvalidateThreadFrameProviders();
783+
784+
public:
779785
// This part handles the breakpoints.
780786

781787
BreakpointList &GetBreakpointList(bool internal = false);

lldb/source/Target/Target.cpp

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3725,47 +3725,45 @@ llvm::Expected<uint32_t> Target::AddScriptedFrameProviderDescriptor(
37253725
if (!descriptor.IsValid())
37263726
return llvm::createStringError("invalid frame provider descriptor");
37273727

3728+
uint32_t descriptor_id = descriptor.GetID();
3729+
37283730
llvm::StringRef name = descriptor.GetName();
37293731
if (name.empty())
37303732
return llvm::createStringError(
37313733
"frame provider descriptor has no class name");
37323734

3733-
std::lock_guard<std::recursive_mutex> guard(
3734-
m_frame_provider_descriptors_mutex);
3735-
3736-
uint32_t descriptor_id = descriptor.GetID();
3737-
m_frame_provider_descriptors[descriptor_id] = descriptor;
3735+
{
3736+
std::unique_lock<std::recursive_mutex> guard(
3737+
m_frame_provider_descriptors_mutex);
3738+
m_frame_provider_descriptors[descriptor_id] = descriptor;
3739+
}
37383740

3739-
// Clear frame providers on existing threads so they reload with new config.
3740-
if (ProcessSP process_sp = GetProcessSP())
3741-
for (ThreadSP thread_sp : process_sp->Threads())
3742-
thread_sp->ClearScriptedFrameProvider();
3741+
InvalidateThreadFrameProviders();
37433742

37443743
return descriptor_id;
37453744
}
37463745

37473746
bool Target::RemoveScriptedFrameProviderDescriptor(uint32_t id) {
3748-
std::lock_guard<std::recursive_mutex> guard(
3749-
m_frame_provider_descriptors_mutex);
3750-
bool removed = m_frame_provider_descriptors.erase(id);
3747+
bool removed = false;
3748+
{
3749+
std::lock_guard<std::recursive_mutex> guard(
3750+
m_frame_provider_descriptors_mutex);
3751+
removed = m_frame_provider_descriptors.erase(id);
3752+
}
37513753

37523754
if (removed)
3753-
if (ProcessSP process_sp = GetProcessSP())
3754-
for (ThreadSP thread_sp : process_sp->Threads())
3755-
thread_sp->ClearScriptedFrameProvider();
3756-
3755+
InvalidateThreadFrameProviders();
37573756
return removed;
37583757
}
37593758

37603759
void Target::ClearScriptedFrameProviderDescriptors() {
3761-
std::lock_guard<std::recursive_mutex> guard(
3762-
m_frame_provider_descriptors_mutex);
3763-
3764-
m_frame_provider_descriptors.clear();
3760+
{
3761+
std::lock_guard<std::recursive_mutex> guard(
3762+
m_frame_provider_descriptors_mutex);
3763+
m_frame_provider_descriptors.clear();
3764+
}
37653765

3766-
if (ProcessSP process_sp = GetProcessSP())
3767-
for (ThreadSP thread_sp : process_sp->Threads())
3768-
thread_sp->ClearScriptedFrameProvider();
3766+
InvalidateThreadFrameProviders();
37693767
}
37703768

37713769
const llvm::DenseMap<uint32_t, ScriptedFrameProviderDescriptor> &
@@ -3775,6 +3773,21 @@ Target::GetScriptedFrameProviderDescriptors() const {
37753773
return m_frame_provider_descriptors;
37763774
}
37773775

3776+
void Target::InvalidateThreadFrameProviders() {
3777+
ProcessSP process_sp = GetProcessSP();
3778+
if (!process_sp)
3779+
return;
3780+
for (ThreadSP thread_sp : process_sp->Threads()) {
3781+
// Clear frame providers on existing threads so they reload with new config.
3782+
thread_sp->ClearScriptedFrameProvider();
3783+
// Notify threads that the stack traces might have changed.
3784+
if (thread_sp->EventTypeHasListeners(Thread::eBroadcastBitStackChanged)) {
3785+
auto data_sp = std::make_shared<Thread::ThreadEventData>(thread_sp);
3786+
thread_sp->BroadcastEvent(Thread::eBroadcastBitStackChanged, data_sp);
3787+
}
3788+
}
3789+
}
3790+
37783791
void Target::FinalizeFileActions(ProcessLaunchInfo &info) {
37793792
Log *log = GetLog(LLDBLog::Process);
37803793

lldb/test/API/functionalities/scripted_frame_provider/TestScriptedFrameProvider.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,3 +1110,80 @@ def test_provider_lifecycle_with_frame_validity(self):
11101110
_ = saved_frames[1].IsValid()
11111111
except Exception as e:
11121112
self.fail(f"Accessing invalidated frames should not crash: {e}")
1113+
1114+
def test_event_broadcasting(self):
1115+
"""Test that adding/removing frame providers broadcasts eBroadcastBitStackChanged."""
1116+
self.build()
1117+
1118+
listener = lldb.SBListener("stack_changed_listener")
1119+
listener.StartListeningForEventClass(
1120+
self.dbg,
1121+
lldb.SBThread.GetBroadcasterClassName(),
1122+
lldb.SBThread.eBroadcastBitStackChanged,
1123+
)
1124+
1125+
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
1126+
self, "Break here", lldb.SBFileSpec(self.source), only_one_thread=False
1127+
)
1128+
1129+
expected_thread_ids = {
1130+
process.GetThreadAtIndex(i).GetIndexID()
1131+
for i in range(process.GetNumThreads())
1132+
}
1133+
1134+
def collect_stack_changed_thread_ids(count):
1135+
event = lldb.SBEvent()
1136+
thread_ids = set()
1137+
for _ in range(count):
1138+
if not listener.WaitForEvent(5, event):
1139+
break
1140+
self.assertEqual(
1141+
event.GetType(),
1142+
lldb.SBThread.eBroadcastBitStackChanged,
1143+
"Event should be stack changed",
1144+
)
1145+
thread_ids.add(lldb.SBThread.GetThreadFromEvent(event).GetIndexID())
1146+
return thread_ids
1147+
1148+
# Import the test frame provider.
1149+
script_path = os.path.join(self.getSourceDir(), "test_frame_providers.py")
1150+
self.runCmd("command script import " + script_path)
1151+
1152+
# 1. Test registration.
1153+
error = lldb.SBError()
1154+
provider_id = target.RegisterScriptedFrameProvider(
1155+
"test_frame_providers.ReplaceFrameProvider",
1156+
lldb.SBStructuredData(),
1157+
error,
1158+
)
1159+
self.assertSuccess(error, f"Failed to register provider: {error}")
1160+
self.assertEqual(
1161+
collect_stack_changed_thread_ids(len(expected_thread_ids)),
1162+
expected_thread_ids,
1163+
"All threads should broadcast eBroadcastBitStackChanged on registration",
1164+
)
1165+
1166+
# 2. Test removal.
1167+
result = target.RemoveScriptedFrameProvider(provider_id)
1168+
self.assertSuccess(result, f"Failed to remove provider: {result}")
1169+
self.assertEqual(
1170+
collect_stack_changed_thread_ids(len(expected_thread_ids)),
1171+
expected_thread_ids,
1172+
"All threads should broadcast eBroadcastBitStackChanged on removal",
1173+
)
1174+
1175+
# 3. Test clear.
1176+
target.RegisterScriptedFrameProvider(
1177+
"test_frame_providers.ReplaceFrameProvider",
1178+
lldb.SBStructuredData(),
1179+
error,
1180+
)
1181+
# Consume registration
1182+
collect_stack_changed_thread_ids(len(expected_thread_ids))
1183+
1184+
self.runCmd("target frame-provider clear")
1185+
self.assertEqual(
1186+
collect_stack_changed_thread_ids(len(expected_thread_ids)),
1187+
expected_thread_ids,
1188+
"All threads should broadcast eBroadcastBitStackChanged on clear",
1189+
)

0 commit comments

Comments
 (0)