[Lldb-commits] [lldb] [lldb] Rework how we pass the execution context to the statusline (PR #159887)

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Tue Sep 23 11:32:44 PDT 2025


https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/159887

>From 4fe7308f3caf0449d9ee03dbf3969cffc867e969 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Fri, 19 Sep 2025 21:37:05 +0000
Subject: [PATCH 1/3] [lldb] Rework how we pass the execution context to the
 statusline

Currently, we always pass the "selected" execution context to the
statusline. When handling a process or thread event, we can be more
precise, and build an execution context from the event data.
---
 lldb/include/lldb/Core/Debugger.h   | 13 +++++--
 lldb/include/lldb/Core/Statusline.h | 17 ++++++---
 lldb/source/Core/Debugger.cpp       | 56 ++++++++++++++++++----------
 lldb/source/Core/IOHandler.cpp      |  2 +-
 lldb/source/Core/Statusline.cpp     | 57 +++++++++++++----------------
 5 files changed, 85 insertions(+), 60 deletions(-)

diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index 250ad64b76d9a..5fe96f759607b 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -181,7 +181,14 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
     return m_target_list.GetSelectedTarget();
   }
 
+  /// Get the execution context for the selected target.
   ExecutionContext GetSelectedExecutionContext();
+
+  /// Similar to GetSelectedExecutionContext but returns a
+  /// ExecutionContextRef, and will hold the dummy target if no target is
+  /// currently selected.
+  ExecutionContextRef GetSelectedExecutionContextRef();
+
   /// Get accessor for the target list.
   ///
   /// The target list is part of the global debugger object. This the single
@@ -419,7 +426,7 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
   void CancelInterruptRequest();
 
   /// Redraw the statusline if enabled.
-  void RedrawStatusline(bool update = true);
+  void RedrawStatusline(std::optional<ExecutionContextRef> exe_ctx_ref);
 
   /// This is the correct way to query the state of Interruption.
   /// If you are on the RunCommandInterpreter thread, it will check the
@@ -701,9 +708,9 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
 
   void HandleBreakpointEvent(const lldb::EventSP &event_sp);
 
-  void HandleProcessEvent(const lldb::EventSP &event_sp);
+  lldb::ProcessSP HandleProcessEvent(const lldb::EventSP &event_sp);
 
-  void HandleThreadEvent(const lldb::EventSP &event_sp);
+  lldb::ThreadSP HandleThreadEvent(const lldb::EventSP &event_sp);
 
   void HandleProgressEvent(const lldb::EventSP &event_sp);
 
diff --git a/lldb/include/lldb/Core/Statusline.h b/lldb/include/lldb/Core/Statusline.h
index 6bda153f822d2..a5ab1927b57f5 100644
--- a/lldb/include/lldb/Core/Statusline.h
+++ b/lldb/include/lldb/Core/Statusline.h
@@ -9,6 +9,8 @@
 #ifndef LLDB_CORE_STATUSLINE_H
 #define LLDB_CORE_STATUSLINE_H
 
+#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Target/ExecutionContext.h"
 #include "lldb/lldb-forward.h"
 #include <cstdint>
 #include <string>
