Author: Charles Zablit
Date: 2026-05-08T16:40:53+01:00
New Revision: b42a2131f2110bf09fc544536af2918c4a6f2429

URL: 
https://github.com/llvm/llvm-project/commit/b42a2131f2110bf09fc544536af2918c4a6f2429
DIFF: 
https://github.com/llvm/llvm-project/commit/b42a2131f2110bf09fc544536af2918c4a6f2429.diff

LOG: [lldb][windows] drain the ConPTY on process exit (#196371)

Added: 
    

Modified: 
    lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
    lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp 
b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 28b5554069c90..eb1c4acc0f2bb 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);
@@ -741,41 +768,10 @@ 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.
-  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 +792,17 @@ ProcessWindows::OnDebugException(bool first_chance,
       LLDB_LOG(log, "Hit non-loader breakpoint at address {0:x}.",
                record.GetExceptionAddress());
     }
-    drain_stdout();
+    // 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;
   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,


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

Reply via email to