[Lldb-commits] [lldb] [lldb][windows] add a Windows FifoFile implementation (PR #185894)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Thu Mar 12 04:53:06 PDT 2026
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/185894
>From c1f32329691b2ceda862c7be900523632b4bce60 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Wed, 11 Mar 2026 14:55:39 +0000
Subject: [PATCH 1/3] [lldb][windows] add a Windows FifoFile implementation
---
lldb/tools/lldb-dap/FifoFiles.cpp | 91 ++++++++++++++++---
lldb/tools/lldb-dap/FifoFiles.h | 24 ++++-
.../tools/lldb-dap/Handler/RequestHandler.cpp | 6 +-
lldb/tools/lldb-dap/RunInTerminal.cpp | 8 +-
lldb/tools/lldb-dap/RunInTerminal.h | 1 +
lldb/unittests/DAP/FifoFilesTest.cpp | 8 +-
6 files changed, 115 insertions(+), 23 deletions(-)
diff --git a/lldb/tools/lldb-dap/FifoFiles.cpp b/lldb/tools/lldb-dap/FifoFiles.cpp
index 1f1bba80bd3b1..a5fa8121260eb 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,17 +28,83 @@ using namespace llvm;
namespace lldb_dap {
-FifoFile::FifoFile(StringRef path) : m_path(path) {}
+FifoFile::FifoFile(StringRef path, lldb::pipe_t pipe) : m_path(path) {
+#ifdef _WIN32
+ if (pipe == INVALID_HANDLE_VALUE)
+ pipe = CreateFileA(m_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+#endif
+ m_pipe = pipe;
+}
FifoFile::~FifoFile() {
-#if !defined(_WIN32)
+#ifdef _WIN32
+ if (m_pipe != INVALID_HANDLE_VALUE) {
+ DisconnectNamedPipe(m_pipe);
+ CloseHandle(m_pipe);
+ }
+#else
unlink(m_path.c_str());
#endif
}
+void FifoFile::WriteLine(std::string line) {
+#ifdef _WIN32
+ DWORD written;
+ line += "\n";
+ WriteFile(m_pipe, line.c_str(), static_cast<DWORD>(line.size()), &written,
+ NULL);
+ FlushFileBuffers(m_pipe);
+#else
+ std::ofstream writer(m_path, std::ofstream::out);
+ writer << line << std::endl;
+#endif
+}
+
+void FifoFile::Connect() {
+#ifdef _WIN32
+ ConnectNamedPipe(m_pipe, NULL);
+#endif
+}
+
+std::string FifoFile::ReadLine() {
+#ifdef _WIN32
+ std::string 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';
+ buffer = read_buffer;
+ }
+
+ 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\\") &&
+ "FifoFile 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,8 +113,10 @@ Expected<std::shared_ptr<FifoFile>> CreateFifoFile(StringRef path) {
#endif
}
-FifoFileIO::FifoFileIO(StringRef fifo_file, StringRef other_endpoint_name)
- : m_fifo_file(fifo_file), m_other_endpoint_name(other_endpoint_name) {}
+FifoFileIO::FifoFileIO(std::shared_ptr<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,9 +124,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 +148,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..87ac2c66e6382 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/Pipe.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/JSON.h"
@@ -20,11 +21,27 @@ namespace lldb_dap {
///
/// The file is destroyed when the destructor is invoked.
struct FifoFile {
- FifoFile(llvm::StringRef path);
+ FifoFile(llvm::StringRef path, lldb::pipe_t pipe = LLDB_INVALID_PIPE);
~FifoFile();
+ void Connect();
+
+ void WriteLine(std::string line);
+
+ std::string ReadLine();
+
+ llvm::StringRef GetPath() { return m_path; }
+
+ /// FifoFile is not copyable.
+ /// @{
+ FifoFile(const FifoFile &rhs) = delete;
+ void operator=(const FifoFile &rhs) = delete;
+ /// @}
+
+protected:
std::string m_path;
+ lldb::pipe_t m_pipe;
};
/// Create a fifo file in the filesystem.
@@ -45,7 +62,8 @@ 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(std::shared_ptr<FifoFile> fifo_file,
+ llvm::StringRef other_endpoint_name);
/// Read the next JSON object from the underlying input fifo file.
///
@@ -76,7 +94,7 @@ class FifoFileIO {
std::chrono::milliseconds timeout = std::chrono::milliseconds(20000));
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 5e8c2163c838f..103d1f7beb873 100644
--- a/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+++ b/lldb/tools/lldb-dap/Handler/RequestHandler.cpp
@@ -103,9 +103,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)
@@ -114,7 +114,7 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
arguments.configuration.program, arguments.args, arguments.env,
- arguments.cwd, comm_file.m_path, debugger_pid, arguments.stdio,
+ arguments.cwd, comm_file->GetPath(), debugger_pid, arguments.stdio,
arguments.console == protocol::eConsoleExternalTerminal);
dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal",
std::move(reverse_request));
diff --git a/lldb/tools/lldb-dap/RunInTerminal.cpp b/lldb/tools/lldb-dap/RunInTerminal.cpp
index 9f309dd78221a..ccdad350cdef7 100644
--- a/lldb/tools/lldb-dap/RunInTerminal.cpp
+++ b/lldb/tools/lldb-dap/RunInTerminal.cpp
@@ -97,7 +97,7 @@ static Error ToError(const RunInTerminalMessage &message) {
RunInTerminalLauncherCommChannel::RunInTerminalLauncherCommChannel(
StringRef comm_file)
- : m_io(comm_file, "debug adapter") {}
+ : m_io(std::make_shared<FifoFile>(comm_file), "debug adapter") {}
Error RunInTerminalLauncherCommChannel::WaitUntilDebugAdapterAttaches(
std::chrono::milliseconds timeout) {
@@ -123,7 +123,11 @@ void RunInTerminalLauncherCommChannel::NotifyError(StringRef error) {
RunInTerminalDebugAdapterCommChannel::RunInTerminalDebugAdapterCommChannel(
StringRef comm_file)
- : m_io(comm_file, "runInTerminal launcher") {}
+ : m_io(std::make_shared<FifoFile>(comm_file), "runInTerminal launcher") {}
+
+RunInTerminalDebugAdapterCommChannel::RunInTerminalDebugAdapterCommChannel(
+ std::shared_ptr<FifoFile> comm_file)
+ : m_io(std::move(comm_file), "runInTerminal launcher") {}
// Can't use \a std::future<llvm::Error> because it doesn't compile on Windows
std::future<lldb::SBError>
diff --git a/lldb/tools/lldb-dap/RunInTerminal.h b/lldb/tools/lldb-dap/RunInTerminal.h
index 457850c8ea538..f6e922e59a095 100644
--- a/lldb/tools/lldb-dap/RunInTerminal.h
+++ b/lldb/tools/lldb-dap/RunInTerminal.h
@@ -99,6 +99,7 @@ class RunInTerminalLauncherCommChannel {
class RunInTerminalDebugAdapterCommChannel {
public:
RunInTerminalDebugAdapterCommChannel(llvm::StringRef comm_file);
+ RunInTerminalDebugAdapterCommChannel(std::shared_ptr<FifoFile> comm_file);
/// Notify the runInTerminal launcher that it was attached.
///
diff --git a/lldb/unittests/DAP/FifoFilesTest.cpp b/lldb/unittests/DAP/FifoFilesTest.cpp
index bbc1b608e91bd..e59fc54cc9543 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, "writer");
+ FifoFileIO reader(*fifo, "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, "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, "writer");
// No reader, should timeout.
llvm::json::Object obj;
>From c968567b3ce784239d89bc1126ff2abcb799d609 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Wed, 11 Mar 2026 17:35:38 +0000
Subject: [PATCH 2/3] fixup! [lldb][windows] add a Windows FifoFile
implementation
---
lldb/tools/lldb-dap/FifoFiles.cpp | 33 ++++++++++++++++++++-----------
lldb/tools/lldb-dap/FifoFiles.h | 2 +-
2 files changed, 23 insertions(+), 12 deletions(-)
diff --git a/lldb/tools/lldb-dap/FifoFiles.cpp b/lldb/tools/lldb-dap/FifoFiles.cpp
index a5fa8121260eb..8d03957b50008 100644
--- a/lldb/tools/lldb-dap/FifoFiles.cpp
+++ b/lldb/tools/lldb-dap/FifoFiles.cpp
@@ -30,9 +30,12 @@ namespace lldb_dap {
FifoFile::FifoFile(StringRef path, lldb::pipe_t pipe) : m_path(path) {
#ifdef _WIN32
- if (pipe == INVALID_HANDLE_VALUE)
+ if (pipe == INVALID_HANDLE_VALUE) {
+ assert(path.starts_with("\\\\.\\pipe\\") &&
+ "FifoFile path should start with '\\\\.\\pipe\\'");
pipe = CreateFileA(m_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ }
#endif
m_pipe = pipe;
}
@@ -48,16 +51,15 @@ FifoFile::~FifoFile() {
#endif
}
-void FifoFile::WriteLine(std::string line) {
+void FifoFile::WriteLine(llvm::StringRef line) {
#ifdef _WIN32
DWORD written;
- line += "\n";
- WriteFile(m_pipe, line.c_str(), static_cast<DWORD>(line.size()), &written,
- NULL);
+ std::string str = line.str() + "\n";
+ WriteFile(m_pipe, str.data(), static_cast<DWORD>(str.size()), &written, NULL);
FlushFileBuffers(m_pipe);
#else
std::ofstream writer(m_path, std::ofstream::out);
- writer << line << std::endl;
+ writer << line.data() << std::endl;
#endif
}
@@ -73,11 +75,20 @@ std::string FifoFile::ReadLine() {
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';
- buffer = read_buffer;
+ while (true) {
+ if (!ReadFile(m_pipe, read_buffer, sizeof(read_buffer), &bytes_read,
+ NULL) ||
+ bytes_read == 0)
+ break;
+
+ buffer.append(read_buffer, bytes_read);
+
+ if (buffer.back() == '\n') {
+ buffer.pop_back();
+ if (!buffer.empty() && buffer.back() == '\r')
+ buffer.pop_back();
+ break;
+ }
}
return buffer;
diff --git a/lldb/tools/lldb-dap/FifoFiles.h b/lldb/tools/lldb-dap/FifoFiles.h
index 87ac2c66e6382..96e6b7f640e61 100644
--- a/lldb/tools/lldb-dap/FifoFiles.h
+++ b/lldb/tools/lldb-dap/FifoFiles.h
@@ -27,7 +27,7 @@ struct FifoFile {
void Connect();
- void WriteLine(std::string line);
+ void WriteLine(llvm::StringRef line);
std::string ReadLine();
>From ec384fe3751b16bc538e712ac0f559b7099d1367 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Thu, 12 Mar 2026 11:50:40 +0000
Subject: [PATCH 3/3] read line by line
---
lldb/tools/lldb-dap/FifoFiles.cpp | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/lldb/tools/lldb-dap/FifoFiles.cpp b/lldb/tools/lldb-dap/FifoFiles.cpp
index 8d03957b50008..1c81c8b503d3a 100644
--- a/lldb/tools/lldb-dap/FifoFiles.cpp
+++ b/lldb/tools/lldb-dap/FifoFiles.cpp
@@ -76,19 +76,11 @@ std::string FifoFile::ReadLine() {
DWORD bytes_read;
while (true) {
- if (!ReadFile(m_pipe, read_buffer, sizeof(read_buffer), &bytes_read,
- NULL) ||
- bytes_read == 0)
- break;
-
+ BOOL success =
+ ReadFile(m_pipe, read_buffer, sizeof(read_buffer), &bytes_read, NULL);
buffer.append(read_buffer, bytes_read);
-
- if (buffer.back() == '\n') {
- buffer.pop_back();
- if (!buffer.empty() && buffer.back() == '\r')
- buffer.pop_back();
+ if (success || GetLastError() != ERROR_MORE_DATA)
break;
- }
}
return buffer;
@@ -106,7 +98,7 @@ Expected<std::shared_ptr<FifoFile>> CreateFifoFile(StringRef path) {
"FifoFile path should start with '\\\\.\\pipe\\'");
HANDLE pipe_handle =
CreateNamedPipeA(path.data(), PIPE_ACCESS_DUPLEX,
- PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, NULL);
if (pipe_handle == INVALID_HANDLE_VALUE) {
More information about the lldb-commits
mailing list