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

>From 7f34a5d7be06a9b0e3fc77f144a189bbd01b33a3 Mon Sep 17 00:00:00 2001
From: Charles Zablit <[email protected]>
Date: Thu, 7 May 2026 18:06:19 +0100
Subject: [PATCH 1/2] [lldb][windows] drain the ConPTY on process exit

---
 .../Process/Windows/Common/ProcessWindows.cpp | 59 ++++++++++---------
 .../Process/Windows/Common/ProcessWindows.h   |  4 ++
 2 files changed, 34 insertions(+), 29 deletions(-)

diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp 
b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 28b5554069c90..9dd49a9e7a1ba 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -655,6 +655,7 @@ void ProcessWindows::OnExitProcess(uint32_t exit_code) {
   LLDB_LOG(log, "Process {0} exited with code {1}", GetID(), exit_code);
 
   if (m_pty) {
+    DrainProcessStdout();
     m_pty->SetStopping(true);
     m_pty->Close();
     m_stdio_communication.InterruptRead();
@@ -675,6 +676,32 @@ void ProcessWindows::OnExitProcess(uint32_t exit_code) {
   ProcessDebugger::OnExitProcess(exit_code);
 }
 
+void ProcessWindows::DrainProcessStdout() {
+  if (!m_stdio_communication.ReadThreadIsRunning())
+    return;
+  m_stdio_communication.SynchronizeWithReadThread();
+  if (!m_pty || m_pty->GetMode() != PseudoConsole::Mode::ConPTY)
+    return;
+
+  HANDLE pipe = m_pty->GetSTDOUTHandle();
+  for (int consec_empty = 0; consec_empty < 3;) {
+    if (!m_stdio_communication.ReadThreadIsRunning())
+      break;
+    DWORD avail = 0;
+    // PeekNamedPipe is thread safe.
+    if (!::PeekNamedPipe(pipe, nullptr, 0, nullptr, &avail, nullptr))
+      break;
+    if (avail > 0) {
+      consec_empty = 0;
+      m_stdio_communication.SynchronizeWithReadThread();
+    } else {
+      ++consec_empty;
+      if (consec_empty < 3)
+        ::SleepEx(1, FALSE);
+    }
+  }
+}
+
 void ProcessWindows::OnDebuggerConnected(lldb::addr_t image_base) {
   DebuggerThreadSP debugger = m_session_data->m_debugger;
   Log *log = GetLog(WindowsLog::Process);
@@ -746,36 +773,10 @@ ProcessWindows::OnDebugException(bool first_chance,
   // synchronization the eBroadcastBitStateChanged(Stopped) event can reach
   // the Debugger event thread before the preceding eBroadcastBitSTDOUT
   // events.
-  auto drain_stdout = [this] {
-    if (!m_stdio_communication.ReadThreadIsRunning())
-      return;
-    m_stdio_communication.SynchronizeWithReadThread();
-    if (!m_pty || m_pty->GetMode() != PseudoConsole::Mode::ConPTY)
-      return;
-
-    HANDLE pipe = m_pty->GetSTDOUTHandle();
-    for (int consec_empty = 0; consec_empty < 3;) {
-      if (!m_stdio_communication.ReadThreadIsRunning())
-        break;
-      DWORD avail = 0;
-      // PeekNamedPipe is thread safe.
-      if (!::PeekNamedPipe(pipe, nullptr, 0, nullptr, &avail, nullptr))
-        break;
-      if (avail > 0) {
-        consec_empty = 0;
-        m_stdio_communication.SynchronizeWithReadThread();
-      } else {
-        ++consec_empty;
-        if (consec_empty < 3)
-          ::SleepEx(1, FALSE);
-      }
-    }
-  };
-
   if (!first_chance) {
     // Not any second chance exception is an application crash by definition.
     // It may be an expression evaluation crash.
-    drain_stdout();
+    DrainProcessStdout();
     SetPrivateState(eStateStopped);
   }
 
@@ -796,12 +797,12 @@ ProcessWindows::OnDebugException(bool first_chance,
       LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.",
                record.GetExceptionAddress());
     }
-    drain_stdout();
+    DrainProcessStdout();
     SetPrivateState(eStateStopped);
     break;
   case EXCEPTION_SINGLE_STEP:
     result = ExceptionResult::BreakInDebugger;
-    drain_stdout();
+    DrainProcessStdout();
     SetPrivateState(eStateStopped);
     break;
   default:
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h 
b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
index 228619d0e3d5e..31cf498a16d50 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
@@ -108,6 +108,10 @@ class ProcessWindows : public Process, public 
ProcessDebugger {
   void SetPseudoConsoleHandle() override;
 
 protected:
+  /// Block until the stdio read thread has surfaced everything currently
+  /// buffered in the ConPTY/pipe to the process's STDOUT cache.
+  void DrainProcessStdout();
+
   ProcessWindows(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp);
 
   Status DoGetMemoryRegionInfo(lldb::addr_t vm_addr,

>From 3c07746a7c45367c36202b9b0dc3646bdb17e946 Mon Sep 17 00:00:00 2001
From: Charles Zablit <[email protected]>
Date: Fri, 8 May 2026 08:20:38 +0100
Subject: [PATCH 2/2] fixup! [lldb][windows] drain the ConPTY on process exit

---
 .../Plugins/Process/Windows/Common/ProcessWindows.cpp  | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp 
b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 9dd49a9e7a1ba..eb1c4acc0f2bb 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -768,11 +768,6 @@ ProcessWindows::OnDebugException(bool first_chance,
     return ExceptionResult::SendToApplication;
   }
 
-  // Drain any in-flight process output before announcing the stop. The I/O
-  // reader thread and this debug-event thread run concurrently. Without
-  // synchronization the eBroadcastBitStateChanged(Stopped) event can reach
-  // the Debugger event thread before the preceding eBroadcastBitSTDOUT
-  // events.
   if (!first_chance) {
     // Not any second chance exception is an application crash by definition.
     // It may be an expression evaluation crash.
@@ -797,6 +792,11 @@ ProcessWindows::OnDebugException(bool first_chance,
       LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.",
                record.GetExceptionAddress());
     }
+    // Drain any in-flight process output before announcing the stop. The I/O
+    // reader thread and this debug-event thread run concurrently. Without
+    // synchronization the eBroadcastBitStateChanged(Stopped) event can reach
+    // the Debugger event thread before the preceding eBroadcastBitSTDOUT
+    // events.
     DrainProcessStdout();
     SetPrivateState(eStateStopped);
     break;

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to