Skip to content

Commit 95b85c7

Browse files
committed
[lldb-dap] Add runInTerminal support for Windows
1 parent 001cc34 commit 95b85c7

File tree

8 files changed

+237
-37
lines changed

8 files changed

+237
-37
lines changed

lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ def isTestSupported(self):
4343
except:
4444
return False
4545

46-
@skipIfWindows
4746
@skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
4847
def test_runInTerminal(self):
4948
if not self.isTestSupported():
@@ -113,7 +112,6 @@ def test_runInTerminalWithObjectEnv(self):
113112
self.assertIn("FOO", request_envs)
114113
self.assertEqual("BAR", request_envs["FOO"])
115114

116-
@skipIfWindows
117115
@skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
118116
def test_runInTerminalInvalidTarget(self):
119117
if not self.isTestSupported():
@@ -132,7 +130,6 @@ def test_runInTerminalInvalidTarget(self):
132130
response["message"],
133131
)
134132

135-
@skipIfWindows
136133
@skipIf(oslist=["linux"], archs=no_match(["x86_64"]))
137134
def test_missingArgInRunInTerminalLauncher(self):
138135
if not self.isTestSupported():

lldb/tools/lldb-dap/FifoFiles.cpp

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,53 @@ using namespace llvm;
2424

2525
namespace lldb_dap {
2626

27-
FifoFile::FifoFile(StringRef path) : m_path(path) {}
27+
#if defined(_WIN32)
28+
FifoFile::FifoFile(StringRef path, HANDLE handle, bool is_server)
29+
: m_path(path), m_is_server(is_server), m_pipe_fd(handle) {}
30+
#else
31+
FifoFile::FifoFile(StringRef path, bool is_server)
32+
: m_path(path), m_is_server(is_server) {}
33+
#endif
2834

2935
FifoFile::~FifoFile() {
30-
#if !defined(_WIN32)
31-
unlink(m_path.c_str());
36+
#if defined(_WIN32)
37+
if (m_pipe_fd == INVALID_HANDLE_VALUE)
38+
return;
39+
if (m_is_server)
40+
DisconnectNamedPipe(m_pipe_fd);
41+
CloseHandle(m_pipe_fd);
42+
#else
43+
if (m_is_server)
44+
unlink(m_path.c_str());
3245
#endif
3346
}
3447

35-
Expected<std::shared_ptr<FifoFile>> CreateFifoFile(StringRef path) {
48+
Expected<std::shared_ptr<FifoFile>> CreateFifoFile(StringRef path,
49+
bool is_server) {
3650
#if defined(_WIN32)
37-
return createStringError(inconvertibleErrorCode(), "Unimplemented");
51+
if (!is_server)
52+
return std::make_shared<FifoFile>(path, INVALID_HANDLE_VALUE, is_server);
53+
HANDLE handle =
54+
CreateNamedPipeA(path.data(), PIPE_ACCESS_DUPLEX,
55+
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1,
56+
1024 * 16, 1024 * 16, 0, NULL);
57+
if (handle == INVALID_HANDLE_VALUE)
58+
return createStringError(
59+
std::error_code(GetLastError(), std::generic_category()),
60+
"Couldn't create fifo file: %s", path.data());
61+
return std::make_shared<FifoFile>(path, handle, is_server);
3862
#else
63+
if (!is_server)
64+
return std::make_shared<FifoFile>(path, is_server);
3965
if (int err = mkfifo(path.data(), 0600))
4066
return createStringError(std::error_code(err, std::generic_category()),
4167
"Couldn't create fifo file: %s", path.data());
42-
return std::make_shared<FifoFile>(path);
68+
return std::make_shared<FifoFile>(path, is_server);
4369
#endif
4470
}
4571

46-
FifoFileIO::FifoFileIO(StringRef fifo_file, StringRef other_endpoint_name)
72+
FifoFileIO::FifoFileIO(std::shared_ptr<FifoFile> fifo_file,
73+
StringRef other_endpoint_name)
4774
: m_fifo_file(fifo_file), m_other_endpoint_name(other_endpoint_name) {}
4875

4976
Expected<json::Value> FifoFileIO::ReadJSON(std::chrono::milliseconds timeout) {
@@ -52,11 +79,27 @@ Expected<json::Value> FifoFileIO::ReadJSON(std::chrono::milliseconds timeout) {
5279
std::optional<std::string> line;
5380
std::future<void> *future =
5481
new std::future<void>(std::async(std::launch::async, [&]() {
55-
std::ifstream reader(m_fifo_file, std::ifstream::in);
82+
#if defined(_WIN32)
83+
std::string buffer;
84+
buffer.reserve(4096);
85+
char ch;
86+
DWORD bytes_read = 0;
87+
while (ReadFile(m_fifo_file->m_pipe_fd, &ch, 1, &bytes_read, NULL) &&
88+
(bytes_read == 1)) {
89+
buffer.push_back(ch);
90+
if (ch == '\n') {
91+
break;
92+
}
93+
}
94+
if (!buffer.empty())
95+
line = std::move(buffer);
96+
#else
97+
std::ifstream reader(m_fifo_file->m_path, std::ifstream::in);
5698
std::string buffer;
5799
std::getline(reader, buffer);
58100
if (!buffer.empty())
59101
line = buffer;
102+
#endif
60103
}));
61104
if (future->wait_for(timeout) == std::future_status::timeout || !line)
62105
// Indeed this is a leak, but it's intentional. "future" obj destructor
@@ -78,9 +121,18 @@ Error FifoFileIO::SendJSON(const json::Value &json,
78121
bool done = false;
79122
std::future<void> *future =
80123
new std::future<void>(std::async(std::launch::async, [&]() {
81-
std::ofstream writer(m_fifo_file, std::ofstream::out);
124+
#if defined(_WIN32)
125+
std::string buffer = JSONToString(json);
126+
buffer.append("\n");
127+
DWORD bytes_write = 0;
128+
WriteFile(m_fifo_file->m_pipe_fd, buffer.c_str(), buffer.size(),
129+
&bytes_write, NULL);
130+
done = bytes_write == buffer.size();
131+
#else
132+
std::ofstream writer(m_fifo_file->m_path, std::ofstream::out);
82133
writer << JSONToString(json) << std::endl;
83134
done = true;
135+
#endif
84136
}));
85137
if (future->wait_for(timeout) == std::future_status::timeout || !done) {
86138
// Indeed this is a leak, but it's intentional. "future" obj destructor will
@@ -98,4 +150,20 @@ Error FifoFileIO::SendJSON(const json::Value &json,
98150
return Error::success();
99151
}
100152

153+
#if defined(_WIN32)
154+
bool FifoFileIO::Connect() {
155+
if (m_fifo_file->m_is_server) {
156+
return ConnectNamedPipe(m_fifo_file->m_pipe_fd, NULL);
157+
}
158+
if (!WaitNamedPipeA(m_fifo_file->m_path.c_str(), NMPWAIT_WAIT_FOREVER))
159+
return false;
160+
m_fifo_file->m_pipe_fd =
161+
CreateFileA(m_fifo_file->m_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0,
162+
NULL, OPEN_EXISTING, 0, NULL);
163+
if (m_fifo_file->m_pipe_fd == INVALID_HANDLE_VALUE)
164+
return false;
165+
return true;
166+
}
167+
#endif
168+
101169
} // namespace lldb_dap

lldb/tools/lldb-dap/FifoFiles.h

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,62 @@
1212
#include "llvm/Support/Error.h"
1313
#include "llvm/Support/JSON.h"
1414

15+
#if defined(_WIN32)
16+
#include "lldb/Host/windows/windows.h"
17+
#endif
18+
1519
#include <chrono>
1620

1721
namespace lldb_dap {
1822

23+
class FifoFileIO;
24+
1925
/// Struct that controls the life of a fifo file in the filesystem.
2026
///
2127
/// The file is destroyed when the destructor is invoked.
2228
struct FifoFile {
23-
FifoFile(llvm::StringRef path);
29+
#if defined(_WIN32)
30+
FifoFile(llvm::StringRef path, HANDLE handle, bool is_server);
31+
#else
32+
FifoFile(llvm::StringRef path, bool is_server);
33+
#endif
2434

2535
~FifoFile();
2636

2737
std::string m_path;
38+
bool m_is_server;
39+
#if defined(_WIN32)
40+
HANDLE m_pipe_fd = INVALID_HANDLE_VALUE;
41+
#endif
42+
43+
friend FifoFileIO;
2844
};
2945

3046
/// Create a fifo file in the filesystem.
3147
///
3248
/// \param[in] path
3349
/// The path for the fifo file.
3450
///
51+
/// \param[in] is_server
52+
/// If \a is_server is true, then created instance of FifoFile will own
53+
/// created file.
54+
///
3555
/// \return
3656
/// A \a std::shared_ptr<FifoFile> if the file could be created, or an
3757
/// \a llvm::Error in case of failures.
38-
llvm::Expected<std::shared_ptr<FifoFile>> CreateFifoFile(llvm::StringRef path);
58+
llvm::Expected<std::shared_ptr<FifoFile>> CreateFifoFile(llvm::StringRef path,
59+
bool is_server);
3960

4061
class FifoFileIO {
4162
public:
4263
/// \param[in] fifo_file
43-
/// The path to an input fifo file that exists in the file system.
64+
/// The std::shared_ptr<FifoFile> to an existing fifo file.
4465
///
4566
/// \param[in] other_endpoint_name
4667
/// A human readable name for the other endpoint that will communicate
4768
/// using this file. This is used for error messages.
48-
FifoFileIO(llvm::StringRef fifo_file, llvm::StringRef other_endpoint_name);
69+
FifoFileIO(std::shared_ptr<FifoFile> fifo_file,
70+
llvm::StringRef other_endpoint_name);
4971

5072
/// Read the next JSON object from the underlying input fifo file.
5173
///
@@ -75,8 +97,12 @@ class FifoFileIO {
7597
const llvm::json::Value &json,
7698
std::chrono::milliseconds timeout = std::chrono::milliseconds(20000));
7799

100+
#if defined(_WIN32)
101+
bool Connect();
102+
#endif
103+
78104
private:
79-
std::string m_fifo_file;
105+
std::shared_ptr<FifoFile> m_fifo_file;
80106
std::string m_other_endpoint_name;
81107
};
82108

lldb/tools/lldb-dap/Handler/RequestHandler.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
119119
CreateRunInTerminalCommFile();
120120
if (!comm_file_or_err)
121121
return comm_file_or_err.takeError();
122-
FifoFile &comm_file = *comm_file_or_err.get();
122+
std::shared_ptr<FifoFile> comm_file = *comm_file_or_err;
123123

124-
RunInTerminalDebugAdapterCommChannel comm_channel(comm_file.m_path);
124+
RunInTerminalDebugAdapterCommChannel comm_channel(comm_file);
125125

126126
lldb::pid_t debugger_pid = LLDB_INVALID_PROCESS_ID;
127127
#if !defined(_WIN32)
@@ -130,10 +130,16 @@ RunInTerminal(DAP &dap, const protocol::LaunchRequestArguments &arguments) {
130130

131131
llvm::json::Object reverse_request = CreateRunInTerminalReverseRequest(
132132
*arguments.configuration.program, arguments.args, arguments.env,
133-
arguments.cwd.value_or(""), comm_file.m_path, debugger_pid);
133+
arguments.cwd.value_or(""), comm_file->m_path, debugger_pid);
134134
dap.SendReverseRequest<LogFailureResponseHandler>("runInTerminal",
135135
std::move(reverse_request));
136136

137+
#if defined(_WIN32)
138+
if (!comm_channel.Connect())
139+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
140+
"Failed to connect to the named pipe.");
141+
#endif
142+
137143
if (llvm::Expected<lldb::pid_t> pid = comm_channel.GetLauncherPid())
138144
attach_info.SetProcessID(*pid);
139145
else

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1463,7 +1463,7 @@ llvm::json::Object CreateRunInTerminalReverseRequest(
14631463
req_args.push_back("--launch-target");
14641464
req_args.push_back(program.str());
14651465
req_args.insert(req_args.end(), args.begin(), args.end());
1466-
run_in_terminal_args.try_emplace("args", args);
1466+
run_in_terminal_args.try_emplace("args", req_args);
14671467

14681468
if (!cwd.empty())
14691469
run_in_terminal_args.try_emplace("cwd", cwd);

lldb/tools/lldb-dap/RunInTerminal.cpp

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
#include "RunInTerminal.h"
1010
#include "JSONUtils.h"
1111

12-
#if !defined(_WIN32)
12+
#if defined(_WIN32)
13+
#include "lldb/Host/windows/windows.h"
14+
#else
1315
#include <sys/stat.h>
1416
#include <sys/types.h>
1517
#include <unistd.h>
@@ -96,7 +98,7 @@ static Error ToError(const RunInTerminalMessage &message) {
9698
}
9799

98100
RunInTerminalLauncherCommChannel::RunInTerminalLauncherCommChannel(
99-
StringRef comm_file)
101+
std::shared_ptr<FifoFile> comm_file)
100102
: m_io(comm_file, "debug adapter") {}
101103

102104
Error RunInTerminalLauncherCommChannel::WaitUntilDebugAdapterAttaches(
@@ -111,8 +113,8 @@ Error RunInTerminalLauncherCommChannel::WaitUntilDebugAdapterAttaches(
111113
return message.takeError();
112114
}
113115

114-
Error RunInTerminalLauncherCommChannel::NotifyPid() {
115-
return m_io.SendJSON(RunInTerminalMessagePid(getpid()).ToJSON());
116+
Error RunInTerminalLauncherCommChannel::NotifyPid(lldb::pid_t pid) {
117+
return m_io.SendJSON(RunInTerminalMessagePid(pid).ToJSON());
116118
}
117119

118120
void RunInTerminalLauncherCommChannel::NotifyError(StringRef error) {
@@ -121,8 +123,12 @@ void RunInTerminalLauncherCommChannel::NotifyError(StringRef error) {
121123
llvm::errs() << llvm::toString(std::move(err)) << "\n";
122124
}
123125

126+
#if defined(_WIN32)
127+
bool RunInTerminalLauncherCommChannel::Connect() { return m_io.Connect(); }
128+
#endif
129+
124130
RunInTerminalDebugAdapterCommChannel::RunInTerminalDebugAdapterCommChannel(
125-
StringRef comm_file)
131+
std::shared_ptr<FifoFile> comm_file)
126132
: m_io(comm_file, "runInTerminal launcher") {}
127133

128134
// 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() {
148154
}
149155
}
150156

157+
#if defined(_WIN32)
158+
bool RunInTerminalDebugAdapterCommChannel::Connect() { return m_io.Connect(); }
159+
#endif
160+
151161
std::string RunInTerminalDebugAdapterCommChannel::GetLauncherError() {
152162
// We know there's been an error, so a small timeout is enough.
153163
if (Expected<RunInTerminalMessageUP> message =
@@ -158,13 +168,25 @@ std::string RunInTerminalDebugAdapterCommChannel::GetLauncherError() {
158168
}
159169

160170
Expected<std::shared_ptr<FifoFile>> CreateRunInTerminalCommFile() {
171+
#if defined(_WIN32)
172+
static constexpr llvm::StringLiteral g_pipe_name_prefix = "\\\\.\\Pipe\\";
173+
SmallString<256> comm_file;
174+
sys::fs::createUniquePath("lldb-dap-run-in-terminal-comm-%%%%%%", comm_file,
175+
+false);
176+
return CreateFifoFile((g_pipe_name_prefix + comm_file.str()).str(), true);
177+
#else
161178
SmallString<256> comm_file;
162179
if (std::error_code EC = sys::fs::getPotentiallyUniqueTempFileName(
163180
"lldb-dap-run-in-terminal-comm", "", comm_file))
164181
return createStringError(EC, "Error making unique file name for "
165182
"runInTerminal communication files");
183+
return CreateFifoFile(comm_file.str(), true);
184+
#endif
185+
}
166186

167-
return CreateFifoFile(comm_file.str());
187+
Expected<std::shared_ptr<FifoFile>>
188+
OpenRunInTerminalCommFile(llvm::StringRef fifo_file) {
189+
return CreateFifoFile(fifo_file, false);
168190
}
169191

170192
} // namespace lldb_dap

0 commit comments

Comments
 (0)