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-exception
6//
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#else
19#include <unistd.h>
20#endif
21
22using namespace llvm;
23
24static constexpr auto kCloseSentinel = StringLiteral::withInnerNUL(Str: "\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(EC: std::errc::bad_file_descriptor,
37 Fmt: "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#else
49 if (::pipe(pipedes: new_fd) == -1) {
50#endif
51 int error = errno;
52 return createStringError(EC: inconvertibleErrorCode(),
53 Fmt: "Couldn't create new pipe %s", Vals: strerror(errnum: 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(stream: file_override);
61
62 // Backup the FD to restore once redirection is complete.
63 m_original_fd = override_fd;
64 m_restore_fd = dup(fd: override_fd);
65
66 // Override the existing fd the new write end of the pipe.
67 if (::dup2(fd: m_fd, fd2: override_fd) == -1)
68 return llvm::errorCodeToError(EC: 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(fd: read_fd, buf: &buffer, nbytes: 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(Suffix: kCloseSentinel);
88 if (data.empty())
89 break;
90
91 callback(data);
92 }
93 ::close(fd: 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 the
106 // 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: fd, buf: kCloseSentinel.data(), n: kCloseSentinel.size());
109 ::close(fd: fd);
110 m_forwarder.join();
111
112 // Restore the fd back to its original state since we stopped the
113 // 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(fd: restore_fd, fd2: original_fd);
121 }
122 }
123}
124
125} // namespace lldb_dap
126

source code of lldb/tools/lldb-dap/OutputRedirector.cpp