Skip to content

[lldb-dap] Add runInTerminal support for Windows #138160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

DrSergei
Copy link

@DrSergei DrSergei commented May 1, 2025

Added runInTerminal support for Windows based on Windows Named Pipes. Adapted existed FifoFile class to Windows client-server pipes model. When server side owns the assosieted filesystem handle and client side only provide read-write acces to it. Also, fixed small typo in JSONUtill.cpp related to runInTerminal functionality.

@DrSergei DrSergei requested a review from JDevlieghere as a code owner May 1, 2025 15:58
Copy link

github-actions bot commented May 1, 2025

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented May 1, 2025

@llvm/pr-subscribers-lldb

Author: None (DrSergei)

Changes

Added runInTerminal support for Windows based on Windows Named Pipes. Adapted existed FifoFile class to Windows client-server pipes model. When server side owns the assosieted filesystem handle and client side only provide read-write acces to it. Also, fixed small typo in JSONUtill.cpp related to runInTerminal functionality.


Patch is 20.14 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/138160.diff

8 Files Affected:

  • (modified) lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py (-3)
  • (modified) lldb/tools/lldb-dap/FifoFiles.cpp (+77-8)
  • (modified) lldb/tools/lldb-dap/FifoFiles.h (+31-5)
  • (modified) lldb/tools/lldb-dap/Handler/RequestHandler.cpp (+9-3)
  • (modified) lldb/tools/lldb-dap/JSONUtils.cpp (+1-1)
  • (modified) lldb/tools/lldb-dap/RunInTerminal.cpp (+28-6)
  • (modified) lldb/tools/lldb-dap/RunInTerminal.h (+25-4)
  • (modified) lldb/tools/lldb-dap/lldb-dap.cpp (+66-6)
diff --git a/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py b/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py
index 9aab7ca3293db..d5355f3bacd9c 100644
--- a/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py
+++ b/lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py
@@ -43,7 +43,6 @@ def isTestSupported(self):
         except:
             return False
 
-    @skipIfWindows
     @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
     def test_runInTerminal(self):
         if not self.isTestSupported():
@@ -113,7 +112,6 @@ def test_runInTerminalWithObjectEnv(self):
         self.assertIn("FOO", request_envs)
         self.assertEqual("BAR", request_envs["FOO"])
 
-    @skipIfWindows
     @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
     def test_runInTerminalInvalidTarget(self):
         if not self.isTestSupported():
@@ -132,7 +130,6 @@ def test_runInTerminalInvalidTarget(self):
             response["message"],
         )
 
-    @skipIfWindows
     @skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
     def test_missingArgInRunInTerminalLauncher(self):
         if not self.isTestSupported():
