Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 95b85c7

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

File tree

8 files changed

+237
-37
lines changed
Filter options

8 files changed

+237
-37
lines changed

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

Copy file name to clipboardExpand all lines: lldb/test/API/tools/lldb-dap/runInTerminal/TestDAP_runInTerminal.py
-3Lines 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

Copy file name to clipboardExpand all lines: lldb/tools/lldb-dap/FifoFiles.cpp
+77-9Lines 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

Copy file name to clipboardExpand all lines: lldb/tools/lldb-dap/FifoFiles.h
+31-5Lines 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

Copy file name to clipboardExpand all lines: lldb/tools/lldb-dap/Handler/RequestHandler.cpp
+9-3Lines 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

Copy file name to clipboardExpand all lines: lldb/tools/lldb-dap/JSONUtils.cpp
+1-1Lines 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

Copy file name to clipboardExpand all lines: lldb/tools/lldb-dap/RunInTerminal.cpp
+28-6Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.