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
