[Lldb-commits] [lldb] [lldb-dap] Implement `runInTerminal` for Windows (PR #121269)
Hu Jialun via lldb-commits
lldb-commits at lists.llvm.org
Thu Feb 27 07:59:00 PST 2025
https://github.com/SuibianP updated https://github.com/llvm/llvm-project/pull/121269
>From 259dfbb0fc181b1f28d3678688778947dad5b317 Mon Sep 17 00:00:00 2001
From: Jialun Hu <jialun.hu at razer.com>
Date: Mon, 24 Feb 2025 22:10:17 +0800
Subject: [PATCH] [lldb-dap] Implement runInTerminal for Windows
Currently, the named pipe is passed by name and a transient ofstream is
constructed at each I/O request. This assumes,
- Blocking semantics: FIFO I/O waits for the other side to connect.
- Buffered semantics: Closing one side does not discard existing data.
The former can be replaced by WaitNamedPipe/ConnectNamedPipe on Win32,
but the second cannot be easily worked around. It is also impossible to
have another "keep-alive" pipe server instance, as server-client pairs
are fixed on connection on Win32 and the client may get connected to it
instead of the real one.
Refactor FifoFile[IO] to use an open file handles rather than file name.
---
Win32 provides no way to replace the process image. Under the hood exec*
actually creates a new process with a new PID. DebugActiveProcess also
cannot get notified of process creations.
Create the new process in a suspended state and resume it after attach.
---
lldb/packages/Python/lldbsuite/test/dotest.py | 2 +-
.../runInTerminal/TestDAP_runInTerminal.py | 3 -
.../API/tools/lldb-dap/runInTerminal/main.c | 6 +-
lldb/tools/lldb-dap/FifoFiles.cpp | 131 +++++++++++++++---
lldb/tools/lldb-dap/FifoFiles.h | 35 +++--
.../tools/lldb-dap/Handler/RequestHandler.cpp | 8 +-
lldb/tools/lldb-dap/RunInTerminal.cpp | 52 ++++---
lldb/tools/lldb-dap/RunInTerminal.h | 11 +-
lldb/tools/lldb-dap/lldb-dap.cpp | 63 +++++++--
9 files changed, 235 insertions(+), 76 deletions(-)
diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py b/lldb/packages/Python/lldbsuite/test/dotest.py
index 681ea1638f2d6..538cece8b4913 100644
--- a/lldb/packages/Python/lldbsuite/test/dotest.py
+++ b/lldb/packages/Python/lldbsuite/test/dotest.py
@@ -545,7 +545,7 @@ def setupSysPath():
lldbDir = os.path.dirname(lldbtest_config.lldbExec)
- lldbDAPExec = os.path.join(lldbDir, "lldb-dap")
+ lldbDAPExec = os.path.join(lldbDir, "lldb-dap.exe" if os.name == "nt" else "lldb-dap")
if is_exe(lldbDAPExec):
os.environ["LLDBDAP_EXEC"] = lldbDAPExec
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 ac96bcc1364a2..18a92dd3fa335 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(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(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(archs=no_match(["x86_64"]))
def test_missingArgInRunInTerminalLauncher(self):
if not self.isTestSupported():
diff --git a/lldb/test/API/tools/lldb-dap/runInTerminal/main.c b/lldb/test/API/tools/lldb-dap/runInTerminal/main.c
index 676bd830e657b..9cc25b2e45a49 100644
--- a/lldb/test/API/tools/lldb-dap/runInTerminal/main.c
+++ b/lldb/test/API/tools/lldb-dap/runInTerminal/main.c
@@ -1,11 +1,13 @@
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
+
+#include <threads.h>
+#include <time.h>
int main(int argc, char *argv[]) {
const char *foo = getenv("FOO");
for (int counter = 1;; counter++) {
- sleep(1); // breakpoint
+ thrd_sleep(&(struct timespec){.tv_sec = 1}, NULL); // breakpoint
}
return 0;
}
diff --git a/lldb/tools/lldb-dap/FifoFiles.cpp b/lldb/tools/lldb-dap/FifoFiles.cpp
index 1f1bba80bd3b1..8069049d78519 100644
--- a/lldb/tools/lldb-dap/FifoFiles.cpp
+++ b/lldb/tools/lldb-dap/FifoFiles.cpp
@@ -8,8 +8,15 @@
#include "FifoFiles.h"
#include "JSONUtils.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/WindowsError.h"
-#if !defined(_WIN32)
+#if defined(_WIN32)
+#include <Windows.h>
+#include <fcntl.h>
+#include <io.h>
+#else
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -24,27 +31,76 @@ using namespace llvm;
namespace lldb_dap {
-FifoFile::FifoFile(StringRef path) : m_path(path) {}
+FifoFile::FifoFile(std::string path, FILE *f) : m_path(path), m_file(f) {}
+
+Expected<FifoFile> FifoFile::create(StringRef path) {
+ auto file = fopen(path.data(), "r+");
+ if (file == nullptr)
+ return createStringError(inconvertibleErrorCode(),
+ "Failed to open fifo file " + path);
+ if (setvbuf(file, NULL, _IONBF, 0))
+ return createStringError(inconvertibleErrorCode(),
+ "Failed to setvbuf on fifo file " + path);
+ return FifoFile(path, file);
+}
+
+FifoFile::FifoFile(StringRef path, FILE *f) : m_path(path), m_file(f) {}
+FifoFile::FifoFile(FifoFile &&other)
+ : m_path(other.m_path), m_file(other.m_file) {
+ other.m_path.clear();
+ other.m_file = nullptr;
+}
FifoFile::~FifoFile() {
+ if (m_file)
+ fclose(m_file);
#if !defined(_WIN32)
+ // Unreferenced named pipes are deleted automatically on Win32
unlink(m_path.c_str());
#endif
}
-Expected<std::shared_ptr<FifoFile>> CreateFifoFile(StringRef path) {
-#if defined(_WIN32)
- return createStringError(inconvertibleErrorCode(), "Unimplemented");
+// This probably belongs to llvm::sys::fs as another FSEntity type
+Error createUniqueNamedPipe(const Twine &prefix, StringRef suffix,
+ int &result_fd,
+ SmallVectorImpl<char> &result_path) {
+ std::error_code EC;
+#ifdef _WIN32
+ const char *middle = suffix.empty() ? "-%%%%%%" : "-%%%%%%.";
+ EC = sys::fs::getPotentiallyUniqueFileName(
+ "\\\\.\\pipe\\LOCAL\\" + prefix + middle + suffix, result_path);
+#else
+ EC = sys::fs::getPotentiallyUniqueTempFileName(prefix, suffix, result_path);
+#endif
+ if (EC)
+ return errorCodeToError(EC);
+ result_path.push_back(0);
+ const char *path = result_path.data();
+#ifdef _WIN32
+ HANDLE h = ::CreateNamedPipeA(
+ path, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 512, 512, 0, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ return createStringError(mapLastWindowsError(), "CreateNamedPipe");
+ result_fd = _open_osfhandle((intptr_t)h, _O_TEXT | _O_RDWR);
+ if (result_fd == -1)
+ return createStringError(mapLastWindowsError(), "_open_osfhandle");
#else
- 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);
+ if (mkfifo(path, 0600) == -1)
+ return createStringError(std::error_code(errno, std::generic_category()),
+ "mkfifo");
+ EC = openFileForWrite(result_path, result_fd, sys::fs::CD_OpenExisting,
+ sys::fs::OF_None, 0600);
+ if (EC)
+ return errorCodeToError(EC);
#endif
+ result_path.pop_back();
+ return Error::success();
}
-FifoFileIO::FifoFileIO(StringRef fifo_file, StringRef other_endpoint_name)
- : m_fifo_file(fifo_file), m_other_endpoint_name(other_endpoint_name) {}
+FifoFileIO::FifoFileIO(FifoFile &&fifo_file, StringRef other_endpoint_name)
+ : m_fifo_file(std::move(fifo_file)),
+ m_other_endpoint_name(other_endpoint_name) {}
Expected<json::Value> FifoFileIO::ReadJSON(std::chrono::milliseconds timeout) {
// We use a pointer for this future, because otherwise its normal destructor
@@ -52,13 +108,28 @@ 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);
- if (!buffer.empty())
- line = buffer;
+ rewind(m_fifo_file.m_file);
+ /*
+ * The types of messages passing through the fifo are:
+ * {"kind": "pid", "pid": 9223372036854775807}
+ * {"kind": "didAttach"}
+ * {"kind": "error", "error": "error message"}
+ *
+ * 512 characters should be more than enough.
+ */
+ constexpr size_t buffer_size = 512;
+ char buffer[buffer_size];
+ char *ptr = fgets(buffer, buffer_size, m_fifo_file.m_file);
+ if (ptr == nullptr || *ptr == 0)
+ return;
+ size_t len = strlen(buffer);
+ if (len <= 1)
+ return;
+ buffer[len - 1] = '\0'; // remove newline
+ line = buffer;
}));
- if (future->wait_for(timeout) == std::future_status::timeout || !line)
+
+ if (future->wait_for(timeout) == std::future_status::timeout)
// Indeed this is a leak, but it's intentional. "future" obj destructor
// will block on waiting for the worker thread to join. And the worker
// thread might be stuck in blocking I/O. Intentionally leaking the obj
@@ -69,6 +140,11 @@ Expected<json::Value> FifoFileIO::ReadJSON(std::chrono::milliseconds timeout) {
return createStringError(inconvertibleErrorCode(),
"Timed out trying to get messages from the " +
m_other_endpoint_name);
+ if (!line) {
+ return createStringError(inconvertibleErrorCode(),
+ "Failed to get messages from the " +
+ m_other_endpoint_name);
+ }
delete future;
return json::parse(*line);
}
@@ -78,8 +154,12 @@ 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;
+ rewind(m_fifo_file.m_file);
+ auto payload = JSONToString(json);
+ if (fputs(payload.c_str(), m_fifo_file.m_file) == EOF ||
+ fputc('\n', m_fifo_file.m_file) == EOF) {
+ return;
+ }
done = true;
}));
if (future->wait_for(timeout) == std::future_status::timeout || !done) {
@@ -98,4 +178,17 @@ Error FifoFileIO::SendJSON(const json::Value &json,
return Error::success();
}
+Error FifoFileIO::WaitForPeer() {
+#ifdef _WIN32
+ if (!::ConnectNamedPipe((HANDLE)_get_osfhandle(fileno(m_fifo_file.m_file)),
+ NULL) &&
+ GetLastError() != ERROR_PIPE_CONNECTED) {
+ return createStringError(mapLastWindowsError(),
+ "Failed to connect to the " +
+ m_other_endpoint_name);
+ }
+#endif
+ return Error::success();
+}
+
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/FifoFiles.h b/lldb/tools/lldb-dap/FifoFiles.h
index 633ebeb2aedd4..99cb75e1343d2 100644
--- a/lldb/tools/lldb-dap/FifoFiles.h
+++ b/lldb/tools/lldb-dap/FifoFiles.h
@@ -11,8 +11,10 @@
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
+#include "llvm/Support/raw_ostream.h"
#include <chrono>
+#include <fstream>
namespace lldb_dap {
@@ -20,22 +22,33 @@ namespace lldb_dap {
///
/// The file is destroyed when the destructor is invoked.
struct FifoFile {
- FifoFile(llvm::StringRef path);
+ FifoFile(FifoFile &&other);
+
+ FifoFile(const FifoFile &) = delete;
+ FifoFile &operator=(const FifoFile &) = delete;
~FifoFile();
+ static llvm::Expected<FifoFile> create(llvm::StringRef path);
+ FifoFile(llvm::StringRef path, FILE *f);
+
std::string m_path;
+ FILE *m_file;
+
+private:
+ FifoFile(std::string path, FILE *f);
};
-/// Create a fifo file in the filesystem.
+/// Create and open a named pipe with a unique name.
///
-/// \param[in] path
-/// The path for the fifo file.
+/// The arguments have identical meanings to those of
+/// llvm::sys::fs::createTemporaryFile.
///
-/// \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);
+/// Note that the resulting filename is further prepended by \c \\.\pipe\\LOCAL\
+/// on Windows and the native temp directory on other platforms.
+llvm::Error createUniqueNamedPipe(const llvm::Twine &prefix,
+ llvm::StringRef suffix, int &result_fd,
+ llvm::SmallVectorImpl<char> &result_path);
class FifoFileIO {
public:
@@ -45,7 +58,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.
///
@@ -75,8 +88,10 @@ class FifoFileIO {
const llvm::json::Value &json,
std::chrono::milliseconds timeout = std::chrono::milliseconds(20000));
+ llvm::Error WaitForPeer();
+
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 0a32e39ea3aff..3c2f36a67e68b 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -92,18 +92,22 @@ static llvm::Error RunInTerminal(DAP &dap,
if (!comm_file_or_err)
return comm_file_or_err.takeError();
FifoFile &comm_file = *comm_file_or_err.get();
+ std::string comm_filename = comm_file.m_path;
- RunInTerminalDebugAdapterCommChannel comm_channel(comm_file.m_path);
+ RunInTerminalDebugAdapterCommChannel comm_channel(comm_file);
lldb::pid_t debugger_pid = LLDB_INVALID_PROCESS_ID;
#if !defined(_WIN32)
debugger_pid = getpid();
#endif
llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
- launch_request, dap.debug_adaptor_path, comm_file.m_path, debugger_pid);
+ launch_request, dap.debug_adaptor_path, comm_filename, debugger_pid);
dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal",
std::move(reverse_request));
+ auto err = comm_channel.WaitForLauncher();
+ if (err)
+ return err;
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 4fe09e2885a8e..382a13fb4a840 100644
--- a/lldb/tools/lldb-dap/RunInTerminal.cpp
+++ b/lldb/tools/lldb-dap/RunInTerminal.cpp
@@ -9,12 +9,6 @@
#include "RunInTerminal.h"
#include "JSONUtils.h"
-#if !defined(_WIN32)
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#endif
-
#include <chrono>
#include <future>
@@ -96,8 +90,8 @@ static Error ToError(const RunInTerminalMessage &message) {
}
RunInTerminalLauncherCommChannel::RunInTerminalLauncherCommChannel(
- StringRef comm_file)
- : m_io(comm_file, "debug adaptor") {}
+ FifoFile &comm_file)
+ : m_io(std::move(comm_file), "debug adaptor") {}
Error RunInTerminalLauncherCommChannel::WaitUntilDebugAdaptorAttaches(
std::chrono::milliseconds timeout) {
@@ -111,28 +105,31 @@ Error RunInTerminalLauncherCommChannel::WaitUntilDebugAdaptorAttaches(
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) {
if (Error err = m_io.SendJSON(RunInTerminalMessageError(error).ToJSON(),
std::chrono::seconds(2)))
- llvm::errs() << llvm::toString(std::move(err)) << "\n";
+ errs() << toString(std::move(err)) << "\n";
}
RunInTerminalDebugAdapterCommChannel::RunInTerminalDebugAdapterCommChannel(
- StringRef comm_file)
- : m_io(comm_file, "runInTerminal launcher") {}
+ FifoFile &comm_file)
+ : m_io(std::move(comm_file), "runInTerminal launcher") {}
+
+Error RunInTerminalDebugAdapterCommChannel::WaitForLauncher() {
+ return m_io.WaitForPeer();
+}
// Can't use \a std::future<llvm::Error> because it doesn't compile on Windows
std::future<lldb::SBError>
RunInTerminalDebugAdapterCommChannel::NotifyDidAttach() {
return std::async(std::launch::async, [&]() {
lldb::SBError error;
- if (llvm::Error err =
- m_io.SendJSON(RunInTerminalMessageDidAttach().ToJSON()))
- error.SetErrorString(llvm::toString(std::move(err)).c_str());
+ if (Error err = m_io.SendJSON(RunInTerminalMessageDidAttach().ToJSON()))
+ error.SetErrorString(toString(std::move(err)).c_str());
return error;
});
}
@@ -158,13 +155,24 @@ std::string RunInTerminalDebugAdapterCommChannel::GetLauncherError() {
}
Expected<std::shared_ptr<FifoFile>> CreateRunInTerminalCommFile() {
+ int comm_fd;
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());
+ if (auto error = createUniqueNamedPipe("lldb-dap-run-in-terminal-comm", "",
+ comm_fd, comm_file))
+ return error;
+ FILE *cf = fdopen(comm_fd, "r+");
+ // There is no portable way to conjure an ofstream from HANDLE, so use FILE *
+ // llvm::raw_fd_stream does not support getline() and there is no
+ // llvm::buffer_istream
+
+ if (cf == NULL)
+ return createStringError(std::error_code(errno, std::generic_category()),
+ "Error converting file descriptor to C FILE for "
+ "runInTerminal comm-file");
+ if (setvbuf(cf, NULL, _IONBF, 0))
+ return createStringError(std::error_code(errno, std::generic_category()),
+ "Error setting unbuffered mode on C FILE");
+ return std::make_shared<FifoFile>(comm_file, cf);
}
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/RunInTerminal.h b/lldb/tools/lldb-dap/RunInTerminal.h
index b20f8beb6071d..335f484837f7d 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(FifoFile &comm_file);
/// Wait until the debug adaptor attaches.
///
@@ -84,10 +84,13 @@ class RunInTerminalLauncherCommChannel {
/// Notify the debug adaptor this process' pid.
///
+ /// \param[in] pid
+ /// The PID to be sent to the debug adaptor
+ ///
/// \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 adaptor that there's been an error.
void NotifyError(llvm::StringRef error);
@@ -98,7 +101,7 @@ class RunInTerminalLauncherCommChannel {
class RunInTerminalDebugAdapterCommChannel {
public:
- RunInTerminalDebugAdapterCommChannel(llvm::StringRef comm_file);
+ RunInTerminalDebugAdapterCommChannel(FifoFile &comm_file);
/// Notify the runInTerminal launcher that it was attached.
///
@@ -118,6 +121,8 @@ class RunInTerminalDebugAdapterCommChannel {
/// default error message if a certain timeout if reached.
std::string GetLauncherError();
+ llvm::Error WaitForLauncher();
+
private:
FifoFileIO m_io;
};
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index e6a28919694bc..48b1e17e60308 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -37,6 +37,7 @@
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/Threading.h"
+#include "llvm/Support/WindowsError.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <condition_variable>
@@ -206,11 +207,6 @@ static llvm::Error LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg,
llvm::StringRef comm_file,
lldb::pid_t debugger_pid,
char *argv[]) {
-#if defined(_WIN32)
- return llvm::createStringError(
- "runInTerminal is only supported on POSIX systems");
-#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.
@@ -219,9 +215,46 @@ 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())
+ const char *target = target_arg.getValue();
+
+#ifdef _WIN32
+ /* Win32 provides no way to replace the process image. exec* are misnomers.
+ Neither is the adapter notified of new processes due to DebugActiveProcess
+ semantics. Hence, we create the new process in a suspended state and resume
+ it after attach.
+ */
+ std::string cmdline;
+ for (char **arg = argv; *arg != nullptr; ++arg) {
+ cmdline += *arg;
+ cmdline += ' ';
+ }
+ STARTUPINFOA si = {};
+ si.cb = sizeof(si);
+ PROCESS_INFORMATION pi = {};
+ bool res = CreateProcessA(target, cmdline.data(), NULL, NULL, FALSE,
+ CREATE_SUSPENDED, NULL, NULL, &si, &pi);
+ if (!res) {
+ llvm::errs() << "Failed to create process: " << GetLastError() << "\n";
+ exit(EXIT_FAILURE);
+ }
+#endif
+
+ auto comm_fifo_file = FifoFile::create(comm_file);
+ if (!comm_fifo_file) {
+ llvm::errs() << "Failed to create fifo file: " << comm_fifo_file.takeError()
+ << "\n";
+ exit(EXIT_FAILURE);
+ }
+ RunInTerminalLauncherCommChannel comm_channel(*comm_fifo_file);
+ if (llvm::Error err = comm_channel.NotifyPid(
+#ifdef _WIN32
+ pi.dwProcessId
+#else
+ getpid()
+#endif
+ )) {
return err;
+ }
// We will wait to be attached with a timeout. We don't wait indefinitely
// using a signal to prevent being paused forever.
@@ -234,15 +267,17 @@ static llvm::Error LaunchRunInTerminalTarget(llvm::opt::Arg &target_arg,
std::chrono::milliseconds(timeout_in_ms))) {
return err;
}
-
- const char *target = target_arg.getValue();
+#ifdef _WIN32
+ if (ResumeThread(pi.hThread) != -1)
+ exit(EXIT_SUCCESS);
+ auto error = llvm::mapLastWindowsError();
+#else
execvp(target, argv);
-
- std::string error = std::strerror(errno);
- comm_channel.NotifyError(error);
- return llvm::createStringError(llvm::inconvertibleErrorCode(),
- std::move(error));
+ std::error_code error(errno, std::generic_category());
#endif
+
+ comm_channel.NotifyError(error.message());
+ return llvm::createStringError(error, "Failed to exec target");
}
/// used only by TestVSCode_redirection_to_console.py
More information about the lldb-commits
mailing list