xref: /llvm-project/lldb/unittests/Core/CommunicationTest.cpp (revision f009241ae595ae6db5a859e498f72fd7e5dc6ce8)
1769d7041SPavel Labath //===-- CommunicationTest.cpp ---------------------------------------------===//
2769d7041SPavel Labath //
3769d7041SPavel Labath // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4769d7041SPavel Labath // See https://llvm.org/LICENSE.txt for license information.
5769d7041SPavel Labath // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6769d7041SPavel Labath //
7769d7041SPavel Labath //===----------------------------------------------------------------------===//
8769d7041SPavel Labath 
9769d7041SPavel Labath #include "lldb/Core/Communication.h"
109823d425SMichał Górny #include "lldb/Core/ThreadedCommunication.h"
11f279e50fSMichał Górny #include "lldb/Host/Config.h"
12769d7041SPavel Labath #include "lldb/Host/ConnectionFileDescriptor.h"
13769d7041SPavel Labath #include "lldb/Host/Pipe.h"
14769d7041SPavel Labath #include "llvm/Testing/Support/Error.h"
15769d7041SPavel Labath #include "gtest/gtest.h"
1659656c04SPavel Labath #include "TestingSupport/Host/SocketTestUtilities.h"
1759656c04SPavel Labath #include "TestingSupport/SubsystemRAII.h"
18769d7041SPavel Labath 
1939e0a87cSMichał Górny #include <chrono>
20f279e50fSMichał Górny #include <thread>
21f279e50fSMichał Górny 
22f279e50fSMichał Górny #if LLDB_ENABLE_POSIX
23f279e50fSMichał Górny #include <fcntl.h>
24f279e50fSMichał Górny #endif
25f279e50fSMichał Górny 
26769d7041SPavel Labath using namespace lldb_private;
27769d7041SPavel Labath 
2859656c04SPavel Labath class CommunicationTest : public testing::Test {
2959656c04SPavel Labath private:
3059656c04SPavel Labath   SubsystemRAII<Socket> m_subsystems;
3159656c04SPavel Labath };
32d6e1e01dSMichał Górny 
CommunicationReadTest(bool use_read_thread)3359656c04SPavel Labath static void CommunicationReadTest(bool use_read_thread) {
3459656c04SPavel Labath   std::unique_ptr<TCPSocket> a, b;
3559656c04SPavel Labath   ASSERT_TRUE(CreateTCPConnectedSockets("localhost", &a, &b));
3659656c04SPavel Labath 
3759656c04SPavel Labath   size_t num_bytes = 4;
3859656c04SPavel Labath   ASSERT_THAT_ERROR(a->Write("test", num_bytes).ToError(), llvm::Succeeded());
3959656c04SPavel Labath   ASSERT_EQ(num_bytes, 4U);
40d6e1e01dSMichał Górny 
419823d425SMichał Górny   ThreadedCommunication comm("test");
4259656c04SPavel Labath   comm.SetConnection(std::make_unique<ConnectionFileDescriptor>(b.release()));
43d6e1e01dSMichał Górny   comm.SetCloseOnEOF(true);
44d6e1e01dSMichał Górny 
4559656c04SPavel Labath   if (use_read_thread) {
46d6e1e01dSMichał Górny     ASSERT_TRUE(comm.StartReadThread());
4759656c04SPavel Labath   }
48d6e1e01dSMichał Górny 
49d6e1e01dSMichał Górny   // This read should wait for the data to become available and return it.
50d6e1e01dSMichał Górny   lldb::ConnectionStatus status = lldb::eConnectionStatusSuccess;
51d6e1e01dSMichał Górny   char buf[16];
5259656c04SPavel Labath   Status error;
53d6e1e01dSMichał Górny   EXPECT_EQ(
54d6e1e01dSMichał Górny       comm.Read(buf, sizeof(buf), std::chrono::seconds(5), status, &error), 4U);
55d6e1e01dSMichał Górny   EXPECT_EQ(status, lldb::eConnectionStatusSuccess);
56d6e1e01dSMichał Górny   EXPECT_THAT_ERROR(error.ToError(), llvm::Succeeded());
57d6e1e01dSMichał Górny   buf[4] = 0;
58d6e1e01dSMichał Górny   EXPECT_STREQ(buf, "test");
59d6e1e01dSMichał Górny 
60d6e1e01dSMichał Górny   // These reads should time out as there is no more data.
61d6e1e01dSMichał Górny   error.Clear();
62d6e1e01dSMichał Górny   EXPECT_EQ(comm.Read(buf, sizeof(buf), std::chrono::microseconds(10), status,
63d6e1e01dSMichał Górny                       &error),
64d6e1e01dSMichał Górny             0U);
65d6e1e01dSMichał Górny   EXPECT_EQ(status, lldb::eConnectionStatusTimedOut);
66d6e1e01dSMichał Górny   EXPECT_THAT_ERROR(error.ToError(), llvm::Failed());
67d6e1e01dSMichał Górny 
68d6e1e01dSMichał Górny   // 0 is special-cased, so we test it separately.
69d6e1e01dSMichał Górny   error.Clear();
70d6e1e01dSMichał Górny   EXPECT_EQ(
71d6e1e01dSMichał Górny       comm.Read(buf, sizeof(buf), std::chrono::seconds(0), status, &error), 0U);
72d6e1e01dSMichał Górny   EXPECT_EQ(status, lldb::eConnectionStatusTimedOut);
73d6e1e01dSMichał Górny   EXPECT_THAT_ERROR(error.ToError(), llvm::Failed());
74d6e1e01dSMichał Górny 
75d6e1e01dSMichał Górny   // This read should return EOF.
7659656c04SPavel Labath   ASSERT_THAT_ERROR(a->Close().ToError(), llvm::Succeeded());
77d6e1e01dSMichał Górny   error.Clear();
78d6e1e01dSMichał Górny   EXPECT_EQ(
79d6e1e01dSMichał Górny       comm.Read(buf, sizeof(buf), std::chrono::seconds(5), status, &error), 0U);
80d6e1e01dSMichał Górny   EXPECT_EQ(status, lldb::eConnectionStatusEndOfFile);
81d6e1e01dSMichał Górny   EXPECT_THAT_ERROR(error.ToError(), llvm::Succeeded());
82d6e1e01dSMichał Górny 
8339e0a87cSMichał Górny   // JoinReadThread() should just return immediately if there was no read
84d6e1e01dSMichał Górny   // thread started.
85d6e1e01dSMichał Górny   EXPECT_TRUE(comm.JoinReadThread());
8639e0a87cSMichał Górny 
8739e0a87cSMichał Górny   // Test using Communication that is disconnected.
8839e0a87cSMichał Górny   ASSERT_EQ(comm.Disconnect(), lldb::eConnectionStatusSuccess);
89*f009241aSJonas Devlieghere   if (use_read_thread) {
9039e0a87cSMichał Górny     ASSERT_TRUE(comm.StartReadThread());
91*f009241aSJonas Devlieghere   }
9239e0a87cSMichał Górny   error.Clear();
9339e0a87cSMichał Górny   EXPECT_EQ(
9439e0a87cSMichał Górny       comm.Read(buf, sizeof(buf), std::chrono::seconds(5), status, &error), 0U);
9539e0a87cSMichał Górny   EXPECT_EQ(status, lldb::eConnectionStatusLostConnection);
9639e0a87cSMichał Górny   EXPECT_THAT_ERROR(error.ToError(), llvm::Failed());
9739e0a87cSMichał Górny   EXPECT_TRUE(comm.JoinReadThread());
9839e0a87cSMichał Górny 
9939e0a87cSMichał Górny   // Test using Communication without a connection.
10039e0a87cSMichał Górny   comm.SetConnection(nullptr);
101*f009241aSJonas Devlieghere   if (use_read_thread) {
10239e0a87cSMichał Górny     ASSERT_TRUE(comm.StartReadThread());
103*f009241aSJonas Devlieghere   }
10439e0a87cSMichał Górny   error.Clear();
10539e0a87cSMichał Górny   EXPECT_EQ(
10639e0a87cSMichał Górny       comm.Read(buf, sizeof(buf), std::chrono::seconds(5), status, &error), 0U);
10739e0a87cSMichał Górny   EXPECT_EQ(status, lldb::eConnectionStatusNoConnection);
10839e0a87cSMichał Górny   EXPECT_THAT_ERROR(error.ToError(), llvm::Failed());
10939e0a87cSMichał Górny   EXPECT_TRUE(comm.JoinReadThread());
110d6e1e01dSMichał Górny }
111d6e1e01dSMichał Górny 
TEST_F(CommunicationTest,Read)11259656c04SPavel Labath TEST_F(CommunicationTest, Read) {
113d6e1e01dSMichał Górny   CommunicationReadTest(/*use_thread=*/false);
114d6e1e01dSMichał Górny }
115d6e1e01dSMichał Górny 
TEST_F(CommunicationTest,ReadThread)11659656c04SPavel Labath TEST_F(CommunicationTest, ReadThread) {
117d6e1e01dSMichał Górny   CommunicationReadTest(/*use_thread=*/true);
118d6e1e01dSMichał Górny }
119d6e1e01dSMichał Górny 
TEST_F(CommunicationTest,SynchronizeWhileClosing)12059656c04SPavel Labath TEST_F(CommunicationTest, SynchronizeWhileClosing) {
12159656c04SPavel Labath   std::unique_ptr<TCPSocket> a, b;
12259656c04SPavel Labath   ASSERT_TRUE(CreateTCPConnectedSockets("localhost", &a, &b));
123769d7041SPavel Labath 
1249823d425SMichał Górny   ThreadedCommunication comm("test");
12559656c04SPavel Labath   comm.SetConnection(std::make_unique<ConnectionFileDescriptor>(b.release()));
126769d7041SPavel Labath   comm.SetCloseOnEOF(true);
127769d7041SPavel Labath   ASSERT_TRUE(comm.StartReadThread());
128769d7041SPavel Labath 
129769d7041SPavel Labath   // Ensure that we can safely synchronize with the read thread while it is
130769d7041SPavel Labath   // closing the read end (in response to us closing the write end).
13159656c04SPavel Labath   ASSERT_THAT_ERROR(a->Close().ToError(), llvm::Succeeded());
132769d7041SPavel Labath   comm.SynchronizeWithReadThread();
133769d7041SPavel Labath 
134769d7041SPavel Labath   ASSERT_TRUE(comm.StopReadThread());
135769d7041SPavel Labath }
136f279e50fSMichał Górny 
137f279e50fSMichał Górny #if LLDB_ENABLE_POSIX
TEST_F(CommunicationTest,WriteAll)13859656c04SPavel Labath TEST_F(CommunicationTest, WriteAll) {
139f279e50fSMichał Górny   Pipe pipe;
140f279e50fSMichał Górny   ASSERT_THAT_ERROR(pipe.CreateNew(/*child_process_inherit=*/false).ToError(),
141f279e50fSMichał Górny                     llvm::Succeeded());
142f279e50fSMichał Górny 
143f279e50fSMichał Górny   // Make the write end non-blocking in order to easily reproduce a partial
144f279e50fSMichał Górny   // write.
145f279e50fSMichał Górny   int write_fd = pipe.ReleaseWriteFileDescriptor();
146f279e50fSMichał Górny   int flags = fcntl(write_fd, F_GETFL);
147f279e50fSMichał Górny   ASSERT_NE(flags, -1);
148f279e50fSMichał Górny   ASSERT_NE(fcntl(write_fd, F_SETFL, flags | O_NONBLOCK), -1);
149f279e50fSMichał Górny 
150f279e50fSMichał Górny   ConnectionFileDescriptor read_conn{pipe.ReleaseReadFileDescriptor(),
151f279e50fSMichał Górny                                      /*owns_fd=*/true};
1529823d425SMichał Górny   Communication write_comm;
153f279e50fSMichał Górny   write_comm.SetConnection(
154f279e50fSMichał Górny       std::make_unique<ConnectionFileDescriptor>(write_fd, /*owns_fd=*/true));
155f279e50fSMichał Górny 
156f279e50fSMichał Górny   std::thread read_thread{[&read_conn]() {
157f279e50fSMichał Górny     // Read using a smaller buffer to increase chances of partial write.
158f279e50fSMichał Górny     char buf[128 * 1024];
159f279e50fSMichał Górny     lldb::ConnectionStatus conn_status;
160f279e50fSMichał Górny 
161f279e50fSMichał Górny     do {
162f279e50fSMichał Górny       read_conn.Read(buf, sizeof(buf), std::chrono::seconds(1), conn_status,
163f279e50fSMichał Górny                      nullptr);
164f279e50fSMichał Górny     } while (conn_status != lldb::eConnectionStatusEndOfFile);
165f279e50fSMichał Górny   }};
166f279e50fSMichał Górny 
167f279e50fSMichał Górny   // Write 1 MiB of data into the pipe.
168f279e50fSMichał Górny   lldb::ConnectionStatus conn_status;
169f279e50fSMichał Górny   Status error;
17004f13da6SPavel Labath   std::vector<uint8_t> data(1024 * 1024, 0x80);
171f279e50fSMichał Górny   EXPECT_EQ(write_comm.WriteAll(data.data(), data.size(), conn_status, &error),
172f279e50fSMichał Górny             data.size());
173f279e50fSMichał Górny   EXPECT_EQ(conn_status, lldb::eConnectionStatusSuccess);
174f279e50fSMichał Górny   EXPECT_FALSE(error.Fail());
175f279e50fSMichał Górny 
176f279e50fSMichał Górny   // Close the write end in order to trigger EOF.
177f279e50fSMichał Górny   write_comm.Disconnect();
178f279e50fSMichał Górny   read_thread.join();
179f279e50fSMichał Górny }
180f279e50fSMichał Górny #endif
181