Skip to content

Commit f21cc55

Browse files
[llvm-jitlink] Add diagnostic output and port executor to getaddrinfo(3) as well
Add diagnostic output for TCP connections on both sides, llvm-jitlink and llvm-jitlink-executor. Port the executor to use getaddrinfo(3) as well. This makes the code more symmetric and seems to be the recommended way for implementing the server side. Reviewed By: rzurob Differential Revision: https://reviews.llvm.org/D98581
1 parent 4a8161f commit f21cc55

File tree

2 files changed

+107
-71
lines changed

2 files changed

+107
-71
lines changed

llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@
1717
#include "llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h"
1818
#include "llvm/Support/DynamicLibrary.h"
1919
#include "llvm/Support/Error.h"
20+
#include "llvm/Support/MathExtras.h"
2021
#include "llvm/Support/raw_ostream.h"
22+
#include <cstring>
2123
#include <sstream>
2224

2325
#ifdef LLVM_ON_UNIX
2426

27+
#include <netdb.h>
2528
#include <netinet/in.h>
2629
#include <sys/socket.h>
2730

@@ -46,37 +49,58 @@ void printErrorAndExit(Twine ErrMsg) {
4649
exit(1);
4750
}
4851

49-
int openListener(std::string Host, int Port) {
52+
int openListener(std::string Host, std::string PortStr) {
5053
#ifndef LLVM_ON_UNIX
5154
// FIXME: Add TCP support for Windows.
5255
printErrorAndExit("listen option not supported");
5356
return 0;
5457
#else
55-
int SockFD = socket(PF_INET, SOCK_STREAM, 0);
56-
struct sockaddr_in ServerAddr, ClientAddr;
57-
socklen_t ClientAddrLen = sizeof(ClientAddr);
58-
memset(&ServerAddr, 0, sizeof(ServerAddr));
59-
ServerAddr.sin_family = PF_INET;
60-
ServerAddr.sin_family = INADDR_ANY;
61-
ServerAddr.sin_port = htons(Port);
62-
63-
{
64-
// lose the "Address already in use" error message
65-
int Yes = 1;
66-
if (setsockopt(SockFD, SOL_SOCKET, SO_REUSEADDR, &Yes, sizeof(int)) == -1) {
67-
errs() << "Error calling setsockopt.\n";
68-
exit(1);
69-
}
58+
addrinfo Hints{};
59+
Hints.ai_family = AF_INET;
60+
Hints.ai_socktype = SOCK_STREAM;
61+
Hints.ai_flags = AI_PASSIVE;
62+
63+
addrinfo *AI;
64+
if (int EC = getaddrinfo(nullptr, PortStr.c_str(), &Hints, &AI)) {
65+
errs() << "Error setting up bind address: " << gai_strerror(EC) << "\n";
66+
exit(1);
67+
}
68+
69+
// Create a socket from first addrinfo structure returned by getaddrinfo.
70+
int SockFD;
71+
if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) {
72+
errs() << "Error creating socket: " << std::strerror(errno) << "\n";
73+
exit(1);
7074
}
7175

72-
if (bind(SockFD, (struct sockaddr *)&ServerAddr, sizeof(ServerAddr)) < 0) {
73-
errs() << "Error on binding.\n";
76+
// Avoid "Address already in use" errors.
77+
const int Yes = 1;
78+
if (setsockopt(SockFD, SOL_SOCKET, SO_REUSEADDR, &Yes, sizeof(int)) == -1) {
79+
errs() << "Error calling setsockopt: " << std::strerror(errno) << "\n";
7480
exit(1);
7581
}
7682

77-
listen(SockFD, 1);
78-
return accept(SockFD, (struct sockaddr *)&ClientAddr, &ClientAddrLen);
83+
// Bind the socket to the desired port.
84+
if (bind(SockFD, AI->ai_addr, AI->ai_addrlen) < 0) {
85+
errs() << "Error on binding: " << std::strerror(errno) << "\n";
86+
exit(1);
87+
}
88+
89+
// Listen for incomming connections.
90+
static constexpr int ConnectionQueueLen = 1;
91+
listen(SockFD, ConnectionQueueLen);
92+
93+
outs() << "Listening at " << Host << ":" << PortStr << "\n";
94+
95+
#if defined(_AIX)
96+
assert(Hi_32(AI->ai_addrlen) == 0 && "Field is a size_t on 64-bit AIX");
97+
socklen_t AddrLen = Lo_32(AI->ai_addrlen);
98+
return accept(SockFD, AI->ai_addr, &AddrLen);
99+
#else
100+
return accept(SockFD, AI->ai_addr, &AI->ai_addrlen);
79101
#endif
102+
103+
#endif // LLVM_ON_UNIX
80104
}
81105

82106
int main(int argc, char *argv[]) {
@@ -105,9 +129,11 @@ int main(int argc, char *argv[]) {
105129

106130
int Port = 0;
107131
if (PortStr.getAsInteger(10, Port))
108-
printErrorAndExit("port" + PortStr + " is not a valid integer");
132+
printErrorAndExit("port number '" + PortStr +
133+
"' is not a valid integer");
109134

110-
InFD = OutFD = openListener(Host.str(), Port);
135+
InFD = OutFD = openListener(Host.str(), PortStr.str());
136+
outs() << "Connection established. Running OrcRPCTPCServer...\n";
111137
} else
112138
printErrorAndExit("invalid specifier type \"" + SpecifierType + "\"");
113139
}

llvm/tools/llvm-jitlink/llvm-jitlink.cpp

Lines changed: 59 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "llvm/Support/TargetSelect.h"
4343
#include "llvm/Support/Timer.h"
4444

45+
#include <cstring>
4546
#include <list>
4647
#include <string>
4748

@@ -668,73 +669,82 @@ LLVMJITLinkRemoteTargetProcessControl::LaunchExecutor() {
668669
#endif
669670
}
670671

671-
Expected<std::unique_ptr<TargetProcessControl>>
672-
LLVMJITLinkRemoteTargetProcessControl::ConnectToExecutor() {
673-
#ifndef LLVM_ON_UNIX
674-
// FIXME: Add TCP support for Windows.
675-
return make_error<StringError>("-" + OutOfProcessExecutorConnect.ArgStr +
676-
" not supported on non-unix platforms",
677-
inconvertibleErrorCode());
678-
#else
679-
680-
shared::registerStringError<LLVMJITLinkChannel>();
681-
682-
StringRef HostNameStr, PortStr;
683-
std::tie(HostNameStr, PortStr) =
684-
StringRef(OutOfProcessExecutorConnect).split(':');
685-
686-
if (HostNameStr.empty())
687-
return make_error<StringError>("host name for -" +
688-
OutOfProcessExecutorConnect.ArgStr +
689-
" can not be empty",
690-
inconvertibleErrorCode());
691-
if (PortStr.empty())
692-
return make_error<StringError>(
693-
"port for -" + OutOfProcessExecutorConnect.ArgStr + " can not be empty",
694-
inconvertibleErrorCode());
695-
696-
std::string HostName = HostNameStr.str();
697-
int Port = 0;
698-
if (PortStr.getAsInteger(10, Port))
699-
return make_error<StringError>("port number " + PortStr +
700-
" is not a valid integer",
701-
inconvertibleErrorCode());
672+
static Error createTCPSocketError(Twine Details) {
673+
return make_error<StringError>(
674+
formatv("Failed to connect TCP socket '{0}': {1}",
675+
OutOfProcessExecutorConnect, Details),
676+
inconvertibleErrorCode());
677+
}
702678

679+
static Expected<int> connectTCPSocket(std::string Host, std::string PortStr) {
703680
addrinfo *AI;
704681
addrinfo Hints{};
705682
Hints.ai_family = AF_INET;
706683
Hints.ai_socktype = SOCK_STREAM;
707684
Hints.ai_flags = AI_NUMERICSERV;
708-
if (int EC =
709-
getaddrinfo(HostName.c_str(), PortStr.str().c_str(), &Hints, &AI))
710-
return make_error<StringError>(formatv("Failed to resolve {0}:{1} ({2})",
711-
HostName, Port, gai_strerror(EC)),
712-
inconvertibleErrorCode());
713685

714-
// getaddrinfo returns a list of address structures. Go through the list
715-
// to find one we can connect to.
686+
if (int EC = getaddrinfo(Host.c_str(), PortStr.c_str(), &Hints, &AI))
687+
return createTCPSocketError("Address resolution failed (" +
688+
StringRef(gai_strerror(EC)) + ")");
689+
690+
// Cycle through the returned addrinfo structures and connect to the first
691+
// reachable endpoint.
716692
int SockFD;
717-
int ConnectRC = -1;
718-
for (addrinfo *Server = AI; Server; Server = Server->ai_next) {
719-
// If socket fails, maybe it's because the address family is not supported.
720-
// Skip to the next addrinfo structure.
693+
addrinfo *Server;
694+
for (Server = AI; Server != nullptr; Server = Server->ai_next) {
695+
// socket might fail, e.g. if the address family is not supported. Skip to
696+
// the next addrinfo structure in such a case.
721697
if ((SockFD = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0)
722698
continue;
723699

724-
ConnectRC = connect(SockFD, Server->ai_addr, Server->ai_addrlen);
725-
if (ConnectRC == 0)
700+
// If connect returns null, we exit the loop with a working socket.
701+
if (connect(SockFD, Server->ai_addr, Server->ai_addrlen) == 0)
726702
break;
727703

728704
close(SockFD);
729705
}
730706
freeaddrinfo(AI);
731-
if (ConnectRC == -1)
732-
return make_error<StringError>("Failed to connect to " + HostName + ":" +
733-
Twine(Port),
734-
inconvertibleErrorCode());
707+
708+
// If we reached the end of the loop without connecting to a valid endpoint,
709+
// dump the last error that was logged in socket() or connect().
710+
if (Server == nullptr)
711+
return createTCPSocketError(std::strerror(errno));
712+
713+
return SockFD;
714+
}
715+
716+
Expected<std::unique_ptr<TargetProcessControl>>
717+
LLVMJITLinkRemoteTargetProcessControl::ConnectToExecutor() {
718+
#ifndef LLVM_ON_UNIX
719+
// FIXME: Add TCP support for Windows.
720+
return make_error<StringError>("-" + OutOfProcessExecutorConnect.ArgStr +
721+
" not supported on non-unix platforms",
722+
inconvertibleErrorCode());
723+
#else
724+
725+
shared::registerStringError<LLVMJITLinkChannel>();
726+
727+
StringRef Host, PortStr;
728+
std::tie(Host, PortStr) = StringRef(OutOfProcessExecutorConnect).split(':');
729+
if (Host.empty())
730+
return createTCPSocketError("Host name for -" +
731+
OutOfProcessExecutorConnect.ArgStr +
732+
" can not be empty");
733+
if (PortStr.empty())
734+
return createTCPSocketError("Port number in -" +
735+
OutOfProcessExecutorConnect.ArgStr +
736+
" can not be empty");
737+
int Port = 0;
738+
if (PortStr.getAsInteger(10, Port))
739+
return createTCPSocketError("Port number '" + PortStr +
740+
"' is not a valid integer");
741+
742+
Expected<int> SockFD = connectTCPSocket(Host.str(), PortStr.str());
743+
if (!SockFD)
744+
return SockFD.takeError();
735745

736746
auto SSP = std::make_shared<SymbolStringPool>();
737-
auto Channel = std::make_unique<shared::FDRawByteChannel>(SockFD, SockFD);
747+
auto Channel = std::make_unique<shared::FDRawByteChannel>(*SockFD, *SockFD);
738748
auto Endpoint = std::make_unique<LLVMJITLinkRPCEndpoint>(*Channel, true);
739749

740750
auto ReportError = [](Error Err) {

0 commit comments

Comments
 (0)