diff --git a/lldb/tools/lldb-dap/OutputRedirector.cpp b/lldb/tools/lldb-dap/OutputRedirector.cpp index 8fcbcfec99c44..7935e17a653be 100644 --- a/lldb/tools/lldb-dap/OutputRedirector.cpp +++ b/lldb/tools/lldb-dap/OutputRedirector.cpp @@ -6,6 +6,9 @@ // //===----------------------------------------------------------------------===/ +#include "OutputRedirector.h" +#include "DAP.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include #if defined(_WIN32) @@ -15,45 +18,59 @@ #include #endif -#include "DAP.h" -#include "OutputRedirector.h" -#include "llvm/ADT/StringRef.h" - using lldb_private::Pipe; -using lldb_private::Status; using llvm::createStringError; using llvm::Error; using llvm::Expected; +using llvm::inconvertibleErrorCode; using llvm::StringRef; namespace lldb_dap { +int OutputRedirector::kInvalidDescriptor = -1; + +OutputRedirector::OutputRedirector() : m_fd(kInvalidDescriptor) {} + Expected OutputRedirector::GetWriteFileDescriptor() { - if (!m_pipe.CanWrite()) + if (m_fd == kInvalidDescriptor) return createStringError(std::errc::bad_file_descriptor, "write handle is not open for writing"); - return m_pipe.GetWriteFileDescriptor(); + return m_fd; } Error OutputRedirector::RedirectTo(std::function callback) { - Status status = m_pipe.CreateNew(/*child_process_inherit=*/false); - if (status.Fail()) - return status.takeError(); + assert(m_fd == kInvalidDescriptor && "Output readirector already started."); + int new_fd[2]; - m_forwarder = std::thread([this, callback]() { - char buffer[OutputBufferSize]; - while (m_pipe.CanRead() && !m_stopped) { - size_t bytes_read; - Status status = m_pipe.Read(&buffer, sizeof(buffer), bytes_read); - if (status.Fail()) - continue; +#if defined(_WIN32) + if (::_pipe(new_fd, OutputBufferSize, O_TEXT) == -1) { +#else + if (::pipe(new_fd) == -1) { +#endif + int error = errno; + return createStringError(inconvertibleErrorCode(), + "Couldn't create new pipe %s", strerror(error)); + } - // EOF detected - if (bytes_read == 0 || m_stopped) + int read_fd = new_fd[0]; + m_fd = new_fd[1]; + m_forwarder = std::thread([this, callback, read_fd]() { + char buffer[OutputBufferSize]; + while (!m_stopped) { + ssize_t bytes_count = ::read(read_fd, &buffer, sizeof(buffer)); + // EOF detected. + if (bytes_count == 0) + break; + if (bytes_count == -1) { + // Skip non-fatal errors. + if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) + continue; break; + } - callback(StringRef(buffer, bytes_read)); + callback(StringRef(buffer, bytes_count)); } + ::close(read_fd); }); return Error::success(); @@ -62,14 +79,15 @@ Error OutputRedirector::RedirectTo(std::function callback) { void OutputRedirector::Stop() { m_stopped = true; - if (m_pipe.CanWrite()) { + if (m_fd != kInvalidDescriptor) { + int fd = m_fd; + m_fd = kInvalidDescriptor; // Closing the pipe may not be sufficient to wake up the thread in case the // write descriptor is duplicated (to stdout/err or to another process). // Write a null byte to ensure the read call returns. char buf[] = "\0"; - size_t bytes_written; - m_pipe.Write(buf, sizeof(buf), bytes_written); - m_pipe.CloseWriteFileDescriptor(); + ::write(fd, buf, sizeof(buf)); + ::close(fd); m_forwarder.join(); } } diff --git a/lldb/tools/lldb-dap/OutputRedirector.h b/lldb/tools/lldb-dap/OutputRedirector.h index 41ea05c22c691..d2bd39797f3d7 100644 --- a/lldb/tools/lldb-dap/OutputRedirector.h +++ b/lldb/tools/lldb-dap/OutputRedirector.h @@ -20,6 +20,8 @@ namespace lldb_dap { class OutputRedirector { public: + static int kInvalidDescriptor; + /// Creates writable file descriptor that will invoke the given callback on /// each write in a background thread. /// @@ -33,13 +35,13 @@ class OutputRedirector { ~OutputRedirector() { Stop(); } - OutputRedirector() = default; + OutputRedirector(); OutputRedirector(const OutputRedirector &) = delete; OutputRedirector &operator=(const OutputRedirector &) = delete; private: std::atomic m_stopped = false; - lldb_private::Pipe m_pipe; + int m_fd; std::thread m_forwarder; };