Skip to content

Commit 4131b8c

Browse files
committed
[lldb-dap] Ensure the IO forwarding threads are managed by the DAP object lifecycle.
This moves the ownership of the threads that forward stdout/stderr to the DAP object itself to ensure that the threads are joined and that the forwarding is cleaned up when the DAP connection is disconnected. This is part of a larger refactor to allow lldb-dap to run in a listening mode and accept multiple connections.
1 parent 1418018 commit 4131b8c

File tree

8 files changed

+231
-213
lines changed

8 files changed

+231
-213
lines changed

lldb/tools/lldb-dap/CMakeLists.txt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
if ( CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "NetBSD" )
2-
list(APPEND extra_libs lldbHost)
3-
endif ()
4-
51
if (HAVE_LIBPTHREAD)
62
list(APPEND extra_libs pthread)
73
endif ()
@@ -32,7 +28,6 @@ add_lldb_tool(lldb-dap
3228
IOStream.cpp
3329
JSONUtils.cpp
3430
LLDBUtils.cpp
35-
OutputRedirector.cpp
3631
ProgressEvent.cpp
3732
RunInTerminal.cpp
3833
SourceBreakpoint.cpp
@@ -42,6 +37,7 @@ add_lldb_tool(lldb-dap
4237

4338
LINK_LIBS
4439
liblldb
40+
lldbHost
4541
${extra_libs}
4642

4743
LINK_COMPONENTS

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 88 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,53 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#include <chrono>
10-
#include <cstdarg>
11-
#include <fstream>
12-
#include <mutex>
13-
149
#include "DAP.h"
1510
#include "JSONUtils.h"
1611
#include "LLDBUtils.h"
12+
#include "lldb/API/SBBreakpoint.h"
1713
#include "lldb/API/SBCommandInterpreter.h"
1814
#include "lldb/API/SBLanguageRuntime.h"
1915
#include "lldb/API/SBListener.h"
2016
#include "lldb/API/SBStream.h"
17+
#include "lldb/Host/FileSystem.h"
18+
#include "lldb/API/SBCommandReturnObject.h"
19+
#include "lldb/API/SBProcess.h"
20+
#include "lldb/Utility/Status.h"
21+
#include "lldb/lldb-defines.h"
22+
#include "lldb/lldb-enumerations.h"
23+
#include "llvm/ADT/ArrayRef.h"
24+
#include "llvm/ADT/Twine.h"
2125
#include "llvm/ADT/StringExtras.h"
26+
#include "llvm/Support/Error.h"
2227
#include "llvm/Support/FormatVariadic.h"
28+
#include "llvm/Support/ErrorHandling.h"
29+
#include "llvm/Support/raw_ostream.h"
30+
#include <algorithm>
31+
#include <cassert>
32+
#include <chrono>
33+
#include <cstdarg>
34+
#include <cstdio>
35+
#include <fstream>
36+
#include <mutex>
37+
#include <utility>
2338

2439
#if defined(_WIN32)
2540
#define NOMINMAX
2641
#include <fcntl.h>
2742
#include <io.h>
2843
#include <windows.h>
44+
#else
45+
#include <unistd.h>
2946
#endif
3047

3148
using namespace lldb_dap;
3249

3350
namespace lldb_dap {
3451

35-
DAP::DAP(llvm::StringRef path, ReplMode repl_mode)
36-
: debug_adaptor_path(path), broadcaster("lldb-dap"),
52+
DAP::DAP(llvm::StringRef path, std::optional<std::ofstream> &log,
53+
ReplMode repl_mode, StreamDescriptor input, StreamDescriptor output)
54+
: debug_adaptor_path(path), log(log), input(std::move(input)),
55+
output(std::move(output)), broadcaster("lldb-dap"),
3756
exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID),
3857
stop_at_entry(false), is_attach(false),
3958
enable_auto_variable_summaries(false),
@@ -43,21 +62,7 @@ DAP::DAP(llvm::StringRef path, ReplMode repl_mode)
4362
configuration_done_sent(false), waiting_for_run_in_terminal(false),
4463
progress_event_reporter(
4564
[&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
46-
reverse_request_seq(0), repl_mode(repl_mode) {
47-
const char *log_file_path = getenv("LLDBDAP_LOG");
48-
#if defined(_WIN32)
49-
// Windows opens stdout and stdin in text mode which converts \n to 13,10
50-
// while the value is just 10 on Darwin/Linux. Setting the file mode to binary
51-
// fixes this.
52-
int result = _setmode(fileno(stdout), _O_BINARY);
53-
assert(result);
54-
result = _setmode(fileno(stdin), _O_BINARY);
55-
UNUSED_IF_ASSERT_DISABLED(result);
56-
assert(result);
57-
#endif
58-
if (log_file_path)
59-
log.reset(new std::ofstream(log_file_path));
60-
}
65+
reverse_request_seq(0), repl_mode(repl_mode) {}
6166

6267
DAP::~DAP() = default;
6368

@@ -173,6 +178,63 @@ ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
173178
return nullptr;
174179
}
175180

181+
llvm::Error DAP::ConfigureIO(std::optional<std::FILE *> overrideOut,
182+
std::optional<std::FILE *> overrideErr) {
183+
auto *inull = lldb_private::FileSystem::Instance().Fopen(
184+
lldb_private::FileSystem::DEV_NULL, "w");
185+
in = lldb::SBFile(inull, true);
186+
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();
194+
195+
if (overrideOut) {
196+
if (dup2(pout.GetWriteFileDescriptor(), fileno(*overrideOut)) == -1) {
197+
return llvm::make_error<llvm::StringError>(
198+
llvm::errnoAsErrorCode(),
199+
llvm::formatv("override fd=%d failed", fileno(*overrideOut))
200+
.str()
201+
.c_str());
202+
}
203+
}
204+
205+
if (overrideErr) {
206+
if (dup2(perr.GetWriteFileDescriptor(), fileno(*overrideErr)) == -1) {
207+
return llvm::make_error<llvm::StringError>(
208+
llvm::errnoAsErrorCode(),
209+
llvm::formatv("override fd=%d failed", fileno(*overrideErr))
210+
.str()
211+
.c_str());
212+
}
213+
}
214+
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+
235+
return llvm::Error::success();
236+
}
237+
176238
// Send the JSON in "json_str" to the "out" stream. Correctly send the
177239
// "Content-Length:" field followed by the length, followed by the raw
178240
// JSON bytes.
@@ -208,19 +270,19 @@ std::string DAP::ReadJSON() {
208270
std::string json_str;
209271
int length;
210272

211-
if (!input.read_expected(log.get(), "Content-Length: "))
273+
if (!input.read_expected(log, "Content-Length: "))
212274
return json_str;
213275

214-
if (!input.read_line(log.get(), length_str))
276+
if (!input.read_line(log, length_str))
215277
return json_str;
216278

217279
if (!llvm::to_integer(length_str, length))
218280
return json_str;
219281

220-
if (!input.read_expected(log.get(), "\r\n"))
282+
if (!input.read_expected(log, "\r\n"))
221283
return json_str;
222284

223-
if (!input.read_full(log.get(), length, json_str))
285+
if (!input.read_full(log, length, json_str))
224286
return json_str;
225287

226288
if (log) {

lldb/tools/lldb-dap/DAP.h

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,38 @@
99
#ifndef LLDB_TOOLS_LLDB_DAP_DAP_H
1010
#define LLDB_TOOLS_LLDB_DAP_DAP_H
1111

12-
#include <cstdio>
13-
#include <iosfwd>
14-
#include <map>
15-
#include <optional>
16-
#include <thread>
17-
18-
#include "llvm/ADT/DenseMap.h"
19-
#include "llvm/ADT/DenseSet.h"
20-
#include "llvm/ADT/StringMap.h"
21-
#include "llvm/ADT/StringRef.h"
22-
#include "llvm/Support/JSON.h"
23-
#include "llvm/Support/Threading.h"
24-
#include "llvm/Support/raw_ostream.h"
25-
26-
#include "lldb/API/SBAttachInfo.h"
27-
#include "lldb/API/SBCommandInterpreter.h"
28-
#include "lldb/API/SBCommandReturnObject.h"
29-
#include "lldb/API/SBDebugger.h"
30-
#include "lldb/API/SBEvent.h"
31-
#include "lldb/API/SBFormat.h"
32-
#include "lldb/API/SBLaunchInfo.h"
33-
#include "lldb/API/SBTarget.h"
34-
#include "lldb/API/SBThread.h"
35-
12+
#include "DAPForward.h"
3613
#include "ExceptionBreakpoint.h"
3714
#include "FunctionBreakpoint.h"
3815
#include "IOStream.h"
3916
#include "InstructionBreakpoint.h"
4017
#include "ProgressEvent.h"
4118
#include "SourceBreakpoint.h"
19+
#include "lldb/API/SBBroadcaster.h"
20+
#include "lldb/API/SBCommandInterpreter.h"
21+
#include "lldb/API/SBDebugger.h"
22+
#include "lldb/API/SBError.h"
23+
#include "lldb/API/SBFile.h"
24+
#include "lldb/API/SBFormat.h"
25+
#include "lldb/API/SBFrame.h"
26+
#include "lldb/API/SBTarget.h"
27+
#include "lldb/API/SBThread.h"
28+
#include "lldb/API/SBValue.h"
29+
#include "lldb/API/SBValueList.h"
30+
#include "lldb/Host/Pipe.h"
31+
#include "lldb/lldb-types.h"
32+
#include "llvm/ADT/DenseMap.h"
33+
#include "llvm/ADT/DenseSet.h"
34+
#include "llvm/ADT/StringMap.h"
35+
#include "llvm/ADT/StringRef.h"
36+
#include "llvm/Support/Error.h"
37+
#include "llvm/Support/JSON.h"
38+
#include "llvm/Support/Threading.h"
39+
#include <map>
40+
#include <mutex>
41+
#include <optional>
42+
#include <thread>
43+
#include <vector>
4244

4345
#define VARREF_LOCALS (int64_t)1
4446
#define VARREF_GLOBALS (int64_t)2
@@ -138,15 +140,20 @@ struct SendEventRequestHandler : public lldb::SBCommandPluginInterface {
138140

139141
struct DAP {
140142
llvm::StringRef debug_adaptor_path;
143+
std::optional<std::ofstream> &log;
141144
InputStream input;
142145
OutputStream output;
146+
lldb::SBFile in;
147+
lldb_private::Pipe pout;
148+
lldb_private::Pipe perr;
143149
lldb::SBDebugger debugger;
144150
lldb::SBTarget target;
145151
Variables variables;
146152
lldb::SBBroadcaster broadcaster;
147153
std::thread event_thread;
148154
std::thread progress_event_thread;
149-
std::unique_ptr<std::ofstream> log;
155+
std::thread stdout_forward_thread;
156+
std::thread stderr_forward_thread;
150157
llvm::StringMap<SourceBreakpointMap> source_breakpoints;
151158
FunctionBreakpointMap function_breakpoints;
152159
InstructionBreakpointMap instruction_breakpoints;
@@ -198,13 +205,21 @@ struct DAP {
198205
// will contain that expression.
199206
std::string last_nonempty_var_expression;
200207

201-
DAP(llvm::StringRef path, ReplMode repl_mode);
208+
DAP(llvm::StringRef path, std::optional<std::ofstream> &log,
209+
ReplMode repl_mode, StreamDescriptor input, StreamDescriptor output);
202210
~DAP();
203211
DAP(const DAP &rhs) = delete;
204212
void operator=(const DAP &rhs) = delete;
205213
ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter);
206214
ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);
207215

216+
/// Redirect stdout and stderr fo the IDE's console output.
217+
///
218+
/// Errors in this operation will be printed to the log file and the IDE's
219+
/// console output as well.
220+
llvm::Error ConfigureIO(std::optional<std::FILE *> overrideOut,
221+
std::optional<std::FILE *> overrideErr);
222+
208223
// Serialize the JSON value into a string and send the JSON packet to
209224
// the "out" stream.
210225
void SendJSON(const llvm::json::Value &json);

lldb/tools/lldb-dap/IOStream.cpp

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

134-
bool InputStream::read_line(std::ofstream *log, std::string &line) {
134+
bool InputStream::read_line(std::optional<std::ofstream> &log,
135+
std::string &line) {
135136
line.clear();
136137
while (true) {
137138
if (!read_full(log, 1, line))
@@ -144,7 +145,8 @@ bool InputStream::read_line(std::ofstream *log, std::string &line) {
144145
return true;
145146
}
146147

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

lldb/tools/lldb-dap/IOStream.h

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,24 @@ struct StreamDescriptor {
5252
struct InputStream {
5353
StreamDescriptor descriptor;
5454

55-
bool read_full(std::ofstream *log, size_t length, std::string &text);
55+
explicit InputStream(StreamDescriptor descriptor)
56+
: descriptor(std::move(descriptor)) {};
5657

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

59-
bool read_expected(std::ofstream *log, llvm::StringRef expected);
61+
bool read_line(std::optional<std::ofstream> &log, std::string &line);
62+
63+
bool read_expected(std::optional<std::ofstream> &log,
64+
llvm::StringRef expected);
6065
};
6166

6267
struct OutputStream {
6368
StreamDescriptor descriptor;
6469

70+
explicit OutputStream(StreamDescriptor descriptor)
71+
: descriptor(std::move(descriptor)) {};
72+
6573
bool write_full(llvm::StringRef str);
6674
};
6775
} // namespace lldb_dap

0 commit comments

Comments
 (0)