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
