Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 42 additions & 24 deletions lldb/tools/lldb-dap/OutputRedirector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
//
//===----------------------------------------------------------------------===/

#include "OutputRedirector.h"
#include "DAP.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include <system_error>
#if defined(_WIN32)
Expand All @@ -15,45 +18,59 @@
#include <unistd.h>
#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<int> 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<void(StringRef)> 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();
Expand All @@ -62,14 +79,15 @@ Error OutputRedirector::RedirectTo(std::function<void(StringRef)> 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();
}
}
Expand Down
6 changes: 4 additions & 2 deletions lldb/tools/lldb-dap/OutputRedirector.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -33,13 +35,13 @@ class OutputRedirector {

~OutputRedirector() { Stop(); }

OutputRedirector() = default;
OutputRedirector();
OutputRedirector(const OutputRedirector &) = delete;
OutputRedirector &operator=(const OutputRedirector &) = delete;

private:
std::atomic<bool> m_stopped = false;
lldb_private::Pipe m_pipe;
int m_fd;
std::thread m_forwarder;
};

Expand Down