Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Host/posix/PipePosix.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- PipePosix.cpp -----------------------------------------------------===//
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 "lldb/Host/posix/PipePosix.h"
10
#include "lldb/Host/HostInfo.h"
11
#include "lldb/Utility/SelectHelper.h"
12
#include "llvm/ADT/SmallString.h"
13
#include "llvm/Support/Errno.h"
14
#include "llvm/Support/FileSystem.h"
15
#include <functional>
16
#include <thread>
17
18
#include <cerrno>
19
#include <climits>
20
#include <fcntl.h>
21
#include <sys/stat.h>
22
#include <sys/types.h>
23
#include <unistd.h>
24
25
using namespace lldb;
26
using namespace lldb_private;
27
28
int PipePosix::kInvalidDescriptor = -1;
29
30
enum PIPES { READ, WRITE }; // Constants 0 and 1 for READ and WRITE
31
32
// pipe2 is supported by a limited set of platforms
33
// TODO: Add more platforms that support pipe2.
34
#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD__ >= 10) ||       \
35
    defined(__NetBSD__)
36
#define PIPE2_SUPPORTED 1
37
#else
38
#define PIPE2_SUPPORTED 0
39
#endif
40
41
static constexpr auto OPEN_WRITER_SLEEP_TIMEOUT_MSECS = 100;
42
43
#if defined(FD_CLOEXEC) && !PIPE2_SUPPORTED
44
16.0k
static bool SetCloexecFlag(int fd) {
45
16.0k
  int flags = ::fcntl(fd, F_GETFD);
46
16.0k
  if (flags == -1)
47
0
    return false;
48
16.0k
  return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
49
16.0k
}
50
#endif
51
52
0
static std::chrono::time_point<std::chrono::steady_clock> Now() {
53
0
  return std::chrono::steady_clock::now();
54
0
}
55
56
PipePosix::PipePosix()
57
10.6k
    : m_fds{PipePosix::kInvalidDescriptor, PipePosix::kInvalidDescriptor} {}
58
59
PipePosix::PipePosix(lldb::pipe_t read, lldb::pipe_t write)
60
0
    : m_fds{read, write} {}
61
62
PipePosix::PipePosix(PipePosix &&pipe_posix)
63
    : PipeBase{std::move(pipe_posix)},
64
      m_fds{pipe_posix.ReleaseReadFileDescriptor(),
65
0
            pipe_posix.ReleaseWriteFileDescriptor()} {}
66
67
0
PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) {
68
0
  PipeBase::operator=(std::move(pipe_posix));
69
0
  m_fds[READ] = pipe_posix.ReleaseReadFileDescriptor();
70
0
  m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptor();
71
0
  return *this;
72
0
}
73
74
10.3k
PipePosix::~PipePosix() { Close(); }
75
76
8.04k
Status PipePosix::CreateNew(bool child_processes_inherit) {
77
8.04k
  if (CanRead() || CanWrite())
78
0
    return Status(EINVAL, eErrorTypePOSIX);
79
80
8.04k
  Status error;
81
#if PIPE2_SUPPORTED
82
  if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)
83
    return error;
84
#else
85
8.04k
  if (::pipe(m_fds) == 0) {
86
8.04k
#ifdef FD_CLOEXEC
87
8.04k
    if (!child_processes_inherit) {
88
8.04k
      if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {
89
0
        error.SetErrorToErrno();
90
0
        Close();
91
0
        return error;
92
0
      }
93
8.04k
    }
94
8.04k
#endif
95
8.04k
    return error;
96
8.04k
  }
97
0
#endif
98
99
0
  error.SetErrorToErrno();
100
0
  m_fds[READ] = PipePosix::kInvalidDescriptor;
101
0
  m_fds[WRITE] = PipePosix::kInvalidDescriptor;
102
0
  return error;
103
8.04k
}
104
105
2
Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {
106
2
  if (CanRead() || CanWrite())
107
0
    return Status("Pipe is already opened");
108
109
2
  Status error;
110
2
  if (::mkfifo(name.str().c_str(), 0660) != 0)
111
0
    error.SetErrorToErrno();
112
113
2
  return error;
114
2
}
115
116
Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix,
117
                                       bool child_process_inherit,
118
2
                                       llvm::SmallVectorImpl<char> &name) {
119
2
  llvm::SmallString<128> named_pipe_path;
120
2
  llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str());
121
2
  FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
122
2
  if (!tmpdir_file_spec)
123
0
    tmpdir_file_spec.AppendPathComponent("/tmp");
124
2
  tmpdir_file_spec.AppendPathComponent(pipe_spec);
125
126
  // It's possible that another process creates the target path after we've
127
  // verified it's available but before we create it, in which case we should
128
  // try again.
129
2
  Status error;
130
2
  do {
131
2
    llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path,
132
2
                                    /*MakeAbsolute=*/false);
133
2
    error = CreateNew(named_pipe_path, child_process_inherit);
134
2
  } while (error.GetError() == EEXIST);
135
136
2
  if (error.Success())
137
2
    name = named_pipe_path;
138
2
  return error;
139
2
}
140
141
Status PipePosix::OpenAsReader(llvm::StringRef name,
142
1
                               bool child_process_inherit) {
143
1
  if (CanRead() || CanWrite())
144
0
    return Status("Pipe is already opened");
145
146
1
  int flags = O_RDONLY | O_NONBLOCK;
147
1
  if (!child_process_inherit)
148
1
    flags |= O_CLOEXEC;
149
150
1
  Status error;
151
1
  int fd = llvm::sys::RetryAfterSignal(-1, ::open, name.str().c_str(), flags);
152
1
  if (fd != -1)
153
1
    m_fds[READ] = fd;
154
0
  else
155
0
    error.SetErrorToErrno();
156
157
1
  return error;
158
1
}
159
160
Status
161
PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
162
                                   bool child_process_inherit,
