File: OutputRedirector.cpp

package info (click to toggle)
llvm-toolchain-21 1%3A21.1.6-3
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 2,245,028 kB
  • sloc: cpp: 7,619,726; ansic: 1,434,018; asm: 1,058,748; python: 252,740; f90: 94,671; objc: 70,685; lisp: 42,813; pascal: 18,401; sh: 8,601; ml: 5,111; perl: 4,720; makefile: 3,675; awk: 3,523; javascript: 2,409; xml: 892; fortran: 770
file content (125 lines) | stat: -rw-r--r-- 3,711 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//===-- OutputRedirector.cpp -----------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===/

#include "OutputRedirector.h"
#include "DAP.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include <cstring>
#include <system_error>
#if defined(_WIN32)
#include <fcntl.h>
#include <io.h>
#else
#include <unistd.h>
#endif

using namespace llvm;

static constexpr auto kCloseSentinel = StringLiteral::withInnerNUL("\0");

namespace lldb_dap {

int OutputRedirector::kInvalidDescriptor = -1;

OutputRedirector::OutputRedirector()
    : m_fd(kInvalidDescriptor), m_original_fd(kInvalidDescriptor),
      m_restore_fd(kInvalidDescriptor) {}

Expected<int> OutputRedirector::GetWriteFileDescriptor() {
  if (m_fd == kInvalidDescriptor)
    return createStringError(std::errc::bad_file_descriptor,
                             "write handle is not open for writing");
  return m_fd;
}

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

#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));
  }

  int read_fd = new_fd[0];
  m_fd = new_fd[1];

  if (file_override) {
    int override_fd = fileno(file_override);

    // Backup the FD to restore once redirection is complete.
    m_original_fd = override_fd;
    m_restore_fd = dup(override_fd);

    // Override the existing fd the new write end of the pipe.
    if (::dup2(m_fd, override_fd) == -1)
      return llvm::errorCodeToError(llvm::errnoAsErrorCode());
  }

  m_forwarder = std::thread([this, callback, read_fd]() {
    char buffer[OutputBufferSize];
    while (!m_stopped) {
      ssize_t bytes_count = ::read(read_fd, &buffer, sizeof(buffer));
      if (bytes_count == -1) {
        // Skip non-fatal errors.
        if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)
          continue;
        break;
      }
      // Skip the null byte used to trigger a Stop.
      if (bytes_count == 1 && buffer[0] == '\0')
        continue;

      StringRef data(buffer, bytes_count);
      if (m_stopped)
        data.consume_back(kCloseSentinel);
      if (data.empty())
        break;

      callback(data);
    }
    ::close(read_fd);
  });

  return Error::success();
}

void OutputRedirector::Stop() {
  m_stopped = true;

  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.
    (void)::write(fd, kCloseSentinel.data(), kCloseSentinel.size());
    ::close(fd);
    m_forwarder.join();

    // Restore the fd back to its original state since we stopped the
    // redirection.
    if (m_restore_fd != kInvalidDescriptor &&
        m_original_fd != kInvalidDescriptor) {
      int restore_fd = m_restore_fd;
      m_restore_fd = kInvalidDescriptor;
      int original_fd = m_original_fd;
      m_original_fd = kInvalidDescriptor;
      ::dup2(restore_fd, original_fd);
    }
  }
}

} // namespace lldb_dap