Skip to content

Commit ce969ff

Browse files
committed
Moving the output redirection logic back into the OutputRedirector.cpp file and making it into an object instead.
This removes some of the duplicate code that was happening between stdout/stderr redirection.
1 parent 1051020 commit ce969ff

File tree

8 files changed

+212
-73
lines changed

8 files changed

+212
-73
lines changed

lldb/tools/lldb-dap/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,19 @@ add_lldb_tool(lldb-dap
2222
lldb-dap.cpp
2323
Breakpoint.cpp
2424
BreakpointBase.cpp
25+
DAP.cpp
2526
ExceptionBreakpoint.cpp
2627
FifoFiles.cpp
2728
FunctionBreakpoint.cpp
29+
InstructionBreakpoint.cpp
2830
IOStream.cpp
2931
JSONUtils.cpp
3032
LLDBUtils.cpp
33+
OutputRedirector.cpp
3134
ProgressEvent.cpp
3235
RunInTerminal.cpp
3336
SourceBreakpoint.cpp
34-
DAP.cpp
3537
Watchpoint.cpp
36-
InstructionBreakpoint.cpp
3738

3839
LINK_LIBS
3940
liblldb

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "DAP.h"
1010
#include "JSONUtils.h"
1111
#include "LLDBUtils.h"
12+
#include "OutputRedirector.h"
1213
#include "lldb/API/SBBreakpoint.h"
1314
#include "lldb/API/SBCommandInterpreter.h"
1415
#include "lldb/API/SBCommandReturnObject.h"
@@ -49,8 +50,8 @@ using namespace lldb_dap;
4950

5051
namespace lldb_dap {
5152

52-
DAP::DAP(llvm::StringRef path, std::optional<std::ofstream> &log,
53-
ReplMode repl_mode, StreamDescriptor input, StreamDescriptor output)
53+
DAP::DAP(llvm::StringRef path, std::ofstream *log, ReplMode repl_mode,
54+
StreamDescriptor input, StreamDescriptor output)
5455
: debug_adaptor_path(path), log(log), input(std::move(input)),
5556
output(std::move(output)), broadcaster("lldb-dap"),
5657
exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID),
@@ -178,63 +179,55 @@ ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
178179
return nullptr;
179180
}
180181

