Skip to content

Commit 0d9cf26

Browse files
ashgtilabath
andauthored
[lldb-dap] Ensure the IO forwarding threads are managed by the DAP object lifecycle. (#120457)
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. --------- Co-authored-by: Pavel Labath <pavel@labath.sk>
1 parent 5130a4e commit 0d9cf26

File tree

7 files changed

+314
-157
lines changed

7 files changed

+314
-157
lines changed

lldb/tools/lldb-dap/CMakeLists.txt

+3-6
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 ()
@@ -26,22 +22,23 @@ add_lldb_tool(lldb-dap
2622
lldb-dap.cpp
2723
Breakpoint.cpp
2824
BreakpointBase.cpp
25+
DAP.cpp
2926
ExceptionBreakpoint.cpp
3027
FifoFiles.cpp
3128
FunctionBreakpoint.cpp
29+
InstructionBreakpoint.cpp
3230
IOStream.cpp
3331
JSONUtils.cpp
3432
LLDBUtils.cpp
3533
OutputRedirector.cpp
3634
ProgressEvent.cpp
3735
RunInTerminal.cpp
3836
SourceBreakpoint.cpp
39-
DAP.cpp
4037
Watchpoint.cpp
41-
InstructionBreakpoint.cpp
4238

4339
LINK_LIBS
4440
liblldb
41+
lldbHost
4542
${extra_libs}
4643

4744
LINK_COMPONENTS

lldb/tools/lldb-dap/DAP.cpp

+79-26
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,62 @@
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 "OutputRedirector.h"
13+
#include "lldb/API/SBBreakpoint.h"
1714
#include "lldb/API/SBCommandInterpreter.h"
15+
#include "lldb/API/SBCommandReturnObject.h"
1816
#include "lldb/API/SBLanguageRuntime.h"
1917
#include "lldb/API/SBListener.h"
18+
#include "lldb/API/SBProcess.h"
2019
#include "lldb/API/SBStream.h"
20+
#include "lldb/Host/FileSystem.h"
21+
#include "lldb/Utility/Status.h"
22+
#include "lldb/lldb-defines.h"
23+
#include "lldb/lldb-enumerations.h"
24+
#include "llvm/ADT/ArrayRef.h"
2125
#include "llvm/ADT/StringExtras.h"
26+
#include "llvm/ADT/Twine.h"
27+
#include "llvm/Support/Error.h"
28+
#include "llvm/Support/ErrorHandling.h"
2229
#include "llvm/Support/FormatVariadic.h"
30+
#include "llvm/Support/raw_ostream.h"
31+
#include <algorithm>
32+
#include <cassert>
33+
#include <chrono>
34+
#include <cstdarg>
35+
#include <cstdio>
36+
#include <fstream>
37+
#include <mutex>
38+
#include <utility>
2339

2440
#if defined(_WIN32)
2541
#define NOMINMAX
2642
#include <fcntl.h>
2743
#include <io.h>
2844
#include <windows.h>
45+
#else
46+
#include <unistd.h>
2947
#endif
3048

3149
using namespace lldb_dap;
3250

51+
namespace {
52+
#ifdef _WIN32
53+
const char DEV_NULL[] = "nul";
54+
#else
55+
const char DEV_NULL[] = "/dev/null";
56+
#endif
57+
} // namespace
58+
3359
namespace lldb_dap {
3460

35-
DAP::DAP(llvm::StringRef path, ReplMode repl_mode)
36-
: debug_adaptor_path(path), broadcaster("lldb-dap"),
61+
DAP::DAP(llvm::StringRef path, std::ofstream *log, ReplMode repl_mode,
62+
StreamDescriptor input, StreamDescriptor output)
63+
: debug_adaptor_path(path), log(log), input(std::move(input)),
64+
output(std::move(output)), broadcaster("lldb-dap"),
3765
exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID),
3866
stop_at_entry(false), is_attach(false),
3967
enable_auto_variable_summaries(false),
@@ -43,21 +71,7 @@ DAP::DAP(llvm::StringRef path, ReplMode repl_mode)
4371
configuration_done_sent(false), waiting_for_run_in_terminal(false),
4472
progress_event_reporter(
4573
[&](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-
}
74+
reverse_request_seq(0), repl_mode(repl_mode) {}
6175

6276
DAP::~DAP() = default;
6377

@@ -173,6 +187,45 @@ ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
173187
return nullptr;
174188
}
175189

190+
llvm::Error DAP::ConfigureIO(std::FILE *overrideOut, std::FILE *overrideErr) {
191+
in = lldb::SBFile(std::fopen(DEV_NULL, "r"), /*transfer_ownership=*/true);
192+
193+
if (auto Error = out.RedirectTo([this](llvm::StringRef output) {
194+
SendOutput(OutputType::Stdout, output);
195+
}))
196+
return Error;
197+
198+
if (overrideOut) {
199+
auto fd = out.GetWriteFileDescriptor();
200+
if (auto Error = fd.takeError())
201+
return Error;
202+
203+
if (dup2(*fd, fileno(overrideOut)) == -1)
204+
return llvm::errorCodeToError(llvm::errnoAsErrorCode());
205+
}
206+
207+
if (auto Error = err.RedirectTo([this](llvm::StringRef output) {
208+
SendOutput(OutputType::Stderr, output);
209+
}))
210+
return Error;
211+
212+
if (overrideErr) {
213+
auto fd = err.GetWriteFileDescriptor();
214+
if (auto Error = fd.takeError())
215+
return Error;
216+
217+
if (dup2(*fd, fileno(overrideErr)) == -1)
218+
return llvm::errorCodeToError(llvm::errnoAsErrorCode());
219+
}
220+
221+
return llvm::Error::success();
222+
}
223+
224+
void DAP::StopIO() {
225+
out.Stop();
226+
err.Stop();
227+
}
228+
176229
// Send the JSON in "json_str" to the "out" stream. Correctly send the
177230
// "Content-Length:" field followed by the length, followed by the raw
178231
// JSON bytes.
@@ -208,19 +261,19 @@ std::string DAP::ReadJSON() {
208261
std::string json_str;
209262
int length;
210263

211-
if (!input.read_expected(log.get(), "Content-Length: "))
264+
if (!input.read_expected(log, "Content-Length: "))
212265
return json_str;
213266

214-
if (!input.read_line(log.get(), length_str))
267+
if (!input.read_line(log, length_str))
215268
return json_str;
216269

217270
if (!llvm::to_integer(length_str, length))
218271
return json_str;
219272

220-
if (!input.read_expected(log.get(), "\r\n"))
273+
if (!input.read_expected(log, "\r\n"))
221274
return json_str;
222275

223-
if (!input.read_full(log.get(), length, json_str))
276+
if (!input.read_full(log, length, json_str))
224277
return json_str;
225278

226279
if (log) {

lldb/tools/lldb-dap/DAP.h

+41-26
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"
17+
#include "OutputRedirector.h"
4018
#include "ProgressEvent.h"
4119
#include "SourceBreakpoint.h"
20+
#include "lldb/API/SBBroadcaster.h"
21+
#include "lldb/API/SBCommandInterpreter.h"
22+
#include "lldb/API/SBDebugger.h"
23+
#include "lldb/API/SBError.h"
24+
#include "lldb/API/SBFile.h"
25+
#include "lldb/API/SBFormat.h"
26+
#include "lldb/API/SBFrame.h"
27+
#include "lldb/API/SBTarget.h"
28+
#include "lldb/API/SBThread.h"
29+
#include "lldb/API/SBValue.h"
30+
#include "lldb/API/SBValueList.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,18 @@ struct SendEventRequestHandler : public lldb::SBCommandPluginInterface {
138140

139141
struct DAP {
140142
llvm::StringRef debug_adaptor_path;
143+
std::ofstream *log;
141144
InputStream input;
142145
OutputStream output;
146+
lldb::SBFile in;
147+
OutputRedirector out;
148+
OutputRedirector err;
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;
150155
llvm::StringMap<SourceBreakpointMap> source_breakpoints;
151156
FunctionBreakpointMap function_breakpoints;
152157
InstructionBreakpointMap instruction_breakpoints;
@@ -198,13 +203,23 @@ struct DAP {
198203
// will contain that expression.
199204
std::string last_nonempty_var_expression;
200205

201-
DAP(llvm::StringRef path, ReplMode repl_mode);
206+
DAP(llvm::StringRef path, std::ofstream *log, ReplMode repl_mode,
207+
StreamDescriptor input, StreamDescriptor output);
202208
~DAP();
203209
DAP(const DAP &rhs) = delete;
204210
void operator=(const DAP &rhs) = delete;
205211
ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter);
206212
ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);
207213

214+
/// Redirect stdout and stderr fo the IDE's console output.
215+
///
216+
/// Errors in this operation will be printed to the log file and the IDE's
217+
/// console output as well.
218+
llvm::Error ConfigureIO(std::FILE *overrideOut, std::FILE *overrideErr);
219+
220+
/// Stop the redirected IO threads and associated pipes.
221+
void StopIO();
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.h

+6
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ struct StreamDescriptor {
5252
struct InputStream {
5353
StreamDescriptor descriptor;
5454

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

5760
bool read_line(std::ofstream *log, std::string &line);
@@ -62,6 +65,9 @@ struct InputStream {
6265
struct OutputStream {
6366
StreamDescriptor descriptor;
6467

68+
explicit OutputStream(StreamDescriptor descriptor)
69+
: descriptor(std::move(descriptor)) {}
70+
6571
bool write_full(llvm::StringRef str);
6672
};
6773
} // namespace lldb_dap

0 commit comments

Comments
 (0)