xref: /llvm-project/lldb/unittests/Core/CommunicationTest.cpp (revision f009241ae595ae6db5a859e498f72fd7e5dc6ce8)
1 //===-- CommunicationTest.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/Core/Communication.h"
10 #include "lldb/Core/ThreadedCommunication.h"
11 #include "lldb/Host/Config.h"
12 #include "lldb/Host/ConnectionFileDescriptor.h"
13 #include "lldb/Host/Pipe.h"
14 #include "llvm/Testing/Support/Error.h"
15 #include "gtest/gtest.h"
16 #include "TestingSupport/Host/SocketTestUtilities.h"
17 #include "TestingSupport/SubsystemRAII.h"
18 
19 #include <chrono>
20 #include <thread>
21 
22 #if LLDB_ENABLE_POSIX
23 #include <fcntl.h>
24 #endif
25 
26 using namespace lldb_private;
27 
28 class CommunicationTest : public testing::Test {
29 private:
30   SubsystemRAII<Socket> m_subsystems;
31 };
32 
CommunicationReadTest(bool use_read_thread)33 static void CommunicationReadTest(bool use_read_thread) {
34   std::unique_ptr<TCPSocket> a, b;
35   ASSERT_TRUE(CreateTCPConnectedSockets("localhost", &a, &b));
36 
37   size_t num_bytes = 4;
38   ASSERT_THAT_ERROR(a->Write("test", num_bytes).ToError(), llvm::Succeeded());
39   ASSERT_EQ(num_bytes, 4U);
40 
41   ThreadedCommunication comm("test");
42   comm.SetConnection(std::make_unique<ConnectionFileDescriptor>(b.release()));
43   comm.SetCloseOnEOF(true);
44 
45   if (use_read_thread) {
46     ASSERT_TRUE(comm.StartReadThread());
47   }
48 
49   // This read should wait for the data to become available and return it.
50   lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
51   char buf[16];
52   Status error;
53   EXPECT_EQ(
54       comm.Read(buf, sizeof(buf), std::chrono::seconds(5), status, &error), 4U);
55   EXPECT_EQ(status, lldb::eConnectionStatusSuccess);
56   EXPECT_THAT_ERROR(error.ToError(), llvm::Succeeded());
57   buf[4] = 0;
58   EXPECT_STREQ(buf, "test");
59 
60   // These reads should time out as there is no more data.
61   error.Clear();
62   EXPECT_EQ(comm.Read(buf, sizeof(buf), std::chrono::microseconds(10), status,
63                       &error),
64             0U);
65   EXPECT_EQ(status, lldb::eConnectionStatusTimedOut);
66   EXPECT_THAT_ERROR(error.ToError(), llvm::Failed());
67 
68   // 0 is special-cased, so we test it separately.
69   error.Clear();
70   EXPECT_EQ(
71       comm.Read(buf, sizeof(buf), std::chrono::seconds(0), status, &error), 0U);
72   EXPECT_EQ(status, lldb::eConnectionStatusTimedOut);
73   EXPECT_THAT_ERROR(error.ToError(), llvm::Failed());
74 
75   // This read should return EOF.
76   ASSERT_THAT_ERROR(a->Close().ToError(), llvm::Succeeded());
77   error.Clear();
78   EXPECT_EQ(
79       comm.Read(buf, sizeof(buf), std::chrono::seconds(5), status, &error), 0U);
80   EXPECT_EQ(status, lldb::eConnectionStatusEndOfFile);
81   EXPECT_THAT_ERROR(error.ToError(), llvm::Succeeded());
82 
83   // JoinReadThread() should just return immediately if there was no read
84   // thread started.
85   EXPECT_TRUE(comm.JoinReadThread());
86 
87   // Test using Communication that is disconnected.
88   ASSERT_EQ(comm.Disconnect(), lldb::eConnectionStatusSuccess);
89   if (use_read_thread) {
90     ASSERT_TRUE(comm.StartReadThread());
91   }
92   error.Clear();
93   EXPECT_EQ(
94       comm.Read(buf, sizeof(buf), std::chrono::seconds(5), status, &error), 0U);
95   EXPECT_EQ(status, lldb::eConnectionStatusLostConnection);
96   EXPECT_THAT_ERROR(error.ToError(), llvm::Failed());
97   EXPECT_TRUE(comm.JoinReadThread());
98 
99   // Test using Communication without a connection.
100   comm.SetConnection(nullptr);
101   if (use_read_thread) {
102     ASSERT_TRUE(comm.StartReadThread());
103   }
104   error.Clear();
105   EXPECT_EQ(
106       comm.Read(buf, sizeof(buf), std::chrono::seconds(5), status, &error), 0U);
107   EXPECT_EQ(status, lldb::eConnectionStatusNoConnection);
108   EXPECT_THAT_ERROR(error.ToError(), llvm::Failed());
109   EXPECT_TRUE(comm.JoinReadThread());
110 }
111 
TEST_F(CommunicationTest,Read)112 TEST_F(CommunicationTest, Read) {
113   CommunicationReadTest(/*use_thread=*/false);
114 }
115 
TEST_F(CommunicationTest,ReadThread)116 TEST_F(CommunicationTest, ReadThread) {
117   CommunicationReadTest(/*use_thread=*/true);
118 }
119 
TEST_F(CommunicationTest,SynchronizeWhileClosing)120 TEST_F(CommunicationTest, SynchronizeWhileClosing) {
121   std::unique_ptr<TCPSocket> a, b;
122   ASSERT_TRUE(CreateTCPConnectedSockets("localhost", &a, &b));
123 
124   ThreadedCommunication comm("test");
125   comm.SetConnection(std::make_unique<ConnectionFileDescriptor>(b.release()));
126   comm.SetCloseOnEOF(true);
127   ASSERT_TRUE(comm.StartReadThread());
128 
129   // Ensure that we can safely synchronize with the read thread while it is
130   // closing the read end (in response to us closing the write end).
131   ASSERT_THAT_ERROR(a->Close().ToError(), llvm::Succeeded());
132   comm.SynchronizeWithReadThread();
133 
134   ASSERT_TRUE(comm.StopReadThread());
135 }
136 
137 #if LLDB_ENABLE_POSIX
TEST_F(CommunicationTest,WriteAll)138 TEST_F(CommunicationTest, WriteAll) {
139   Pipe pipe;
140   ASSERT_THAT_ERROR(pipe.CreateNew(/*child_process_inherit=*/false).ToError(),
141                     llvm::Succeeded());
142 
143   // Make the write end non-blocking in order to easily reproduce a partial
144   // write.
145   int write_fd = pipe.ReleaseWriteFileDescriptor();
146   int flags = fcntl(write_fd, F_GETFL);
147   ASSERT_NE(flags, -1);
148   ASSERT_NE(fcntl(write_fd, F_SETFL, flags | O_NONBLOCK), -1);
149 
150   ConnectionFileDescriptor read_conn{pipe.ReleaseReadFileDescriptor(),
151                                      /*owns_fd=*/true};
152   Communication write_comm;
153   write_comm.SetConnection(
154       std::make_unique<ConnectionFileDescriptor>(write_fd, /*owns_fd=*/true));
155 
156   std::thread read_thread{[&read_conn]() {
157     // Read using a smaller buffer to increase chances of partial write.
158     char buf[128 * 1024];
159     lldb::ConnectionStatus conn_status;
160 
161     do {
162       read_conn.Read(buf, sizeof(buf), std::chrono::seconds(1), conn_status,
163                      nullptr);
164     } while (conn_status != lldb::eConnectionStatusEndOfFile);
165   }};
166 
167   // Write 1 MiB of data into the pipe.
168   lldb::ConnectionStatus conn_status;
169   Status error;
170   std::vector<uint8_t> data(1024 * 1024, 0x80);
171   EXPECT_EQ(write_comm.WriteAll(data.data(), data.size(), conn_status, &error),
172             data.size());
173   EXPECT_EQ(conn_status, lldb::eConnectionStatusSuccess);
174   EXPECT_FALSE(error.Fail());
175 
176   // Close the write end in order to trigger EOF.
177   write_comm.Disconnect();
178   read_thread.join();
179 }
180 #endif
181