https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/175812
>From dc27faa0750f8a9a77dc4a0edd387da61ef15d29 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Tue, 13 Jan 2026 18:54:43 +0000 Subject: [PATCH 1/9] [lldb][windows] prevent IOHandlerProcessSTDIOWindows from consuming non text inputs --- .../Process/Windows/Common/ProcessWindows.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index 226cc147aadae..e135628d9009b 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -984,6 +984,9 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { HANDLE hInterrupt = (HANDLE)_get_osfhandle(m_pipe.GetReadFileDescriptor()); HANDLE waitHandles[2] = {hStdin, hInterrupt}; + DWORD consoleMode; + bool isConsole = GetConsoleMode(hStdin, &consoleMode) != 0; + while (true) { { std::lock_guard<std::mutex> guard(m_mutex); @@ -996,6 +999,20 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { case WAIT_FAILED: goto exit_loop; case WAIT_OBJECT_0: { + if (isConsole) { + INPUT_RECORD inputRecord; + DWORD numRead = 0; + if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead) || + numRead == 0) + goto exit_loop; + // We only care about text input. Ignore all non text input events and + // let other IOHandlers handle them. + if (inputRecord.EventType != KEY_EVENT || + !inputRecord.Event.KeyEvent.bKeyDown || + inputRecord.Event.KeyEvent.uChar.AsciiChar == 0) + break; + } + char ch = 0; DWORD read = 0; if (!ReadFile(hStdin, &ch, 1, &read, nullptr) || read != 1) >From 319724127263f589eedded0c9acbd9ccbd6c63f8 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Mon, 19 Jan 2026 13:50:09 +0000 Subject: [PATCH 2/9] consume non text input --- .../Process/Windows/Common/ProcessWindows.cpp | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index e135628d9009b..71dbb3d964804 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -1000,17 +1000,23 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { goto exit_loop; case WAIT_OBJECT_0: { if (isConsole) { - INPUT_RECORD inputRecord; - DWORD numRead = 0; - if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead) || - numRead == 0) - goto exit_loop; - // We only care about text input. Ignore all non text input events and - // let other IOHandlers handle them. - if (inputRecord.EventType != KEY_EVENT || - !inputRecord.Event.KeyEvent.bKeyDown || - inputRecord.Event.KeyEvent.uChar.AsciiChar == 0) - break; + while (true) { + INPUT_RECORD inputRecord; + DWORD numRead = 0; + if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead) || + numRead == 0) + goto exit_loop; + + // We only care about text input. Consume all non text input events before letting ReadFile handle text input. + if (inputRecord.EventType == KEY_EVENT && + inputRecord.Event.KeyEvent.bKeyDown && + inputRecord.Event.KeyEvent.uChar.AsciiChar != 0) + break; + + if (!ReadConsoleInput(hStdin, &inputRecord, 1, &numRead) || + numRead == 0) + goto exit_loop; + } } char ch = 0; >From 01d9cebd8c3ab18553ecf71c86bb85fbb7bf78dd Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Mon, 19 Jan 2026 13:57:50 +0000 Subject: [PATCH 3/9] fix formatting --- lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index 71dbb3d964804..0cdb51cc5a086 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -1007,7 +1007,8 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { numRead == 0) goto exit_loop; - // We only care about text input. Consume all non text input events before letting ReadFile handle text input. + // We only care about text input. Consume all non text input events + // before letting ReadFile handle text input. if (inputRecord.EventType == KEY_EVENT && inputRecord.Event.KeyEvent.bKeyDown && inputRecord.Event.KeyEvent.uChar.AsciiChar != 0) >From 071b55b80df6d0976b58cd384db56e39dd6d8171 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Mon, 19 Jan 2026 15:50:09 +0000 Subject: [PATCH 4/9] fix control flow --- lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index 0cdb51cc5a086..0d68240c2749d 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -1003,8 +1003,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { while (true) { INPUT_RECORD inputRecord; DWORD numRead = 0; - if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead) || - numRead == 0) + if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead)) goto exit_loop; // We only care about text input. Consume all non text input events >From 33cb556acde1c6710cf7a2b421c2aa2ac7110d61 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Mon, 19 Jan 2026 17:32:34 +0000 Subject: [PATCH 5/9] fix control flow --- .../Plugins/Process/Windows/Common/ProcessWindows.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index 0d68240c2749d..a4b46e65617eb 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -988,6 +988,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { bool isConsole = GetConsoleMode(hStdin, &consoleMode) != 0; while (true) { + read_loop:; { std::lock_guard<std::mutex> guard(m_mutex); if (GetIsDone()) @@ -1006,6 +1007,9 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead)) goto exit_loop; + if (numRead == 0) + goto read_loop; + // We only care about text input. Consume all non text input events // before letting ReadFile handle text input. if (inputRecord.EventType == KEY_EVENT && @@ -1013,8 +1017,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { inputRecord.Event.KeyEvent.uChar.AsciiChar != 0) break; - if (!ReadConsoleInput(hStdin, &inputRecord, 1, &numRead) || - numRead == 0) + if (!ReadConsoleInput(hStdin, &inputRecord, 1, &numRead)) goto exit_loop; } } @@ -1048,7 +1051,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { } exit_loop:; - SetIsRunning(false); + SetIsRunning(false); } void Cancel() override { >From 84959c70fa48e4172e9d1574540e73ecc493de82 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Tue, 20 Jan 2026 11:15:27 +0000 Subject: [PATCH 6/9] add comment --- lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index a4b46e65617eb..940c953089166 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -1007,6 +1007,8 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead)) goto exit_loop; + // If the pipe is empty, go back to waiting on + // WaitForMultipleObjects rather than ReadFile. if (numRead == 0) goto read_loop; @@ -1051,7 +1053,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { } exit_loop:; - SetIsRunning(false); + SetIsRunning(false); } void Cancel() override { >From 91e75a5b6c7ec1d6c639ebaf29ec58504257517a Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Tue, 20 Jan 2026 14:33:32 +0000 Subject: [PATCH 7/9] remove secondary pipe --- .../Process/Windows/Common/ProcessWindows.cpp | 109 ++++++++++-------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index 940c953089166..d16e84b6d307c 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -959,7 +959,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { m_process(process), m_read_file(GetInputFD(), File::eOpenOptionReadOnly, false), m_write_file(conpty_input) { - m_pipe.CreateNew(); + m_interrupt_event = INVALID_HANDLE_VALUE; } ~IOHandlerProcessSTDIOWindows() override = default; @@ -970,9 +970,36 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { m_is_running = running; } + /// Peek the console for input. If it has any, drain the pipe until text input + /// is found or the pipe is empty. + /// + /// \param[in, out] hStdin + /// The handle to the standard input's pipe. + /// + /// \return + /// true if the pipe has text input. + llvm::Expected<bool> ConsoleHasTextInput(HANDLE hStdin) { + while (true) { + INPUT_RECORD inputRecord; + DWORD numRead = 0; + if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead)) + return llvm::createStringError("Failed to peek standard input."); + + if (numRead == 0) + return false; + + if (inputRecord.EventType == KEY_EVENT && + inputRecord.Event.KeyEvent.bKeyDown && + inputRecord.Event.KeyEvent.uChar.AsciiChar != 0) + return true; + + if (!ReadConsoleInput(hStdin, &inputRecord, 1, &numRead)) + return llvm::createStringError("Failed to read standard input."); + } + } + void Run() override { - if (!m_read_file.IsValid() || m_write_file == INVALID_HANDLE_VALUE || - !m_pipe.CanRead() || !m_pipe.CanWrite()) { + if (!m_read_file.IsValid() || m_write_file == INVALID_HANDLE_VALUE) { SetIsDone(true); return; } @@ -980,15 +1007,13 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { SetIsDone(false); SetIsRunning(true); - HANDLE hStdin = (HANDLE)_get_osfhandle(m_read_file.GetDescriptor()); - HANDLE hInterrupt = (HANDLE)_get_osfhandle(m_pipe.GetReadFileDescriptor()); - HANDLE waitHandles[2] = {hStdin, hInterrupt}; + HANDLE hStdin = m_read_file.GetWaitableHandle(); + HANDLE waitHandles[2] = {hStdin, m_interrupt_event}; DWORD consoleMode; bool isConsole = GetConsoleMode(hStdin, &consoleMode) != 0; while (true) { - read_loop:; { std::lock_guard<std::mutex> guard(m_mutex); if (GetIsDone()) @@ -1001,27 +1026,13 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { goto exit_loop; case WAIT_OBJECT_0: { if (isConsole) { - while (true) { - INPUT_RECORD inputRecord; - DWORD numRead = 0; - if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead)) - goto exit_loop; - - // If the pipe is empty, go back to waiting on - // WaitForMultipleObjects rather than ReadFile. - if (numRead == 0) - goto read_loop; - - // We only care about text input. Consume all non text input events - // before letting ReadFile handle text input. - if (inputRecord.EventType == KEY_EVENT && - inputRecord.Event.KeyEvent.bKeyDown && - inputRecord.Event.KeyEvent.uChar.AsciiChar != 0) - break; - - if (!ReadConsoleInput(hStdin, &inputRecord, 1, &numRead)) - goto exit_loop; - } + auto hasInputOrErr = ConsoleHasTextInput(hStdin); + if (hasInputOrErr.takeError()) + goto exit_loop; + + // If no text input is ready, go back to waiting. + if (!*hasInputOrErr) + continue; } char ch = 0; @@ -1035,14 +1046,10 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { break; } case WAIT_OBJECT_0 + 1: { - char ch = 0; - DWORD read = 0; - if (!ReadFile(hInterrupt, &ch, 1, &read, nullptr) || read != 1) + ControlOp op = m_pending_op.exchange(eControlOpNone); + if (op == eControlOpQuit) goto exit_loop; - - if (ch == eControlOpQuit) - goto exit_loop; - if (ch == eControlOpInterrupt && + if (op == eControlOpInterrupt && StateIsRunningState(m_process->GetState())) m_process->SendAsyncInterrupt(); break; @@ -1060,18 +1067,16 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { std::lock_guard<std::mutex> guard(m_mutex); SetIsDone(true); if (m_is_running) { - char ch = eControlOpQuit; - if (llvm::Error err = m_pipe.Write(&ch, 1).takeError()) { - LLDB_LOG_ERROR(GetLog(LLDBLog::Process), std::move(err), - "Pipe write failed: {0}"); - } + m_pending_op.store(eControlOpQuit); + ::SetEvent(m_interrupt_event); } } bool Interrupt() override { if (m_active) { - char ch = eControlOpInterrupt; - return !errorToBool(m_pipe.Write(&ch, 1).takeError()); + m_pending_op.store(eControlOpInterrupt); + ::SetEvent(m_interrupt_event); + return true; } if (StateIsRunningState(m_process->GetState())) { m_process->SendAsyncInterrupt(); @@ -1083,19 +1088,21 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { void GotEOF() override {} private: - Process *m_process; - NativeFile m_read_file; // Read from this file (usually actual STDIN for LLDB - HANDLE m_write_file = - INVALID_HANDLE_VALUE; // Write to this file (usually the primary pty for - // getting io to debuggee) - Pipe m_pipe; - std::mutex m_mutex; - bool m_is_running = false; - enum ControlOp : char { eControlOpQuit = 'q', eControlOpInterrupt = 'i', + eControlOpNone = 0, }; + + Process *m_process; + /// Read from this file (usually actual STDIN for LLDB) + NativeFile m_read_file; + /// Write to this file (usually the primary pty for getting io to debuggee) + HANDLE m_write_file = INVALID_HANDLE_VALUE; + HANDLE m_interrupt_event = INVALID_HANDLE_VALUE; + std::atomic<ControlOp> m_pending_op{eControlOpNone}; + std::mutex m_mutex; + bool m_is_running = false; }; void ProcessWindows::SetPseudoConsoleHandle( >From 9ddd179d5c6193e666071d3c0faa1b800b925533 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Tue, 20 Jan 2026 14:49:09 +0000 Subject: [PATCH 8/9] edit signature --- .../Plugins/Process/Windows/Common/ProcessWindows.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index d16e84b6d307c..21909fb1b3eba 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -958,9 +958,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { IOHandler::Type::ProcessIO), m_process(process), m_read_file(GetInputFD(), File::eOpenOptionReadOnly, false), - m_write_file(conpty_input) { - m_interrupt_event = INVALID_HANDLE_VALUE; - } + m_write_file(conpty_input), m_interrupt_event(INVALID_HANDLE_VALUE) {} ~IOHandlerProcessSTDIOWindows() override = default; @@ -973,12 +971,12 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { /// Peek the console for input. If it has any, drain the pipe until text input /// is found or the pipe is empty. /// - /// \param[in, out] hStdin + /// \param hStdin /// The handle to the standard input's pipe. /// /// \return /// true if the pipe has text input. - llvm::Expected<bool> ConsoleHasTextInput(HANDLE hStdin) { + llvm::Expected<bool> ConsoleHasTextInput(const HANDLE hStdin) { while (true) { INPUT_RECORD inputRecord; DWORD numRead = 0; >From 7fbc30bae0d4a0951b89bf07d93ed715ac35b78e Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Wed, 21 Jan 2026 15:49:16 +0000 Subject: [PATCH 9/9] fix stdin --- .../Host/windows/ProcessLauncherWindows.cpp | 7 ++--- .../Process/Windows/Common/ProcessWindows.cpp | 29 ++++++++++++++++--- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/lldb/source/Host/windows/ProcessLauncherWindows.cpp b/lldb/source/Host/windows/ProcessLauncherWindows.cpp index 76feceadf46f3..a8b27e6a30528 100644 --- a/lldb/source/Host/windows/ProcessLauncherWindows.cpp +++ b/lldb/source/Host/windows/ProcessLauncherWindows.cpp @@ -116,9 +116,8 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, startupinfoex.StartupInfo.dwFlags |= STARTF_USESTDHANDLES; HPCON hPC = launch_info.GetPTY().GetPseudoTerminalHandle(); - bool use_pty = hPC != INVALID_HANDLE_VALUE && - launch_info.GetNumFileActions() == 0 && - launch_info.GetFlags().Test(lldb::eLaunchFlagLaunchInTTY); + bool use_pty = + hPC != INVALID_HANDLE_VALUE && launch_info.GetNumFileActions() == 0; HANDLE stdin_handle = GetStdioHandle(launch_info, STDIN_FILENO); HANDLE stdout_handle = GetStdioHandle(launch_info, STDOUT_FILENO); @@ -199,7 +198,7 @@ ProcessLauncherWindows::LaunchProcess(const ProcessLaunchInfo &launch_info, BOOL result = ::CreateProcessW( wexecutable.c_str(), pwcommandLine, NULL, NULL, - /*bInheritHandles=*/!inherited_handles.empty(), flags, environment.data(), + /*bInheritHandles=*/TRUE, flags, environment.data(), wworkingDirectory.size() == 0 ? NULL : wworkingDirectory.c_str(), reinterpret_cast<STARTUPINFOW *>(&startupinfoex), &pi); diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp index 21909fb1b3eba..e72ef1da4b620 100644 --- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp @@ -958,9 +958,15 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { IOHandler::Type::ProcessIO), m_process(process), m_read_file(GetInputFD(), File::eOpenOptionReadOnly, false), - m_write_file(conpty_input), m_interrupt_event(INVALID_HANDLE_VALUE) {} - - ~IOHandlerProcessSTDIOWindows() override = default; + m_write_file(conpty_input), + m_interrupt_event( + CreateEvent(/*lpEventAttributes=*/NULL, /*bManualReset=*/FALSE, + /*bInitialState=*/FALSE, /*lpName=*/NULL)) {} + + ~IOHandlerProcessSTDIOWindows() override { + if (m_interrupt_event != INVALID_HANDLE_VALUE) + ::CloseHandle(m_interrupt_event); + } void SetIsRunning(bool running) { std::lock_guard<std::mutex> guard(m_mutex); @@ -977,6 +983,14 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { /// \return /// true if the pipe has text input. llvm::Expected<bool> ConsoleHasTextInput(const HANDLE hStdin) { + // Check if there are already characters buffered. Pressing enter counts as + // 2 characters '\r\n' and only one of them is a keyDown event. + DWORD bytesAvailable = 0; + if (PeekNamedPipe(hStdin, NULL, 0, NULL, &bytesAvailable, NULL)) { + if (bytesAvailable > 0) + return true; + } + while (true) { INPUT_RECORD inputRecord; DWORD numRead = 0; @@ -1010,6 +1024,9 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { DWORD consoleMode; bool isConsole = GetConsoleMode(hStdin, &consoleMode) != 0; + DWORD oldConsoleMode = consoleMode; + SetConsoleMode(hStdin, + consoleMode & ~ENABLE_LINE_INPUT & ~ENABLE_ECHO_INPUT); while (true) { { @@ -1025,8 +1042,10 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { case WAIT_OBJECT_0: { if (isConsole) { auto hasInputOrErr = ConsoleHasTextInput(hStdin); - if (hasInputOrErr.takeError()) + if (!hasInputOrErr) { + llvm::consumeError(hasInputOrErr.takeError()); goto exit_loop; + } // If no text input is ready, go back to waiting. if (!*hasInputOrErr) @@ -1059,6 +1078,8 @@ class IOHandlerProcessSTDIOWindows : public IOHandler { exit_loop:; SetIsRunning(false); + SetIsDone(true); + SetConsoleMode(hStdin, oldConsoleMode); } void Cancel() override { _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
