xref: /llvm-project/lldb/unittests/Host/PipeTest.cpp (revision 2fae58e9c7becc376454005da69acb3fa993350e)
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 "gtest/gtest.h"
14 #include <fcntl.h>
15 #include <numeric>
16 #include <vector>
17 
18 using namespace lldb_private;
19 
20 class PipeTest : public testing::Test {
21 public:
22   SubsystemRAII<FileSystem, HostInfo> subsystems;
23 };
24 
25 TEST_F(PipeTest, CreateWithUniqueName) {
26   Pipe pipe;
27   llvm::SmallString<0> name;
28   ASSERT_THAT_ERROR(pipe.CreateWithUniqueName("PipeTest-CreateWithUniqueName",
29                                               /*child_process_inherit=*/false,
30                                               name)
31                         .ToError(),
32                     llvm::Succeeded());
33 }
34 
35 // Test broken
36 #ifndef _WIN32
37 TEST_F(PipeTest, OpenAsReader) {
38   Pipe pipe;
39   llvm::SmallString<0> name;
40   ASSERT_THAT_ERROR(pipe.CreateWithUniqueName("PipeTest-OpenAsReader",
41                                               /*child_process_inherit=*/false,
42                                               name)
43                         .ToError(),
44                     llvm::Succeeded());
45 
46   // Ensure name is not null-terminated
47   size_t name_len = name.size();
48   name += "foobar";
49   llvm::StringRef name_ref(name.data(), name_len);
50   ASSERT_THAT_ERROR(
51       pipe.OpenAsReader(name_ref, /*child_process_inherit=*/false).ToError(),
52       llvm::Succeeded());
53 
54   ASSERT_TRUE(pipe.CanRead());
55 }
56 #endif
57 
58 // This test is flaky on Windows on Arm.
59 #ifndef _WIN32
60 TEST_F(PipeTest, WriteWithTimeout) {
61   Pipe pipe;
62   ASSERT_THAT_ERROR(pipe.CreateNew(false).ToError(), llvm::Succeeded());
63 
64   // The pipe buffer is 1024 for PipeWindows and at least 512 on Darwin.
65   // In Linux versions before 2.6.11, the capacity of a pipe was the same as the
66   // system page size (e.g., 4096 bytes on i386).
67   // Since Linux 2.6.11, the pipe capacity is 16 pages (i.e., 65,536 bytes in a
68   // system with a page size of 4096 bytes).
69   // Since Linux 2.6.35, the default pipe capacity is 16 pages, but the capacity
70   // can be queried and set using the fcntl(2) F_GETPIPE_SZ and F_SETPIPE_SZ
71   // operations:
72 
73 #if !defined(_WIN32) && defined(F_SETPIPE_SZ)
74   ::fcntl(pipe.GetWriteFileDescriptor(), F_SETPIPE_SZ, 4096);
75 #endif
76 
77   const size_t buf_size = 66000;
78 
79   // Note write_chunk_size must be less than the pipe buffer.
80   const size_t write_chunk_size = 234;
81 
82   std::vector<int32_t> write_buf(buf_size / sizeof(int32_t));
83   std::iota(write_buf.begin(), write_buf.end(), 0);
84   std::vector<int32_t> read_buf(write_buf.size() + 100, -1);
85 
86   char *write_ptr = reinterpret_cast<char *>(write_buf.data());
87   char *read_ptr = reinterpret_cast<char *>(read_buf.data());
88   size_t write_bytes = 0;
89   size_t read_bytes = 0;
90   size_t num_bytes = 0;
91 
92   // Write to the pipe until it is full.
93   while (write_bytes + write_chunk_size <= buf_size) {
94     Status error =
95         pipe.WriteWithTimeout(write_ptr + write_bytes, write_chunk_size,
96                               std::chrono::milliseconds(10), num_bytes);
97     if (error.Fail())
98       break; // The write buffer is full.
99     write_bytes += num_bytes;
100   }
101   ASSERT_LE(write_bytes + write_chunk_size, buf_size)
102       << "Pipe buffer larger than expected";
103 
104   // Attempt a write with a long timeout.
105   auto start_time = std::chrono::steady_clock::now();
106   ASSERT_THAT_ERROR(pipe.WriteWithTimeout(write_ptr + write_bytes,
107                                           write_chunk_size,
108                                           std::chrono::seconds(2), num_bytes)
109                         .ToError(),
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_ERROR(
117       pipe.WriteWithTimeout(write_ptr + write_bytes, write_chunk_size,
118                             std::chrono::milliseconds(200), num_bytes)
119           .ToError(),
120       llvm::Failed());
121   dur = std::chrono::steady_clock::now() - start_time;
122   ASSERT_GE(dur, std::chrono::milliseconds(200));
123   ASSERT_LT(dur, std::chrono::seconds(2));
124 
125   // Drain the pipe.
126   while (read_bytes < write_bytes) {
127     ASSERT_THAT_ERROR(
128         pipe.ReadWithTimeout(read_ptr + read_bytes, write_bytes - read_bytes,
129                              std::chrono::milliseconds(10), num_bytes)
130             .ToError(),
131         llvm::Succeeded());
132     read_bytes += num_bytes;
133   }
134 
135   // Be sure the pipe is empty.
136   ASSERT_THAT_ERROR(pipe.ReadWithTimeout(read_ptr + read_bytes, 100,
137                                          std::chrono::milliseconds(10),
138                                          num_bytes)
139                         .ToError(),
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_ERROR(pipe.WriteWithTimeout(write_ptr, write_chunk_size,
150                                           std::chrono::milliseconds(10),
151                                           num_bytes)
152                         .ToError(),
153                     llvm::Succeeded());
154 }
155 #endif /*ifndef _WIN32*/
156