@@ -19,15 +21,16 @@ class Statusline {
   Statusline(Debugger &debugger);
   ~Statusline();
 
+  using Context = std::pair<ExecutionContextRef, SymbolContext>;
+
   /// Reduce the scroll window and draw the statusline.
-  void Enable();
+  void Enable(std::optional<ExecutionContextRef> exe_ctx_ref);
 
   /// Hide the statusline and extend the scroll window.
   void Disable();
 
-  /// Redraw the statusline. If update is false, this will redraw the last
-  /// string.
-  void Redraw(bool update = true);
+  /// Redraw the statusline.
+  void Redraw(std::optional<ExecutionContextRef> exe_ctx_ref);
 
   /// Inform the statusline that the terminal dimensions have changed.
   void TerminalSizeChanged();
@@ -46,7 +49,11 @@ class Statusline {
   void UpdateScrollWindow(ScrollWindowMode mode);
 
   Debugger &m_debugger;
-  std::string m_last_str;
+
+  /// Cached copy of the execution context that allows us to redraw the
+  /// statusline.
+  ExecutionContextRef m_exe_ctx_ref;
+
   uint64_t m_terminal_width = 0;
   uint64_t m_terminal_height = 0;
 };
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index ed674ee1275c7..c761d39d40030 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -253,16 +253,18 @@ Status Debugger::SetPropertyValue(const ExecutionContext *exe_ctx,
       // Statusline setting changed. If we have a statusline instance, update it
       // now. Otherwise it will get created in the default event handler.
       std::lock_guard<std::mutex> guard(m_statusline_mutex);
-      if (StatuslineSupported())
+      if (StatuslineSupported()) {
         m_statusline.emplace(*this);
-      else
+        m_statusline->Enable(GetSelectedExecutionContextRef());
+      } else {
         m_statusline.reset();
+      }
     } else if (property_path ==
                    g_debugger_properties[ePropertyStatuslineFormat].name ||
                property_path ==
                    g_debugger_properties[ePropertySeparator].name) {
       // Statusline format changed. Redraw the statusline.
-      RedrawStatusline();
+      RedrawStatusline(std::nullopt);
     } else if (property_path ==
                g_debugger_properties[ePropertyUseSourceCache].name) {
       // use-source-cache changed. Wipe out the cache contents if it was
@@ -501,7 +503,7 @@ FormatEntity::Entry Debugger::GetStatuslineFormat() const {
 bool Debugger::SetStatuslineFormat(const FormatEntity::Entry &format) {
   constexpr uint32_t idx = ePropertyStatuslineFormat;
   bool ret = SetPropertyAtIndex(idx, format);
-  RedrawStatusline();
+  RedrawStatusline(std::nullopt);
   return ret;
 }
 
@@ -526,7 +528,7 @@ llvm::StringRef Debugger::GetDisabledAnsiSuffix() const {
 bool Debugger::SetSeparator(llvm::StringRef s) {
   constexpr uint32_t idx = ePropertySeparator;
   bool ret = SetPropertyAtIndex(idx, s);
-  RedrawStatusline();
+  RedrawStatusline(std::nullopt);
   return ret;
 }
 
@@ -1210,14 +1212,18 @@ void Debugger::RestoreInputTerminalState() {
   {
     std::lock_guard<std::mutex> guard(m_statusline_mutex);
     if (m_statusline)
-      m_statusline->Enable();
+      m_statusline->Enable(GetSelectedExecutionContext());
   }
 }
 
-void Debugger::RedrawStatusline(bool update) {
+void Debugger::RedrawStatusline(
+    std::optional<ExecutionContextRef> exe_ctx_ref) {
   std::lock_guard<std::mutex> guard(m_statusline_mutex);
-  if (m_statusline)
-    m_statusline->Redraw(update);
+
+  if (!m_statusline)
+    return;
+
+  m_statusline->Redraw(GetSelectedExecutionContextRef());
 }
 
 ExecutionContext Debugger::GetSelectedExecutionContext() {
@@ -1226,6 +1232,13 @@ ExecutionContext Debugger::GetSelectedExecutionContext() {
   return ExecutionContext(exe_ctx_ref);
 }
 
+ExecutionContextRef Debugger::GetSelectedExecutionContextRef() {
+  if (TargetSP selected_target_sp = GetSelectedTarget())
+    return ExecutionContextRef(selected_target_sp.get(),
+                               /*adopt_selected=*/true);
+  return ExecutionContextRef(m_dummy_target_sp.get(), /*adopt_selected=*/false);
+}
+
 void Debugger::DispatchInputInterrupt() {
   std::lock_guard<std::recursive_mutex> guard(m_io_handler_stack.GetMutex());
   IOHandlerSP reader_sp(m_io_handler_stack.Top());
@@ -1941,8 +1954,7 @@ void Debugger::FlushProcessOutput(Process &process, bool flush_stdout,
 }
 
 // This function handles events that were broadcast by the process.
-void Debugger::HandleProcessEvent(const EventSP &event_sp) {
-  using namespace lldb;
+ProcessSP Debugger::HandleProcessEvent(const EventSP &event_sp) {
   const uint32_t event_type = event_sp->GetType();
   ProcessSP process_sp =
       (event_type == Process::eBroadcastBitStructuredData)
@@ -2024,23 +2036,24 @@ void Debugger::HandleProcessEvent(const EventSP &event_sp) {
     if (pop_process_io_handler)
       process_sp->PopProcessIOHandler();
   }
+  return process_sp;
 }
 
-void Debugger::HandleThreadEvent(const EventSP &event_sp) {
+ThreadSP Debugger::HandleThreadEvent(const EventSP &event_sp) {
   // At present the only thread event we handle is the Frame Changed event, and
   // all we do for that is just reprint the thread status for that thread.
-  using namespace lldb;
   const uint32_t event_type = event_sp->GetType();
   const bool stop_format = true;
+  ThreadSP thread_sp;
   if (event_type == Thread::eBroadcastBitStackChanged ||
       event_type == Thread::eBroadcastBitThreadSelected) {
-    ThreadSP thread_sp(
-        Thread::ThreadEventData::GetThreadFromEvent(event_sp.get()));
+    thread_sp = Thread::ThreadEventData::GetThreadFromEvent(event_sp.get());
     if (thread_sp) {
       thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1, stop_format,
                            /*show_hidden*/ true);
     }
   }
+  return thread_sp;
 }
 
 bool Debugger::IsForwardingEvents() { return (bool)m_forward_listener_sp; }
@@ -2109,28 +2122,33 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
 
   if (StatuslineSupported()) {
     std::lock_guard<std::mutex> guard(m_statusline_mutex);
-    if (!m_statusline)
+    if (!m_statusline) {
       m_statusline.emplace(*this);
+      m_statusline->Enable(GetSelectedExecutionContextRef());
+    }
   }
 
   bool done = false;
   while (!done) {
     EventSP event_sp;
     if (listener_sp->GetEvent(event_sp, std::nullopt)) {
+      std::optional<ExecutionContextRef> exe_ctx_ref = std::nullopt;
       if (event_sp) {
         Broadcaster *broadcaster = event_sp->GetBroadcaster();
         if (broadcaster) {
           uint32_t event_type = event_sp->GetType();
           ConstString broadcaster_class(broadcaster->GetBroadcasterClass());
           if (broadcaster_class == broadcaster_class_process) {
-            HandleProcessEvent(event_sp);
+            if (ProcessSP process_sp = HandleProcessEvent(event_sp))
+              exe_ctx_ref = ExecutionContext(process_sp);
           } else if (broadcaster_class == broadcaster_class_target) {
             if (Breakpoint::BreakpointEventData::GetEventDataFromEvent(
                     event_sp.get())) {
               HandleBreakpointEvent(event_sp);
             }
           } else if (broadcaster_class == broadcaster_class_thread) {
-            HandleThreadEvent(event_sp);
+            if (ThreadSP thread_sp = HandleThreadEvent(event_sp))
+              exe_ctx_ref = ExecutionContext(thread_sp);
           } else if (broadcaster == m_command_interpreter_up.get()) {
             if (event_type &
                 CommandInterpreter::eBroadcastBitQuitCommandReceived) {
@@ -2168,7 +2186,7 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
         if (m_forward_listener_sp)
           m_forward_listener_sp->AddEvent(event_sp);
       }
-      RedrawStatusline();
+      RedrawStatusline(exe_ctx_ref);
     }
   }
 
diff --git a/lldb/source/Core/IOHandler.cpp b/lldb/source/Core/IOHandler.cpp
index f65a1113f3592..57819eeade6e8 100644
--- a/lldb/source/Core/IOHandler.cpp
+++ b/lldb/source/Core/IOHandler.cpp
@@ -442,7 +442,7 @@ void IOHandlerEditline::AutoCompleteCallback(CompletionRequest &request) {
 }
 
 void IOHandlerEditline::RedrawCallback() {
-  m_debugger.RedrawStatusline(/*update=*/false);
+  m_debugger.RedrawStatusline(std::nullopt);
 }
 
 #endif
diff --git a/lldb/source/Core/Statusline.cpp b/lldb/source/Core/Statusline.cpp
index 393d427241021..d765bf852922c 100644
--- a/lldb/source/Core/Statusline.cpp
+++ b/lldb/source/Core/Statusline.cpp
@@ -35,9 +35,7 @@ using namespace lldb_private;
 
 Statusline::Statusline(Debugger &debugger)
     : m_debugger(debugger), m_terminal_width(m_debugger.GetTerminalWidth()),
-      m_terminal_height(m_debugger.GetTerminalHeight()) {
-  Enable();
-}
+      m_terminal_height(m_debugger.GetTerminalHeight()) {}
 
 Statusline::~Statusline() { Disable(); }
 
@@ -47,16 +45,16 @@ void Statusline::TerminalSizeChanged() {
 
   UpdateScrollWindow(ResizeStatusline);
 
-  // Draw the old statusline.
-  Redraw(/*update=*/false);
+  // Redraw the old statusline.
+  Redraw(std::nullopt);
 }
 
-void Statusline::Enable() {
+void Statusline::Enable(std::optional<ExecutionContextRef> exe_ctx_ref) {
   // Reduce the scroll window to make space for the status bar below.
   UpdateScrollWindow(EnableStatusline);
 
   // Draw the statusline.
-  Redraw(/*update=*/true);
+  Redraw(exe_ctx_ref);
 }
 
 void Statusline::Disable() {
@@ -69,8 +67,6 @@ void Statusline::Draw(std::string str) {
   if (!stream_sp)
     return;
 
-  m_last_str = str;
-
   str = ansi::TrimAndPad(str, m_terminal_width);
 
   LockedStreamFile locked_stream = stream_sp->Lock();
@@ -127,33 +123,30 @@ void Statusline::UpdateScrollWindow(ScrollWindowMode mode) {
   m_debugger.RefreshIOHandler();
 }
 
-void Statusline::Redraw(bool update) {
-  if (!update) {
-    Draw(m_last_str);
-    return;
-  }
-
-  ExecutionContext exe_ctx = m_debugger.GetSelectedExecutionContext();
-
-  // For colors and progress events, the format entity needs access to the
-  // debugger, which requires a target in the execution context.
-  if (!exe_ctx.HasTargetScope())
-    exe_ctx.SetTargetPtr(&m_debugger.GetSelectedOrDummyTarget());
-
-  SymbolContext symbol_ctx;
-  if (ProcessSP process_sp = exe_ctx.GetProcessSP()) {
-    // Check if the process is stopped, and if it is, make sure it remains
-    // stopped until we've computed the symbol context.
-    Process::StopLocker stop_locker;
-    if (stop_locker.TryLock(&process_sp->GetRunLock())) {
-      if (auto frame_sp = exe_ctx.GetFrameSP())
-        symbol_ctx = frame_sp->GetSymbolContext(eSymbolContextEverything);
-    }
+void Statusline::Redraw(std::optional<ExecutionContextRef> exe_ctx_ref) {
+  // Update the cached execution context.
+  if (exe_ctx_ref)
+    m_exe_ctx_ref = *exe_ctx_ref;
+
+  // Lock the execution context.
+  ExecutionContext exe_ctx =
+      m_exe_ctx_ref.Lock(/*thread_and_frame_only_if_stopped=*/true);
+
+  // Compute the symbol context if we're stopped.
+  SymbolContext sym_ctx;
+  llvm::Expected<StoppedExecutionContext> stopped_exe_ctx =
+      GetStoppedExecutionContext(&m_exe_ctx_ref);
+  if (stopped_exe_ctx) {
+    if (auto frame_sp = stopped_exe_ctx->GetFrameSP())
+      sym_ctx = frame_sp->GetSymbolContext(eSymbolContextEverything);
+  } else {
+    // We can draw the statusline without being stopped.
+    llvm::consumeError(stopped_exe_ctx.takeError());
   }
 
   StreamString stream;
   FormatEntity::Entry format = m_debugger.GetStatuslineFormat();
-  FormatEntity::Format(format, stream, &symbol_ctx, &exe_ctx, nullptr, nullptr,
+  FormatEntity::Format(format, stream, &sym_ctx, &exe_ctx, nullptr, nullptr,
                        false, false);
 
   Draw(stream.GetString().str());

>From afdf9a700df198fc2394552445a72636cf26db71 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Mon, 22 Sep 2025 16:41:09 -0700
Subject: [PATCH 2/3] Address Jim's feedback

---
 lldb/include/lldb/Core/Debugger.h           |  3 +-
 lldb/include/lldb/Target/ExecutionContext.h | 17 +++-
 lldb/source/Core/Debugger.cpp               |  8 +-
 lldb/source/Core/Statusline.cpp             |  2 +-
 lldb/source/Target/ExecutionContext.cpp     | 95 +++++++++++++--------
 5 files changed, 80 insertions(+), 45 deletions(-)

diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index 5fe96f759607b..06136ed40471d 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -181,7 +181,8 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
     return m_target_list.GetSelectedTarget();
   }
 
-  /// Get the execution context for the selected target.
+  /// Get the execution context representing the selected entities in the
+  /// selected target.
   ExecutionContext GetSelectedExecutionContext();
 
   /// Similar to GetSelectedExecutionContext but returns a
diff --git a/lldb/include/lldb/Target/ExecutionContext.h b/lldb/include/lldb/Target/ExecutionContext.h
index f105e38fa69aa..fe8bce7f69713 100644
--- a/lldb/include/lldb/Target/ExecutionContext.h
+++ b/lldb/include/lldb/Target/ExecutionContext.h
@@ -92,10 +92,21 @@ class ExecutionContextRef {
 
   /// Construct using the target and all the selected items inside of it (the
   /// process and its selected thread, and the thread's selected frame). If
-  /// there is no selected thread, default to the first thread If there is no
+  /// there is no selected thread, default to the first thread. If there is no
   /// selected frame, default to the first frame.
   ExecutionContextRef(Target *target, bool adopt_selected);
 
+  /// Construct using the process and all the selected items inside of it (
+  /// the selected thread, and the thread's selected frame). If
+  /// there is no selected thread, default to the first thread. If there is no
+  /// selected frame, default to the first frame.
+  ExecutionContextRef(Process *process, bool adopt_selected);
+
+  /// Construct using the thread and all the selected items inside of it ( the
+  /// selected frame). If there is no selected frame, default to the first
+  /// frame.
+  ExecutionContextRef(Thread *thread, bool adopt_selected);
+
   /// Construct using an execution context scope.
   ///
   /// If the ExecutionContextScope object is valid and refers to a frame, make
@@ -199,9 +210,9 @@ class ExecutionContextRef {
 
   void SetTargetPtr(Target *target, bool adopt_selected);
 
-  void SetProcessPtr(Process *process);
+  void SetProcessPtr(Process *process, bool adopt_selected = false);
 
-  void SetThreadPtr(Thread *thread);
+  void SetThreadPtr(Thread *thread, bool adopt_selected = false);
 
   void SetFramePtr(StackFrame *frame);
 
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index c761d39d40030..c3273dd44ca5e 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -1223,7 +1223,7 @@ void Debugger::RedrawStatusline(
   if (!m_statusline)
     return;
 
-  m_statusline->Redraw(GetSelectedExecutionContextRef());
+  m_statusline->Redraw(exe_ctx_ref);
 }
 
 ExecutionContext Debugger::GetSelectedExecutionContext() {
@@ -2140,7 +2140,8 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
           ConstString broadcaster_class(broadcaster->GetBroadcasterClass());
           if (broadcaster_class == broadcaster_class_process) {
             if (ProcessSP process_sp = HandleProcessEvent(event_sp))
-              exe_ctx_ref = ExecutionContext(process_sp);
+              exe_ctx_ref = ExecutionContextRef(process_sp.get(),
+                                                /*adopt_selected=*/true);
           } else if (broadcaster_class == broadcaster_class_target) {
             if (Breakpoint::BreakpointEventData::GetEventDataFromEvent(
                     event_sp.get())) {
@@ -2148,7 +2149,8 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
             }
           } else if (broadcaster_class == broadcaster_class_thread) {
             if (ThreadSP thread_sp = HandleThreadEvent(event_sp))
-              exe_ctx_ref = ExecutionContext(thread_sp);
+              exe_ctx_ref =
+                  ExecutionContextRef(thread_sp.get(), /*adopt_selected=*/true);
           } else if (broadcaster == m_command_interpreter_up.get()) {
             if (event_type &
                 CommandInterpreter::eBroadcastBitQuitCommandReceived) {
diff --git a/lldb/source/Core/Statusline.cpp b/lldb/source/Core/Statusline.cpp
index d765bf852922c..09d48baae7193 100644
--- a/lldb/source/Core/Statusline.cpp
+++ b/lldb/source/Core/Statusline.cpp
@@ -130,7 +130,7 @@ void Statusline::Redraw(std::optional<ExecutionContextRef> exe_ctx_ref) {
 
   // Lock the execution context.
   ExecutionContext exe_ctx =
-      m_exe_ctx_ref.Lock(/*thread_and_frame_only_if_stopped=*/true);
+      m_exe_ctx_ref.Lock(/*thread_and_frame_only_if_stopped=*/false);
 
   // Compute the symbol context if we're stopped.
   SymbolContext sym_ctx;
diff --git a/lldb/source/Target/ExecutionContext.cpp b/lldb/source/Target/ExecutionContext.cpp
index 9d232e420f71c..a795913047639 100644
--- a/lldb/source/Target/ExecutionContext.cpp
+++ b/lldb/source/Target/ExecutionContext.cpp
@@ -429,6 +429,16 @@ ExecutionContextRef::ExecutionContextRef(Target *target, bool adopt_selected)
   SetTargetPtr(target, adopt_selected);
 }
 
+ExecutionContextRef::ExecutionContextRef(Process *process, bool adopt_selected)
+    : m_target_wp(), m_process_wp(), m_thread_wp(), m_stack_id() {
+  SetProcessPtr(process, adopt_selected);
+}
+
+ExecutionContextRef::ExecutionContextRef(Thread *thread, bool adopt_selected)
+    : m_target_wp(), m_process_wp(), m_thread_wp(), m_stack_id() {
+  SetThreadPtr(thread, adopt_selected);
+}
+
 ExecutionContextRef::ExecutionContextRef(const ExecutionContextRef &rhs)
 
     = default;
@@ -513,55 +523,66 @@ void ExecutionContextRef::SetFrameSP(const lldb::StackFrameSP &frame_sp) {
 void ExecutionContextRef::SetTargetPtr(Target *target, bool adopt_selected) {
   Clear();
   if (target) {
-    lldb::TargetSP target_sp(target->shared_from_this());
-    if (target_sp) {
-      m_target_wp = target_sp;
-      if (adopt_selected) {
-        lldb::ProcessSP process_sp(target_sp->GetProcessSP());
-        if (process_sp) {
-          m_process_wp = process_sp;
-          if (process_sp) {
-            // Only fill in the thread and frame if our process is stopped
-            // Don't just check the state, since we might be in the middle of
-            // resuming.
-            Process::StopLocker stop_locker;
-
-            if (stop_locker.TryLock(&process_sp->GetRunLock()) &&
-                StateIsStoppedState(process_sp->GetState(), true)) {
-              lldb::ThreadSP thread_sp(
-                  process_sp->GetThreadList().GetSelectedThread());
-              if (!thread_sp)
-                thread_sp = process_sp->GetThreadList().GetThreadAtIndex(0);
-
-              if (thread_sp) {
-                SetThreadSP(thread_sp);
-                lldb::StackFrameSP frame_sp(
-                    thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame));
-                if (!frame_sp)
-                  frame_sp = thread_sp->GetStackFrameAtIndex(0);
-                if (frame_sp)
-                  SetFrameSP(frame_sp);
-              }
-            }
-          }
-        }
-      }
+    lldb::TargetSP target_sp = target->shared_from_this();
+    SetTargetSP(target_sp);
+    if (adopt_selected) {
+      if (lldb::ProcessSP process_sp = target_sp->GetProcessSP())
+        SetProcessPtr(process_sp.get(), adopt_selected);
     }
   }
 }
 
-void ExecutionContextRef::SetProcessPtr(Process *process) {
+void ExecutionContextRef::SetProcessPtr(Process *process, bool adopt_selected) {
   if (process) {
-    SetProcessSP(process->shared_from_this());
+    lldb::ProcessSP process_sp = process->shared_from_this();
+    SetProcessSP(process_sp);
+    if (adopt_selected) {
+      // Only fill in the thread if our process is stopped.
+      // Don't just check the state, since we might be in the middle of
+      // resuming.
+      Process::StopLocker stop_locker;
+      if (stop_locker.TryLock(&process_sp->GetRunLock()) &&
+          StateIsStoppedState(process_sp->GetState(), true)) {
+        lldb::ThreadSP thread_sp(
+            process_sp->GetThreadList().GetSelectedThread());
+        if (!thread_sp)
+          thread_sp = process_sp->GetThreadList().GetThreadAtIndex(0);
+        if (thread_sp) {
+          SetThreadSP(thread_sp);
+          lldb::StackFrameSP frame_sp =
+              thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame);
+          if (!frame_sp)
+            frame_sp = thread_sp->GetStackFrameAtIndex(0);
+          if (frame_sp)
+            SetFrameSP(frame_sp);
+        }
+      }
+    }
   } else {
     m_process_wp.reset();
     m_target_wp.reset();
   }
 }
 
-void ExecutionContextRef::SetThreadPtr(Thread *thread) {
+void ExecutionContextRef::SetThreadPtr(Thread *thread, bool adopt_selected) {
   if (thread) {
-    SetThreadSP(thread->shared_from_this());
+    lldb::ThreadSP thread_sp = thread->shared_from_this();
+    SetThreadSP(thread_sp);
+    if (adopt_selected) {
+      // Only fill in the frame if our process is stopped.
+      // Don't just check the state, since we might be in the middle of
+      // resuming.
+      Process::StopLocker stop_locker;
+      if (stop_locker.TryLock(&thread->GetProcess()->GetRunLock()) &&
+          StateIsStoppedState(thread->GetProcess()->GetState(), true)) {
+        lldb::StackFrameSP frame_sp =
+            thread_sp->GetSelectedFrame(DoNoSelectMostRelevantFrame);
+        if (!frame_sp)
+          frame_sp = thread_sp->GetStackFrameAtIndex(0);
+        if (frame_sp)
+          SetFrameSP(frame_sp);
+      }
+    }
   } else {
     ClearThread();
     m_process_wp.reset();

>From 06817bbb3e27ba333fca93a32b37fc50d0f1d16d Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Tue, 23 Sep 2025 18:32:27 +0000
Subject: [PATCH 3/3] Add workaround for issue #160216

---
 lldb/source/Core/Debugger.cpp | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index c3273dd44ca5e..568cd9d3d03b6 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -2081,6 +2081,11 @@ bool Debugger::StatuslineSupported() {
   return false;
 }
 
+static bool RequiresFollowChildWorkaround(const Process &process) {
+  // FIXME: https://github.com/llvm/llvm-project/issues/160216
+  return process.GetFollowForkMode() == eFollowChild;
+}
+
 lldb::thread_result_t Debugger::DefaultEventHandler() {
   ListenerSP listener_sp(GetListener());
   ConstString broadcaster_class_target(Target::GetStaticBroadcasterClass());
@@ -2140,8 +2145,9 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
           ConstString broadcaster_class(broadcaster->GetBroadcasterClass());
           if (broadcaster_class == broadcaster_class_process) {
             if (ProcessSP process_sp = HandleProcessEvent(event_sp))
-              exe_ctx_ref = ExecutionContextRef(process_sp.get(),
-                                                /*adopt_selected=*/true);
+              if (!RequiresFollowChildWorkaround(*process_sp))
+                exe_ctx_ref = ExecutionContextRef(process_sp.get(),
+                                                  /*adopt_selected=*/true);
           } else if (broadcaster_class == broadcaster_class_target) {
             if (Breakpoint::BreakpointEventData::GetEventDataFromEvent(
                     event_sp.get())) {
@@ -2149,8 +2155,9 @@ lldb::thread_result_t Debugger::DefaultEventHandler() {
             }
           } else if (broadcaster_class == broadcaster_class_thread) {
             if (ThreadSP thread_sp = HandleThreadEvent(event_sp))
-              exe_ctx_ref =
-                  ExecutionContextRef(thread_sp.get(), /*adopt_selected=*/true);
+              if (!RequiresFollowChildWorkaround(*thread_sp->GetProcess()))
+                exe_ctx_ref = ExecutionContextRef(thread_sp.get(),
+                                                  /*adopt_selected=*/true);
           } else if (broadcaster == m_command_interpreter_up.get()) {
             if (event_type &
                 CommandInterpreter::eBroadcastBitQuitCommandReceived) {



More information about the lldb-commits mailing list