181-
llvm::Error DAP::ConfigureIO(std::optional<std::FILE *> overrideOut,
182-
std::optional<std::FILE *> overrideErr) {
182+
llvm::Error DAP::ConfigureIO(std::FILE *overrideOut, std::FILE *overrideErr) {
183183
auto *inull = lldb_private::FileSystem::Instance().Fopen(
184184
lldb_private::FileSystem::DEV_NULL, "w");
185185
in = lldb::SBFile(inull, true);
186186

187-
lldb_private::Status status;
188-
status = pout.CreateNew(/*child_process_inherit=*/false);
189-
if (status.Fail())
190-
return status.takeError();
191-
status = perr.CreateNew(/*child_process_inherit=*/false);
192-
if (status.Fail())
193-
return status.takeError();
187+
if (auto Error = out.RedirectTo([this](llvm::StringRef output) {
188+
SendOutput(OutputType::Stdout, output);
189+
}))
190+
return Error;
194191

195192
if (overrideOut) {
196-
if (dup2(pout.GetWriteFileDescriptor(), fileno(*overrideOut)) == -1) {
193+
auto fd = out.GetWriteFileDescriptor();
194+
if (auto Error = fd.takeError())
195+
return Error;
196+
197+
if (dup2(*fd, fileno(overrideOut)) == -1)
197198
return llvm::make_error<llvm::StringError>(
198199
llvm::errnoAsErrorCode(),
199-
llvm::formatv("override fd=%d failed", fileno(*overrideOut))
200+
llvm::formatv("override fd=%d failed", fileno(overrideOut))
200201
.str()
201202
.c_str());
202-
}
203203
}
204204

205+
if (auto Error = err.RedirectTo([this](llvm::StringRef output) {
206+
SendOutput(OutputType::Stderr, output);
207+
}))
208+
return Error;
209+
205210
if (overrideErr) {
206-
if (dup2(perr.GetWriteFileDescriptor(), fileno(*overrideErr)) == -1) {
211+
auto fd = err.GetWriteFileDescriptor();
212+
if (auto Error = fd.takeError())
213+
return Error;
214+
215+
if (dup2(*fd, fileno(overrideErr)) == -1)
207216
return llvm::make_error<llvm::StringError>(
208217
llvm::errnoAsErrorCode(),
209-
llvm::formatv("override fd=%d failed", fileno(*overrideErr))
218+
llvm::formatv("override fd=%d failed", fileno(overrideErr))
210219
.str()
211220
.c_str());
212-
}
213221
}
214222

215-
auto forwarder = [&](lldb_private::Pipe &pipe, OutputType outputType) {
216-
char buffer[4098];
217-
size_t bytes_read;
218-
while (pipe.CanRead()) {
219-
lldb_private::Status error = pipe.ReadWithTimeout(
220-
&buffer, sizeof(buffer), std::chrono::seconds(1), bytes_read);
221-
if (error.Success()) {
222-
// zero bytes returned on EOF.
223-
if (bytes_read == 0)
224-
break;
225-
SendOutput(outputType, llvm::StringRef(buffer, bytes_read));
226-
}
227-
}
228-
};
229-
230-
stdout_forward_thread =
231-
std::thread(forwarder, std::ref(pout), OutputType::Stdout);
232-
stderr_forward_thread =
233-
std::thread(forwarder, std::ref(perr), OutputType::Stderr);
234-
235223
return llvm::Error::success();
236224
}
237225

226+
void DAP::StopIO() {
227+
out.Stop();
228+
err.Stop();
229+
}
230+
238231
// Send the JSON in "json_str" to the "out" stream. Correctly send the
239232
// "Content-Length:" field followed by the length, followed by the raw
240233
// JSON bytes.

lldb/tools/lldb-dap/DAP.h

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "FunctionBreakpoint.h"
1515
#include "IOStream.h"
1616
#include "InstructionBreakpoint.h"
17+
#include "OutputRedirector.h"
1718
#include "ProgressEvent.h"
1819
#include "SourceBreakpoint.h"
1920
#include "lldb/API/SBBroadcaster.h"
@@ -27,7 +28,6 @@
2728
#include "lldb/API/SBThread.h"
2829
#include "lldb/API/SBValue.h"
2930
#include "lldb/API/SBValueList.h"
30-
#include "lldb/Host/Pipe.h"
3131
#include "lldb/lldb-types.h"
3232
#include "llvm/ADT/DenseMap.h"
3333
#include "llvm/ADT/DenseSet.h"
@@ -140,12 +140,12 @@ struct SendEventRequestHandler : public lldb::SBCommandPluginInterface {
140140

141141
struct DAP {
142142
llvm::StringRef debug_adaptor_path;
143-
std::optional<std::ofstream> &log;
143+
std::ofstream *log;
144144
InputStream input;
145145
OutputStream output;
146146
lldb::SBFile in;
147-
lldb_private::Pipe pout;
148-
lldb_private::Pipe perr;
147+
OutputRedirector out;
148+
OutputRedirector err;
149149
lldb::SBDebugger debugger;
150150
lldb::SBTarget target;
151151
Variables variables;
@@ -205,8 +205,8 @@ struct DAP {
205205
// will contain that expression.
206206
std::string last_nonempty_var_expression;
207207

208-
DAP(llvm::StringRef path, std::optional<std::ofstream> &log,
209-
ReplMode repl_mode, StreamDescriptor input, StreamDescriptor output);
208+
DAP(llvm::StringRef path, std::ofstream *log, ReplMode repl_mode,
209+
StreamDescriptor input, StreamDescriptor output);
210210
~DAP();
211211
DAP(const DAP &rhs) = delete;
212212
void operator=(const DAP &rhs) = delete;
@@ -217,8 +217,10 @@ struct DAP {
217217
///
218218
/// Errors in this operation will be printed to the log file and the IDE's
219219
/// console output as well.
220-
llvm::Error ConfigureIO(std::optional<std::FILE *> overrideOut,
221-
std::optional<std::FILE *> overrideErr);
220+
llvm::Error ConfigureIO(std::FILE *overrideOut, std::FILE *overrideErr);
221+
222+
/// Stop the redirected IO threads and associated pipes.
223+
void StopIO();
222224

223225
// Serialize the JSON value into a string and send the JSON packet to
224226
// the "out" stream.

lldb/tools/lldb-dap/IOStream.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ bool OutputStream::write_full(llvm::StringRef str) {
8787
return true;
8888
}
8989

90-
bool InputStream::read_full(std::optional<std::ofstream> &log, size_t length,
90+
bool InputStream::read_full(std::ofstream *log, size_t length,
9191
std::string &text) {
9292
std::string data;
9393
data.resize(length);
@@ -131,8 +131,7 @@ bool InputStream::read_full(std::optional<std::ofstream> &log, size_t length,
131131
return true;
132132
}
133133

134-
bool InputStream::read_line(std::optional<std::ofstream> &log,
135-
std::string &line) {
134+
bool InputStream::read_line(std::ofstream *log, std::string &line) {
136135
line.clear();
137136
while (true) {
138137
if (!read_full(log, 1, line))
@@ -145,8 +144,7 @@ bool InputStream::read_line(std::optional<std::ofstream> &log,
145144
return true;
146145
}
147146

148-
bool InputStream::read_expected(std::optional<std::ofstream> &log,
149-
llvm::StringRef expected) {
147+
bool InputStream::read_expected(std::ofstream *log, llvm::StringRef expected) {
150148
std::string result;
151149
if (!read_full(log, expected.size(), result))
152150
return false;

lldb/tools/lldb-dap/IOStream.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,11 @@ struct InputStream {
5555
explicit InputStream(StreamDescriptor descriptor)
5656
: descriptor(std::move(descriptor)) {};
5757

58-
bool read_full(std::optional<std::ofstream> &log, size_t length,
59-
std::string &text);
58+
bool read_full(std::ofstream *log, size_t length, std::string &text);
6059

61-
bool read_line(std::optional<std::ofstream> &log, std::string &line);
60+
bool read_line(std::ofstream *log, std::string &line);
6261

63-
bool read_expected(std::optional<std::ofstream> &log,
64-
llvm::StringRef expected);
62+
bool read_expected(std::ofstream *log, llvm::StringRef expected);
6563
};
6664

6765
struct OutputStream {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//===-- OutputRedirector.cpp -----------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===/
8+
9+
#include "llvm/Support/Error.h"
10+
#include <system_error>
11+
#if defined(_WIN32)
12+
#include <fcntl.h>
13+
#include <io.h>
14+
#else
15+
#include <unistd.h>
16+
#endif
17+
18+
#include "DAP.h"
19+
#include "OutputRedirector.h"
20+
#include "llvm/ADT/StringRef.h"
21+
22+
using lldb_private::Pipe;
23+
using lldb_private::Status;
24+
using llvm::createStringError;
25+
using llvm::Error;
26+
using llvm::Expected;
27+
using llvm::StringRef;
28+
29+
namespace lldb_dap {
30+
31+
Expected<int> OutputRedirector::GetWriteFileDescriptor() {
32+
if (!m_pipe.CanWrite())
33+
return createStringError(std::errc::bad_file_descriptor,
34+
"write handle is not open for writing");
35+
return m_pipe.GetWriteFileDescriptor();
36+
}
37+
38+
Error OutputRedirector::RedirectTo(std::function<void(StringRef)> callback) {
39+
Status status = m_pipe.CreateNew(/*child_process_inherit=*/false);
40+
if (status.Fail())
41+
return status.takeError();
42+
43+
m_forwarder = std::thread([this, callback]() {
44+
char buffer[OutputBufferSize];
45+
while (m_pipe.CanRead() && !m_stopped) {
46+
size_t bytes_read;
47+
Status status = m_pipe.Read(&buffer, sizeof(buffer), bytes_read);
48+
if (status.Fail())
49+
continue;
50+
51+
// EOF detected
52+
if (bytes_read == 0)
53+
break;
54+
55+
callback(StringRef(buffer, bytes_read));
56+
}
57+
});
58+
59+
return Error::success();
60+
}
61+
62+
void OutputRedirector::Stop() {
63+
m_stopped = true;
64+
65+
if (m_pipe.CanWrite()) {
66+
// If the fd is waiting for input and is closed it may not return from the
67+
// current select/poll/kqueue/etc. asyncio wait operation. Write a null byte
68+
// to ensure the read fd wakes to detect the closed FD.
69+
char buf[] = "\0";
70+
size_t bytes_written;
71+
m_pipe.Write(buf, sizeof(buf), bytes_written);
72+
m_pipe.CloseWriteFileDescriptor();
73+
m_forwarder.join();
74+
}
75+
}
76+
77+
} // namespace lldb_dap
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===-- OutputRedirector.h -------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===/
8+
9+
#ifndef LLDB_TOOLS_LLDB_DAP_OUTPUT_REDIRECTOR_H
10+
#define LLDB_TOOLS_LLDB_DAP_OUTPUT_REDIRECTOR_H
11+
12+
#include "lldb/Host/Pipe.h"
13+
#include "llvm/ADT/StringRef.h"
14+
#include "llvm/Support/Error.h"
15+
#include <atomic>
16+
#include <functional>
17+
#include <thread>
18+
19+
namespace lldb_dap {
20+
21+
struct OutputRedirector {
22+
/// Creates writable file descriptor that will invoke the given callback on
23+
/// each write in a background thread.
24+
///
25+
/// \return
26+
/// \a Error::success if the redirection was set up correctly, or an error
27+
/// otherwise.
28+
llvm::Error RedirectTo(std::function<void(llvm::StringRef)> callback);
29+
30+
llvm::Expected<int> GetWriteFileDescriptor();
31+
void Stop();
32+
33+
~OutputRedirector() { Stop(); }
34+
35+
OutputRedirector() = default;
36+
OutputRedirector(const OutputRedirector &) = delete;
37+
OutputRedirector &operator=(const OutputRedirector &) = delete;
38+
39+
private:
40+
std::atomic<bool> m_stopped = false;
41+
lldb_private::Pipe m_pipe;
42+
std::thread m_forwarder;
43+
};
44+
45+
} // namespace lldb_dap
46+
47+
#endif // LLDB_TOOLS_LLDB_DAP_OUTPUT_REDIRECTOR_H

0 commit comments

Comments
 (0)