Skip to content

Commit c5629f2

Browse files
authored
[lldb] Add Socket::CreatePair (#145015)
It creates a pair of connected sockets using the simplest mechanism for the given platform (TCP on windows, socketpair(2) elsewhere). Main motivation is to remove the ugly platform-specific code in ProcessGDBRemote::LaunchAndConnectToDebugserver, but it can also be used in other places where we need to create a pair of connected sockets.
1 parent bc6faf9 commit c5629f2

File tree

9 files changed

+138
-37
lines changed

9 files changed

+138
-37
lines changed

lldb/include/lldb/Host/Socket.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ class Socket : public IOObject {
106106
static std::unique_ptr<Socket> Create(const SocketProtocol protocol,
107107
Status &error);
108108

109+
using Pair = std::pair<std::unique_ptr<Socket>, std::unique_ptr<Socket>>;
110+
static llvm::Expected<Pair>
111+
CreatePair(std::optional<SocketProtocol> protocol = std::nullopt);
112+
109113
virtual Status Connect(llvm::StringRef name) = 0;
110114
virtual Status Listen(llvm::StringRef name, int backlog) = 0;
111115

lldb/include/lldb/Host/common/TCPSocket.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ class TCPSocket : public Socket {
2323
TCPSocket(NativeSocket socket, bool should_close);
2424
~TCPSocket() override;
2525

26+
using Pair =
27+
std::pair<std::unique_ptr<TCPSocket>, std::unique_ptr<TCPSocket>>;
28+
static llvm::Expected<Pair> CreatePair();
29+
2630
// returns port number or 0 if error
2731
uint16_t GetLocalPortNumber() const;
2832

lldb/include/lldb/Host/posix/DomainSocket.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ class DomainSocket : public Socket {
1919
DomainSocket(NativeSocket socket, bool should_close);
2020
explicit DomainSocket(bool should_close);
2121

22+
using Pair =
23+
std::pair<std::unique_ptr<DomainSocket>, std::unique_ptr<DomainSocket>>;
24+
static llvm::Expected<Pair> CreatePair();
25+
2226
Status Connect(llvm::StringRef name) override;
2327
Status Listen(llvm::StringRef name, int backlog) override;
2428

lldb/source/Host/common/Socket.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,23 @@ std::unique_ptr<Socket> Socket::Create(const SocketProtocol protocol,
234234
return socket_up;
235235
}
236236

237+
llvm::Expected<Socket::Pair>
238+
Socket::CreatePair(std::optional<SocketProtocol> protocol) {
239+
constexpr SocketProtocol kBestProtocol =
240+
LLDB_ENABLE_POSIX ? ProtocolUnixDomain : ProtocolTcp;
241+
switch (protocol.value_or(kBestProtocol)) {
242+
case ProtocolTcp:
243+
return TCPSocket::CreatePair();
244+
#if LLDB_ENABLE_POSIX
245+
case ProtocolUnixDomain:
246+
case ProtocolUnixAbstract:
247+
return DomainSocket::CreatePair();
248+
#endif
249+
default:
250+
return llvm::createStringError("Unsupported protocol");
251+
}
252+
}
253+
237254
llvm::Expected<std::unique_ptr<Socket>>
238255
Socket::TcpConnect(llvm::StringRef host_and_port) {
239256
Log *log = GetLog(LLDBLog::Connection);

lldb/source/Host/common/TCPSocket.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,32 @@ TCPSocket::TCPSocket(NativeSocket socket, bool should_close)
5252

5353
TCPSocket::~TCPSocket() { CloseListenSockets(); }
5454

55+
llvm::Expected<TCPSocket::Pair> TCPSocket::CreatePair() {
56+
auto listen_socket_up = std::make_unique<TCPSocket>(true);
57+
if (Status error = listen_socket_up->Listen("localhost:0", 5); error.Fail())
58+
return error.takeError();
59+
60+
std::string connect_address =
61+
llvm::StringRef(listen_socket_up->GetListeningConnectionURI()[0])
62+
.split("://")
63+
.second.str();
64+
65+
auto connect_socket_up = std::make_unique<TCPSocket>(true);
66+
if (Status error = connect_socket_up->Connect(connect_address); error.Fail())
67+
return error.takeError();
68+
69+
// Connection has already been made above, so a short timeout is sufficient.
70+
Socket *accept_socket;
71+
if (Status error =
72+
listen_socket_up->Accept(std::chrono::seconds(1), accept_socket);
73+
error.Fail())
74+
return error.takeError();
75+
76+
return Pair(
77+
std::move(connect_socket_up),
78+
std::unique_ptr<TCPSocket>(static_cast<TCPSocket *>(accept_socket)));
79+
}
80+
5581
bool TCPSocket::IsValid() const {
5682
return m_socket != kInvalidSocketValue || m_listen_sockets.size() != 0;
5783
}

lldb/source/Host/posix/DomainSocket.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
#endif
1414

1515
#include "llvm/Support/Errno.h"
16+
#include "llvm/Support/Error.h"
1617
#include "llvm/Support/FileSystem.h"
1718

1819
#include <cstddef>
20+
#include <fcntl.h>
1921
#include <memory>
2022
#include <sys/socket.h>
2123
#include <sys/un.h>
@@ -76,6 +78,31 @@ DomainSocket::DomainSocket(SocketProtocol protocol, NativeSocket socket,
7678
m_socket = socket;
7779
}
7880

81+
llvm::Expected<DomainSocket::Pair> DomainSocket::CreatePair() {
82+
int sockets[2];
83+
int type = SOCK_STREAM;
84+
#ifdef SOCK_CLOEXEC
85+
type |= SOCK_CLOEXEC;
86+
#endif
87+
if (socketpair(AF_UNIX, type, 0, sockets) == -1)
88+
return llvm::errorCodeToError(llvm::errnoAsErrorCode());
89+
90+
#ifndef SOCK_CLOEXEC
91+
for (int s : sockets) {
92+
int r = fcntl(s, F_SETFD, FD_CLOEXEC | fcntl(s, F_GETFD));
93+
assert(r == 0);
94+
(void)r;
95+
}
96+
#endif
97+
98+
return Pair(std::unique_ptr<DomainSocket>(
99+
new DomainSocket(ProtocolUnixDomain, sockets[0],
100+
/*should_close=*/true)),
101+
std::unique_ptr<DomainSocket>(
102+
new DomainSocket(ProtocolUnixDomain, sockets[1],
103+
/*should_close=*/true)));
104+
}
105+
79106
Status DomainSocket::Connect(llvm::StringRef name) {
80107
sockaddr_un saddr_un;
81108
socklen_t saddr_un_len;

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1141,34 +1141,14 @@ void GDBRemoteCommunication::DumpHistory(Stream &strm) { m_history.Dump(strm); }
11411141
llvm::Error
11421142
GDBRemoteCommunication::ConnectLocally(GDBRemoteCommunication &client,
11431143
GDBRemoteCommunication &server) {
1144-
const int backlog = 5;
1145-
TCPSocket listen_socket(true);
1146-
if (llvm::Error error =
1147-
listen_socket.Listen("localhost:0", backlog).ToError())
1148-
return error;
1144+
llvm::Expected<Socket::Pair> pair = Socket::CreatePair();
1145+
if (!pair)
1146+
return pair.takeError();
11491147

1150-
llvm::SmallString<32> remote_addr;
1151-
llvm::raw_svector_ostream(remote_addr)
1152-
<< "connect://localhost:" << listen_socket.GetLocalPortNumber();
1153-
1154-
std::unique_ptr<ConnectionFileDescriptor> conn_up(
1155-
new ConnectionFileDescriptor());
1156-
Status status;
1157-
if (conn_up->Connect(remote_addr, &status) != lldb::eConnectionStatusSuccess)
1158-
return llvm::createStringError(llvm::inconvertibleErrorCode(),
1159-
"Unable to connect: %s", status.AsCString());
1160-
1161-
// The connection was already established above, so a short timeout is
1162-
// sufficient.
1163-
Socket *accept_socket = nullptr;
1164-
if (Status accept_status =
1165-
listen_socket.Accept(std::chrono::seconds(1), accept_socket);
1166-
accept_status.Fail())
1167-
return accept_status.takeError();
1168-
1169-
client.SetConnection(std::move(conn_up));
1148+
client.SetConnection(
1149+
std::make_unique<ConnectionFileDescriptor>(pair->first.release()));
11701150
server.SetConnection(
1171-
std::make_unique<ConnectionFileDescriptor>(accept_socket));
1151+
std::make_unique<ConnectionFileDescriptor>(pair->second.release()));
11721152
return llvm::Error::success();
11731153
}
11741154

lldb/unittests/Core/CommunicationTest.cpp

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "lldb/Core/Communication.h"
10+
#include "TestingSupport/SubsystemRAII.h"
1011
#include "lldb/Core/ThreadedCommunication.h"
1112
#include "lldb/Host/Config.h"
1213
#include "lldb/Host/ConnectionFileDescriptor.h"
1314
#include "lldb/Host/Pipe.h"
15+
#include "lldb/Host/Socket.h"
1416
#include "llvm/Testing/Support/Error.h"
1517
#include "gtest/gtest.h"
16-
#include "TestingSupport/Host/SocketTestUtilities.h"
17-
#include "TestingSupport/SubsystemRAII.h"
1818

1919
#include <chrono>
2020
#include <thread>
@@ -31,15 +31,17 @@ class CommunicationTest : public testing::Test {
3131
};
3232

3333
static void CommunicationReadTest(bool use_read_thread) {
34-
std::unique_ptr<TCPSocket> a, b;
35-
ASSERT_TRUE(CreateTCPConnectedSockets("localhost", &a, &b));
34+
llvm::Expected<Socket::Pair> pair = Socket::CreatePair();
35+
ASSERT_THAT_EXPECTED(pair, llvm::Succeeded());
36+
Socket &a = *pair->first;
3637

3738
size_t num_bytes = 4;
38-
ASSERT_THAT_ERROR(a->Write("test", num_bytes).ToError(), llvm::Succeeded());
39+
ASSERT_THAT_ERROR(a.Write("test", num_bytes).ToError(), llvm::Succeeded());
3940
ASSERT_EQ(num_bytes, 4U);
4041

4142
ThreadedCommunication comm("test");
42-
comm.SetConnection(std::make_unique<ConnectionFileDescriptor>(b.release()));
43+
comm.SetConnection(
44+
std::make_unique<ConnectionFileDescriptor>(pair->second.release()));
4345
comm.SetCloseOnEOF(true);
4446

4547
if (use_read_thread) {
@@ -73,7 +75,7 @@ static void CommunicationReadTest(bool use_read_thread) {
7375
EXPECT_THAT_ERROR(error.ToError(), llvm::Failed());
7476

7577
// This read should return EOF.
76-
ASSERT_THAT_ERROR(a->Close().ToError(), llvm::Succeeded());
78+
ASSERT_THAT_ERROR(a.Close().ToError(), llvm::Succeeded());
7779
error.Clear();
7880
EXPECT_EQ(
7981
comm.Read(buf, sizeof(buf), std::chrono::seconds(5), status, &error), 0U);
@@ -118,17 +120,19 @@ TEST_F(CommunicationTest, ReadThread) {
118120
}
119121

120122
TEST_F(CommunicationTest, SynchronizeWhileClosing) {
121-
std::unique_ptr<TCPSocket> a, b;
122-
ASSERT_TRUE(CreateTCPConnectedSockets("localhost", &a, &b));
123+
llvm::Expected<Socket::Pair> pair = Socket::CreatePair();
124+
ASSERT_THAT_EXPECTED(pair, llvm::Succeeded());
125+
Socket &a = *pair->first;
123126

124127
ThreadedCommunication comm("test");
125-
comm.SetConnection(std::make_unique<ConnectionFileDescriptor>(b.release()));
128+
comm.SetConnection(
129+
std::make_unique<ConnectionFileDescriptor>(pair->second.release()));
126130
comm.SetCloseOnEOF(true);
127131
ASSERT_TRUE(comm.StartReadThread());
128132

129133
// Ensure that we can safely synchronize with the read thread while it is
130134
// closing the read end (in response to us closing the write end).
131-
ASSERT_THAT_ERROR(a->Close().ToError(), llvm::Succeeded());
135+
ASSERT_THAT_ERROR(a.Close().ToError(), llvm::Succeeded());
132136
comm.SynchronizeWithReadThread();
133137

134138
ASSERT_TRUE(comm.StopReadThread());

lldb/unittests/Host/SocketTest.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,41 @@ TEST_F(SocketTest, DecodeHostAndPort) {
7474
llvm::HasValue(Socket::HostAndPort{"abcd:12fg:AF58::1", 12345}));
7575
}
7676

77+
TEST_F(SocketTest, CreatePair) {
78+
std::vector<std::optional<Socket::SocketProtocol>> functional_protocols = {
79+
std::nullopt,
80+
Socket::ProtocolTcp,
81+
#if LLDB_ENABLE_POSIX
82+
Socket::ProtocolUnixDomain,
83+
Socket::ProtocolUnixAbstract,
84+
#endif
85+
};
86+
for (auto p : functional_protocols) {
87+
auto expected_socket_pair = Socket::CreatePair(p);
88+
ASSERT_THAT_EXPECTED(expected_socket_pair, llvm::Succeeded());
89+
Socket &a = *expected_socket_pair->first;
90+
Socket &b = *expected_socket_pair->second;
91+
size_t num_bytes = 1;
92+
ASSERT_THAT_ERROR(a.Write("a", num_bytes).takeError(), llvm::Succeeded());
93+
ASSERT_EQ(num_bytes, 1);
94+
char c;
95+
ASSERT_THAT_ERROR(b.Read(&c, num_bytes).takeError(), llvm::Succeeded());
96+
ASSERT_EQ(num_bytes, 1);
97+
ASSERT_EQ(c, 'a');
98+
}
99+
100+
std::vector<Socket::SocketProtocol> erroring_protocols = {
101+
#if !LLDB_ENABLE_POSIX
102+
Socket::ProtocolUnixDomain,
103+
Socket::ProtocolUnixAbstract,
104+
#endif
105+
};
106+
for (auto p : erroring_protocols) {
107+
ASSERT_THAT_EXPECTED(Socket::CreatePair(p),
108+
llvm::FailedWithMessage("Unsupported protocol"));
109+
}
110+
}
111+
77112
#if LLDB_ENABLE_POSIX
78113
TEST_F(SocketTest, DomainListenConnectAccept) {
79114
llvm::SmallString<64> Path;

0 commit comments

Comments
 (0)