[Lldb-commits] [lldb] [lldb][Windows] Report a single step whose trap is still queued at a stop (PR #231502)

Charles Zablit via lldb-commits lldb-commits at lists.llvm.org
Wed Oct 14 04:25:09 PDT 2026


https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/231502

>From 16ad6523c6925a2bb0a5b9640ff894058a127859 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 13 Oct 2026 14:15:51 +0100
Subject: [PATCH 1/2] [lldb][Windows] Report debug events queued together in
 one stop

---
 .../lldb/Host/common/NativeProcessProtocol.h  |  5 ++-
 .../Process/Windows/Common/DebuggerThread.cpp | 37 ++++++++++++++++--
 .../Process/Windows/Common/DebuggerThread.h   |  4 ++
 .../Process/Windows/Common/IDebugDelegate.h   |  3 ++
 .../Windows/Common/NativeProcessWindows.cpp   | 38 ++++++++++++++++---
 .../Windows/Common/NativeProcessWindows.h     | 17 +++++++++
 6 files changed, 94 insertions(+), 10 deletions(-)

diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 209cb068aba215..da1fa26abefa9e 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -544,8 +544,11 @@ class NativeProcessProtocol {
 
   NativeThreadProtocol *GetThreadByIDUnlocked(lldb::tid_t tid);
 
-private:
+  /// Notify the delegates of \p state. For plugins that set a state with
+  /// SetState(state, /*notify_delegates=*/false) and report it later.
   void SynchronouslyNotifyProcessStateChanged(lldb::StateType state);
+
+private:
   llvm::Expected<SoftwareBreakpoint>
   EnableSoftwareBreakpoint(lldb::addr_t addr, uint32_t size_hint);
 };
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
index cb1e31b4334f54..6dd051e574885b 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp
@@ -262,6 +262,13 @@ void DebuggerThread::ContinueAsyncException(ExceptionResult result) {
   m_exception_pred.SetValue(result, eBroadcastAlways);
 }
 
+void DebuggerThread::ReportDeferredStopAndWaitForResume() {
+  m_dll_event_pred.SetValue(false, eBroadcastNever);
+  m_debug_delegate->ReportDeferredStop();
+  if (!m_is_shutting_down)
+    m_dll_event_pred.WaitForValueEqualTo(true);
+}
+
 void DebuggerThread::ContinueAsyncDllEvent() {
   m_dll_event_pred.SetValue(true, eBroadcastAlways);
 }
@@ -279,10 +286,28 @@ void DebuggerThread::DebugLoop() {
   Log *log = GetLog(WindowsLog::Event);
   DEBUG_EVENT dbe = {};
   bool should_debug = true;
+  constexpr unsigned max_drained_events = 64;
+  unsigned drained_events = 0;
   LLDB_LOG_VERBOSE(log, "Entering WaitForDebugEventEx loop");
   while (should_debug) {
+    bool draining = !m_is_shutting_down && m_debug_delegate->HasDeferredStop();
+    if (draining && drained_events >= max_drained_events) {
+      LLDB_LOG(log, "reporting the stop after taking {0} queued debug events",
+               drained_events);
+      ReportDeferredStopAndWaitForResume();
+      draining = false;
+    }
+    if (!draining)
+      drained_events = 0;
     LLDB_LOG_VERBOSE(log, "Calling WaitForDebugEvent");
-    BOOL wait_result = g_wait_for_debug_event(&dbe, INFINITE);
+    BOOL wait_result = g_wait_for_debug_event(&dbe, draining ? 0 : INFINITE);
+    if (draining) {
+      if (!wait_result) {
+        ReportDeferredStopAndWaitForResume();
+        continue;
+      }
+      ++drained_events;
+    }
     if (wait_result) {
       DWORD continue_status = DBG_CONTINUE;
       bool shutting_down = m_is_shutting_down;
@@ -447,6 +472,8 @@ DebuggerThread::HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info,
   // result is what the wait below returns.
   if (result != ExceptionResult::BreakInDebugger)
     ContinueAsyncException(result);
+  else
+    m_debug_delegate->ReportDeferredStop();
 
   LLDB_LOG(log, "waiting for ExceptionPred != BreakInDebugger");
   result = *m_exception_pred.WaitForValueNotEqualTo(
@@ -864,8 +891,10 @@ DebuggerThread::HandleLoadDllEvent(const LOAD_DLL_DEBUG_INFO &info,
   if (info.hFile != nullptr)
     ::CloseHandle(info.hFile);
 
-  if (action == DllEventAction::ParkDebugLoop && !m_is_shutting_down.load())
+  if (action == DllEventAction::ParkDebugLoop && !m_is_shutting_down.load()) {
+    m_debug_delegate->ReportDeferredStop();
     m_dll_event_pred.WaitForValueEqualTo(true);
+  }
   return DBG_CONTINUE;
 }
 
@@ -879,8 +908,10 @@ DebuggerThread::HandleUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO &info,
   m_dll_event_pred.SetValue(false, eBroadcastNever);
   DllEventAction action = m_debug_delegate->OnUnloadDll(
       reinterpret_cast<lldb::addr_t>(info.lpBaseOfDll), thread_id);
-  if (action == DllEventAction::ParkDebugLoop && !m_is_shutting_down.load())
+  if (action == DllEventAction::ParkDebugLoop && !m_is_shutting_down.load()) {
+    m_debug_delegate->ReportDeferredStop();
     m_dll_event_pred.WaitForValueEqualTo(true);
+  }
   return DBG_CONTINUE;
 }
 
diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h
index a9fa3ec0c7599d..cc2d3f740b77e9 100644
--- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h
+++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h
@@ -55,6 +55,10 @@ class DebuggerThread : public std::enable_shared_from_this<DebuggerThread> {
   void ContinueAsyncDllEvent();
 
 private:
+  /// Have the delegate report the stop it collected, then wait until the
+  /// client resumes before taking the next debug event.
+  void ReportDeferredStopAndWaitForResume();
+
   void FreeProcessHandles();
   void DebugLoop();
   ExceptionResult HandleExceptionEvent(const EXCEPTION_DEBUG_INFO &info,
diff --git a/lldb/source/Plugins/Process/Windows/Common/IDebugDelegate.h b/lldb/source/Plugins/Process/Windows/Common/IDebugDelegate.h
index cecd58a15f9c2c..f5c9b53447f3b9 100644
--- a/lldb/source/Plugins/Process/Windows/Common/IDebugDelegate.h
+++ b/lldb/source/Plugins/Process/Windows/Common/IDebugDelegate.h
@@ -37,6 +37,9 @@ class IDebugDelegate {
                                    lldb::tid_t thread_id) = 0;
   virtual DllEventAction OnUnloadDll(lldb::addr_t module_addr,
                                      lldb::tid_t thread_id) = 0;
+  virtual bool HasDeferredStop() { return false; }
+  /// Report the stop the delegate holds, if any.
+  virtual void ReportDeferredStop() {}
   virtual void OnDebugString(lldb::addr_t debug_string_addr, bool is_unicode,
                              uint16_t length_lower_word) = 0;
   virtual void OnDebuggerError(const Status &error, uint32_t type) = 0;
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
index 9d2cdf9f58510c..6885ca024b06a7 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
@@ -533,6 +533,9 @@ void NativeProcessWindows::OnExitProcess(uint32_t exit_code) {
   bool started = m_session_data && m_session_data->m_initial_stop_received;
   ProcessDebugger::OnExitProcess(exit_code);
 
+  // The exit replaces a stop that has not been reported yet.
+  m_deferred_stop = false;
+
   // No signal involved.  It is just an exit event.
   WaitStatus wait_status(WaitStatus::Exit, exit_code);
   SetExitStatus(wait_status, started);
@@ -583,6 +586,29 @@ void NativeProcessWindows::OnDebuggerConnected(lldb::addr_t image_base) {
       *this, m_session_data->m_debugger->GetMainThread()));
 }
 
+void NativeProcessWindows::DeferStop() {
+  if (GetState() == eStateStopped) {
+    if (!m_deferred_stop)
+      LLDB_LOG(GetLog(WindowsLog::Exception),
+               "debug event on thread {0:x} while a reported stop is current",
+               GetCurrentThreadID());
+    return;
+  }
+  SetState(eStateStopped, /*notify_delegates=*/false);
+  m_deferred_stop = true;
+  m_deferred_stop_tid = GetCurrentThreadID();
+}
+
+void NativeProcessWindows::ReportDeferredStop() {
+  llvm::sys::ScopedLock lock(m_mutex);
+  if (!m_deferred_stop)
+    return;
+  m_deferred_stop = false;
+  if (GetThreadByID(m_deferred_stop_tid))
+    SetCurrentThreadID(m_deferred_stop_tid);
+  SynchronouslyNotifyProcessStateChanged(eStateStopped);
+}
+
 bool NativeProcessWindows::RewindTrapOfRemovedBreakpoint(
     const ExceptionRecord &record) {
   if (!m_initial_stop_seen)
@@ -646,7 +672,7 @@ NativeProcessWindows::HandleSingleStepException(const ExceptionRecord &record) {
     StopThread(record.GetThreadID(), StopReason::eStopReasonTrace);
   }
 
-  SetState(eStateStopped, true);
+  DeferStop();
   return ExceptionResult::MaskException;
 }
 
@@ -665,7 +691,7 @@ NativeProcessWindows::HandleBreakpointException(const ExceptionRecord &record) {
       StopThread(thread_id, StopReason::eStopReasonBreakpoint);
       // The current PC is AFTER the BP opcode, on all architectures.
       reg_ctx.SetPC(reg_ctx.GetPC() - GetSoftwareBreakpointPCOffset());
-      SetState(eStateStopped, true);
+      DeferStop();
       return ExceptionResult::MaskException;
     }
 
@@ -692,7 +718,7 @@ NativeProcessWindows::HandleBreakpointException(const ExceptionRecord &record) {
                     exception_addr)
                 .str();
         StopThread(thread_id, StopReason::eStopReasonWatchpoint, desc);
-        SetState(eStateStopped, true);
+        DeferStop();
         return ExceptionResult::MaskException;
       }
     }
@@ -750,7 +776,7 @@ NativeProcessWindows::HandleBreakpointException(const ExceptionRecord &record) {
     SetCurrentThreadID(thread_id);
     if (NativeThreadWindows *injected = GetThreadByID(thread_id))
       injected->SetStopReason(signal_info, "interrupt");
-    SetState(eStateStopped, true);
+    DeferStop();
     return ExceptionResult::BreakInDebugger;
   }
 
@@ -767,7 +793,7 @@ NativeProcessWindows::HandleBreakpointException(const ExceptionRecord &record) {
                              record.GetExceptionValue(), exception_addr)
                          .str();
   StopThread(thread_id, StopReason::eStopReasonException, std::move(desc));
-  SetState(eStateStopped, true);
+  DeferStop();
   return ExceptionResult::MaskException;
 }
 
@@ -793,7 +819,7 @@ NativeProcessWindows::HandleGenericException(bool first_chance,
   StopThread(record.GetThreadID(), StopReason::eStopReasonException,
              std::move(desc));
 
-  SetState(eStateStopped, true);
+  DeferStop();
   return ExceptionResult::BreakInDebugger;
 }
 
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
index e79596fecc9f80..79046dd6846a46 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
@@ -140,6 +140,8 @@ class NativeProcessWindows : public NativeProcessProtocol,
                              lldb::tid_t thread_id) override;
   void OnDebugString(lldb::addr_t debug_string_addr, bool is_unicode,
                      uint16_t length_lower_word) override;
+  bool HasDeferredStop() const { return m_deferred_stop; }
+  void ReportDeferredStop();
 
 protected:
   NativeThreadWindows *GetThreadByID(lldb::tid_t thread_id);
@@ -167,6 +169,9 @@ class NativeProcessWindows : public NativeProcessProtocol,
   NativeProcessWindows(lldb::pid_t pid, int terminal_fd,
                        NativeDelegate &delegate, llvm::Error &E);
 
+  /// Stop the process without notifying the delegate yet.
+  void DeferStop();
+
   /// A breakpoint trap whose debug event was only delivered after the client
   /// removed that breakpoint: the thread executed the trap before the stop
   /// that removed it, but its event was held back until it ran again. Moves
@@ -196,6 +201,14 @@ class NativeProcessWindows : public NativeProcessProtocol,
   /// Set when Halt() / Interrupt() schedules a DebugBreakProcess injection.
   bool m_pending_halt = false;
 
+  /// Set by DeferStop(), cleared once the stop is reported or the process
+  /// exits.
+  bool m_deferred_stop = false;
+
+  /// The thread whose debug event started the deferred stop. Reported as the
+  /// stop's thread even if later events made another thread current.
+  lldb::tid_t m_deferred_stop_tid = LLDB_INVALID_THREAD_ID;
+
   bool m_client_supports_libraries_read = false;
 
   /// PseudoConsole for the lldb-server stdio-forwarding path.
@@ -264,6 +277,10 @@ class NativeDebugDelegate : public IDebugDelegate {
     return m_process.OnDebuggerError(error, type);
   }
 
+  bool HasDeferredStop() override { return m_process.HasDeferredStop(); }
+
+  void ReportDeferredStop() override { m_process.ReportDeferredStop(); }
+
 private:
   NativeProcessWindows &m_process;
 };

