Skip to content

Commit 27c788d

Browse files
ashgtiJDevlieghere
andauthored
[lldb-dap] Restore the override FD used by the output redirect on stop. (#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]>
1 parent 462eb7e commit 27c788d

File tree

4 files changed

+53
-48
lines changed

4 files changed

+53
-48
lines changed

lldb/test/API/tools/lldb-dap/io/TestDAP_io.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,19 @@ def cleanup():
1818
# If the process is still alive, terminate it.
1919
if process.poll() is None:
2020
process.terminate()
21-
stdout_data = process.stdout.read()
22-
stderr_data = process.stderr.read()
23-
print("========= STDOUT =========")
24-
print(stdout_data)
25-
print("========= END =========")
26-
print("========= STDERR =========")
27-
print(stderr_data)
28-
print("========= END =========")
29-
print("========= DEBUG ADAPTER PROTOCOL LOGS =========")
30-
with open(log_file_path, "r") as file:
31-
print(file.read())
32-
print("========= END =========")
21+
process.wait()
22+
stdout_data = process.stdout.read()
23+
stderr_data = process.stderr.read()
24+
print("========= STDOUT =========")
25+
print(stdout_data)
26+
print("========= END =========")
27+
print("========= STDERR =========")
28+
print(stderr_data)
29+
print("========= END =========")
30+
print("========= DEBUG ADAPTER PROTOCOL LOGS =========")
31+
with open(log_file_path, "r") as file:
32+
print(file.read())
33+
print("========= END =========")
3334

3435
# Execute the cleanup function during test case tear down.
3536
self.addTearDownHook(cleanup)

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -195,34 +195,16 @@ ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
195195
llvm::Error DAP::ConfigureIO(std::FILE *overrideOut, std::FILE *overrideErr) {
196196
in = lldb::SBFile(std::fopen(DEV_NULL, "r"), /*transfer_ownership=*/true);
197197

198-
if (auto Error = out.RedirectTo([this](llvm::StringRef output) {
198+
if (auto Error = out.RedirectTo(overrideOut, [this](llvm::StringRef output) {
199199
SendOutput(OutputType::Stdout, output);
200200
}))
201201
return Error;
202202

203-
if (overrideOut) {
204-
auto fd = out.GetWriteFileDescriptor();
205-
if (auto Error = fd.takeError())
206-
return Error;
207-
208-
if (dup2(*fd, fileno(overrideOut)) == -1)
209-
return llvm::errorCodeToError(llvm::errnoAsErrorCode());
210-
}
211-
212-
if (auto Error = err.RedirectTo([this](llvm::StringRef output) {
203+
if (auto Error = err.RedirectTo(overrideErr, [this](llvm::StringRef output) {
213204
SendOutput(OutputType::Stderr, output);
214205
}))
215206
return Error;
216207

217-
if (overrideErr) {
218-
auto fd = err.GetWriteFileDescriptor();
219-
if (auto Error = fd.takeError())
220-
return Error;
221-
222-
if (dup2(*fd, fileno(overrideErr)) == -1)
223-
return llvm::errorCodeToError(llvm::errnoAsErrorCode());
224-
}
225-
226208
return llvm::Error::success();
227209
}
228210

@@ -729,15 +711,11 @@ PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
729711

730712
llvm::StringRef json_sref(json);
731713
llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
732-
if (!json_value) {
733-
auto error = json_value.takeError();
734-
if (log) {
735-
std::string error_str;
736-
llvm::raw_string_ostream strm(error_str);
737-
strm << error;
714+
if (auto error = json_value.takeError()) {
715+
std::string error_str = llvm::toString(std::move(error));
716+
if (log)
738717
*log << "error: failed to parse JSON: " << error_str << std::endl
739718
<< json << std::endl;
740-
}
741719
return PacketStatus::JSONMalformed;
742720
}
743721

@@ -848,19 +826,15 @@ lldb::SBError DAP::Disconnect(bool terminateDebuggee) {
848826

849827
SendTerminatedEvent();
850828

851-
// Stop forwarding the debugger output and error handles.
852-
out.Stop();
853-
err.Stop();
854-
855829
disconnecting = true;
856830

857831
return error;
858832
}
859833

860834
llvm::Error DAP::Loop() {
861835
auto cleanup = llvm::make_scope_exit([this]() {
862-
if (output.descriptor)
863-
output.descriptor->Close();
836+
out.Stop();
837+
err.Stop();
864838
StopEventHandlers();
865839
});
866840
while (!disconnecting) {

lldb/tools/lldb-dap/OutputRedirector.cpp

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ namespace lldb_dap {
2727

2828
int OutputRedirector::kInvalidDescriptor = -1;
2929

30-
OutputRedirector::OutputRedirector() : m_fd(kInvalidDescriptor) {}
30+
OutputRedirector::OutputRedirector()
31+
: m_fd(kInvalidDescriptor), m_original_fd(kInvalidDescriptor),
32+
m_restore_fd(kInvalidDescriptor) {}
3133

3234
Expected<int> OutputRedirector::GetWriteFileDescriptor() {
3335
if (m_fd == kInvalidDescriptor)
@@ -36,7 +38,8 @@ Expected<int> OutputRedirector::GetWriteFileDescriptor() {
3638
return m_fd;
3739
}
3840

39-
Error OutputRedirector::RedirectTo(std::function<void(StringRef)> callback) {
41+
Error OutputRedirector::RedirectTo(std::FILE *file_override,
42+
std::function<void(StringRef)> callback) {
4043
assert(m_fd == kInvalidDescriptor && "Output readirector already started.");
4144
int new_fd[2];
4245

@@ -52,6 +55,19 @@ Error OutputRedirector::RedirectTo(std::function<void(StringRef)> callback) {
5255

5356
int read_fd = new_fd[0];
5457
m_fd = new_fd[1];
58+
59+
if (override) {
60+
int override_fd = fileno(override);
61+
62+
// Backup the FD to restore once redirection is complete.
63+
m_original_fd = override_fd;
64+
m_restore_fd = dup(override_fd);
65+
66+
// Override the existing fd the new write end of the pipe.
67+
if (::dup2(m_fd, override_fd) == -1)
68+
return llvm::errorCodeToError(llvm::errnoAsErrorCode());
69+
}
70+
5571
m_forwarder = std::thread([this, callback, read_fd]() {
5672
char buffer[OutputBufferSize];
5773
while (!m_stopped) {
@@ -92,6 +108,17 @@ void OutputRedirector::Stop() {
92108
(void)::write(fd, kCloseSentinel.data(), kCloseSentinel.size());
93109
::close(fd);
94110
m_forwarder.join();
111+
112+
// Restore the fd back to its original state since we stopped the
113+
// redirection.
114+
if (m_restore_fd != kInvalidDescriptor &&
115+
m_original_fd != kInvalidDescriptor) {
116+
int restore_fd = m_restore_fd;
117+
m_restore_fd = kInvalidDescriptor;
118+
int original_fd = m_original_fd;
119+
m_original_fd = kInvalidDescriptor;
120+
::dup2(restore_fd, original_fd);
121+
}
95122
}
96123
}
97124

lldb/tools/lldb-dap/OutputRedirector.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ class OutputRedirector {
2727
/// \return
2828
/// \a Error::success if the redirection was set up correctly, or an error
2929
/// otherwise.
30-
llvm::Error RedirectTo(std::function<void(llvm::StringRef)> callback);
30+
llvm::Error RedirectTo(std::FILE *overrideFile,
31+
std::function<void(llvm::StringRef)> callback);
3132

3233
llvm::Expected<int> GetWriteFileDescriptor();
3334
void Stop();
@@ -41,6 +42,8 @@ class OutputRedirector {
4142
private:
4243
std::atomic<bool> m_stopped = false;
4344
int m_fd;
45+
int m_original_fd;
46+
int m_restore_fd;
4447
std::thread m_forwarder;
4548
};
4649

0 commit comments

Comments
 (0)