https://github.com/charles-zablit created 
https://github.com/llvm/llvm-project/pull/174635

None

>From f1065389eeb8e8d5fc2a3fab7c9277c5deac934e Mon Sep 17 00:00:00 2001
From: Charles Zablit <[email protected]>
Date: Tue, 6 Jan 2026 19:22:35 +0000
Subject: [PATCH] [lldb-dap][windows] add integratedTerminal support

---
 lldb/tools/lldb-dap/FifoFiles.cpp             |  86 ++++++++--
 lldb/tools/lldb-dap/FifoFiles.h               |  13 +-
 .../tools/lldb-dap/Handler/RequestHandler.cpp |   4 +-
 lldb/tools/lldb-dap/RunInTerminal.cpp         |  23 ++-
 lldb/tools/lldb-dap/RunInTerminal.h           |   3 +
 lldb/tools/lldb-dap/tool/lldb-dap.cpp         | 149 +++++++++++++++++-
 lldb/unittests/DAP/FifoFilesTest.cpp          |   8 +-
 7 files changed, 264 insertions(+), 22 deletions(-)

diff --git a/lldb/tools/lldb-dap/FifoFiles.cpp 
b/lldb/tools/lldb-dap/FifoFiles.cpp
index 1f1bba80bd3b1..30acb00e4e1da 100644
--- a/lldb/tools/lldb-dap/FifoFiles.cpp
+++ b/lldb/tools/lldb-dap/FifoFiles.cpp
@@ -9,7 +9,11 @@
 #include "FifoFiles.h"
 #include "JSONUtils.h"
 
-#if !defined(_WIN32)
+#ifdef _WIN32
+#include "lldb/Host/windows/PipeWindows.h"
+#include "lldb/Host/windows/windows.h"
+#include "llvm/Support/Path.h"
+#else
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -24,7 +28,11 @@ using namespace llvm;
 
 namespace lldb_dap {
 
-FifoFile::FifoFile(StringRef path) : m_path(path) {}
+FifoFile::FifoFile(StringRef path) : m_path(path) {
+  m_pipe = CreateFileA(m_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                       OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+}
+FifoFile::FifoFile(StringRef path, HANDLE pipe) : m_path(path), m_pipe(pipe) {}
 
 FifoFile::~FifoFile() {
 #if !defined(_WIN32)
@@ -32,9 +40,72 @@ FifoFile::~FifoFile() {
 #endif
 }
 
+void FifoFile::WriteLine(std::string line) {
+#if defined(_WIN32)
+  if (m_pipe == INVALID_HANDLE_VALUE)
+    return;
+  DWORD written;
+  line += "\n";
+  WriteFile(m_pipe, line.c_str(), static_cast<DWORD>(line.size()), &written,
+            NULL);
+  FlushFileBuffers(m_pipe); // Ensure data is sent
+#else
+  std::ofstream writer(m_path, std::ofstream::out);
+  writer << line << std::endl;
+#endif
+}
+
+void FifoFile::Connect() { ConnectNamedPipe(m_pipe, NULL); }
+
+std::string FifoFile::ReadLine() {
+#ifdef _WIN32
+  std::string buffer;
+  if (m_pipe == INVALID_HANDLE_VALUE)
+    return buffer;
+
+  char read_buffer[4096];
+  DWORD bytes_read;
+
+  if (ReadFile(m_pipe, read_buffer, sizeof(read_buffer) - 1, &bytes_read,
+               NULL) &&
+      bytes_read > 0) {
+    read_buffer[bytes_read] = '\0';
+
+    // TODO: Verify that we are not discarding the text after the new line here
+    // (this looks like it). what happens if the read_buffer is too small?
+    for (DWORD i = 0; i < bytes_read; ++i) {
+      if (read_buffer[i] == '\n')
+        break;
+      if (read_buffer[i] != '\r')
+        buffer += read_buffer[i];
+    }
+  }
+
+  return buffer;
+#else
+  std::ifstream reader(m_path, std::ifstream::in);
+  std::string buffer;
+  std::getline(reader, buffer);
+  return buffer;
+#endif
+}
+
 Expected<std::shared_ptr<FifoFile>> CreateFifoFile(StringRef path) {
 #if defined(_WIN32)
-  return createStringError(inconvertibleErrorCode(), "Unimplemented");
+  assert(path.starts_with("\\\\.\\pipe\\") &&
+         "FiFo path should start with '\\\\.\\pipe\\'");
+  HANDLE pipe_handle =
+      CreateNamedPipeA(path.data(), PIPE_ACCESS_DUPLEX,
+                       PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+                       PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, NULL);
+
+  if (pipe_handle == INVALID_HANDLE_VALUE) {
+    DWORD error = GetLastError();
+    return createStringError(std::error_code(error, std::system_category()),
+                             "Couldn't create named pipe: %s", path.data());
+  }
+
+  return std::make_shared<FifoFile>(path, pipe_handle);
 #else
   if (int err = mkfifo(path.data(), 0600))
     return createStringError(std::error_code(err, std::generic_category()),
@@ -43,7 +114,7 @@ Expected<std::shared_ptr<FifoFile>> CreateFifoFile(StringRef 
path) {
 #endif
 }
 
-FifoFileIO::FifoFileIO(StringRef fifo_file, StringRef other_endpoint_name)
+FifoFileIO::FifoFileIO(FifoFile fifo_file, StringRef other_endpoint_name)
     : m_fifo_file(fifo_file), m_other_endpoint_name(other_endpoint_name) {}
 
 Expected<json::Value> FifoFileIO::ReadJSON(std::chrono::milliseconds timeout) {
@@ -52,9 +123,7 @@ Expected<json::Value> 
FifoFileIO::ReadJSON(std::chrono::milliseconds timeout) {
   std::optional<std::string> line;
   std::future<void> *future =
       new std::future<void>(std::async(std::launch::async, [&]() {
-        std::ifstream reader(m_fifo_file, std::ifstream::in);
-        std::string buffer;
-        std::getline(reader, buffer);
+        std::string buffer = m_fifo_file.ReadLine();
         if (!buffer.empty())
           line = buffer;
       }));
@@ -78,8 +147,7 @@ Error FifoFileIO::SendJSON(const json::Value &json,
   bool done = false;
   std::future<void> *future =
       new std::future<void>(std::async(std::launch::async, [&]() {
-        std::ofstream writer(m_fifo_file, std::ofstream::out);
-        writer << JSONToString(json) << std::endl;
+        m_fifo_file.WriteLine(JSONToString(json));
         done = true;
       }));
   if (future->wait_for(timeout) == std::future_status::timeout || !done) {
diff --git a/lldb/tools/lldb-dap/FifoFiles.h b/lldb/tools/lldb-dap/FifoFiles.h
index 633ebeb2aedd4..356877317bb0e 100644
--- a/lldb/tools/lldb-dap/FifoFiles.h
+++ b/lldb/tools/lldb-dap/FifoFiles.h
@@ -9,6 +9,7 @@
 #ifndef LLDB_TOOLS_LLDB_DAP_FIFOFILES_H
 #define LLDB_TOOLS_LLDB_DAP_FIFOFILES_H
 
+#include "lldb/Host/windows/PipeWindows.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
 
@@ -21,10 +22,18 @@ namespace lldb_dap {
 /// The file is destroyed when the destructor is invoked.
 struct FifoFile {
   FifoFile(llvm::StringRef path);
+  FifoFile(llvm::StringRef path, HANDLE pipe);
 
   ~FifoFile();
 
+  void Connect();
+
+  void WriteLine(std::string line);
+
+  std::string ReadLine();
+
   std::string m_path;
+  HANDLE m_pipe;
 };
 
 /// Create a fifo file in the filesystem.
@@ -45,7 +54,7 @@ class FifoFileIO {
   /// \param[in] other_endpoint_name
   ///     A human readable name for the other endpoint that will communicate
   ///     using this file. This is used for error messages.
-  FifoFileIO(llvm::StringRef fifo_file, llvm::StringRef other_endpoint_name);
+  FifoFileIO(FifoFile fifo_file, llvm::StringRef other_endpoint_name);
 
   /// Read the next JSON object from the underlying input fifo file.
   ///
@@ -76,7 +85,7 @@ class FifoFileIO {
       std::chrono::milliseconds timeout = std::chrono::milliseconds(20000));
 
 private:
-  std::string m_fifo_file;
+  FifoFile m_fifo_file;
   std::string m_other_endpoint_name;
 };
 
diff --git a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp 
b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
index c60c86219f67d..b0a4e52cbeedb 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -99,7 +99,7 @@ RunInTerminal(DAP &dap, const 
protocol::LaunchRequestArguments &arguments) {
     return comm_file_or_err.takeError();
   FifoFile &comm_file = *comm_file_or_err.get();
 
-  RunInTerminalDebugAdapterCommChannel comm_channel(comm_file.m_path);
+  RunInTerminalDebugAdapterCommChannel comm_channel(comm_file);
 
   lldb::pid_t debugger_pid = LLDB_INVALID_PROCESS_ID;
 #if !defined(_WIN32)
@@ -112,7 +112,7 @@ RunInTerminal(DAP &dap, const 
protocol::LaunchRequestArguments &arguments) {
       arguments.console == protocol::eConsoleExternalTerminal);
   dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal",
                                                     
std::move(reverse_request));
-
+  comm_file.Connect(); // we need to wait for the client to connect to the 
pipe.
   if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid())
     attach_info.SetProcessID(*pid);
   else
diff --git a/lldb/tools/lldb-dap/RunInTerminal.cpp 
b/lldb/tools/lldb-dap/RunInTerminal.cpp
index 9f309dd78221a..3100f261b891f 100644
--- a/lldb/tools/lldb-dap/RunInTerminal.cpp
+++ b/lldb/tools/lldb-dap/RunInTerminal.cpp
@@ -9,7 +9,9 @@
 #include "RunInTerminal.h"
 #include "JSONUtils.h"
 
-#if !defined(_WIN32)
+#ifdef _WIN32
+#include "lldb/Host/windows/windows.h"
+#else
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -97,7 +99,7 @@ static Error ToError(const RunInTerminalMessage &message) {
 
 RunInTerminalLauncherCommChannel::RunInTerminalLauncherCommChannel(
     StringRef comm_file)
-    : m_io(comm_file, "debug adapter") {}
+    : m_io(FifoFileIO(FifoFile(comm_file), "debug adapter")) {}
 
 Error RunInTerminalLauncherCommChannel::WaitUntilDebugAdapterAttaches(
     std::chrono::milliseconds timeout) {
@@ -115,6 +117,10 @@ Error RunInTerminalLauncherCommChannel::NotifyPid() {
   return m_io.SendJSON(RunInTerminalMessagePid(getpid()).ToJSON());
 }
 
+Error RunInTerminalLauncherCommChannel::NotifyPid(int pid) {
+  return m_io.SendJSON(RunInTerminalMessagePid(pid).ToJSON());
+}
+
 void RunInTerminalLauncherCommChannel::NotifyError(StringRef error) {
   if (Error err = m_io.SendJSON(RunInTerminalMessageError(error).ToJSON(),
                                 std::chrono::seconds(2)))
@@ -125,6 +131,10 @@ 
RunInTerminalDebugAdapterCommChannel::RunInTerminalDebugAdapterCommChannel(
     StringRef comm_file)
     : m_io(comm_file, "runInTerminal launcher") {}
 
+RunInTerminalDebugAdapterCommChannel::RunInTerminalDebugAdapterCommChannel(
+    FifoFile comm_file)
+    : m_io(comm_file, "runInTerminal launcher") {}
+
 // Can't use \a std::future<llvm::Error> because it doesn't compile on Windows
 std::future<lldb::SBError>
 RunInTerminalDebugAdapterCommChannel::NotifyDidAttach() {
@@ -139,7 +149,7 @@ RunInTerminalDebugAdapterCommChannel::NotifyDidAttach() {
 
 Expected<lldb::pid_t> RunInTerminalDebugAdapterCommChannel::GetLauncherPid() {
   if (Expected<RunInTerminalMessageUP> message =
-          GetNextMessage(m_io, std::chrono::seconds(20))) {
+          GetNextMessage(m_io, std::chrono::seconds(2000))) {
     if (message.get()->kind == eRunInTerminalMessageKindPID)
       return message.get()->GetAsPidMessage()->pid;
     return ToError(*message.get());
@@ -159,12 +169,19 @@ std::string 
RunInTerminalDebugAdapterCommChannel::GetLauncherError() {
 
 Expected<std::shared_ptr<FifoFile>> CreateRunInTerminalCommFile() {
   SmallString<256> comm_file;
+#if _WIN32
+  char pipe_name[MAX_PATH];
+  sprintf(pipe_name, "\\\\.\\pipe\\lldb-dap-run-in-terminal-comm-%d",
+          GetCurrentProcessId());
+  return CreateFifoFile(pipe_name);
+#else
   if (std::error_code EC = sys::fs::getPotentiallyUniqueTempFileName(
           "lldb-dap-run-in-terminal-comm", "", comm_file))
     return createStringError(EC, "Error making unique file name for "
                                  "runInTerminal communication files");
 
   return CreateFifoFile(comm_file.str());
+#endif
 }
 
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/RunInTerminal.h 
b/lldb/tools/lldb-dap/RunInTerminal.h
index 457850c8ea538..ff727603709d3 100644
--- a/lldb/tools/lldb-dap/RunInTerminal.h
+++ b/lldb/tools/lldb-dap/RunInTerminal.h
@@ -89,6 +89,8 @@ class RunInTerminalLauncherCommChannel {
   ///     out.
   llvm::Error NotifyPid();
 
+  llvm::Error NotifyPid(int pid);
+
   /// Notify the debug adapter that there's been an error.
   void NotifyError(llvm::StringRef error);
 
@@ -99,6 +101,7 @@ class RunInTerminalLauncherCommChannel {
 class RunInTerminalDebugAdapterCommChannel {
 public:
   RunInTerminalDebugAdapterCommChannel(llvm::StringRef comm_file);
+  RunInTerminalDebugAdapterCommChannel(FifoFile comm_file);
 
   /// Notify the runInTerminal launcher that it was attached.
   ///
diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp 
b/lldb/tools/lldb-dap/tool/lldb-dap.cpp
index e3b9d57e7d3a1..ef271ce6979fe 100644
--- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp
@@ -271,9 +271,154 @@ static llvm::Error 
LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg,
                                              lldb::pid_t debugger_pid,
                                              llvm::StringRef stdio,
                                              char *argv[]) {
+  const char *timeout_env_var = getenv("LLDB_DAP_RIT_TIMEOUT_IN_MS");
+  int timeout_in_ms =
+      timeout_env_var != nullptr ? atoi(timeout_env_var) : 20000;
+
 #if defined(_WIN32)
-  return llvm::createStringError(
-      "runInTerminal is only supported on POSIX systems");
+  lldb_private::FileSystem::Initialize();
+
+  RunInTerminalLauncherCommChannel comm_channel(comm_file);
+
+  std::string cmdline;
+  for (int i = 0; argv[i] != nullptr; ++i) {
+    if (i > 0)
+      cmdline += " ";
+
+    std::string arg = argv[i];
+    if (arg.find(' ') != std::string::npos) {
+      cmdline += "\"" + arg + "\"";
+    } else {
+      cmdline += arg;
+    }
+  }
+
+  STARTUPINFOA startup_info = {};
+  startup_info.cb = sizeof(startup_info);
+
+  HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
+  HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
+  HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
+  bool handles_inherited = false;
+
+  if (!stdio.empty()) {
+    llvm::SmallVector<llvm::StringRef, 3> files;
+    stdio.split(files, ':');
+    while (files.size() < 3)
+      files.push_back(files.back());
+
+    SECURITY_ATTRIBUTES sa = {};
+    sa.nLength = sizeof(sa);
+    sa.bInheritHandle = TRUE;
+
+    // STDIN
+    if (!files[0].empty()) {
+      stdin_handle = CreateFileA(files[0].str().c_str(), GENERIC_READ,
+                                 FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
+                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+      if (stdin_handle == INVALID_HANDLE_VALUE) {
+        std::string error = "Failed to open stdin file: " + files[0].str();
+        comm_channel.NotifyError(error);
+        return llvm::createStringError(
+            std::error_code(GetLastError(), std::system_category()),
+            std::move(error));
+      }
+    }
+
+    // STDOUT
+    if (!files[1].empty()) {
+      stdout_handle = CreateFileA(files[1].str().c_str(), GENERIC_WRITE,
+                                  FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
+                                  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+      if (stdout_handle == INVALID_HANDLE_VALUE) {
+        if (stdin_handle != GetStdHandle(STD_INPUT_HANDLE))
+          CloseHandle(stdin_handle);
+        std::string error = "Failed to open stdout file: " + files[1].str();
+        comm_channel.NotifyError(error);
+        return llvm::createStringError(
+            std::error_code(GetLastError(), std::system_category()),
+            std::move(error));
+      }
+    }
+
+    // STDERR
+    if (!files[2].empty()) {
+      if (files[2] == files[1]) {
+        if (!DuplicateHandle(GetCurrentProcess(), stdout_handle,
+                             GetCurrentProcess(), &stderr_handle, 0, TRUE,
+                             DUPLICATE_SAME_ACCESS)) {
+          if (stdin_handle != GetStdHandle(STD_INPUT_HANDLE))
+            CloseHandle(stdin_handle);
+          if (stdout_handle != GetStdHandle(STD_OUTPUT_HANDLE))
+            CloseHandle(stdout_handle);
+          std::string error = "Failed to duplicate stdout handle for stderr";
+          comm_channel.NotifyError(error);
+          return llvm::createStringError(
+              std::error_code(GetLastError(), std::system_category()),
+              std::move(error));
+        }
+      } else {
+        stderr_handle = CreateFileA(files[2].str().c_str(), GENERIC_WRITE,
+                                    FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
+                                    CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 
NULL);
+        if (stderr_handle == INVALID_HANDLE_VALUE) {
+          if (stdin_handle != GetStdHandle(STD_INPUT_HANDLE))
+            CloseHandle(stdin_handle);
+          if (stdout_handle != GetStdHandle(STD_OUTPUT_HANDLE))
+            CloseHandle(stdout_handle);
+          std::string error = "Failed to open stderr file: " + files[2].str();
+          comm_channel.NotifyError(error);
+          return llvm::createStringError(
+              std::error_code(GetLastError(), std::system_category()),
+              std::move(error));
+        }
+      }
+    }
+
+    startup_info.dwFlags |= STARTF_USESTDHANDLES;
+    startup_info.hStdInput = stdin_handle;
+    startup_info.hStdOutput = stdout_handle;
+    startup_info.hStdError = stderr_handle;
+    handles_inherited = true;
+  }
+
+  PROCESS_INFORMATION process_info = {};
+
+  // Start the process in a suspended state, while we attach the debugger.
+  BOOL success = CreateProcessA(NULL, const_cast<char *>(cmdline.c_str()), 
NULL,
+                                NULL, handles_inherited, CREATE_SUSPENDED, 
NULL,
+                                NULL, &startup_info, &process_info);
+
+  if (stdin_handle != GetStdHandle(STD_INPUT_HANDLE))
+    CloseHandle(stdin_handle);
+  if (stdout_handle != GetStdHandle(STD_OUTPUT_HANDLE))
+    CloseHandle(stdout_handle);
+  if (stderr_handle != GetStdHandle(STD_ERROR_HANDLE))
+    CloseHandle(stderr_handle);
+
+  if (!success) {
+    DWORD error_code = GetLastError();
+    std::string error = "Failed to launch target process";
+    comm_channel.NotifyError(error);
+    return llvm::createStringError(
+        std::error_code(error_code, std::system_category()), std::move(error));
+  }
+
+  // Notify the pid of the process to debug to the debugger. It will attach to
+  // the newly created process.
+  if (llvm::Error err = comm_channel.NotifyPid(process_info.dwProcessId))
+    return err;
+
+  if (llvm::Error err = comm_channel.WaitUntilDebugAdapterAttaches(
+          std::chrono::milliseconds(timeout_in_ms)))
+    return err;
+
+  // The debugger attached to the process. We can resume it.
+  ResumeThread(process_info.hThread);
+
+  // Wait for child to complete to match POSIX behavior.
+  WaitForSingleObject(process_info.hProcess, INFINITE);
+  ExitProcess(0); // TODO: Should we return the exit code of the process?
 #else
 
   // On Linux with the Yama security module enabled, a process can only attach
diff --git a/lldb/unittests/DAP/FifoFilesTest.cpp 
b/lldb/unittests/DAP/FifoFilesTest.cpp
index bbc1b608e91bd..bd8aa64ef1ecd 100644
--- a/lldb/unittests/DAP/FifoFilesTest.cpp
+++ b/lldb/unittests/DAP/FifoFilesTest.cpp
@@ -45,8 +45,8 @@ TEST(FifoFilesTest, SendAndReceiveJSON) {
   auto fifo = CreateFifoFile(fifo_path);
   EXPECT_THAT_EXPECTED(fifo, llvm::Succeeded());
 
-  FifoFileIO writer(fifo_path, "writer");
-  FifoFileIO reader(fifo_path, "reader");
+  FifoFileIO writer(*fifo.get(), "writer");
+  FifoFileIO reader(*fifo.get(), "reader");
 
   llvm::json::Object obj;
   obj["foo"] = "bar";
@@ -79,7 +79,7 @@ TEST(FifoFilesTest, ReadTimeout) {
   auto fifo = CreateFifoFile(fifo_path);
   EXPECT_THAT_EXPECTED(fifo, llvm::Succeeded());
 
-  FifoFileIO reader(fifo_path, "reader");
+  FifoFileIO reader(*fifo.get(), "reader");
 
   // No writer, should timeout.
   auto result = reader.ReadJSON(std::chrono::milliseconds(100));
@@ -91,7 +91,7 @@ TEST(FifoFilesTest, WriteTimeout) {
   auto fifo = CreateFifoFile(fifo_path);
   EXPECT_THAT_EXPECTED(fifo, llvm::Succeeded());
 
-  FifoFileIO writer(fifo_path, "writer");
+  FifoFileIO writer(*fifo.get(), "writer");
 
   // No reader, should timeout.
   llvm::json::Object obj;

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

Reply via email to