126 lines · cpp
1//===-- OutputRedirector.cpp -----------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===/8 9#include "OutputRedirector.h"10#include "DAP.h"11#include "llvm/ADT/StringRef.h"12#include "llvm/Support/Error.h"13#include <cstring>14#include <system_error>15#if defined(_WIN32)16#include <fcntl.h>17#include <io.h>18#else19#include <unistd.h>20#endif21 22using namespace llvm;23 24static constexpr auto kCloseSentinel = StringLiteral::withInnerNUL("\0");25 26namespace lldb_dap {27 28int OutputRedirector::kInvalidDescriptor = -1;29 30OutputRedirector::OutputRedirector()31 : m_fd(kInvalidDescriptor), m_original_fd(kInvalidDescriptor),32 m_restore_fd(kInvalidDescriptor) {}33 34Expected<int> OutputRedirector::GetWriteFileDescriptor() {35 if (m_fd == kInvalidDescriptor)36 return createStringError(std::errc::bad_file_descriptor,37 "write handle is not open for writing");38 return m_fd;39}40 41Error OutputRedirector::RedirectTo(std::FILE *file_override,42 std::function<void(StringRef)> callback) {43 assert(m_fd == kInvalidDescriptor && "Output readirector already started.");44 int new_fd[2];45 46#if defined(_WIN32)47 if (::_pipe(new_fd, OutputBufferSize, O_TEXT) == -1) {48#else49 if (::pipe(new_fd) == -1) {50#endif51 int error = errno;52 return createStringError(inconvertibleErrorCode(),53 "Couldn't create new pipe %s", strerror(error));54 }55 56 int read_fd = new_fd[0];57 m_fd = new_fd[1];58 59 if (file_override) {60 int override_fd = fileno(file_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 71 m_forwarder = std::thread([this, callback, read_fd]() {72 char buffer[OutputBufferSize];73 while (!m_stopped) {74 ssize_t bytes_count = ::read(read_fd, &buffer, sizeof(buffer));75 if (bytes_count == -1) {76 // Skip non-fatal errors.77 if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)78 continue;79 break;80 }81 // Skip the null byte used to trigger a Stop.82 if (bytes_count == 1 && buffer[0] == '\0')83 continue;84 85 StringRef data(buffer, bytes_count);86 if (m_stopped)87 data.consume_back(kCloseSentinel);88 if (data.empty())89 break;90 91 callback(data);92 }93 ::close(read_fd);94 });95 96 return Error::success();97}98 99void OutputRedirector::Stop() {100 m_stopped = true;101 102 if (m_fd != kInvalidDescriptor) {103 int fd = m_fd;104 m_fd = kInvalidDescriptor;105 // Closing the pipe may not be sufficient to wake up the thread in case the106 // write descriptor is duplicated (to stdout/err or to another process).107 // Write a null byte to ensure the read call returns.108 (void)::write(fd, kCloseSentinel.data(), kCloseSentinel.size());109 ::close(fd);110 m_forwarder.join();111 112 // Restore the fd back to its original state since we stopped the113 // 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 }122 }123}124 125} // namespace lldb_dap126