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