Skip to content

Commit 54ef8f5

Browse files
committed
[lldb] Add Socket::CreatePair
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 7cbb141 commit 54ef8f5

File tree

9 files changed

+144
-39
lines changed

9 files changed

+144
-39
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+
static llvm::Expected<
110+
std::pair<std::unique_ptr<Socket>, std::unique_ptr<Socket>>>
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+
static llvm::Expected<
27+
std::pair<std::unique_ptr<TCPSocket>, std::unique_ptr<TCPSocket>>>
28+
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+
static llvm::Expected<
23+
std::pair<std::unique_ptr<DomainSocket>, std::unique_ptr<DomainSocket>>>
24+
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<std::pair<std::unique_ptr<Socket>, std::unique_ptr<Socket>>>
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: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,34 @@ TCPSocket::TCPSocket(NativeSocket socket, bool should_close)
5252

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

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

lldb/source/Host/posix/DomainSocket.cpp

Lines changed: 29 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,33 @@ DomainSocket::DomainSocket(SocketProtocol protocol, NativeSocket socket,
7678
m_socket = socket;
7779
}
7880

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

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

Lines changed: 8 additions & 28 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;
1149-
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));
1170-
server.SetConnection(
1171-
std::make_unique<ConnectionFileDescriptor>(accept_socket));
1144+
auto expected_socket_pair = Socket::CreatePair();
1145+
if (!expected_socket_pair)
1146+
return expected_socket_pair.takeError();
1147+
1148+
client.SetConnection(std::make_unique<ConnectionFileDescriptor>(
1149+
expected_socket_pair->first.release()));
1150+
server.SetConnection(std::make_unique<ConnectionFileDescriptor>(
1151+
expected_socket_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+
auto maybe_socket_pair = Socket::CreatePair();
35+
ASSERT_THAT_EXPECTED(maybe_socket_pair, llvm::Succeeded());
36+
Socket &a = *maybe_socket_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(std::make_unique<ConnectionFileDescriptor>(
44+
maybe_socket_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+
auto maybe_socket_pair = Socket::CreatePair();
124+
ASSERT_THAT_EXPECTED(maybe_socket_pair, llvm::Succeeded());
125+
Socket &a = *maybe_socket_pair->first;
123126

124127
ThreadedCommunication comm("test");
125-
comm.SetConnection(std::make_unique<ConnectionFileDescriptor>(b.release()));
128+
comm.SetConnection(std::make_unique<ConnectionFileDescriptor>(
129+
maybe_socket_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)