163
0
                                   const std::chrono::microseconds &timeout) {
164
0
  if (CanRead() || CanWrite())
165
0
    return Status("Pipe is already opened");
166
167
0
  int flags = O_WRONLY | O_NONBLOCK;
168
0
  if (!child_process_inherit)
169
0
    flags |= O_CLOEXEC;
170
171
0
  using namespace std::chrono;
172
0
  const auto finish_time = Now() + timeout;
173
174
0
  while (!CanWrite()) {
175
0
    if (timeout != microseconds::zero()) {
176
0
      const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
177
0
      if (dur <= 0)
178
0
        return Status("timeout exceeded - reader hasn't opened so far");
179
0
    }
180
181
0
    errno = 0;
182
0
    int fd = ::open(name.str().c_str(), flags);
183
0
    if (fd == -1) {
184
0
      const auto errno_copy = errno;
185
      // We may get ENXIO if a reader side of the pipe hasn't opened yet.
186
0
      if (errno_copy != ENXIO && errno_copy != EINTR)
187
0
        return Status(errno_copy, eErrorTypePOSIX);
188
189
0
      std::this_thread::sleep_for(
190
0
          milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
191
0
    } else {
192
0
      m_fds[WRITE] = fd;
193
0
    }
194
0
  }
195
196
0
  return Status();
197
0
}
198
199
866k
int PipePosix::GetReadFileDescriptor() const { return m_fds[READ]; }
200
201
19.2k
int PipePosix::GetWriteFileDescriptor() const { return m_fds[WRITE]; }
202
203
84
int PipePosix::ReleaseReadFileDescriptor() {
204
84
  const int fd = m_fds[READ];
205
84
  m_fds[READ] = PipePosix::kInvalidDescriptor;
206
84
  return fd;
207
84
}
208
209
83
int PipePosix::ReleaseWriteFileDescriptor() {
210
83
  const int fd = m_fds[WRITE];
211
83
  m_fds[WRITE] = PipePosix::kInvalidDescriptor;
212
83
  return fd;
213
83
}
214
215
26.3k
void PipePosix::Close() {
216
26.3k
  CloseReadFileDescriptor();
217
26.3k
  CloseWriteFileDescriptor();
218
26.3k
}
219
220
0
Status PipePosix::Delete(llvm::StringRef name) {
221
0
  return llvm::sys::fs::remove(name);
222
0
}
223
224
34.3k
bool PipePosix::CanRead() const {
225
34.3k
  return m_fds[READ] != PipePosix::kInvalidDescriptor;
226
34.3k
}
227
228
53.6k
bool PipePosix::CanWrite() const {
229
53.6k
  return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
230
53.6k
}
231
232
26.3k
void PipePosix::CloseReadFileDescriptor() {
233
26.3k
  if (CanRead()) {
234
7.96k
    close(m_fds[READ]);
235
7.96k
    m_fds[READ] = PipePosix::kInvalidDescriptor;
236
7.96k
  }
237
26.3k
}
238
239
26.3k
void PipePosix::CloseWriteFileDescriptor() {
240
26.3k
  if (CanWrite()) {
241
7.96k
    close(m_fds[WRITE]);
242
7.96k
    m_fds[WRITE] = PipePosix::kInvalidDescriptor;
243
7.96k
  }
244
26.3k
}
245
246
Status PipePosix::ReadWithTimeout(void *buf, size_t size,
247
                                  const std::chrono::microseconds &timeout,
248
5
                                  size_t &bytes_read) {
249
5
  bytes_read = 0;
250
5
  if (!CanRead())
251
0
    return Status(EINVAL, eErrorTypePOSIX);
252
253
5
  const int fd = GetReadFileDescriptor();
254
255
5
  SelectHelper select_helper;
256
5
  select_helper.SetTimeout(timeout);
257
5
  select_helper.FDSetRead(fd);
258
259
5
  Status error;
260
5
  while (error.Success()) {
261
5
    error = select_helper.Select();
262
5
    if (error.Success()) {
263
5
      auto result =
264
5
          ::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read);
265
5
      if (result != -1) {
266
5
        bytes_read += result;
267
5
        if (bytes_read == size || 
result == 00
)
268
5
          break;
269
5
      } else 
if (errno0
== EINTR0
) {
270
0
        continue;
271
0
      } else {
272
0
        error.SetErrorToErrno();
273
0
        break;
274
0
      }
275
5
    }
276
5
  }
277
5
  return error;
278
5
}
279
280
19.2k
Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {
281
19.2k
  bytes_written = 0;
282
19.2k
  if (!CanWrite())
283
0
    return Status(EINVAL, eErrorTypePOSIX);
284
285
19.2k
  const int fd = GetWriteFileDescriptor();
286
19.2k
  SelectHelper select_helper;
287
19.2k
  select_helper.SetTimeout(std::chrono::seconds(0));
288
19.2k
  select_helper.FDSetWrite(fd);
289
290
19.2k
  Status error;
291
19.2k
  while (error.Success()) {
292
19.2k
    error = select_helper.Select();
293
19.2k
    if (error.Success()) {
294
19.2k
      auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written,
295
19.2k
                            size - bytes_written);
296
19.2k
      if (result != -1) {
297
19.2k
        bytes_written += result;
298
19.2k
        if (bytes_written == size)
299
19.2k
          break;
300
19.2k
      } else 
if (errno0
== EINTR0
) {
301
0
        continue;
302
0
      } else {
303
0
        error.SetErrorToErrno();
304
0
      }
305
19.2k
    }
306
19.2k
  }
307
19.2k
  return error;
308
19.2k
}