diff --git a/lldb/tools/lldb-dap/FifoFiles.cpp b/lldb/tools/lldb-dap/FifoFiles.cpp
index 1f1bba80bd3b1..43eb4679b592f 100644
--- a/lldb/tools/lldb-dap/FifoFiles.cpp
+++ b/lldb/tools/lldb-dap/FifoFiles.cpp
@@ -24,26 +24,53 @@ using namespace llvm;
 
 namespace lldb_dap {
 
-FifoFile::FifoFile(StringRef path) : m_path(path) {}
+#if defined(_WIN32)
+FifoFile::FifoFile(StringRef path, HANDLE handle, bool is_server)
+    : m_path(path), m_is_server(is_server), m_pipe_fd(handle) {}
+#else
+FifoFile::FifoFile(StringRef path, bool is_server)
+    : m_path(path), m_is_server(is_server) {}
+#endif
 
 FifoFile::~FifoFile() {
-#if !defined(_WIN32)
-  unlink(m_path.c_str());
+#if defined(_WIN32)
+  if (m_pipe_fd == INVALID_HANDLE_VALUE)
+    return;
+  if (m_is_server)
+    DisconnectNamedPipe(m_pipe_fd);
+  CloseHandle(m_pipe_fd);
+#else
+  if (m_is_server)
+    unlink(m_path.c_str());
 #endif
 }
 
-Expected<std::shared_ptr<FifoFile>> CreateFifoFile(StringRef path) {
+Expected<std::shared_ptr<FifoFile>> CreateFifoFile(StringRef path,
+                                                   bool is_server) {
 #if defined(_WIN32)
-  return createStringError(inconvertibleErrorCode(), "Unimplemented");
+  if (!is_server)
+    return std::make_shared<FifoFile>(path, INVALID_HANDLE_VALUE, is_server);
+  HANDLE handle =
+      CreateNamedPipeA(path.data(), PIPE_ACCESS_DUPLEX,
+                       PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1,
+                       1024 * 16, 1024 * 16, 0, NULL);
+  if (handle == INVALID_HANDLE_VALUE)
+    return createStringError(
+        std::error_code(GetLastError(), std::generic_category()),
+        "Couldn't create fifo file: %s", path.data());
+  return std::make_shared<FifoFile>(path, handle, is_server);
 #else
+  if (!is_server)
+    return std::make_shared<FifoFile>(path, is_server);
   if (int err = mkfifo(path.data(), 0600))
     return createStringError(std::error_code(err, std::generic_category()),
                              "Couldn't create fifo file: %s", path.data());
-  return std::make_shared<FifoFile>(path);
+  return std::make_shared<FifoFile>(path, is_server);
 #endif
 }
 
-FifoFileIO::FifoFileIO(StringRef fifo_file, StringRef other_endpoint_name)
+FifoFileIO::FifoFileIO(std::shared_ptr<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,11 +79,27 @@ 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);
+#if defined(_WIN32)
+        std::string buffer;
+        buffer.reserve(4096);
+        char ch;
+        DWORD bytes_read = 0;
+        while (ReadFile(m_fifo_file->m_pipe_fd, &ch, 1, &bytes_read, NULL) &&
+               (bytes_read == 1)) {
+          buffer.push_back(ch);
+          if (ch == '\n') {
+            break;
+          }
+        }
+        if (!buffer.empty())
+          line = std::move(buffer);
+#else
+        std::ifstream reader(m_fifo_file->m_path, std::ifstream::in);
         std::string buffer;
         std::getline(reader, buffer);
         if (!buffer.empty())
           line = buffer;
+#endif
       }));
   if (future->wait_for(timeout) == std::future_status::timeout || !line)
     // Indeed this is a leak, but it's intentional. "future" obj destructor
