1//===-- PipeTest.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/Pipe.h"
10#include "TestingSupport/SubsystemRAII.h"
11#include "lldb/Host/FileSystem.h"
12#include "lldb/Host/HostInfo.h"
13#include "llvm/Testing/Support/Error.h"
14#include "gtest/gtest.h"
15#include <chrono>
16#include <fcntl.h>
17#include <future>
18#include <numeric>
19#include <thread>
20#include <vector>
21
22using namespace lldb_private;
23
24class PipeTest : public testing::Test {
25public:
26 SubsystemRAII<FileSystem, HostInfo> subsystems;
27};
28
29TEST_F(PipeTest, CreateWithUniqueName) {
30 Pipe pipe;
31 llvm::SmallString<0> name;
32 ASSERT_THAT_ERROR(pipe.CreateWithUniqueName("PipeTest-CreateWithUniqueName",
33 /*child_process_inherit=*/false,
34 name)
35 .ToError(),
36 llvm::Succeeded());
37}
38
39// Test broken
40#ifndef _WIN32
41TEST_F(PipeTest, OpenAsReader) {
42 Pipe pipe;
43 llvm::SmallString<0> name;
44 ASSERT_THAT_ERROR(pipe.CreateWithUniqueName("PipeTest-OpenAsReader",
45 /*child_process_inherit=*/false,
46 name)
47 .ToError(),
48 llvm::Succeeded());
49
50 // Ensure name is not null-terminated
51 size_t name_len = name.size();
52 name += "foobar";
53 llvm::StringRef name_ref(name.data(), name_len);
54 ASSERT_THAT_ERROR(
55 pipe.OpenAsReader(name_ref, /*child_process_inherit=*/false).ToError(),
56 llvm::Succeeded());
57
58 ASSERT_TRUE(pipe.CanRead());
59}
60#endif
61
62// Tests flaky on Windows
63#ifndef _WIN32
64TEST_F(PipeTest, WriteWithTimeout) {
65 Pipe pipe;
66 ASSERT_THAT_ERROR(pipe.CreateNew(false).ToError(), llvm::Succeeded());
67
68 // The pipe buffer is 1024 for PipeWindows and at least 512 on Darwin.
69 // In Linux versions before 2.6.11, the capacity of a pipe was the same as the
70 // system page size (e.g., 4096 bytes on i386).
71 // Since Linux 2.6.11, the pipe capacity is 16 pages (i.e., 65,536 bytes in a
72 // system with a page size of 4096 bytes).
73 // Since Linux 2.6.35, the default pipe capacity is 16 pages, but the capacity
74 // can be queried and set using the fcntl(2) F_GETPIPE_SZ and F_SETPIPE_SZ
75 // operations:
76
77#if !defined(_WIN32) && defined(F_SETPIPE_SZ)
78 ::fcntl(fd: pipe.GetWriteFileDescriptor(), F_SETPIPE_SZ, 4096);
79#endif
80
81 const size_t buf_size = 66000;
82
83 // Note write_chunk_size must be less than the pipe buffer.
84 const size_t write_chunk_size = 234;
85
86 std::vector<int32_t> write_buf(buf_size / sizeof(int32_t));
87 std::iota(first: write_buf.begin(), last: write_buf.end(), value: 0);
88 std::vector<int32_t> read_buf(write_buf.size() + 100, -1);
89
90 char *write_ptr = reinterpret_cast<char *>(write_buf.data());
91 char *read_ptr = reinterpret_cast<char *>(read_buf.data());
92 size_t write_bytes = 0;
93 size_t read_bytes = 0;
94
95 // Write to the pipe until it is full.
96 while (write_bytes + write_chunk_size <= buf_size) {
97 llvm::Expected<size_t> num_bytes =
98 pipe.Write(buf: write_ptr + write_bytes, size: write_chunk_size,
99 timeout: std::chrono::milliseconds(10));
100 if (num_bytes) {
101 write_bytes += *num_bytes;
102 } else {
103 ASSERT_THAT_ERROR(num_bytes.takeError(), llvm::Failed());
104 break; // The write buffer is full.
105 }
106 }
107 ASSERT_LE(write_bytes + write_chunk_size, buf_size)
108 << "Pipe buffer larger than expected";
109
110 // Attempt a write with a long timeout.
111 auto start_time = std::chrono::steady_clock::now();
112 // TODO: Assert a specific error (EAGAIN?) here.
113 ASSERT_THAT_EXPECTED(pipe.Write(write_ptr + write_bytes, write_chunk_size,
114 std::chrono::seconds(2)),
115 llvm::Failed());
116 auto dur = std::chrono::steady_clock::now() - start_time;
117 ASSERT_GE(dur, std::chrono::seconds(2));
118
119 // Attempt a write with a short timeout.
120 start_time = std::chrono::steady_clock::now();
121 ASSERT_THAT_EXPECTED(pipe.Write(write_ptr + write_bytes, write_chunk_size,
122 std::chrono::milliseconds(200)),
123 llvm::Failed());
124 dur = std::chrono::steady_clock::now() - start_time;
125 ASSERT_GE(dur, std::chrono::milliseconds(200));
126 ASSERT_LT(dur, std::chrono::seconds(2));
127
128 // Drain the pipe.
129 while (read_bytes < write_bytes) {
130 llvm::Expected<size_t> num_bytes =
131 pipe.Read(buf: read_ptr + read_bytes, size: write_bytes - read_bytes,
132 timeout: std::chrono::milliseconds(10));
133 ASSERT_THAT_EXPECTED(num_bytes, llvm::Succeeded());
134 read_bytes += *num_bytes;
135 }
136
137 // Be sure the pipe is empty.
138 ASSERT_THAT_EXPECTED(
139 pipe.Read(read_ptr + read_bytes, 100, std::chrono::milliseconds(10)),
140 llvm::Failed());
141
142 // Check that we got what we wrote.
143 ASSERT_EQ(write_bytes, read_bytes);
144 ASSERT_TRUE(std::equal(write_buf.begin(),
145 write_buf.begin() + write_bytes / sizeof(uint32_t),
146 read_buf.begin()));
147
148 // Write to the pipe again and check that it succeeds.
149 ASSERT_THAT_EXPECTED(
150 pipe.Write(write_ptr, write_chunk_size, std::chrono::milliseconds(10)),
151 llvm::Succeeded());
152}
153
154TEST_F(PipeTest, ReadWithTimeout) {
155 Pipe pipe;
156 ASSERT_THAT_ERROR(pipe.CreateNew(false).ToError(), llvm::Succeeded());
157
158 char buf[100];
159 // The pipe is initially empty. A polling read returns immediately.
160 ASSERT_THAT_EXPECTED(pipe.Read(buf, sizeof(buf), std::chrono::seconds(0)),
161 llvm::Failed());
162
163 // With a timeout, we should wait for at least this amount of time (but not
164 // too much).
165 auto start = std::chrono::steady_clock::now();
166 ASSERT_THAT_EXPECTED(
167 pipe.Read(buf, sizeof(buf), std::chrono::milliseconds(200)),
168 llvm::Failed());
169 auto dur = std::chrono::steady_clock::now() - start;
170 EXPECT_GT(dur, std::chrono::milliseconds(200));
171 EXPECT_LT(dur, std::chrono::seconds(2));
172
173 // Write something into the pipe, and read it back. The blocking read call
174 // should return even though it hasn't filled the buffer.
175 llvm::StringRef hello_world("Hello world!");
176 ASSERT_THAT_EXPECTED(pipe.Write(hello_world.data(), hello_world.size()),
177 llvm::HasValue(hello_world.size()));
178 ASSERT_THAT_EXPECTED(pipe.Read(buf, sizeof(buf)),
179 llvm::HasValue(hello_world.size()));
180 EXPECT_EQ(llvm::StringRef(buf, hello_world.size()), hello_world);
181
182 // Now write something and try to read it in chunks.
183 memset(s: buf, c: 0, n: sizeof(buf));
184 ASSERT_THAT_EXPECTED(pipe.Write(hello_world.data(), hello_world.size()),
185 llvm::HasValue(hello_world.size()));
186 ASSERT_THAT_EXPECTED(pipe.Read(buf, 4), llvm::HasValue(4));
187 ASSERT_THAT_EXPECTED(pipe.Read(buf + 4, sizeof(buf) - 4),
188 llvm::HasValue(hello_world.size() - 4));
189 EXPECT_EQ(llvm::StringRef(buf, hello_world.size()), hello_world);
190
191 // A blocking read should wait until the data arrives.
192 memset(s: buf, c: 0, n: sizeof(buf));
193 std::future<llvm::Expected<size_t>> future_num_bytes = std::async(
194 policy: std::launch::async, fn: [&] { return pipe.Read(buf, size: sizeof(buf)); });
195 std::this_thread::sleep_for(rtime: std::chrono::milliseconds(10));
196 ASSERT_THAT_EXPECTED(pipe.Write(hello_world.data(), hello_world.size()),
197 llvm::HasValue(hello_world.size()));
198 ASSERT_THAT_EXPECTED(future_num_bytes.get(),
199 llvm::HasValue(hello_world.size()));
200 EXPECT_EQ(llvm::StringRef(buf, hello_world.size()), hello_world);
201}
202#endif /*ifndef _WIN32*/
203

source code of lldb/unittests/Host/PipeTest.cpp