Coverage Report

Created: 2023-09-30 09:22

/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/FileSystem.h"
11
#include "lldb/Host/HostInfo.h"
12
#include "lldb/Utility/SelectHelper.h"
13
#include "llvm/ADT/SmallString.h"
14
#include "llvm/Support/Errno.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
13.5k
static bool SetCloexecFlag(int fd) {
45
13.5k
  int flags = ::fcntl(fd, F_GETFD);
46
13.5k
  if (flags == -1)
47
0
    return false;
48
13.5k
  return (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0);
49
13.5k
}
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
8.89k
    : 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
0
    : PipeBase{std::move(pipe_posix)},
64
0
      m_fds{pipe_posix.ReleaseReadFileDescriptor(),
65
0
            pipe_posix.ReleaseWriteFileDescriptor()} {}
66
67
0
PipePosix &PipePosix::operator=(PipePosix &&pipe_posix) {
68
0
  std::scoped_lock<std::mutex, std::mutex, std::mutex, std::mutex> guard(
69
0
      m_read_mutex, m_write_mutex, pipe_posix.m_read_mutex,
70
0
      pipe_posix.m_write_mutex);
71
72
0
  PipeBase::operator=(std::move(pipe_posix));
73
0
  m_fds[READ] = pipe_posix.ReleaseReadFileDescriptorUnlocked();
74
0
  m_fds[WRITE] = pipe_posix.ReleaseWriteFileDescriptorUnlocked();
75
0
  return *this;
76
0
}
77
78
8.69k
PipePosix::~PipePosix() { Close(); }
79
80
6.78k
Status PipePosix::CreateNew(bool child_processes_inherit) {
81
6.78k
  std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
82
6.78k
  if (CanReadUnlocked() || CanWriteUnlocked())
83
0
    return Status(EINVAL, eErrorTypePOSIX);
84
85
6.78k
  Status error;
86
#if PIPE2_SUPPORTED
87
  if (::pipe2(m_fds, (child_processes_inherit) ? 0 : O_CLOEXEC) == 0)
88
    return error;
89
#else
90
6.78k
  if (::pipe(m_fds) == 0) {
91
6.78k
#ifdef FD_CLOEXEC
92
6.78k
    if (!child_processes_inherit) {
93
6.78k
      if (!SetCloexecFlag(m_fds[0]) || !SetCloexecFlag(m_fds[1])) {
94
0
        error.SetErrorToErrno();
95
0
        CloseUnlocked();
96
0
        return error;
97
0
      }
98
6.78k
    }
99
6.78k
#endif
100
6.78k
    return error;
101
6.78k
  }
102
0
#endif
103
104
0
  error.SetErrorToErrno();
105
0
  m_fds[READ] = PipePosix::kInvalidDescriptor;
106
0
  m_fds[WRITE] = PipePosix::kInvalidDescriptor;
107
0
  return error;
108
6.78k
}
109
110
2
Status PipePosix::CreateNew(llvm::StringRef name, bool child_process_inherit) {
111
2
  std::scoped_lock<std::mutex, std::mutex> (m_read_mutex, m_write_mutex);
112
2
  if (CanReadUnlocked() || CanWriteUnlocked())
113
0
    return Status("Pipe is already opened");
114
115
2
  Status error;
116
2
  if (::mkfifo(name.str().c_str(), 0660) != 0)
117
0
    error.SetErrorToErrno();
118
119
2
  return error;
120
2
}
121
122
Status PipePosix::CreateWithUniqueName(llvm::StringRef prefix,
123
                                       bool child_process_inherit,
124
2
                                       llvm::SmallVectorImpl<char> &name) {
125
2
  llvm::SmallString<128> named_pipe_path;
126
2
  llvm::SmallString<128> pipe_spec((prefix + ".%%%%%%").str());
127
2
  FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
128
2
  if (!tmpdir_file_spec)
129
0
    tmpdir_file_spec.AppendPathComponent("/tmp");
130
2
  tmpdir_file_spec.AppendPathComponent(pipe_spec);
131
132
  // It's possible that another process creates the target path after we've
133
  // verified it's available but before we create it, in which case we should
134
  // try again.
135
2
  Status error;
136
2
  do {
137
2
    llvm::sys::fs::createUniquePath(tmpdir_file_spec.GetPath(), named_pipe_path,
138
2
                                    /*MakeAbsolute=*/false);
139
2
    error = CreateNew(named_pipe_path, child_process_inherit);
140
2
  } while (error.GetError() == EEXIST);
141
142
2
  if (error.Success())
143
2
    name = named_pipe_path;
144
2
  return error;
145
2
}
146
147
Status PipePosix::OpenAsReader(llvm::StringRef name,
148
1
                               bool child_process_inherit) {
149
1
  std::scoped_lock<std::mutex, std::mutex> (m_read_mutex, m_write_mutex);
150
151
1
  if (CanReadUnlocked() || CanWriteUnlocked())
152
0
    return Status("Pipe is already opened");
153
154
1
  int flags = O_RDONLY | O_NONBLOCK;
155
1
  if (!child_process_inherit)
156
1
    flags |= O_CLOEXEC;
157
158
1
  Status error;
159
1
  int fd = FileSystem::Instance().Open(name.str().c_str(), flags);
160
1
  if (fd != -1)
161
1
    m_fds[READ] = fd;
162
0
  else
163
0
    error.SetErrorToErrno();
164
165
1
  return error;
166
1
}
167
168
Status
169
PipePosix::OpenAsWriterWithTimeout(llvm::StringRef name,
170
                                   bool child_process_inherit,
171
0
                                   const std::chrono::microseconds &timeout) {
172
0
  std::lock_guard<std::mutex> guard(m_write_mutex);
173
0
  if (CanReadUnlocked() || CanWriteUnlocked())
174
0
    return Status("Pipe is already opened");
175
176
0
  int flags = O_WRONLY | O_NONBLOCK;
177
0
  if (!child_process_inherit)
178
0
    flags |= O_CLOEXEC;
179
180
0
  using namespace std::chrono;
181
0
  const auto finish_time = Now() + timeout;
182
183
0
  while (!CanWriteUnlocked()) {
184
0
    if (timeout != microseconds::zero()) {
185
0
      const auto dur = duration_cast<microseconds>(finish_time - Now()).count();
186
0
      if (dur <= 0)
187
0
        return Status("timeout exceeded - reader hasn't opened so far");
188
0
    }
189
190
0
    errno = 0;
191
0
    int fd = ::open(name.str().c_str(), flags);
192
0
    if (fd == -1) {
193
0
      const auto errno_copy = errno;
194
      // We may get ENXIO if a reader side of the pipe hasn't opened yet.
195
0
      if (errno_copy != ENXIO && errno_copy != EINTR)
196
0
        return Status(errno_copy, eErrorTypePOSIX);
197
198
0
      std::this_thread::sleep_for(
199
0
          milliseconds(OPEN_WRITER_SLEEP_TIMEOUT_MSECS));
200
0
    } else {
201
0
      m_fds[WRITE] = fd;
202
0
    }
203
0
  }
204
205
0
  return Status();
206
0
}
207
208
846k
int PipePosix::GetReadFileDescriptor() const {
209
846k
  std::lock_guard<std::mutex> guard(m_read_mutex);
210
846k
  return GetReadFileDescriptorUnlocked();
211
846k
}
212
213
846k
int PipePosix::GetReadFileDescriptorUnlocked() const {
214
846k
  return m_fds[READ];
215
846k
}
216
217
0
int PipePosix::GetWriteFileDescriptor() const {
218
0
  std::lock_guard<std::mutex> guard(m_write_mutex);
219
0
  return GetWriteFileDescriptorUnlocked();
220
0
}
221
222
15.4k
int PipePosix::GetWriteFileDescriptorUnlocked() const {
223
15.4k
  return m_fds[WRITE];
224
15.4k
}
225
226
86
int PipePosix::ReleaseReadFileDescriptor() {
227
86
  std::lock_guard<std::mutex> guard(m_read_mutex);
228
86
  return ReleaseReadFileDescriptorUnlocked();
229
86
}
230
231
86
int PipePosix::ReleaseReadFileDescriptorUnlocked() {
232
86
  const int fd = m_fds[READ];
233
86
  m_fds[READ] = PipePosix::kInvalidDescriptor;
234
86
  return fd;
235
86
}
236
237
86
int PipePosix::ReleaseWriteFileDescriptor() {
238
86
  std::lock_guard<std::mutex> guard(m_write_mutex);
239
86
  return ReleaseWriteFileDescriptorUnlocked();
240
86
}
241
242
86
int PipePosix::ReleaseWriteFileDescriptorUnlocked() {
243
86
  const int fd = m_fds[WRITE];
244
86
  m_fds[WRITE] = PipePosix::kInvalidDescriptor;
245
86
  return fd;
246
86
}
247
248
22.1k
void PipePosix::Close() {
249
22.1k
  std::scoped_lock<std::mutex, std::mutex> guard(m_read_mutex, m_write_mutex);
250
22.1k
  CloseUnlocked();
251
22.1k
}
252
253
22.1k
void PipePosix::CloseUnlocked() {
254
22.1k
  CloseReadFileDescriptorUnlocked();
255
22.1k
  CloseWriteFileDescriptorUnlocked();
256
22.1k
}
257
258
0
Status PipePosix::Delete(llvm::StringRef name) {
259
0
  return llvm::sys::fs::remove(name);
260
0
}
261
262
5
bool PipePosix::CanRead() const {
263
5
  std::lock_guard<std::mutex> guard(m_read_mutex);
264
5
  return CanReadUnlocked();
265
5
}
266
267
28.9k
bool PipePosix::CanReadUnlocked() const {
268
28.9k
  return m_fds[READ] != PipePosix::kInvalidDescriptor;
269
28.9k
}
270
271
5
bool PipePosix::CanWrite() const {
272
5
  std::lock_guard<std::mutex> guard(m_write_mutex);
273
5
  return CanWriteUnlocked();
274
5
}
275
276
44.3k
bool PipePosix::CanWriteUnlocked() const {
277
44.3k
  return m_fds[WRITE] != PipePosix::kInvalidDescriptor;
278
44.3k
}
279
280
0
void PipePosix::CloseReadFileDescriptor() {
281
0
  std::lock_guard<std::mutex> guard(m_read_mutex);
282
0
  CloseReadFileDescriptorUnlocked();
283
0
}
284
22.1k
void PipePosix::CloseReadFileDescriptorUnlocked() {
285
22.1k
  if (CanReadUnlocked()) {
286
6.70k
    close(m_fds[READ]);
287
6.70k
    m_fds[READ] = PipePosix::kInvalidDescriptor;
288
6.70k
  }
289
22.1k
}
290
291
0
void PipePosix::CloseWriteFileDescriptor() {
292
0
  std::lock_guard<std::mutex> guard(m_write_mutex);
293
0
  CloseWriteFileDescriptorUnlocked();
294
0
}
295
296
22.1k
void PipePosix::CloseWriteFileDescriptorUnlocked() {
297
22.1k
  if (CanWriteUnlocked()) {
298
6.70k
    close(m_fds[WRITE]);
299
6.70k
    m_fds[WRITE] = PipePosix::kInvalidDescriptor;
300
6.70k
  }
301
22.1k
}
302
303
Status PipePosix::ReadWithTimeout(void *buf, size_t size,
304
                                  const std::chrono::microseconds &timeout,
305
5
                                  size_t &bytes_read) {
306
5
  std::lock_guard<std::mutex> guard(m_read_mutex);
307
5
  bytes_read = 0;
308
5
  if (!CanReadUnlocked())
309
0
    return Status(EINVAL, eErrorTypePOSIX);
310
311
5
  const int fd = GetReadFileDescriptorUnlocked();
312
313
5
  SelectHelper select_helper;
314
5
  select_helper.SetTimeout(timeout);
315
5
  select_helper.FDSetRead(fd);
316
317
5
  Status error;
318
5
  while (error.Success()) {
319
5
    error = select_helper.Select();
320
5
    if (error.Success()) {
321
5
      auto result =
322
5
          ::read(fd, static_cast<char *>(buf) + bytes_read, size - bytes_read);
323
5
      if (result != -1) {
324
5
        bytes_read += result;
325
5
        if (bytes_read == size || 
result == 00
)
326
5
          break;
327
5
      } else 
if (errno0
== EINTR0
) {
328
0
        continue;
329
0
      } else {
330
0
        error.SetErrorToErrno();
331
0
        break;
332
0
      }
333
5
    }
334
5
  }
335
5
  return error;
336
5
}
337
338
15.4k
Status PipePosix::Write(const void *buf, size_t size, size_t &bytes_written) {
339
15.4k
  std::lock_guard<std::mutex> guard(m_write_mutex);
340
15.4k
  bytes_written = 0;
341
15.4k
  if (!CanWriteUnlocked())
342
1
    return Status(EINVAL, eErrorTypePOSIX);
343
344
15.4k
  const int fd = GetWriteFileDescriptorUnlocked();
345
15.4k
  SelectHelper select_helper;
346
15.4k
  select_helper.SetTimeout(std::chrono::seconds(0));
347
15.4k
  select_helper.FDSetWrite(fd);
348
349
15.4k
  Status error;
350
15.4k
  while (error.Success()) {
351
15.4k
    error = select_helper.Select();
352
15.4k
    if (error.Success()) {
353
15.4k
      auto result = ::write(fd, static_cast<const char *>(buf) + bytes_written,
354
15.4k
                            size - bytes_written);
355
15.4k
      if (result != -1) {
356
15.4k
        bytes_written += result;
357
15.4k
        if (bytes_written == size)
358
15.4k
          break;
359
15.4k
      } else 
if (errno0
== EINTR0
) {
360
0
        continue;
361
0
      } else {
362
0
        error.SetErrorToErrno();
363
0
      }
364
15.4k
    }
365
15.4k
  }
366
15.4k
  return error;
367
15.4k
}