[llvm-branch-commits] [lldb] c4f2f25 - [NFC][lldb][Windows] extract IOHandlerProcessSTDIOWindows (#201651)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Jun 5 09:27:29 PDT 2026


Author: Charles Zablit
Date: 2026-06-05T17:10:25+01:00
New Revision: c4f2f2535438cbdf23d9f84689ea0f518ffa4a95

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

LOG: [NFC][lldb][Windows] extract IOHandlerProcessSTDIOWindows (#201651)

Co-authored-by: Nerixyz <nero.9 at hotmail.de>

Added: 
    lldb/source/Plugins/Process/Windows/Common/IOHandlerProcessSTDIOWindows.cpp
    lldb/source/Plugins/Process/Windows/Common/IOHandlerProcessSTDIOWindows.h

Modified: 
    lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt
    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/CMakeLists.txt b/lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt
index 9854b79fbb8d6..bd076d5d64df1 100644
--- a/lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt
+++ b/lldb/source/Plugins/Process/Windows/Common/CMakeLists.txt
@@ -1,5 +1,6 @@
 
 add_lldb_library(lldbPluginProcessWindowsCommon PLUGIN
+  IOHandlerProcessSTDIOWindows.cpp
   DebuggerThread.cpp
   LocalDebugDelegate.cpp
   NativeProcessWindows.cpp

diff  --git a/lldb/source/Plugins/Process/Windows/Common/IOHandlerProcessSTDIOWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/IOHandlerProcessSTDIOWindows.cpp
new file mode 100644
index 0000000000000..2b9ab9ce0812b
--- /dev/null
+++ b/lldb/source/Plugins/Process/Windows/Common/IOHandlerProcessSTDIOWindows.cpp
@@ -0,0 +1,172 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "IOHandlerProcessSTDIOWindows.h"
+
+#include "lldb/Host/windows/windows.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/State.h"
+
+using namespace lldb_private;
+
+IOHandlerProcessSTDIOWindows::IOHandlerProcessSTDIOWindows(Process *process)
+    : IOHandler(process->GetTarget().GetDebugger(), IOHandler::Type::ProcessIO),
+      m_process(process),
+      m_read_file(GetInputFD(), File::eOpenOptionReadOnly, false),
+      m_interrupt_event(
+          CreateEvent(/*lpEventAttributes=*/nullptr, /*bManualReset=*/FALSE,
+                      /*bInitialState=*/FALSE, /*lpName=*/nullptr)) {}
+
+IOHandlerProcessSTDIOWindows::~IOHandlerProcessSTDIOWindows() {
+  if (m_interrupt_event != INVALID_HANDLE_VALUE)
+    ::CloseHandle(m_interrupt_event);
+}
+
+void IOHandlerProcessSTDIOWindows::SetIsRunning(bool running) {
+  std::lock_guard<std::mutex> guard(m_mutex);
+  SetIsDone(!running);
+  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 hStdin
+///     The handle to the standard input's pipe.
+///
+/// \return
+///     true if the pipe has text input.
+llvm::Expected<bool>
+IOHandlerProcessSTDIOWindows::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, nullptr, 0, nullptr, &bytesAvailable, nullptr)) {
+    if (bytesAvailable > 0)
+      return true;
+  }
+
+  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 IOHandlerProcessSTDIOWindows::Run() {
+  if (!m_read_file.IsValid()) {
+    SetIsDone(true);
+    return;
+  }
+
+  SetIsDone(false);
+  SetIsRunning(true);
+
+  HANDLE hStdin = m_read_file.GetWaitableHandle();
+  HANDLE waitHandles[2] = {hStdin, m_interrupt_event};
+
+  DWORD consoleMode;
+  bool isConsole = GetConsoleMode(hStdin, &consoleMode) != 0;
+  // With ENABLE_LINE_INPUT, ReadFile returns only when a carriage return is
+  // read. This will block lldb in ReadFile until the user hits enter. Save
+  // the previous console mode to restore it later and remove
+  // ENABLE_LINE_INPUT.
+  DWORD oldConsoleMode = consoleMode;
+  SetConsoleMode(hStdin, consoleMode & ~ENABLE_LINE_INPUT & ~ENABLE_ECHO_INPUT);
+
+  while (true) {
+    {
+      std::lock_guard<std::mutex> guard(m_mutex);
+      if (GetIsDone())
+        goto exit_loop;
+    }
+
+    DWORD result = WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE);
+    switch (result) {
+    case WAIT_FAILED:
+      goto exit_loop;
+    case WAIT_OBJECT_0: {
+      if (isConsole) {
+        auto hasInputOrErr = ConsoleHasTextInput(hStdin);
+        if (!hasInputOrErr) {
+          Log *log = GetLog(LLDBLog::Process);
+          LLDB_LOG_ERROR(log, hasInputOrErr.takeError(),
+                         "failed to process debuggee's IO: {0}");
+          goto exit_loop;
+        }
+
+        // If no text input is ready, go back to waiting.
+        if (!*hasInputOrErr)
+          continue;
+      }
+
+      char ch = 0;
+      DWORD read = 0;
+      if (!ReadFile(hStdin, &ch, 1, &read, nullptr) || read != 1)
+        goto exit_loop;
+
+      Status err;
+      m_process->PutSTDIN(&ch, 1, err);
+      if (err.Fail())
+        goto exit_loop;
+      break;
+    }
+    case WAIT_OBJECT_0 + 1: {
+      ControlOp op = m_pending_op.exchange(eControlOpNone);
+      if (op == eControlOpQuit)
+        goto exit_loop;
+      if (op == eControlOpInterrupt &&
+          StateIsRunningState(m_process->GetState()))
+        m_process->SendAsyncInterrupt();
+      break;
+    }
+    default:
+      goto exit_loop;
+    }
+  }
+
+exit_loop:;
+  SetIsRunning(false);
+  SetIsDone(true);
+  SetConsoleMode(hStdin, oldConsoleMode);
+}
+
+void IOHandlerProcessSTDIOWindows::Cancel() {
+  std::lock_guard<std::mutex> guard(m_mutex);
+  SetIsDone(true);
+  if (m_is_running) {
+    m_pending_op.store(eControlOpQuit);
+    ::SetEvent(m_interrupt_event);
+  }
+}
+
+bool IOHandlerProcessSTDIOWindows::Interrupt() {
+  if (m_active) {
+    m_pending_op.store(eControlOpInterrupt);
+    ::SetEvent(m_interrupt_event);
+    return true;
+  }
+  if (StateIsRunningState(m_process->GetState())) {
+    m_process->SendAsyncInterrupt();
+    return true;
+  }
+  return false;
+}

diff  --git a/lldb/source/Plugins/Process/Windows/Common/IOHandlerProcessSTDIOWindows.h b/lldb/source/Plugins/Process/Windows/Common/IOHandlerProcessSTDIOWindows.h
new file mode 100644
index 0000000000000..79a414eeda12f
--- /dev/null
+++ b/lldb/source/Plugins/Process/Windows/Common/IOHandlerProcessSTDIOWindows.h
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LIBLLDB_PLUGINS_PROCESS_WINDOWS_COMMON_IO_HANDLER_PROCESS_STDIO_WINDOWS_H_
+#define LIBLLDB_PLUGINS_PROCESS_WINDOWS_COMMON_IO_HANDLER_PROCESS_STDIO_WINDOWS_H_
+
+#include "lldb/Core/IOHandler.h"
+#include "lldb/Host/File.h"
+#include "lldb/Target/Process.h"
+
+using HANDLE = void *;
+
+using namespace lldb_private;
+
+class IOHandlerProcessSTDIOWindows : public IOHandler {
+public:
+  IOHandlerProcessSTDIOWindows(Process *process);
+
+  ~IOHandlerProcessSTDIOWindows() override;
+
+  void SetIsRunning(bool running);
+
+  /// Peek the console for input. If it has any, drain the pipe until text input
+  /// is found or the pipe is empty.
+  ///
+  /// \param hStdin
+  ///     The handle to the standard input's pipe.
+  ///
+  /// \return
+  ///     true if the pipe has text input.
+  llvm::Expected<bool> ConsoleHasTextInput(const HANDLE hStdin);
+
+  void Run() override;
+
+  void Cancel() override;
+
+  bool Interrupt() override;
+
+  void GotEOF() override {}
+
+private:
+  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;
+  HANDLE m_interrupt_event =
+      reinterpret_cast<HANDLE>(static_cast<intptr_t>(-1));
+  std::atomic<ControlOp> m_pending_op{eControlOpNone};
+  std::mutex m_mutex;
+  bool m_is_running = false;
+};
+
+#endif // LIBLLDB_PLUGINS_PROCESS_WINDOWS_COMMON_IO_HANDLER_PROCESS_STDIO_WINDOWS_H_

diff  --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 9a273463792ce..941c25e9ecb77 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -44,6 +44,7 @@
 #include "DebuggerThread.h"
 #include "ExceptionRecord.h"
 #include "ForwardDecl.h"
+#include "IOHandlerProcessSTDIOWindows.h"
 #include "LocalDebugDelegate.h"
 #include "ProcessWindowsLog.h"
 #include "TargetThreadWindows.h"
@@ -1084,184 +1085,15 @@ Status ProcessWindows::DisableWatchpoint(WatchpointSP wp_sp, bool notify) {
   return error;
 }
 
-class IOHandlerProcessSTDIOWindows : public IOHandler {
-public:
-  IOHandlerProcessSTDIOWindows(Process *process, HANDLE conpty_input)
-      : IOHandler(process->GetTarget().GetDebugger(),
-                  IOHandler::Type::ProcessIO),
-        m_process(process),
-        m_read_file(GetInputFD(), File::eOpenOptionReadOnly, false),
-        m_write_file(conpty_input),
-        m_interrupt_event(
-            CreateEvent(/*lpEventAttributes=*/nullptr, /*bManualReset=*/FALSE,
-                        /*bInitialState=*/FALSE, /*lpName=*/nullptr)) {}
-
-  ~IOHandlerProcessSTDIOWindows() override {
-    if (m_interrupt_event != INVALID_HANDLE_VALUE)
-      ::CloseHandle(m_interrupt_event);
+size_t ProcessWindows::PutSTDIN(const char *src, size_t src_len,
+                                Status &error) {
+  if (!m_stdio_communication.IsConnected()) {
+    error = Status::FromErrorString("stdin not connected");
+    return 0;
   }
-
-  void SetIsRunning(bool running) {
-    std::lock_guard<std::mutex> guard(m_mutex);
-    SetIsDone(!running);
-    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 hStdin
-  ///     The handle to the standard input's pipe.
-  ///
-  /// \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, nullptr, 0, nullptr, &bytesAvailable, nullptr)) {
-      if (bytesAvailable > 0)
-        return true;
-    }
-
-    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) {
-      SetIsDone(true);
-      return;
-    }
-
-    SetIsDone(false);
-    SetIsRunning(true);
-
-    HANDLE hStdin = m_read_file.GetWaitableHandle();
-    HANDLE waitHandles[2] = {hStdin, m_interrupt_event};
-
-    DWORD consoleMode;
-    bool isConsole = GetConsoleMode(hStdin, &consoleMode) != 0;
-    // With ENABLE_LINE_INPUT, ReadFile returns only when a carriage return is
-    // read. This will block lldb in ReadFile until the user hits enter. Save
-    // the previous console mode to restore it later and remove
-    // ENABLE_LINE_INPUT.
-    DWORD oldConsoleMode = consoleMode;
-    SetConsoleMode(hStdin,
-                   consoleMode & ~ENABLE_LINE_INPUT & ~ENABLE_ECHO_INPUT);
-
-    while (true) {
-      {
-        std::lock_guard<std::mutex> guard(m_mutex);
-        if (GetIsDone())
-          goto exit_loop;
-      }
-
-      DWORD result = WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE);
-      switch (result) {
-      case WAIT_FAILED:
-        goto exit_loop;
-      case WAIT_OBJECT_0: {
-        if (isConsole) {
-          auto hasInputOrErr = ConsoleHasTextInput(hStdin);
-          if (!hasInputOrErr) {
-            Log *log = GetLog(WindowsLog::Process);
-            LLDB_LOG_ERROR(log, hasInputOrErr.takeError(),
-                           "failed to process debuggee's IO: {0}");
-            goto exit_loop;
-          }
-
-          // If no text input is ready, go back to waiting.
-          if (!*hasInputOrErr)
-            continue;
-        }
-
-        char ch = 0;
-        DWORD read = 0;
-        if (!ReadFile(hStdin, &ch, 1, &read, nullptr) || read != 1)
-          goto exit_loop;
-
-        DWORD written = 0;
-        if (!WriteFile(m_write_file, &ch, 1, &written, nullptr) || written != 1)
-          goto exit_loop;
-        break;
-      }
-      case WAIT_OBJECT_0 + 1: {
-        ControlOp op = m_pending_op.exchange(eControlOpNone);
-        if (op == eControlOpQuit)
-          goto exit_loop;
-        if (op == eControlOpInterrupt &&
-            StateIsRunningState(m_process->GetState()))
-          m_process->SendAsyncInterrupt();
-        break;
-      }
-      default:
-        goto exit_loop;
-      }
-    }
-
-  exit_loop:;
-    SetIsRunning(false);
-    SetIsDone(true);
-    SetConsoleMode(hStdin, oldConsoleMode);
-  }
-
-  void Cancel() override {
-    std::lock_guard<std::mutex> guard(m_mutex);
-    SetIsDone(true);
-    if (m_is_running) {
-      m_pending_op.store(eControlOpQuit);
-      ::SetEvent(m_interrupt_event);
-    }
-  }
-
-  bool Interrupt() override {
-    if (m_active) {
-      m_pending_op.store(eControlOpInterrupt);
-      ::SetEvent(m_interrupt_event);
-      return true;
-    }
-    if (StateIsRunningState(m_process->GetState())) {
-      m_process->SendAsyncInterrupt();
-      return true;
-    }
-    return false;
-  }
-
-  void GotEOF() override {}
-
-private:
-  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;
-};
+  ConnectionStatus status;
+  return m_stdio_communication.WriteAll(src, src_len, status, &error);
+}
 
 void ProcessWindows::SetPseudoConsoleHandle() {
   if (m_pty == nullptr)
@@ -1277,8 +1109,8 @@ void ProcessWindows::SetPseudoConsoleHandle() {
     {
       std::lock_guard<std::mutex> guard(m_process_input_reader_mutex);
       if (!m_process_input_reader)
-        m_process_input_reader = std::make_shared<IOHandlerProcessSTDIOWindows>(
-            this, m_pty->GetSTDINHandle());
+        m_process_input_reader =
+            std::make_shared<IOHandlerProcessSTDIOWindows>(this);
     }
   }
 }

diff  --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
index da35ae923dcee..677ade4971eed 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
@@ -113,6 +113,8 @@ class ProcessWindows : public Process, public ProcessDebugger {
   /// buffered in the ConPTY/pipe to the process's STDOUT cache.
   void DrainProcessStdout();
 
+  size_t PutSTDIN(const char *src, size_t src_len, Status &error) override;
+
   ProcessWindows(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp);
 
   Status DoGetMemoryRegionInfo(lldb::addr_t vm_addr,


        


More information about the llvm-branch-commits mailing list