[Lldb-commits] [lldb] [lldb-dap] Ensure the IO forwarding threads are managed by the DAP object lifecycle. (PR #120457)
John Harrison via lldb-commits
lldb-commits at lists.llvm.org
Wed Dec 18 09:14:01 PST 2024
https://github.com/ashgti created https://github.com/llvm/llvm-project/pull/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.
>From f59cf06ff3d11baabee10ba47151a8c9d4e03733 Mon Sep 17 00:00:00 2001
From: John Harrison <harjohn at google.com>
Date: Tue, 17 Dec 2024 17:45:34 -0800
Subject: [PATCH] [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.
---
lldb/tools/lldb-dap/CMakeLists.txt | 6 +-
lldb/tools/lldb-dap/DAP.cpp | 114 ++++++++++++++----
lldb/tools/lldb-dap/DAP.h | 67 +++++++----
lldb/tools/lldb-dap/IOStream.cpp | 8 +-
lldb/tools/lldb-dap/IOStream.h | 14 ++-
lldb/tools/lldb-dap/OutputRedirector.cpp | 63 ----------
lldb/tools/lldb-dap/OutputRedirector.h | 26 ----
lldb/tools/lldb-dap/lldb-dap.cpp | 146 +++++++++++++----------
8 files changed, 231 insertions(+), 213 deletions(-)
delete mode 100644 lldb/tools/lldb-dap/OutputRedirector.cpp
delete mode 100644 lldb/tools/lldb-dap/OutputRedirector.h
diff --git a/lldb/tools/lldb-dap/CMakeLists.txt b/lldb/tools/lldb-dap/CMakeLists.txt
index d68098bf7b3266..00906e8ac10904 100644
--- a/lldb/tools/lldb-dap/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/CMakeLists.txt
@@ -1,7 +1,3 @@
-if ( CMAKE_SYSTEM_NAME MATCHES "Windows" OR CMAKE_SYSTEM_NAME MATCHES "NetBSD" )
- list(APPEND extra_libs lldbHost)
-endif ()
-
if (HAVE_LIBPTHREAD)
list(APPEND extra_libs pthread)
endif ()
@@ -32,7 +28,6 @@ add_lldb_tool(lldb-dap
IOStream.cpp
JSONUtils.cpp
LLDBUtils.cpp
- OutputRedirector.cpp
ProgressEvent.cpp
RunInTerminal.cpp
SourceBreakpoint.cpp
@@ -42,6 +37,7 @@ add_lldb_tool(lldb-dap
LINK_LIBS
liblldb
+ lldbHost
${extra_libs}
LINK_COMPONENTS
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index 35250d9eef608a..4e0de6fa3a4e90 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -6,34 +6,53 @@
//
//===----------------------------------------------------------------------===//
-#include <chrono>
-#include <cstdarg>
-#include <fstream>
-#include <mutex>
-
#include "DAP.h"
#include "JSONUtils.h"
#include "LLDBUtils.h"
+#include "lldb/API/SBBreakpoint.h"
#include "lldb/API/SBCommandInterpreter.h"
#include "lldb/API/SBLanguageRuntime.h"
#include "lldb/API/SBListener.h"
#include "lldb/API/SBStream.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBProcess.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-enumerations.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Twine.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <cstdarg>
+#include <cstdio>
+#include <fstream>
+#include <mutex>
+#include <utility>
#if defined(_WIN32)
#define NOMINMAX
#include <fcntl.h>
#include <io.h>
#include <windows.h>
+#else
+#include <unistd.h>
#endif
using namespace lldb_dap;
namespace lldb_dap {
-DAP::DAP(llvm::StringRef path, ReplMode repl_mode)
- : debug_adaptor_path(path), broadcaster("lldb-dap"),
+DAP::DAP(llvm::StringRef path, std::optional<std::ofstream> &log,
+ ReplMode repl_mode, StreamDescriptor input, StreamDescriptor output)
+ : debug_adaptor_path(path), log(log), input(std::move(input)),
+ output(std::move(output)), broadcaster("lldb-dap"),
exception_breakpoints(), focus_tid(LLDB_INVALID_THREAD_ID),
stop_at_entry(false), is_attach(false),
enable_auto_variable_summaries(false),
@@ -43,21 +62,7 @@ DAP::DAP(llvm::StringRef path, ReplMode repl_mode)
configuration_done_sent(false), waiting_for_run_in_terminal(false),
progress_event_reporter(
[&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
- reverse_request_seq(0), repl_mode(repl_mode) {
- const char *log_file_path = getenv("LLDBDAP_LOG");
-#if defined(_WIN32)
- // Windows opens stdout and stdin in text mode which converts \n to 13,10
- // while the value is just 10 on Darwin/Linux. Setting the file mode to binary
- // fixes this.
- int result = _setmode(fileno(stdout), _O_BINARY);
- assert(result);
- result = _setmode(fileno(stdin), _O_BINARY);
- UNUSED_IF_ASSERT_DISABLED(result);
- assert(result);
-#endif
- if (log_file_path)
- log.reset(new std::ofstream(log_file_path));
-}
+ reverse_request_seq(0), repl_mode(repl_mode) {}
DAP::~DAP() = default;
@@ -173,6 +178,63 @@ ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
return nullptr;
}
+llvm::Error DAP::ConfigureIO(std::optional<std::FILE *> overrideOut,
+ std::optional<std::FILE *> overrideErr) {
+ auto *inull = lldb_private::FileSystem::Instance().Fopen(
+ lldb_private::FileSystem::DEV_NULL, "w");
+ in = lldb::SBFile(inull, true);
+
+ lldb_private::Status status;
+ status = pout.CreateNew(/*child_process_inherit=*/false);
+ if (status.Fail())
+ return status.takeError();
+ status = perr.CreateNew(/*child_process_inherit=*/false);
+ if (status.Fail())
+ return status.takeError();
+
+ if (overrideOut) {
+ if (dup2(pout.GetWriteFileDescriptor(), fileno(*overrideOut)) == -1) {
+ return llvm::make_error<llvm::StringError>(
+ llvm::errnoAsErrorCode(),
+ llvm::formatv("override fd=%d failed", fileno(*overrideOut))
+ .str()
+ .c_str());
+ }
+ }
+
+ if (overrideErr) {
+ if (dup2(perr.GetWriteFileDescriptor(), fileno(*overrideErr)) == -1) {
+ return llvm::make_error<llvm::StringError>(
+ llvm::errnoAsErrorCode(),
+ llvm::formatv("override fd=%d failed", fileno(*overrideErr))
+ .str()
+ .c_str());
+ }
+ }
+
+ auto forwarder = [&](lldb_private::Pipe &pipe, OutputType outputType) {
+ char buffer[4098];
+ size_t bytes_read;
+ while (pipe.CanRead()) {
+ lldb_private::Status error = pipe.ReadWithTimeout(
+ &buffer, sizeof(buffer), std::chrono::seconds(1), bytes_read);
+ if (error.Success()) {
+ // zero bytes returned on EOF.
+ if (bytes_read == 0)
+ break;
+ SendOutput(outputType, llvm::StringRef(buffer, bytes_read));
+ }
+ }
+ };
+
+ stdout_forward_thread =
+ std::thread(forwarder, std::ref(pout), OutputType::Stdout);
+ stderr_forward_thread =
+ std::thread(forwarder, std::ref(perr), OutputType::Stderr);
+
+ return llvm::Error::success();
+}
+
// Send the JSON in "json_str" to the "out" stream. Correctly send the
// "Content-Length:" field followed by the length, followed by the raw
// JSON bytes.
@@ -208,19 +270,19 @@ std::string DAP::ReadJSON() {
std::string json_str;
int length;
- if (!input.read_expected(log.get(), "Content-Length: "))
+ if (!input.read_expected(log, "Content-Length: "))
return json_str;
- if (!input.read_line(log.get(), length_str))
+ if (!input.read_line(log, length_str))
return json_str;
if (!llvm::to_integer(length_str, length))
return json_str;
- if (!input.read_expected(log.get(), "\r\n"))
+ if (!input.read_expected(log, "\r\n"))
return json_str;
- if (!input.read_full(log.get(), length, json_str))
+ if (!input.read_full(log, length, json_str))
return json_str;
if (log) {
diff --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index ae496236f13369..f9cedd32cd0bd3 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -9,36 +9,38 @@
#ifndef LLDB_TOOLS_LLDB_DAP_DAP_H
#define LLDB_TOOLS_LLDB_DAP_DAP_H
-#include <cstdio>
-#include <iosfwd>
-#include <map>
-#include <optional>
-#include <thread>
-
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/StringMap.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/JSON.h"
-#include "llvm/Support/Threading.h"
-#include "llvm/Support/raw_ostream.h"
-
-#include "lldb/API/SBAttachInfo.h"
-#include "lldb/API/SBCommandInterpreter.h"
-#include "lldb/API/SBCommandReturnObject.h"
-#include "lldb/API/SBDebugger.h"
-#include "lldb/API/SBEvent.h"
-#include "lldb/API/SBFormat.h"
-#include "lldb/API/SBLaunchInfo.h"
-#include "lldb/API/SBTarget.h"
-#include "lldb/API/SBThread.h"
-
+#include "DAPForward.h"
#include "ExceptionBreakpoint.h"
#include "FunctionBreakpoint.h"
#include "IOStream.h"
#include "InstructionBreakpoint.h"
#include "ProgressEvent.h"
#include "SourceBreakpoint.h"
+#include "lldb/API/SBBroadcaster.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBError.h"
+#include "lldb/API/SBFile.h"
+#include "lldb/API/SBFormat.h"
+#include "lldb/API/SBFrame.h"
+#include "lldb/API/SBTarget.h"
+#include "lldb/API/SBThread.h"
+#include "lldb/API/SBValue.h"
+#include "lldb/API/SBValueList.h"
+#include "lldb/Host/Pipe.h"
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Threading.h"
+#include <map>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <vector>
#define VARREF_LOCALS (int64_t)1
#define VARREF_GLOBALS (int64_t)2
@@ -138,15 +140,20 @@ struct SendEventRequestHandler : public lldb::SBCommandPluginInterface {
struct DAP {
llvm::StringRef debug_adaptor_path;
+ std::optional<std::ofstream> &log;
InputStream input;
OutputStream output;
+ lldb::SBFile in;
+ lldb_private::Pipe pout;
+ lldb_private::Pipe perr;
lldb::SBDebugger debugger;
lldb::SBTarget target;
Variables variables;
lldb::SBBroadcaster broadcaster;
std::thread event_thread;
std::thread progress_event_thread;
- std::unique_ptr<std::ofstream> log;
+ std::thread stdout_forward_thread;
+ std::thread stderr_forward_thread;
llvm::StringMap<SourceBreakpointMap> source_breakpoints;
FunctionBreakpointMap function_breakpoints;
InstructionBreakpointMap instruction_breakpoints;
@@ -198,13 +205,21 @@ struct DAP {
// will contain that expression.
std::string last_nonempty_var_expression;
- DAP(llvm::StringRef path, ReplMode repl_mode);
+ DAP(llvm::StringRef path, std::optional<std::ofstream> &log,
+ ReplMode repl_mode, StreamDescriptor input, StreamDescriptor output);
~DAP();
DAP(const DAP &rhs) = delete;
void operator=(const DAP &rhs) = delete;
ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter);
ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);
+ /// Redirect stdout and stderr fo the IDE's console output.
+ ///
+ /// Errors in this operation will be printed to the log file and the IDE's
+ /// console output as well.
+ llvm::Error ConfigureIO(std::optional<std::FILE *> overrideOut,
+ std::optional<std::FILE *> overrideErr);
+
// Serialize the JSON value into a string and send the JSON packet to
// the "out" stream.
void SendJSON(const llvm::json::Value &json);
diff --git a/lldb/tools/lldb-dap/IOStream.cpp b/lldb/tools/lldb-dap/IOStream.cpp
index d2e8ec40b0a7b8..a078104595037d 100644
--- a/lldb/tools/lldb-dap/IOStream.cpp
+++ b/lldb/tools/lldb-dap/IOStream.cpp
@@ -87,7 +87,7 @@ bool OutputStream::write_full(llvm::StringRef str) {
return true;
}
-bool InputStream::read_full(std::ofstream *log, size_t length,
+bool InputStream::read_full(std::optional<std::ofstream> &log, size_t length,
std::string &text) {
std::string data;
data.resize(length);
@@ -131,7 +131,8 @@ bool InputStream::read_full(std::ofstream *log, size_t length,
return true;
}
-bool InputStream::read_line(std::ofstream *log, std::string &line) {
+bool InputStream::read_line(std::optional<std::ofstream> &log,
+ std::string &line) {
line.clear();
while (true) {
if (!read_full(log, 1, line))
@@ -144,7 +145,8 @@ bool InputStream::read_line(std::ofstream *log, std::string &line) {
return true;
}
-bool InputStream::read_expected(std::ofstream *log, llvm::StringRef expected) {
+bool InputStream::read_expected(std::optional<std::ofstream> &log,
+ llvm::StringRef expected) {
std::string result;
if (!read_full(log, expected.size(), result))
return false;
diff --git a/lldb/tools/lldb-dap/IOStream.h b/lldb/tools/lldb-dap/IOStream.h
index 57d5fd458b7165..f4829473b7b0fb 100644
--- a/lldb/tools/lldb-dap/IOStream.h
+++ b/lldb/tools/lldb-dap/IOStream.h
@@ -52,16 +52,24 @@ struct StreamDescriptor {
struct InputStream {
StreamDescriptor descriptor;
- bool read_full(std::ofstream *log, size_t length, std::string &text);
+ explicit InputStream(StreamDescriptor descriptor)
+ : descriptor(std::move(descriptor)) {};
- bool read_line(std::ofstream *log, std::string &line);
+ bool read_full(std::optional<std::ofstream> &log, size_t length,
+ std::string &text);
- bool read_expected(std::ofstream *log, llvm::StringRef expected);
+ bool read_line(std::optional<std::ofstream> &log, std::string &line);
+
+ bool read_expected(std::optional<std::ofstream> &log,
+ llvm::StringRef expected);
};
struct OutputStream {
StreamDescriptor descriptor;
+ explicit OutputStream(StreamDescriptor descriptor)
+ : descriptor(std::move(descriptor)) {};
+
bool write_full(llvm::StringRef str);
};
} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/OutputRedirector.cpp b/lldb/tools/lldb-dap/OutputRedirector.cpp
deleted file mode 100644
index 2c2f49569869b4..00000000000000
--- a/lldb/tools/lldb-dap/OutputRedirector.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-//===-- OutputRedirector.cpp -----------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===/
-
-#if defined(_WIN32)
-#include <fcntl.h>
-#include <io.h>
-#else
-#include <unistd.h>
-#endif
-
-#include "DAP.h"
-#include "OutputRedirector.h"
-#include "llvm/ADT/StringRef.h"
-
-using namespace llvm;
-
-namespace lldb_dap {
-
-Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback) {
- int new_fd[2];
-#if defined(_WIN32)
- if (_pipe(new_fd, 4096, O_TEXT) == -1) {
-#else
- if (pipe(new_fd) == -1) {
-#endif
- int error = errno;
- return createStringError(inconvertibleErrorCode(),
- "Couldn't create new pipe for fd %d. %s", fd,
- strerror(error));
- }
-
- if (dup2(new_fd[1], fd) == -1) {
- int error = errno;
- return createStringError(inconvertibleErrorCode(),
- "Couldn't override the fd %d. %s", fd,
- strerror(error));
- }
-
- int read_fd = new_fd[0];
- std::thread t([read_fd, callback]() {
- char buffer[OutputBufferSize];
- while (true) {
- ssize_t bytes_count = read(read_fd, &buffer, sizeof(buffer));
- if (bytes_count == 0)
- return;
- if (bytes_count == -1) {
- if (errno == EAGAIN || errno == EINTR)
- continue;
- break;
- }
- callback(StringRef(buffer, bytes_count));
- }
- });
- t.detach();
- return Error::success();
-}
-
-} // namespace lldb_dap
diff --git a/lldb/tools/lldb-dap/OutputRedirector.h b/lldb/tools/lldb-dap/OutputRedirector.h
deleted file mode 100644
index e26d1648b104f9..00000000000000
--- a/lldb/tools/lldb-dap/OutputRedirector.h
+++ /dev/null
@@ -1,26 +0,0 @@
-//===-- OutputRedirector.h -------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===/
-
-#ifndef LLDB_TOOLS_LLDB_DAP_OUTPUT_REDIRECTOR_H
-#define LLDB_TOOLS_LLDB_DAP_OUTPUT_REDIRECTOR_H
-
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Error.h"
-
-namespace lldb_dap {
-
-/// Redirects the output of a given file descriptor to a callback.
-///
-/// \return
-/// \a Error::success if the redirection was set up correctly, or an error
-/// otherwise.
-llvm::Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback);
-
-} // namespace lldb_dap
-
-#endif // LLDB_TOOLS_LLDB_DAP_OUTPUT_REDIRECTOR_H
diff --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 3bfc578806021e..0d44530d48f92a 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -10,7 +10,6 @@
#include "FifoFiles.h"
#include "JSONUtils.h"
#include "LLDBUtils.h"
-#include "OutputRedirector.h"
#include "RunInTerminal.h"
#include "Watchpoint.h"
#include "lldb/API/SBDeclaration.h"
@@ -18,6 +17,7 @@
#include "lldb/API/SBListener.h"
#include "lldb/API/SBMemoryRegionInfo.h"
#include "lldb/API/SBStream.h"
+#include "lldb/API/SBEvent.h"
#include "lldb/API/SBStringList.h"
#include "lldb/Host/Config.h"
#include "llvm/ADT/ArrayRef.h"
@@ -137,15 +137,14 @@ lldb::SBValueList *GetTopLevelScope(DAP &dap, int64_t variablesReference) {
}
}
-SOCKET AcceptConnection(DAP &dap, int portno) {
+SOCKET AcceptConnection(std::optional<std::ofstream> &log, int portno) {
// Accept a socket connection from any host on "portno".
SOCKET newsockfd = -1;
struct sockaddr_in serv_addr, cli_addr;
SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
- if (dap.log)
- *dap.log << "error: opening socket (" << strerror(errno) << ")"
- << std::endl;
+ if (log)
+ *log << "error: opening socket (" << strerror(errno) << ")" << std::endl;
} else {
memset((char *)&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
@@ -153,9 +152,9 @@ SOCKET AcceptConnection(DAP &dap, int portno) {
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
- if (dap.log)
- *dap.log << "error: binding socket (" << strerror(errno) << ")"
- << std::endl;
+ if (log)
+ *log << "error: binding socket (" << strerror(errno) << ")"
+ << std::endl;
} else {
listen(sockfd, 5);
socklen_t clilen = sizeof(cli_addr);
@@ -163,8 +162,8 @@ SOCKET AcceptConnection(DAP &dap, int portno) {
llvm::sys::RetryAfterSignal(static_cast<SOCKET>(-1), accept, sockfd,
(struct sockaddr *)&cli_addr, &clilen);
if (newsockfd < 0)
- if (dap.log)
- *dap.log << "error: accept (" << strerror(errno) << ")" << std::endl;
+ if (log)
+ *log << "error: accept (" << strerror(errno) << ")" << std::endl;
}
#if defined(_WIN32)
closesocket(sockfd);
@@ -1099,6 +1098,14 @@ void request_disconnect(DAP &dap, const llvm::json::Object &request) {
dap.broadcaster.BroadcastEventByType(eBroadcastBitStopProgressThread);
dap.progress_event_thread.join();
}
+ if (dap.stdout_forward_thread.joinable()) {
+ dap.pout.Close();
+ dap.stdout_forward_thread.join();
+ }
+ if (dap.stderr_forward_thread.joinable()) {
+ dap.perr.Close();
+ dap.stderr_forward_thread.join();
+ }
dap.disconnecting = true;
}
@@ -1868,7 +1875,22 @@ void request_initialize(DAP &dap, const llvm::json::Object &request) {
// which may affect the outcome of tests.
bool source_init_file = GetBoolean(arguments, "sourceInitFile", true);
- dap.debugger = lldb::SBDebugger::Create(source_init_file);
+ // Do not source init files until in/out/err are configured.
+ dap.debugger = lldb::SBDebugger::Create(false);
+ dap.debugger.SetInputFile(dap.in);
+ dap.debugger.SetOutputFile(lldb::SBFile(dap.pout.GetWriteFileDescriptor(), "w", false));
+ dap.debugger.SetErrorFile(lldb::SBFile(dap.perr.GetWriteFileDescriptor(), "w", false));
+
+ auto interp = dap.debugger.GetCommandInterpreter();
+
+ if (source_init_file) {
+ dap.debugger.SkipLLDBInitFiles(false);
+ dap.debugger.SkipAppInitFiles(false);
+ lldb::SBCommandReturnObject init;
+ interp.SourceInitFileInGlobalDirectory(init);
+ interp.SourceInitFileInHomeDirectory(init);
+ }
+
if (llvm::Error err = dap.RunPreInitCommands()) {
response["success"] = false;
EmplaceSafeString(response, "message", llvm::toString(std::move(err)));
@@ -4907,38 +4929,6 @@ static void redirection_test() {
fflush(stderr);
}
-/// Redirect stdout and stderr fo the IDE's console output.
-///
-/// Errors in this operation will be printed to the log file and the IDE's
-/// console output as well.
-///
-/// \return
-/// A fd pointing to the original stdout.
-static int SetupStdoutStderrRedirection(DAP &dap) {
- int stdoutfd = fileno(stdout);
- int new_stdout_fd = dup(stdoutfd);
- auto output_callback_stderr = [&dap](llvm::StringRef data) {
- dap.SendOutput(OutputType::Stderr, data);
- };
- auto output_callback_stdout = [&dap](llvm::StringRef data) {
- dap.SendOutput(OutputType::Stdout, data);
- };
- if (llvm::Error err = RedirectFd(stdoutfd, output_callback_stdout)) {
- std::string error_message = llvm::toString(std::move(err));
- if (dap.log)
- *dap.log << error_message << std::endl;
- output_callback_stderr(error_message);
- }
- if (llvm::Error err = RedirectFd(fileno(stderr), output_callback_stderr)) {
- std::string error_message = llvm::toString(std::move(err));
- if (dap.log)
- *dap.log << error_message << std::endl;
- output_callback_stderr(error_message);
- }
-
- return new_stdout_fd;
-}
-
int main(int argc, char *argv[]) {
llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false);
#if !defined(__APPLE__)
@@ -5027,6 +5017,11 @@ int main(int argc, char *argv[]) {
}
#endif
+ std::optional<std::ofstream> log = std::nullopt;
+ const char *log_file_path = getenv("LLDBDAP_LOG");
+ if (log_file_path)
+ log.emplace(log_file_path);
+
// Initialize LLDB first before we do anything.
lldb::SBDebugger::Initialize();
@@ -5034,36 +5029,65 @@ int main(int argc, char *argv[]) {
auto terminate_debugger =
llvm::make_scope_exit([] { lldb::SBDebugger::Terminate(); });
- DAP dap = DAP(program_path.str(), default_repl_mode);
-
- RegisterRequestCallbacks(dap);
-
- // stdout/stderr redirection to the IDE's console
- int new_stdout_fd = SetupStdoutStderrRedirection(dap);
-
+ StreamDescriptor input;
+ StreamDescriptor output;
+ std::optional<std::FILE *> redirectOut = std::nullopt;
+ std::optional<std::FILE *> redirectErr = std::nullopt;
if (portno != -1) {
printf("Listening on port %i...\n", portno);
- SOCKET socket_fd = AcceptConnection(dap, portno);
- if (socket_fd >= 0) {
- dap.input.descriptor = StreamDescriptor::from_socket(socket_fd, true);
- dap.output.descriptor = StreamDescriptor::from_socket(socket_fd, false);
- } else {
+ SOCKET socket_fd = AcceptConnection(log, portno);
+ if (socket_fd < 0)
return EXIT_FAILURE;
- }
+
+ input = StreamDescriptor::from_socket(socket_fd, true);
+ output = StreamDescriptor::from_socket(socket_fd, false);
} else {
- dap.input.descriptor = StreamDescriptor::from_file(fileno(stdin), false);
- dap.output.descriptor = StreamDescriptor::from_file(new_stdout_fd, false);
+#if defined(_WIN32)
+ // Windows opens stdout and stdin in text mode which converts \n to 13,10
+ // while the value is just 10 on Darwin/Linux. Setting the file mode to
+ // binary fixes this.
+ int result = _setmode(fileno(stdout), _O_BINARY);
+ assert(result);
+ result = _setmode(fileno(stdin), _O_BINARY);
+ UNUSED_IF_ASSERT_DISABLED(result);
+ assert(result);
+#endif
- /// used only by TestVSCode_redirection_to_console.py
- if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
- redirection_test();
+ int stdout_fd = dup(fileno(stdout));
+ if (stdout_fd == -1) {
+ llvm::errs() << "Failed to configure stdout redirect: "
+ << lldb_private::Status::FromErrno().takeError() << "\n";
+ return EXIT_FAILURE;
+ }
+
+ redirectOut = stdout;
+ redirectErr = stderr;
+
+ input = StreamDescriptor::from_file(fileno(stdin), false);
+ output = StreamDescriptor::from_file(stdout_fd, false);
}
+ DAP dap = DAP(program_path.str(), log, default_repl_mode, std::move(input),
+ std::move(output));
+
+ // stdout/stderr redirection to the IDE's console
+ if (auto Err = dap.ConfigureIO(redirectOut, redirectErr)) {
+ llvm::errs() << "Failed to configure lldb-dap IO operations: " << Err
+ << "\n";
+ return EXIT_FAILURE;
+ }
+
+ RegisterRequestCallbacks(dap);
+
for (const std::string &arg :
input_args.getAllArgValues(OPT_pre_init_command)) {
dap.pre_init_commands.push_back(arg);
}
+ // used only by TestVSCode_redirection_to_console.py
+ if (getenv("LLDB_DAP_TEST_STDOUT_STDERR_REDIRECTION") != nullptr)
+ redirection_test();
+
bool CleanExit = true;
if (auto Err = dap.Loop()) {
if (dap.log)
More information about the lldb-commits
mailing list