1*89c4ff92SAndroid Build Coastguard Worker //
2*89c4ff92SAndroid Build Coastguard Worker // Copyright © 2019 Arm Ltd and Contributors. All rights reserved.
3*89c4ff92SAndroid Build Coastguard Worker // SPDX-License-Identifier: MIT
4*89c4ff92SAndroid Build Coastguard Worker //
5*89c4ff92SAndroid Build Coastguard Worker
6*89c4ff92SAndroid Build Coastguard Worker #include "SocketProfilingConnection.hpp"
7*89c4ff92SAndroid Build Coastguard Worker
8*89c4ff92SAndroid Build Coastguard Worker #include <common/include/SocketConnectionException.hpp>
9*89c4ff92SAndroid Build Coastguard Worker
10*89c4ff92SAndroid Build Coastguard Worker #include <cerrno>
11*89c4ff92SAndroid Build Coastguard Worker #include <cstring>
12*89c4ff92SAndroid Build Coastguard Worker #include <fcntl.h>
13*89c4ff92SAndroid Build Coastguard Worker #include <string>
14*89c4ff92SAndroid Build Coastguard Worker
15*89c4ff92SAndroid Build Coastguard Worker
16*89c4ff92SAndroid Build Coastguard Worker namespace arm
17*89c4ff92SAndroid Build Coastguard Worker {
18*89c4ff92SAndroid Build Coastguard Worker namespace pipe
19*89c4ff92SAndroid Build Coastguard Worker {
20*89c4ff92SAndroid Build Coastguard Worker
SocketProfilingConnection()21*89c4ff92SAndroid Build Coastguard Worker SocketProfilingConnection::SocketProfilingConnection()
22*89c4ff92SAndroid Build Coastguard Worker {
23*89c4ff92SAndroid Build Coastguard Worker #if !defined(ARMNN_DISABLE_SOCKETS)
24*89c4ff92SAndroid Build Coastguard Worker arm::pipe::Initialize();
25*89c4ff92SAndroid Build Coastguard Worker memset(m_Socket, 0, sizeof(m_Socket));
26*89c4ff92SAndroid Build Coastguard Worker // Note: we're using Linux specific SOCK_CLOEXEC flag.
27*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
28*89c4ff92SAndroid Build Coastguard Worker if (m_Socket[0].fd == -1)
29*89c4ff92SAndroid Build Coastguard Worker {
30*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
31*89c4ff92SAndroid Build Coastguard Worker std::string("SocketProfilingConnection: Socket construction failed: ") + strerror(errno),
32*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd,
33*89c4ff92SAndroid Build Coastguard Worker errno);
34*89c4ff92SAndroid Build Coastguard Worker }
35*89c4ff92SAndroid Build Coastguard Worker
36*89c4ff92SAndroid Build Coastguard Worker // Connect to the named unix domain socket.
37*89c4ff92SAndroid Build Coastguard Worker sockaddr_un server{};
38*89c4ff92SAndroid Build Coastguard Worker memset(&server, 0, sizeof(sockaddr_un));
39*89c4ff92SAndroid Build Coastguard Worker // As m_GatorNamespace begins with a null character we need to ignore that when getting its length.
40*89c4ff92SAndroid Build Coastguard Worker memcpy(server.sun_path, m_GatorNamespace, strlen(m_GatorNamespace + 1) + 1);
41*89c4ff92SAndroid Build Coastguard Worker server.sun_family = AF_UNIX;
42*89c4ff92SAndroid Build Coastguard Worker if (0 != connect(m_Socket[0].fd, reinterpret_cast<const sockaddr*>(&server), sizeof(sockaddr_un)))
43*89c4ff92SAndroid Build Coastguard Worker {
44*89c4ff92SAndroid Build Coastguard Worker Close();
45*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
46*89c4ff92SAndroid Build Coastguard Worker std::string("SocketProfilingConnection: Cannot connect to stream socket: ") + strerror(errno),
47*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd,
48*89c4ff92SAndroid Build Coastguard Worker errno);
49*89c4ff92SAndroid Build Coastguard Worker }
50*89c4ff92SAndroid Build Coastguard Worker
51*89c4ff92SAndroid Build Coastguard Worker // Our socket will only be interested in polling reads.
52*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].events = POLLIN;
53*89c4ff92SAndroid Build Coastguard Worker
54*89c4ff92SAndroid Build Coastguard Worker // Make the socket non blocking.
55*89c4ff92SAndroid Build Coastguard Worker if (!arm::pipe::SetNonBlocking(m_Socket[0].fd))
56*89c4ff92SAndroid Build Coastguard Worker {
57*89c4ff92SAndroid Build Coastguard Worker Close();
58*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
59*89c4ff92SAndroid Build Coastguard Worker std::string("SocketProfilingConnection: Failed to set socket as non blocking: ") + strerror(errno),
60*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd,
61*89c4ff92SAndroid Build Coastguard Worker errno);
62*89c4ff92SAndroid Build Coastguard Worker }
63*89c4ff92SAndroid Build Coastguard Worker #endif
64*89c4ff92SAndroid Build Coastguard Worker }
65*89c4ff92SAndroid Build Coastguard Worker
IsOpen() const66*89c4ff92SAndroid Build Coastguard Worker bool SocketProfilingConnection::IsOpen() const
67*89c4ff92SAndroid Build Coastguard Worker {
68*89c4ff92SAndroid Build Coastguard Worker #if !defined(ARMNN_DISABLE_SOCKETS)
69*89c4ff92SAndroid Build Coastguard Worker return m_Socket[0].fd > 0;
70*89c4ff92SAndroid Build Coastguard Worker #else
71*89c4ff92SAndroid Build Coastguard Worker return false;
72*89c4ff92SAndroid Build Coastguard Worker #endif
73*89c4ff92SAndroid Build Coastguard Worker }
74*89c4ff92SAndroid Build Coastguard Worker
Close()75*89c4ff92SAndroid Build Coastguard Worker void SocketProfilingConnection::Close()
76*89c4ff92SAndroid Build Coastguard Worker {
77*89c4ff92SAndroid Build Coastguard Worker #if !defined(ARMNN_DISABLE_SOCKETS)
78*89c4ff92SAndroid Build Coastguard Worker if (arm::pipe::Close(m_Socket[0].fd) != 0)
79*89c4ff92SAndroid Build Coastguard Worker {
80*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
81*89c4ff92SAndroid Build Coastguard Worker std::string("SocketProfilingConnection: Cannot close stream socket: ") + strerror(errno),
82*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd,
83*89c4ff92SAndroid Build Coastguard Worker errno);
84*89c4ff92SAndroid Build Coastguard Worker }
85*89c4ff92SAndroid Build Coastguard Worker
86*89c4ff92SAndroid Build Coastguard Worker memset(m_Socket, 0, sizeof(m_Socket));
87*89c4ff92SAndroid Build Coastguard Worker #endif
88*89c4ff92SAndroid Build Coastguard Worker }
89*89c4ff92SAndroid Build Coastguard Worker
WritePacket(const unsigned char * buffer,uint32_t length)90*89c4ff92SAndroid Build Coastguard Worker bool SocketProfilingConnection::WritePacket(const unsigned char* buffer, uint32_t length)
91*89c4ff92SAndroid Build Coastguard Worker {
92*89c4ff92SAndroid Build Coastguard Worker if (buffer == nullptr || length == 0)
93*89c4ff92SAndroid Build Coastguard Worker {
94*89c4ff92SAndroid Build Coastguard Worker return false;
95*89c4ff92SAndroid Build Coastguard Worker }
96*89c4ff92SAndroid Build Coastguard Worker #if !defined(ARMNN_DISABLE_SOCKETS)
97*89c4ff92SAndroid Build Coastguard Worker return arm::pipe::Write(m_Socket[0].fd, buffer, length) != -1;
98*89c4ff92SAndroid Build Coastguard Worker #else
99*89c4ff92SAndroid Build Coastguard Worker return false;
100*89c4ff92SAndroid Build Coastguard Worker #endif
101*89c4ff92SAndroid Build Coastguard Worker }
102*89c4ff92SAndroid Build Coastguard Worker
ReadPacket(uint32_t timeout)103*89c4ff92SAndroid Build Coastguard Worker arm::pipe::Packet SocketProfilingConnection::ReadPacket(uint32_t timeout)
104*89c4ff92SAndroid Build Coastguard Worker {
105*89c4ff92SAndroid Build Coastguard Worker #if !defined(ARMNN_DISABLE_SOCKETS)
106*89c4ff92SAndroid Build Coastguard Worker // Is there currently at least a header worth of data waiting to be read?
107*89c4ff92SAndroid Build Coastguard Worker int bytes_available = 0;
108*89c4ff92SAndroid Build Coastguard Worker arm::pipe::Ioctl(m_Socket[0].fd, FIONREAD, &bytes_available);
109*89c4ff92SAndroid Build Coastguard Worker if (bytes_available >= 8)
110*89c4ff92SAndroid Build Coastguard Worker {
111*89c4ff92SAndroid Build Coastguard Worker // Yes there is. Read it:
112*89c4ff92SAndroid Build Coastguard Worker return ReceivePacket();
113*89c4ff92SAndroid Build Coastguard Worker }
114*89c4ff92SAndroid Build Coastguard Worker
115*89c4ff92SAndroid Build Coastguard Worker // Poll for data on the socket or until timeout occurs
116*89c4ff92SAndroid Build Coastguard Worker int pollResult = arm::pipe::Poll(&m_Socket[0], 1, static_cast<int>(timeout));
117*89c4ff92SAndroid Build Coastguard Worker
118*89c4ff92SAndroid Build Coastguard Worker switch (pollResult)
119*89c4ff92SAndroid Build Coastguard Worker {
120*89c4ff92SAndroid Build Coastguard Worker case -1: // Error
121*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
122*89c4ff92SAndroid Build Coastguard Worker std::string("SocketProfilingConnection: Error occured while reading from socket: ") + strerror(errno),
123*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd,
124*89c4ff92SAndroid Build Coastguard Worker errno);
125*89c4ff92SAndroid Build Coastguard Worker
126*89c4ff92SAndroid Build Coastguard Worker case 0: // Timeout
127*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::TimeoutException("SocketProfilingConnection: Timeout while reading from socket");
128*89c4ff92SAndroid Build Coastguard Worker
129*89c4ff92SAndroid Build Coastguard Worker default: // Normal poll return but it could still contain an error signal
130*89c4ff92SAndroid Build Coastguard Worker // Check if the socket reported an error
131*89c4ff92SAndroid Build Coastguard Worker if (m_Socket[0].revents & (POLLNVAL | POLLERR | POLLHUP))
132*89c4ff92SAndroid Build Coastguard Worker {
133*89c4ff92SAndroid Build Coastguard Worker if (m_Socket[0].revents == POLLNVAL)
134*89c4ff92SAndroid Build Coastguard Worker {
135*89c4ff92SAndroid Build Coastguard Worker // This is an unrecoverable error.
136*89c4ff92SAndroid Build Coastguard Worker Close();
137*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
138*89c4ff92SAndroid Build Coastguard Worker std::string("SocketProfilingConnection: Error occured while polling receiving socket: POLLNVAL."),
139*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd);
140*89c4ff92SAndroid Build Coastguard Worker }
141*89c4ff92SAndroid Build Coastguard Worker if (m_Socket[0].revents == POLLERR)
142*89c4ff92SAndroid Build Coastguard Worker {
143*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
144*89c4ff92SAndroid Build Coastguard Worker std::string(
145*89c4ff92SAndroid Build Coastguard Worker "SocketProfilingConnection: Error occured while polling receiving socket: POLLERR: ")
146*89c4ff92SAndroid Build Coastguard Worker + strerror(errno),
147*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd,
148*89c4ff92SAndroid Build Coastguard Worker errno);
149*89c4ff92SAndroid Build Coastguard Worker }
150*89c4ff92SAndroid Build Coastguard Worker if (m_Socket[0].revents == POLLHUP)
151*89c4ff92SAndroid Build Coastguard Worker {
152*89c4ff92SAndroid Build Coastguard Worker // This is an unrecoverable error.
153*89c4ff92SAndroid Build Coastguard Worker Close();
154*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
155*89c4ff92SAndroid Build Coastguard Worker std::string("SocketProfilingConnection: Connection closed by remote client: POLLHUP."),
156*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd);
157*89c4ff92SAndroid Build Coastguard Worker }
158*89c4ff92SAndroid Build Coastguard Worker }
159*89c4ff92SAndroid Build Coastguard Worker
160*89c4ff92SAndroid Build Coastguard Worker // Check if there is data to read
161*89c4ff92SAndroid Build Coastguard Worker if (!(m_Socket[0].revents & (POLLIN)))
162*89c4ff92SAndroid Build Coastguard Worker {
163*89c4ff92SAndroid Build Coastguard Worker // This is a corner case. The socket as been woken up but not with any data.
164*89c4ff92SAndroid Build Coastguard Worker // We'll throw a timeout exception to loop around again.
165*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::TimeoutException(
166*89c4ff92SAndroid Build Coastguard Worker "SocketProfilingConnection: File descriptor was polled but no data was available to receive.");
167*89c4ff92SAndroid Build Coastguard Worker }
168*89c4ff92SAndroid Build Coastguard Worker
169*89c4ff92SAndroid Build Coastguard Worker return ReceivePacket();
170*89c4ff92SAndroid Build Coastguard Worker }
171*89c4ff92SAndroid Build Coastguard Worker #else
172*89c4ff92SAndroid Build Coastguard Worker IgnoreUnused(timeout);
173*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::TimeoutException(
174*89c4ff92SAndroid Build Coastguard Worker "SocketProfilingConnection: Cannot use ReadPacket function with sockets disabled");
175*89c4ff92SAndroid Build Coastguard Worker #endif
176*89c4ff92SAndroid Build Coastguard Worker }
177*89c4ff92SAndroid Build Coastguard Worker
ReceivePacket()178*89c4ff92SAndroid Build Coastguard Worker arm::pipe::Packet SocketProfilingConnection::ReceivePacket()
179*89c4ff92SAndroid Build Coastguard Worker {
180*89c4ff92SAndroid Build Coastguard Worker #if !defined(ARMNN_DISABLE_SOCKETS)
181*89c4ff92SAndroid Build Coastguard Worker char header[8] = {};
182*89c4ff92SAndroid Build Coastguard Worker long receiveResult = arm::pipe::Read(m_Socket[0].fd, &header, sizeof(header));
183*89c4ff92SAndroid Build Coastguard Worker // We expect 8 as the result here. 0 means EOF, socket is closed. -1 means there been some other kind of error.
184*89c4ff92SAndroid Build Coastguard Worker switch( receiveResult )
185*89c4ff92SAndroid Build Coastguard Worker {
186*89c4ff92SAndroid Build Coastguard Worker case 0:
187*89c4ff92SAndroid Build Coastguard Worker // Socket has closed.
188*89c4ff92SAndroid Build Coastguard Worker Close();
189*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
190*89c4ff92SAndroid Build Coastguard Worker std::string("SocketProfilingConnection: Remote socket has closed the connection."),
191*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd);
192*89c4ff92SAndroid Build Coastguard Worker case -1:
193*89c4ff92SAndroid Build Coastguard Worker // There's been a socket error. We will presume it's unrecoverable.
194*89c4ff92SAndroid Build Coastguard Worker Close();
195*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
196*89c4ff92SAndroid Build Coastguard Worker std::string("SocketProfilingConnection: Error occured while reading the packet: ") + strerror(errno),
197*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd,
198*89c4ff92SAndroid Build Coastguard Worker errno);
199*89c4ff92SAndroid Build Coastguard Worker default:
200*89c4ff92SAndroid Build Coastguard Worker if (receiveResult < 8)
201*89c4ff92SAndroid Build Coastguard Worker {
202*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
203*89c4ff92SAndroid Build Coastguard Worker std::string(
204*89c4ff92SAndroid Build Coastguard Worker "SocketProfilingConnection: The received packet did not contains a valid PIPE header."),
205*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd);
206*89c4ff92SAndroid Build Coastguard Worker }
207*89c4ff92SAndroid Build Coastguard Worker break;
208*89c4ff92SAndroid Build Coastguard Worker }
209*89c4ff92SAndroid Build Coastguard Worker
210*89c4ff92SAndroid Build Coastguard Worker // stream_metadata_identifier is the first 4 bytes
211*89c4ff92SAndroid Build Coastguard Worker uint32_t metadataIdentifier = 0;
212*89c4ff92SAndroid Build Coastguard Worker std::memcpy(&metadataIdentifier, header, sizeof(metadataIdentifier));
213*89c4ff92SAndroid Build Coastguard Worker
214*89c4ff92SAndroid Build Coastguard Worker // data_length is the next 4 bytes
215*89c4ff92SAndroid Build Coastguard Worker uint32_t dataLength = 0;
216*89c4ff92SAndroid Build Coastguard Worker std::memcpy(&dataLength, header + 4u, sizeof(dataLength));
217*89c4ff92SAndroid Build Coastguard Worker
218*89c4ff92SAndroid Build Coastguard Worker std::unique_ptr<unsigned char[]> packetData;
219*89c4ff92SAndroid Build Coastguard Worker if (dataLength > 0)
220*89c4ff92SAndroid Build Coastguard Worker {
221*89c4ff92SAndroid Build Coastguard Worker packetData = std::make_unique<unsigned char[]>(dataLength);
222*89c4ff92SAndroid Build Coastguard Worker long receivedLength = arm::pipe::Read(m_Socket[0].fd, packetData.get(), dataLength);
223*89c4ff92SAndroid Build Coastguard Worker if (receivedLength < 0)
224*89c4ff92SAndroid Build Coastguard Worker {
225*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
226*89c4ff92SAndroid Build Coastguard Worker std::string("SocketProfilingConnection: Error occured while reading the packet: ") + strerror(errno),
227*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd,
228*89c4ff92SAndroid Build Coastguard Worker errno);
229*89c4ff92SAndroid Build Coastguard Worker }
230*89c4ff92SAndroid Build Coastguard Worker if (dataLength != static_cast<uint32_t>(receivedLength))
231*89c4ff92SAndroid Build Coastguard Worker {
232*89c4ff92SAndroid Build Coastguard Worker // What do we do here if we can't read in a full packet?
233*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::SocketConnectionException(
234*89c4ff92SAndroid Build Coastguard Worker std::string("SocketProfilingConnection: Invalid PIPE packet."),
235*89c4ff92SAndroid Build Coastguard Worker m_Socket[0].fd);
236*89c4ff92SAndroid Build Coastguard Worker }
237*89c4ff92SAndroid Build Coastguard Worker }
238*89c4ff92SAndroid Build Coastguard Worker
239*89c4ff92SAndroid Build Coastguard Worker return arm::pipe::Packet(metadataIdentifier, dataLength, packetData);
240*89c4ff92SAndroid Build Coastguard Worker #else
241*89c4ff92SAndroid Build Coastguard Worker throw arm::pipe::TimeoutException(
242*89c4ff92SAndroid Build Coastguard Worker "SocketProfilingConnection: Cannot use ReceivePacket function with sockets disabled");
243*89c4ff92SAndroid Build Coastguard Worker #endif
244*89c4ff92SAndroid Build Coastguard Worker }
245*89c4ff92SAndroid Build Coastguard Worker
246*89c4ff92SAndroid Build Coastguard Worker } // namespace pipe
247*89c4ff92SAndroid Build Coastguard Worker } // namespace arm
248