>From b5599c7d8bdc58f032f31cd055f5c30517daf4f0 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 13 Oct 2026 14:16:12 +0100
Subject: [PATCH 2/2] [lldb][Windows] Report a single step whose trap is still
 queued at a stop

---
 .../Windows/Common/NativeProcessWindows.cpp   | 27 +++++++++++++++++--
 .../Windows/Common/NativeThreadWindows.cpp    | 17 +++++++++++-
 .../Windows/Common/NativeThreadWindows.h      | 13 ++++++++-
 .../backtrace/TestThreadBacktraceRepeat.py    |  2 --
 .../hello_watchlocation/TestWatchLocation.py  |  1 -
 .../thread/num_threads/TestNumThreads.py      |  1 -
 .../thread/step_out/TestThreadStepOut.py      | 13 ++++-----
 .../watchlocation/TestSetWatchlocation.py     |  1 -
 .../stopped-events/TestDAP_stopped_events.py  |  8 +++++-
 .../TestGdbRemoteThreadsInStopReply.py        |  1 -
 10 files changed, 67 insertions(+), 17 deletions(-)

diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
index 6885ca024b06a7..03bc2d2f5ccefb 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
@@ -604,6 +604,16 @@ void NativeProcessWindows::ReportDeferredStop() {
   if (!m_deferred_stop)
     return;
   m_deferred_stop = false;
+  for (const auto &t : m_threads) {
+    auto *thread = static_cast<NativeThreadWindows *>(t.get());
+    if (thread->CompleteStepWithoutTrap()) {
+      LLDB_LOG(GetLog(WindowsLog::Exception),
+               "thread {0:x} completed its single step but its trap is not in "
+               "yet, reporting the step",
+               thread->GetID());
+      SetStopReasonForThread(*thread, StopReason::eStopReasonTrace);
+    }
+  }
   if (GetThreadByID(m_deferred_stop_tid))
     SetCurrentThreadID(m_deferred_stop_tid);
   SynchronouslyNotifyProcessStateChanged(eStateStopped);
@@ -639,10 +649,13 @@ bool NativeProcessWindows::RewindTrapOfRemovedBreakpoint(
 
 ExceptionResult
 NativeProcessWindows::HandleSingleStepException(const ExceptionRecord &record) {
+  NativeThreadWindows *stepped_thread = GetThreadByID(record.GetThreadID());
+  const bool step_reported =
+      stepped_thread && stepped_thread->TakePendingStepTrap();
   uint32_t wp_id = LLDB_INVALID_INDEX32;
 #ifndef __aarch64__
   Log *log = GetLog(WindowsLog::Exception);
-  if (NativeThreadWindows *thread = GetThreadByID(record.GetThreadID())) {
+  if (NativeThreadWindows *thread = stepped_thread) {
     NativeRegisterContextWindows &reg_ctx = thread->GetRegisterContext();
     Status error =
         reg_ctx.GetWatchpointHitIndex(wp_id, record.GetExceptionAddress());
@@ -661,7 +674,17 @@ NativeProcessWindows::HandleSingleStepException(const ExceptionRecord &record) {
   }
 #endif
   if (wp_id == LLDB_INVALID_INDEX32) {
-    NativeThreadWindows *thread = GetThreadByID(record.GetThreadID());
+    NativeThreadWindows *thread = stepped_thread;
+    if (step_reported) {
+      LLDB_LOG(GetLog(WindowsLog::Exception),
+               "dropping the trap of the single step thread {0:x} already "
+               "reported",
+               record.GetThreadID());
+      // Re-arm the current step in case this trap consumed its trace flag.
+      if (thread->IsSingleStepping())
+        thread->SetSingleStepFlag();
+      return ExceptionResult::MaskException;
+    }
     if (thread && !thread->IsSingleStepping()) {
       LLDB_LOG(GetLog(WindowsLog::Exception),
                "ignoring a late single-step trap on thread {0:x}, which this "
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp
index eeba7d186e34ba..84e25584684f3e 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.cpp
@@ -51,8 +51,11 @@ Status NativeThreadWindows::DoResume(lldb::StateType resume_state) {
   if (resume_state == current_state)
     return Status();
 
-  if (resume_state == eStateStepping)
+  lldb::addr_t step_start_pc = LLDB_INVALID_ADDRESS;
+  if (resume_state == eStateStepping) {
+    step_start_pc = GetRegisterContext().GetPC(LLDB_INVALID_ADDRESS);
     SetSingleStepFlag();
+  }
 
   if (resume_state == eStateStepping || resume_state == eStateRunning) {
     // Clear any stop info left over from a previous stop.
@@ -73,6 +76,7 @@ Status NativeThreadWindows::DoResume(lldb::StateType resume_state) {
     } while (previous_suspend_count > 1);
     m_state = eStateRunning;
     m_single_stepping = resume_state == eStateStepping;
+    m_step_start_pc = step_start_pc;
   }
 
   return Status();
@@ -104,6 +108,17 @@ void NativeThreadWindows::SetSingleStepFlag() {
   GetRegisterContext().WriteRegisterFromUnsigned(flags_index, flags_value);
 }
 
+bool NativeThreadWindows::CompleteStepWithoutTrap() {
+  lldb::addr_t start_pc = std::exchange(m_step_start_pc, LLDB_INVALID_ADDRESS);
+  if (start_pc == LLDB_INVALID_ADDRESS ||
+      (m_stop_info.reason != eStopReasonInvalid &&
+       m_stop_info.reason != eStopReasonNone) ||
+      GetRegisterContext().GetPC(start_pc) == start_pc)
+    return false;
+  m_step_trap_pending = true;
+  return true;
+}
+
 std::string NativeThreadWindows::GetName() {
   Log *log = GetLog(LLDBLog::Thread);
   static LazyImport<HRESULT(WINAPI *)(HANDLE, PWSTR *)>
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.h b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.h
index c594ae03637414..916a7e04e2ce8c 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeThreadWindows.h
@@ -28,11 +28,20 @@ class NativeThreadWindows : public NativeThreadProtocol {
   Status DoStop();
   Status DoResume(lldb::StateType resume_state);
 
-  /// Whether the current resume single-steps this thread.
+  /// Whether the last DoResume() single-stepped this thread.
   bool IsSingleStepping() const { return m_single_stepping; }
 
+  /// Called when a stop is reported. Returns true if the single step of the
+  /// last DoResume() moved the PC but its trap was not handled: the kernel
+  /// holds that trap until the thread runs again.
+  bool CompleteStepWithoutTrap();
+
   void ClearSingleStepping() { m_single_stepping = false; }
 
+  bool TakePendingStepTrap() {
+    return std::exchange(m_step_trap_pending, false);
+  }
+
   void SetSingleStepFlag();
 
   std::string GetName() override;
@@ -65,6 +74,8 @@ class NativeThreadWindows : public NativeThreadProtocol {
   lldb::StateType m_state = lldb::StateType::eStateInvalid;
   std::string m_name;
   bool m_single_stepping = false;
+  lldb::addr_t m_step_start_pc = LLDB_INVALID_ADDRESS;
+  bool m_step_trap_pending = false;
   std::unique_ptr<NativeRegisterContextWindows> m_reg_context_up;
   // Cache address and index of the watchpoints and hardware breakpoints since
   // the register context does not.
diff --git a/lldb/test/API/commands/thread/backtrace/TestThreadBacktraceRepeat.py b/lldb/test/API/commands/thread/backtrace/TestThreadBacktraceRepeat.py
index 50ed9dfe1b31c9..7eb1f116a772be 100644
--- a/lldb/test/API/commands/thread/backtrace/TestThreadBacktraceRepeat.py
+++ b/lldb/test/API/commands/thread/backtrace/TestThreadBacktraceRepeat.py
@@ -128,8 +128,6 @@ def check_two_threads(
     # the comments whether it was getting two threads to the same breakpoint that was
     # problematic, or the step-out part.  This test stops at the rendevous point so I'm
     # removing the skipIfLinux to see if we see any flakiness in just this part of the test.
-    @skipIfWindows  # This test will hang on windows llvm.org/pr21753
-    @expectedFailureAll(oslist=["windows"])
     @expectedFailureNetBSD
     def test_thread_backtrace_two_threads(self):
         """Test that repeat works even when backtracing on more than one thread."""
diff --git a/lldb/test/API/commands/watchpoints/hello_watchlocation/TestWatchLocation.py b/lldb/test/API/commands/watchpoints/hello_watchlocation/TestWatchLocation.py
index bce56ce6e79e45..8986fd6fa88d28 100644
--- a/lldb/test/API/commands/watchpoints/hello_watchlocation/TestWatchLocation.py
+++ b/lldb/test/API/commands/watchpoints/hello_watchlocation/TestWatchLocation.py
@@ -43,7 +43,6 @@ def affected_by_radar_93863107(self):
     @expectedFailureAll(triple=re.compile("^mips"))
     # SystemZ and PowerPC also currently supports only one H/W watchpoint
     @expectedFailureAll(archs=["powerpc64le", "s390x"])
-    @skipIfWindows  # This test is flaky on Windows
     def test_hello_watchlocation(self):
         """Test watching a location with '-s size' option."""
         self.build(dictionary=self.d)
diff --git a/lldb/test/API/functionalities/thread/num_threads/TestNumThreads.py b/lldb/test/API/functionalities/thread/num_threads/TestNumThreads.py
index 93ca5c88fa35b1..2787c751693e47 100644
--- a/lldb/test/API/functionalities/thread/num_threads/TestNumThreads.py
+++ b/lldb/test/API/functionalities/thread/num_threads/TestNumThreads.py
@@ -71,7 +71,6 @@ def test_number_of_threads(self):
         )
 
     @skipIfDarwin  # rdar://33462362
-    @skipIfWindows  # This is flakey on Windows: llvm.org/pr37658, llvm.org/pr38373
     def test_unique_stacks(self):
         """Test backtrace unique with multiple threads executing the same stack."""
         self.build()
diff --git a/lldb/test/API/functionalities/thread/step_out/TestThreadStepOut.py b/lldb/test/API/functionalities/thread/step_out/TestThreadStepOut.py
index a7e7b43a398c5e..3f6f62ba742d58 100644
--- a/lldb/test/API/functionalities/thread/step_out/TestThreadStepOut.py
+++ b/lldb/test/API/functionalities/thread/step_out/TestThreadStepOut.py
@@ -11,16 +11,17 @@
 
 @requireThreadSupport
 class ThreadStepOutTestCase(TestBase):
-    @skipIfWindows  # This test will hang on windows llvm.org/pr21753
-    @expectedFailureAll(oslist=["windows"])
     @expectedFailureNetBSD
     def test_step_single_thread(self):
         """Test thread step out on one thread via command interpreter."""
         self.build()
         self.step_out_test(self.step_out_single_thread_with_cmd)
 
-    @skipIfWindows  # This test will hang on windows llvm.org/pr21753
-    @expectedFailureAll(oslist=["windows"])
+    # The in-process Windows plugin (ProcessWindows) reports one thread's
+    # exception per stop. When both threads trap together, the other thread's
+    # event only arrives after the next resume, so the step-out ends on that
+    # thread's trace or with no stop reason, differently from run to run.
+    @skipIfWindowsAndNoLLDBServer
     @expectedFailureAll(
         oslist=["watchos"], archs=["armv7k"], bugnumber="rdar://problem/34674488"
     )  # stop reason is trace when it should be step-out
@@ -30,8 +31,8 @@ def test_step_all_threads(self):
         self.build()
         self.step_out_test(self.step_out_all_threads_with_cmd)
 
-    @skipIfWindows  # This test will hang on windows llvm.org/pr21753
-    @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr24681")
+    # Same in-process Windows limitation as test_step_all_threads.
+    @skipIfWindowsAndNoLLDBServer
     @expectedFailureNetBSD
     def test_python(self):
         """Test thread step out on one thread via Python API (dwarf)."""
diff --git a/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py b/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py
index ce8e06c5dacf48..fc1061060a47fb 100644
--- a/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py
+++ b/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py
@@ -22,7 +22,6 @@ def setUp(self):
         # This is for verifying that watch location works.
         self.violating_func = "do_bad_thing_with_location"
 
-    @skipIfWindows  # This test is flaky on Windows
     def test_watch_location(self):
         """Exercise SBValue.WatchPointee() API to set a watchpoint."""
         self.build()
diff --git a/lldb/test/API/tools/lldb-dap/stopped-events/TestDAP_stopped_events.py b/lldb/test/API/tools/lldb-dap/stopped-events/TestDAP_stopped_events.py
index a1fd2347b82289..8e4a7386f259e9 100644
--- a/lldb/test/API/tools/lldb-dap/stopped-events/TestDAP_stopped_events.py
+++ b/lldb/test/API/tools/lldb-dap/stopped-events/TestDAP_stopped_events.py
@@ -10,7 +10,13 @@
 from lldbsuite.test.tools.lldb_dap.types import LaunchArgs, StoppedEvent, ThreadsArgs
 
 
- at skipIfWindows  # This is flakey on Windows: llvm.org/pr24668, llvm.org/pr38373
+# Needs both threads' breakpoint hits in one stop.
+# The in-process Windows plugin (ProcessWindows) reports one thread's exception
+# per stop.
+# lldb-server puts every hit that is already queued into the one stop, but
+# on a loaded machine the second thread can reach the trap only after the stop
+# was reported, so it still flakes there.
+ at skipIfWindows
 @skipIfLinux
 @requireThreadSupport
 class TestDAP_stopped_events(DAPTestCaseBase):
diff --git a/lldb/test/API/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py b/lldb/test/API/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py
index dc0c62bd375028..8578c191d0d48e 100644
--- a/lldb/test/API/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py
+++ b/lldb/test/API/tools/lldb-server/TestGdbRemoteThreadsInStopReply.py
@@ -254,7 +254,6 @@ def classify(thread_info):
         self.assertTrue(saw_stopped, "no jThreadsInfo entry for the stopped thread")
 
     @skipIfNetBSD
-    @skipIfWindows  # Flaky on Windows
     def test_stop_reply_contains_thread_pcs(self):
         self.build()
         self.set_inferior_startup_launch()



More information about the lldb-commits mailing list