@@ -78,9 +121,19 @@ Error FifoFileIO::SendJSON(const json::Value &json,
   bool done = false;
   std::future<void> *future =
       new std::future<void>(std::async(std::launch::async, [&]() {
+#if defined(_WIN32)
+        std::string buffer = JSONToString(json);
+        buffer.append("\n");
+        DWORD bytes_write = 0;
+        WriteFile(m_fifo_file->m_pipe_fd, buffer.c_str(), buffer.size(),
+                  &bytes_write, NULL);
+        done = bytes_write == buffer.size();
+#else
+        std::ofstream writer(m_fifo_file->m_path, std::ofstream::out);
         std::ofstream writer(m_fifo_file, std::ofstream::out);
         writer << JSONToString(json) << std::endl;
         done = true;
+#endif
       }));
   if (future->wait_for(timeout) == std::future_status::timeout || !done) {
     // Indeed this is a leak, but it's intentional. "future" obj destructor will
@@ -98,4 +151,20 @@ Error FifoFileIO::SendJSON(const json::Value &json,
   return Error::success();
 }
 
+#if defined(_WIN32)
+bool FifoFileIO::Connect() {
+  if (m_fifo_file->m_is_server) {
+    return ConnectNamedPipe(m_fifo_file->m_pipe_fd, NULL);
+  }
+  if (!WaitNamedPipeA(m_fifo_file->m_path.c_str(), NMPWAIT_WAIT_FOREVER))
+    return false;
+  m_fifo_file->m_pipe_fd =
+      CreateFileA(m_fifo_file->m_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0,
+                  NULL, OPEN_EXISTING, 0, NULL);
+  if (m_fifo_file->m_pipe_fd == INVALID_HANDLE_VALUE)
+    return false;
+  return true;
+}
+#endif
+
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/FifoFiles.h b/lldb/tools/lldb-dap/FifoFiles.h
index 633ebeb2aedd4..335c7561040dd 100644
--- a/lldb/tools/lldb-dap/FifoFiles.h
+++ b/lldb/tools/lldb-dap/FifoFiles.h
@@ -12,19 +12,35 @@
 #include "llvm/Support/Error.h"
 #include "llvm/Support/JSON.h"
 
+#if defined(_WIN32)
+#include "lldb/Host/windows/windows.h"
+#endif
+
 #include <chrono>
 
 namespace lldb_dap {
 
+class FifoFileIO;
+
 /// Struct that controls the life of a fifo file in the filesystem.
 ///
 /// The file is destroyed when the destructor is invoked.
 struct FifoFile {
-  FifoFile(llvm::StringRef path);
+#if defined(_WIN32)
+  FifoFile(llvm::StringRef path, HANDLE handle, bool is_server);
+#else
+  FifoFile(llvm::StringRef path, bool is_server);
+#endif
 
   ~FifoFile();
 
   std::string m_path;
+  bool m_is_server;
+#if defined(_WIN32)
+  HANDLE m_pipe_fd = INVALID_HANDLE_VALUE;
+#endif
+
+  friend FifoFileIO;
 };
 
 /// Create a fifo file in the filesystem.
@@ -32,20 +48,26 @@ struct FifoFile {
 /// \param[in] path
 ///     The path for the fifo file.
 ///
+/// \param[in] is_server
+///     If \a is_server is true, then created instance of FifoFile will own
+///     created file.
+///
 /// \return
 ///     A \a std::shared_ptr<FifoFile> if the file could be created, or an
 ///     \a llvm::Error in case of failures.
-llvm::Expected<std::shared_ptr<FifoFile>> CreateFifoFile(llvm::StringRef path);
+llvm::Expected<std::shared_ptr<FifoFile>> CreateFifoFile(llvm::StringRef path,
+                                                         bool is_server);
 
 class FifoFileIO {
 public:
   /// \param[in] fifo_file
-  ///     The path to an input fifo file that exists in the file system.
+  ///     The std::shared_ptr<FifoFile> to an existing fifo file.
   ///
   /// \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(std::shared_ptr<FifoFile> fifo_file,
+             llvm::StringRef other_endpoint_name);
 
   /// Read the next JSON object from the underlying input fifo file.
   ///
@@ -75,8 +97,12 @@ class FifoFileIO {
       const llvm::json::Value &json,
       std::chrono::milliseconds timeout = std::chrono::milliseconds(20000));
 
+#if defined(_WIN32)
+  bool Connect();
+#endif
+
 private:
-  std::string m_fifo_file;
+  std::shared_ptr<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 7a75cd93abc19..e68597194c296 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -119,9 +119,9 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
       CreateRunInTerminalCommFile();
   if (!comm_file_or_err)
     return comm_file_or_err.takeError();
-  FifoFile &comm_file = *comm_file_or_err.get();
+  std::shared_ptr<FifoFile> comm_file = *comm_file_or_err;
 
-  RunInTerminalDebugAdapterCommChannel comm_channel(comm_file.m_path);
+  RunInTerminalDebugAdapterCommChannel comm_channel(comm_file);
 
   lldb::pid_t debugger_pid = LLDB_INVALID_PROCESS_ID;
 #if !defined(_WIN32)
@@ -130,10 +130,16 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
 
   llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
       *arguments.configuration.program, arguments.args, arguments.env,
-      arguments.cwd.value_or(""), comm_file.m_path, debugger_pid);
+      arguments.cwd.value_or(""), comm_file->m_path, debugger_pid);
   dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal",
                                                     std::move(reverse_request));
 
+#if defined(_WIN32)
+  if (!comm_channel.Connect())
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "Failed to connect to the named pipe.");
+#endif
+
   if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid())
     attach_info.SetProcessID(*pid);
   else
