-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb-dap] Restore the override FD used by the output redirect on stop. #129964
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
While running lldb-dap over stdin/stdout the `stdout` and `stderr` FD's are replaced with a pipe that is reading the output to forward to the dap client. During shutdown we were not properly restoring those FDs, which means if any component attempted to write to stderr it would trigger a SIGPIPE due to the pipe being closed during the shutdown process. This can happen if we have an error reported from the `DAP::Loop` call that would then log to stderr, such as an error parsing a malformed DAP message or if lldb-dap crashed and it was trying to write the stack trace to stderr. There is one place we were not handling an `llvm::Error` if there was no logging setup that could trigger this condition. To address this, I updated the OutputRedirector to restore the FD to the prior state when `Stop` is called.
@llvm/pr-subscribers-lldb Author: John Harrison (ashgti) ChangesWhile running lldb-dap over stdin/stdout the There is one place we were not handling an To address this, I updated the OutputRedirector to restore the FD to the prior state when Full diff: https://github.com/llvm/llvm-project/pull/129964.diff 4 Files Affected:
diff --git a/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
index a39bd17ceb3b3..04414cd7a3cdf 100644
--- a/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
+++ b/lldb/test/API/tools/lldb-dap/io/TestDAP_io.py
@@ -18,18 +18,19 @@ def cleanup():
# If the process is still alive, terminate it.
if process.poll() is None:
process.terminate()
- stdout_data = process.stdout.read()
- stderr_data = process.stderr.read()
- print("========= STDOUT =========")
- print(stdout_data)
- print("========= END =========")
- print("========= STDERR =========")
- print(stderr_data)
- print("========= END =========")
- print("========= DEBUG ADAPTER PROTOCOL LOGS =========")
- with open(log_file_path, "r") as file:
- print(file.read())
- print("========= END =========")
+ process.wait()
+ stdout_data = process.stdout.read()
+ stderr_data = process.stderr.read()
+ print("========= STDOUT =========")
+ print(stdout_data)
+ print("========= END =========")
+ print("========= STDERR =========")
+ print(stderr_data)
+ print("========= END =========")
+ print("========= DEBUG ADAPTER PROTOCOL LOGS =========")
+ with open(log_file_path, "r") as file:
+ print(file.read())
+ print("========= END =========")
# Execute the cleanup function during test case tear down.
self.addTearDownHook(cleanup)
diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index b21b83a79aec7..1f7b25e7c5bcc 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -195,34 +195,16 @@ ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
llvm::Error DAP::ConfigureIO(std::FILE *overrideOut, std::FILE *overrideErr) {
in = lldb::SBFile(std::fopen(DEV_NULL, "r"), /*transfer_ownership=*/true);
- if (auto Error = out.RedirectTo([this](llvm::StringRef output) {
+ if (auto Error = out.RedirectTo(overrideOut, [this](llvm::StringRef output) {
SendOutput(OutputType::Stdout, output);
}))
return Error;
- if (overrideOut) {
- auto fd = out.GetWriteFileDescriptor();
- if (auto Error = fd.takeError())
- return Error;
-
- if (dup2(*fd, fileno(overrideOut)) == -1)
- return llvm::errorCodeToError(llvm::errnoAsErrorCode());
- }
-
- if (auto Error = err.RedirectTo([this](llvm::StringRef output) {
+ if (auto Error = err.RedirectTo(overrideErr, [this](llvm::StringRef output) {
SendOutput(OutputType::Stderr, output);
}))
return Error;
- if (overrideErr) {
- auto fd = err.GetWriteFileDescriptor();
- if (auto Error = fd.takeError())
- return Error;
-
- if (dup2(*fd, fileno(overrideErr)) == -1)
- return llvm::errorCodeToError(llvm::errnoAsErrorCode());
- }
-
return llvm::Error::success();
}
@@ -729,15 +711,11 @@ PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
llvm::StringRef json_sref(json);
llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
- if (!json_value) {
- auto error = json_value.takeError();
- if (log) {
- std::string error_str;
- llvm::raw_string_ostream strm(error_str);
- strm << error;
+ if (auto error = json_value.takeError()) {
+ std::string error_str = llvm::toString(std::move(error));
+ if (log)
*log << "error: failed to parse JSON: " << error_str << std::endl
<< json << std::endl;
- }
return PacketStatus::JSONMalformed;
}
@@ -848,10 +826,6 @@ lldb::SBError DAP::Disconnect(bool terminateDebuggee) {
SendTerminatedEvent();
- // Stop forwarding the debugger output and error handles.
- out.Stop();
- err.Stop();
-
disconnecting = true;
return error;
@@ -859,8 +833,8 @@ lldb::SBError DAP::Disconnect(bool terminateDebuggee) {
llvm::Error DAP::Loop() {
auto cleanup = llvm::make_scope_exit([this]() {
- if (output.descriptor)
- output.descriptor->Close();
+ out.Stop();
+ err.Stop();
StopEventHandlers();
});
while (!disconnecting) {
diff --git a/lldb/tools/lldb-dap/OutputRedirector.cpp b/lldb/tools/lldb-dap/OutputRedirector.cpp
index 79321aebe9aac..5da3839973706 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.cpp
+++ b/lldb/tools/lldb-dap/OutputRedirector.cpp
@@ -27,7 +27,9 @@ namespace lldb_dap {
int OutputRedirector::kInvalidDescriptor = -1;
-OutputRedirector::OutputRedirector() : m_fd(kInvalidDescriptor) {}
+OutputRedirector::OutputRedirector()
+ : m_fd(kInvalidDescriptor), m_original_fd(kInvalidDescriptor),
+ m_restore_fd(kInvalidDescriptor) {}
Expected<int> OutputRedirector::GetWriteFileDescriptor() {
if (m_fd == kInvalidDescriptor)
@@ -36,7 +38,8 @@ Expected<int> OutputRedirector::GetWriteFileDescriptor() {
return m_fd;
}
-Error OutputRedirector::RedirectTo(std::function<void(StringRef)> callback) {
+Error OutputRedirector::RedirectTo(std::FILE *override,
+ std::function<void(StringRef)> callback) {
assert(m_fd == kInvalidDescriptor && "Output readirector already started.");
int new_fd[2];
@@ -52,6 +55,19 @@ Error OutputRedirector::RedirectTo(std::function<void(StringRef)> callback) {
int read_fd = new_fd[0];
m_fd = new_fd[1];
+
+ if (override) {
+ int override_fd = fileno(override);
+
+ // Backup the FD to restore once redirection is complete.
+ m_original_fd = override_fd;
+ m_restore_fd = dup(override_fd);
+
+ // Override the existing fd the new write end of the pipe.
+ if (::dup2(m_fd, override_fd) == -1)
+ return llvm::errorCodeToError(llvm::errnoAsErrorCode());
+ }
+
m_forwarder = std::thread([this, callback, read_fd]() {
char buffer[OutputBufferSize];
while (!m_stopped) {
@@ -92,6 +108,17 @@ void OutputRedirector::Stop() {
(void)::write(fd, kCloseSentinel.data(), kCloseSentinel.size());
::close(fd);
m_forwarder.join();
+
+ // Restore the fd back to its original state since we stopped the
+ // redirection.
+ if (m_restore_fd != kInvalidDescriptor &&
+ m_original_fd != kInvalidDescriptor) {
+ int restore_fd = m_restore_fd;
+ m_restore_fd = kInvalidDescriptor;
+ int original_fd = m_original_fd;
+ m_original_fd = kInvalidDescriptor;
+ ::dup2(restore_fd, original_fd);
+ }
}
}
diff --git a/lldb/tools/lldb-dap/OutputRedirector.h b/lldb/tools/lldb-dap/OutputRedirector.h
index a47ea96f71f14..45571c0d5f344 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.h
+++ b/lldb/tools/lldb-dap/OutputRedirector.h
@@ -27,7 +27,8 @@ class OutputRedirector {
/// \return
/// \a Error::success if the redirection was set up correctly, or an error
/// otherwise.
- llvm::Error RedirectTo(std::function<void(llvm::StringRef)> callback);
+ llvm::Error RedirectTo(std::FILE *overrideFile,
+ std::function<void(llvm::StringRef)> callback);
llvm::Expected<int> GetWriteFileDescriptor();
void Stop();
@@ -41,6 +42,8 @@ class OutputRedirector {
private:
std::atomic<bool> m_stopped = false;
int m_fd;
+ int m_original_fd;
+ int m_restore_fd;
std::thread m_forwarder;
};
|
Checking this with:
Without the llvm-project/lldb/tools/lldb-dap/lldb-dap.cpp Line 596 in 12c5a46
|
Thanks, I've confirmed this fixes the issue for me. Ironically, it's my MemoryMonitor patch that triggered it, I guess it caused something to get written to stdout/stderr. |
Co-authored-by: Jonas Devlieghere <[email protected]>
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/162/builds/17524 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/181/builds/14956 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/18/builds/12507 Here is the relevant piece of the build log for the reference
|
#130186 should fix the failure. |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/60/builds/21306 Here is the relevant piece of the build log for the reference
|
…p. (llvm#129964) While running lldb-dap over stdin/stdout the `stdout` and `stderr` FD's are replaced with a pipe that is reading the output to forward to the dap client. During shutdown we were not properly restoring those FDs, which means if any component attempted to write to stderr it would trigger a SIGPIPE due to the pipe being closed during the shutdown process. This can happen if we have an error reported from the `DAP::Loop` call that would then log to stderr, such as an error parsing a malformed DAP message or if lldb-dap crashed and it was trying to write the stack trace to stderr. There is one place we were not handling an `llvm::Error` if there was no logging setup that could trigger this condition. To address this, I updated the OutputRedirector to restore the FD to the prior state when `Stop` is called. --------- Co-authored-by: Jonas Devlieghere <[email protected]>
While running lldb-dap over stdin/stdout the
stdout
andstderr
FD's are replaced with a pipe that is reading the output to forward to the dap client. During shutdown we were not properly restoring those FDs, which means if any component attempted to write to stderr it would trigger a SIGPIPE due to the pipe being closed during the shutdown process. This can happen if we have an error reported from theDAP::Loop
call that would then log to stderr, such as an error parsing a malformed DAP message or if lldb-dap crashed and it was trying to write the stack trace to stderr.There is one place we were not handling an
llvm::Error
if there was no logging setup that could trigger this condition.To address this, I updated the OutputRedirector to restore the FD to the prior state when
Stop
is called.