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

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