diff --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 4409cf5b27e5b..f29ab09e4feb9 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -1463,7 +1463,7 @@ llvm::json::Object CreateRunInTerminalReverseRequest(
   req_args.push_back("--launch-target");
   req_args.push_back(program.str());
   req_args.insert(req_args.end(), args.begin(), args.end());
-  run_in_terminal_args.try_emplace("args", args);
+  run_in_terminal_args.try_emplace("args", req_args);
 
   if (!cwd.empty())
     run_in_terminal_args.try_emplace("cwd", cwd);
diff --git a/lldb/tools/lldb-dap/RunInTerminal.cpp b/lldb/tools/lldb-dap/RunInTerminal.cpp
index 9f309dd78221a..8afe6e8907710 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)
+#if defined(_WIN32)
+#include "lldb/Host/windows/windows.h"
+#else
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -96,7 +98,7 @@ static Error ToError(const RunInTerminalMessage &message) {
 }
 
 RunInTerminalLauncherCommChannel::RunInTerminalLauncherCommChannel(
-    StringRef comm_file)
+    std::shared_ptr<FifoFile> comm_file)
     : m_io(comm_file, "debug adapter") {}
 
 Error RunInTerminalLauncherCommChannel::WaitUntilDebugAdapterAttaches(
@@ -111,8 +113,8 @@ Error RunInTerminalLauncherCommChannel::WaitUntilDebugAdapterAttaches(
     return message.takeError();
 }
 
-Error RunInTerminalLauncherCommChannel::NotifyPid() {
-  return m_io.SendJSON(RunInTerminalMessagePid(getpid()).ToJSON());
+Error RunInTerminalLauncherCommChannel::NotifyPid(lldb::pid_t pid) {
+  return m_io.SendJSON(RunInTerminalMessagePid(pid).ToJSON());
 }
 
 void RunInTerminalLauncherCommChannel::NotifyError(StringRef error) {
@@ -121,8 +123,12 @@ void RunInTerminalLauncherCommChannel::NotifyError(StringRef error) {
     llvm::errs() << llvm::toString(std::move(err)) << "\n";
 }
 
+#if defined(_WIN32)
+bool RunInTerminalLauncherCommChannel::Connect() { return m_io.Connect(); }
+#endif
+
 RunInTerminalDebugAdapterCommChannel::RunInTerminalDebugAdapterCommChannel(
-    StringRef comm_file)
+    std::shared_ptr<FifoFile> comm_file)
     : m_io(comm_file, "runInTerminal launcher") {}
 
 // Can't use \a std::future<llvm::Error> because it doesn't compile on Windows
@@ -148,6 +154,10 @@ Expected<lldb::pid_t> RunInTerminalDebugAdapterCommChannel::GetLauncherPid() {
   }
 }
 
+#if defined(_WIN32)
+bool RunInTerminalDebugAdapterCommChannel::Connect() { return m_io.Connect(); }
+#endif
+
 std::string RunInTerminalDebugAdapterCommChannel::GetLauncherError() {
   // We know there's been an error, so a small timeout is enough.
   if (Expected<RunInTerminalMessageUP> message =
@@ -158,13 +168,25 @@ std::string RunInTerminalDebugAdapterCommChannel::GetLauncherError() {
 }
 
 Expected<std::shared_ptr<FifoFile>> CreateRunInTerminalCommFile() {
+#if defined(_WIN32)
+  static constexpr llvm::StringLiteral g_pipe_name_prefix = "\\\\.\\Pipe\\";
+  SmallString<256> comm_file;
+  sys::fs::createUniquePath("lldb-dap-run-in-terminal-comm-%%%%%%", comm_file,
+                            +false);
+  return CreateFifoFile((g_pipe_name_prefix + comm_file.str()).str(), true);
+#else
   SmallString<256> comm_file;
   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(), true);
+#endif
+}
 
-  return CreateFifoFile(comm_file.str());
+Expected<std::shared_ptr<FifoFile>>
+OpenRunInTerminalCommFile(llvm::StringRef fifo_file) {
+  return CreateFifoFile(fifo_file, false);
 }
 
 } // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/RunInTerminal.h b/lldb/tools/lldb-dap/RunInTerminal.h
index 457850c8ea538..10e715d1d12e1 100644
--- a/lldb/tools/lldb-dap/RunInTerminal.h
+++ b/lldb/tools/lldb-dap/RunInTerminal.h
@@ -70,7 +70,7 @@ struct RunInTerminalMessageDidAttach : RunInTerminalMessage {
 
 class RunInTerminalLauncherCommChannel {
 public:
-  RunInTerminalLauncherCommChannel(llvm::StringRef comm_file);
+  RunInTerminalLauncherCommChannel(std::shared_ptr<FifoFile> comm_file);
 
   /// Wait until the debug adapter attaches.
   ///
@@ -82,23 +82,31 @@ class RunInTerminalLauncherCommChannel {
   ///     out.
   llvm::Error WaitUntilDebugAdapterAttaches(std::chrono::milliseconds timeout);
 
-  /// Notify the debug adapter this process' pid.
+  /// Notify the debug adaptor debuggee's  pid.
+  ///
+  /// \param[in] pid
+  ///    The process ID to be attached.
   ///
   /// \return
   ///     An \a llvm::Error object in case of errors or if this operation times
   ///     out.
-  llvm::Error NotifyPid();
+  llvm::Error NotifyPid(lldb::pid_t pid);
 
   /// Notify the debug adapter that there's been an error.
   void NotifyError(llvm::StringRef error);
 
+#if defined(_WIN32)
+  /// Connect to RunInTerminalDebugAdapterCommChannel instance.
+  bool Connect();
+#endif
+
 private:
   FifoFileIO m_io;
 };
 
 class RunInTerminalDebugAdapterCommChannel {
 public:
-  RunInTerminalDebugAdapterCommChannel(llvm::StringRef comm_file);
+  RunInTerminalDebugAdapterCommChannel(std::shared_ptr<FifoFile> comm_file);
 
   /// Notify the runInTerminal launcher that it was attached.
   ///
@@ -118,6 +126,11 @@ class RunInTerminalDebugAdapterCommChannel {
   /// default error message if a certain timeout if reached.
   std::string GetLauncherError();
 
+#if defined(_WIN32)
+  /// Connect to RunInTerminalLauncherCommChannel instance.
+  bool Connect();
+#endif
+
 private:
   FifoFileIO m_io;
 };
@@ -126,6 +139,14 @@ class RunInTerminalDebugAdapterCommChannel {
 /// the runInTerminal launcher.
 llvm::Expected<std::shared_ptr<FifoFile>> CreateRunInTerminalCommFile();
 
+/// Open a fifo file used to communicate the debug adaptor with
+/// the runInTerminal launcher.
+///
+/// \param[in] fifo_file
+///    The path to a fifo file that exists in the file system.
+llvm::Expected<std::shared_ptr<FifoFile>>
+OpenRunInTerminalCommFile(llvm::StringRef fifo_file);
+
 } // namespace lldb_dap
 
 #endif // LLDB_TOOLS_LLDB_DAP_RUNINTERMINAL_H
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 6e17b13cc9e33..13fdd0e57ad1a 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -44,9 +44,11 @@
 #include <cstdio>
 #include <cstdlib>
 #include <fcntl.h>
+#include <iomanip>
 #include <map>
 #include <memory>
 #include <mutex>
+#include <sstream>
 #include <string>
 #include <system_error>
 #include <thread>
@@ -204,14 +206,73 @@ static void PrintVersion() {
 // In case of errors launching the target, a suitable error message will be
 // emitted to the debug adapter.
 static llvm::Error LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg,
-                                             llvm::StringRef comm_file,
+                                             llvm::StringRef fifo_file,
                                              lldb::pid_t debugger_pid,
                                              char *argv[]) {
+  llvm::Expected<std::shared_ptr<FifoFile>> comm_file_or_err =
+      OpenRunInTerminalCommFile(fifo_file);
+  if (!comm_file_or_err) {
+    llvm::errs() << llvm::toString(comm_file_or_err.takeError()) << "\n";
+    exit(EXIT_FAILURE);
+  }
+  std::shared_ptr<FifoFile> comm_file = *comm_file_or_err;
+  RunInTerminalLauncherCommChannel comm_channel(comm_file);
+
 #if defined(_WIN32)
-  return llvm::createStringError(
-      "runInTerminal is only supported on POSIX systems");
-#else
+  if (!comm_channel.Connect()) {
+    llvm::errs() << "Failed to connect to the named pipe.\n";
+    exit(EXIT_FAILURE);
+  }
+
+  STARTUPINFOA si;
+  PROCESS_INFORMATION pi;
+  ZeroMemory(&si, sizeof(si));
+  si.cb = sizeof(si);
+  ZeroMemory(&pi, sizeof(pi));
+
+  std::string cmd;
+  while (*argv != nullptr) {
+    std::stringstream ss;
+    ss << std::quoted(*(argv++));
+    cmd += ss.str();
+    cmd += " ";
+  }
+  cmd.pop_back();
+
+  if (!CreateProcessA(NULL, cmd.data(), NULL, NULL, FALSE, CREATE_SUSPENDED,
+                      NULL, NULL, &si, &pi)) {
+    llvm::errs() << "Create process failed: " << GetLastError() << "\n";
+    exit(EXIT_FAILURE);
+  }
 
+  if (llvm::Error err = comm_channel.NotifyPid(pi.dwProcessId)) {
+    llvm::errs() << llvm::toString(std::move(err)) << "\n";
+    exit(EXIT_FAILURE);
+  }
+
+  // We will wait to be attached with a timeout. We don't wait indefinitely
+  // using a signal to prevent being paused forever.
+
+  // This env var should be used only for tests.
+  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 (llvm::Error err = comm_channel.WaitUntilDebugAdapterAttaches(
+          std::chrono::milliseconds(timeout_in_ms))) {
+    llvm::errs() << llvm::toString(std::move(err)) << "\n";
+    exit(EXIT_FAILURE);
+  }
+
+  if (ResumeThread(pi.hThread) == (DWORD)-1) {
+    llvm::errs() << "Resume process failed: " << GetLastError() << "\n";
+    exit(EXIT_FAILURE);
+  }
+
+  WaitForSingleObject(pi.hProcess, INFINITE);
+  CloseHandle(pi.hProcess);
+  CloseHandle(pi.hThread);
+  exit(EXIT_SUCCESS);
+#else
   // On Linux with the Yama security module enabled, a process can only attach
   // to its descendants by default. In the runInTerminal case the target
   // process is launched by the client so we need to allow tracing explicitly.
@@ -220,8 +281,7 @@ static llvm::Error LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg,
     (void)prctl(PR_SET_PTRACER, debugger_pid, 0, 0, 0);
 #endif
 
-  RunInTerminalLauncherCommChannel comm_channel(comm_file);
-  if (llvm::Error err = comm_channel.NotifyPid())
+  if (ll...
[truncated]

@DrSergei DrSergei force-pushed the lldb-dap-run-in-terminal-windows-support branch from c60a556 to 95b85c7 Compare May 1, 2025 19:40
@JDevlieghere
Copy link
Member

There's an existing PR that adds this functionality as well: #121269 with quite a bit of discussion. Does this PR have anything that's lacking in the other PR?

@DrSergei
Copy link
Author

DrSergei commented May 1, 2025

There's an existing PR that adds this functionality as well: #121269 with quite a bit of discussion. Does this PR have anything that's lacking in the other PR?

It's quite similar. I use slightly different functionalites, but main idea is the same. Another PR looks abandoned. I am very interested in this functionality. You can close my PR and continue work on previous PR, thanks.

@DrSergei
Copy link
Author

DrSergei commented May 1, 2025

Just a question, as far as I see, there is no Static scope and static variables are included in the Global scope. This may not be optimal for large projects with a large number of global variables (for example, global variables provided by libraries). Basically, there are not many static variables that are visible from the current frame, and they are more relevant. For, example codelldb and cortex-debug have Static scope. I can try to implement it, if you let. Also, some debug adapters provides opportunity to launch executable in external console. Looks like, DAP has support for that, I can work on that for lldb-dap.

@SuibianP
Copy link

SuibianP commented May 5, 2025

Another PR looks abandoned

It is actually not (yet). I have been actively waiting for maintainers to drop another review and have pinged & rebased several times over these three months.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants