brintos

brintos / llvm-project-archived public Read only

0
0
Text · 3.6 KiB · fe278fa Raw
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