blob: a211567f7fa20ef3ddd0a78f406fec008bed7e4e [file] [log] [blame]
Teresa Charlin9bab4962019-09-06 12:28:35 +01001//
Jim Flynn6da6a452020-07-14 14:26:27 +01002// Copyright © 2019 Arm Ltd and Contributors. All rights reserved.
Teresa Charlin9bab4962019-09-06 12:28:35 +01003// SPDX-License-Identifier: MIT
4//
5
6#include "SocketProfilingConnection.hpp"
7
Jim Flynndecd08b2022-03-13 22:35:46 +00008#include <common/include/SocketConnectionException.hpp>
Sadik Armagana97a0be2020-03-03 10:44:56 +00009
Colm Donelana21620d2019-10-11 13:09:49 +010010#include <cerrno>
Jim Flynndecd08b2022-03-13 22:35:46 +000011#include <cstring>
Teresa Charlin9bab4962019-09-06 12:28:35 +010012#include <fcntl.h>
Teresa Charlin9bab4962019-09-06 12:28:35 +010013#include <string>
14
Rob Hughes25b74362020-01-13 11:14:59 +000015
Cathal Corbett5aa9fd72022-02-25 15:33:28 +000016namespace arm
Teresa Charlin9bab4962019-09-06 12:28:35 +010017{
Cathal Corbett5aa9fd72022-02-25 15:33:28 +000018namespace pipe
Teresa Charlin9bab4962019-09-06 12:28:35 +010019{
20
21SocketProfilingConnection::SocketProfilingConnection()
22{
Jim Flynnbbfe6032020-07-20 16:57:44 +010023 arm::pipe::Initialize();
Teresa Charlin9bab4962019-09-06 12:28:35 +010024 memset(m_Socket, 0, sizeof(m_Socket));
25 // Note: we're using Linux specific SOCK_CLOEXEC flag.
26 m_Socket[0].fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
27 if (m_Socket[0].fd == -1)
28 {
Jim Flynnbbfe6032020-07-20 16:57:44 +010029 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +000030 std::string("SocketProfilingConnection: Socket construction failed: ") + strerror(errno),
31 m_Socket[0].fd,
32 errno);
Teresa Charlin9bab4962019-09-06 12:28:35 +010033 }
34
35 // Connect to the named unix domain socket.
Rob Hughes25b74362020-01-13 11:14:59 +000036 sockaddr_un server{};
Teresa Charlin9bab4962019-09-06 12:28:35 +010037 memset(&server, 0, sizeof(sockaddr_un));
38 // As m_GatorNamespace begins with a null character we need to ignore that when getting its length.
39 memcpy(server.sun_path, m_GatorNamespace, strlen(m_GatorNamespace + 1) + 1);
40 server.sun_family = AF_UNIX;
41 if (0 != connect(m_Socket[0].fd, reinterpret_cast<const sockaddr*>(&server), sizeof(sockaddr_un)))
42 {
Colm Donelan9ea77002019-10-17 14:02:44 +010043 Close();
Jim Flynnbbfe6032020-07-20 16:57:44 +010044 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +000045 std::string("SocketProfilingConnection: Cannot connect to stream socket: ") + strerror(errno),
46 m_Socket[0].fd,
47 errno);
Teresa Charlin9bab4962019-09-06 12:28:35 +010048 }
49
50 // Our socket will only be interested in polling reads.
51 m_Socket[0].events = POLLIN;
52
53 // Make the socket non blocking.
Jim Flynnbbfe6032020-07-20 16:57:44 +010054 if (!arm::pipe::SetNonBlocking(m_Socket[0].fd))
Teresa Charlin9bab4962019-09-06 12:28:35 +010055 {
Colm Donelan9ea77002019-10-17 14:02:44 +010056 Close();
Jim Flynnbbfe6032020-07-20 16:57:44 +010057 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +000058 std::string("SocketProfilingConnection: Failed to set socket as non blocking: ") + strerror(errno),
59 m_Socket[0].fd,
60 errno);
Teresa Charlin9bab4962019-09-06 12:28:35 +010061 }
62}
63
Matteo Martincigh54fb9572019-10-02 12:50:57 +010064bool SocketProfilingConnection::IsOpen() const
Teresa Charlin9bab4962019-09-06 12:28:35 +010065{
Matteo Martincigh24e8f922019-09-19 11:57:46 +010066 return m_Socket[0].fd > 0;
Teresa Charlin9bab4962019-09-06 12:28:35 +010067}
68
69void SocketProfilingConnection::Close()
70{
Jim Flynnbbfe6032020-07-20 16:57:44 +010071 if (arm::pipe::Close(m_Socket[0].fd) != 0)
FinnWilliamsArma0c78712019-09-16 12:06:47 +010072 {
Jim Flynnbbfe6032020-07-20 16:57:44 +010073 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +000074 std::string("SocketProfilingConnection: Cannot close stream socket: ") + strerror(errno),
75 m_Socket[0].fd,
76 errno);
FinnWilliamsArma0c78712019-09-16 12:06:47 +010077 }
Matteo Martincigh24e8f922019-09-19 11:57:46 +010078
79 memset(m_Socket, 0, sizeof(m_Socket));
Teresa Charlin9bab4962019-09-06 12:28:35 +010080}
81
Matteo Martincigh24e8f922019-09-19 11:57:46 +010082bool SocketProfilingConnection::WritePacket(const unsigned char* buffer, uint32_t length)
Teresa Charlin9bab4962019-09-06 12:28:35 +010083{
Matteo Martincigh24e8f922019-09-19 11:57:46 +010084 if (buffer == nullptr || length == 0)
FinnWilliamsArma0c78712019-09-16 12:06:47 +010085 {
86 return false;
87 }
Matteo Martincigh24e8f922019-09-19 11:57:46 +010088
Jim Flynnbbfe6032020-07-20 16:57:44 +010089 return arm::pipe::Write(m_Socket[0].fd, buffer, length) != -1;
Teresa Charlin9bab4962019-09-06 12:28:35 +010090}
91
Jim Flynnbbfe6032020-07-20 16:57:44 +010092arm::pipe::Packet SocketProfilingConnection::ReadPacket(uint32_t timeout)
Teresa Charlin9bab4962019-09-06 12:28:35 +010093{
Matteo Martincigh8d9590e2019-10-15 09:35:29 +010094 // Is there currently at least a header worth of data waiting to be read?
95 int bytes_available = 0;
Jim Flynnbbfe6032020-07-20 16:57:44 +010096 arm::pipe::Ioctl(m_Socket[0].fd, FIONREAD, &bytes_available);
Colm Donelana21620d2019-10-11 13:09:49 +010097 if (bytes_available >= 8)
FinnWilliamsArma0c78712019-09-16 12:06:47 +010098 {
Colm Donelana21620d2019-10-11 13:09:49 +010099 // Yes there is. Read it:
100 return ReceivePacket();
101 }
Matteo Martincigh8d9590e2019-10-15 09:35:29 +0100102
103 // Poll for data on the socket or until timeout occurs
Jim Flynnbbfe6032020-07-20 16:57:44 +0100104 int pollResult = arm::pipe::Poll(&m_Socket[0], 1, static_cast<int>(timeout));
Matteo Martincigh8d9590e2019-10-15 09:35:29 +0100105
106 switch (pollResult)
Colm Donelana21620d2019-10-11 13:09:49 +0100107 {
Matteo Martincigh8d9590e2019-10-15 09:35:29 +0100108 case -1: // Error
Jim Flynnbbfe6032020-07-20 16:57:44 +0100109 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +0000110 std::string("SocketProfilingConnection: Error occured while reading from socket: ") + strerror(errno),
111 m_Socket[0].fd,
112 errno);
Matteo Martincigh24e8f922019-09-19 11:57:46 +0100113
Matteo Martincigh8d9590e2019-10-15 09:35:29 +0100114 case 0: // Timeout
Jim Flynnbbfe6032020-07-20 16:57:44 +0100115 throw arm::pipe::TimeoutException("SocketProfilingConnection: Timeout while reading from socket");
Matteo Martincigh8d9590e2019-10-15 09:35:29 +0100116
117 default: // Normal poll return but it could still contain an error signal
Matteo Martincigh8d9590e2019-10-15 09:35:29 +0100118 // Check if the socket reported an error
119 if (m_Socket[0].revents & (POLLNVAL | POLLERR | POLLHUP))
FinnWilliamsArma0c78712019-09-16 12:06:47 +0100120 {
Colm Donelan9ea77002019-10-17 14:02:44 +0100121 if (m_Socket[0].revents == POLLNVAL)
122 {
123 // This is an unrecoverable error.
124 Close();
Jim Flynnbbfe6032020-07-20 16:57:44 +0100125 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +0000126 std::string("SocketProfilingConnection: Error occured while polling receiving socket: POLLNVAL."),
127 m_Socket[0].fd);
Colm Donelan9ea77002019-10-17 14:02:44 +0100128 }
129 if (m_Socket[0].revents == POLLERR)
130 {
Jim Flynnbbfe6032020-07-20 16:57:44 +0100131 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +0000132 std::string(
133 "SocketProfilingConnection: Error occured while polling receiving socket: POLLERR: ")
134 + strerror(errno),
135 m_Socket[0].fd,
136 errno);
Colm Donelan9ea77002019-10-17 14:02:44 +0100137 }
138 if (m_Socket[0].revents == POLLHUP)
139 {
140 // This is an unrecoverable error.
141 Close();
Jim Flynnbbfe6032020-07-20 16:57:44 +0100142 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +0000143 std::string("SocketProfilingConnection: Connection closed by remote client: POLLHUP."),
144 m_Socket[0].fd);
Colm Donelan9ea77002019-10-17 14:02:44 +0100145 }
FinnWilliamsArma0c78712019-09-16 12:06:47 +0100146 }
Matteo Martincigh8d9590e2019-10-15 09:35:29 +0100147
148 // Check if there is data to read
149 if (!(m_Socket[0].revents & (POLLIN)))
150 {
Colm Donelan9ea77002019-10-17 14:02:44 +0100151 // This is a corner case. The socket as been woken up but not with any data.
152 // We'll throw a timeout exception to loop around again.
Jim Flynndecd08b2022-03-13 22:35:46 +0000153 throw arm::pipe::TimeoutException(
Sadik Armagana97a0be2020-03-03 10:44:56 +0000154 "SocketProfilingConnection: File descriptor was polled but no data was available to receive.");
Matteo Martincigh8d9590e2019-10-15 09:35:29 +0100155 }
156
157 return ReceivePacket();
Colm Donelana21620d2019-10-11 13:09:49 +0100158 }
159}
FinnWilliamsArma0c78712019-09-16 12:06:47 +0100160
Jim Flynnbbfe6032020-07-20 16:57:44 +0100161arm::pipe::Packet SocketProfilingConnection::ReceivePacket()
Colm Donelana21620d2019-10-11 13:09:49 +0100162{
163 char header[8] = {};
Jim Flynnbbfe6032020-07-20 16:57:44 +0100164 long receiveResult = arm::pipe::Read(m_Socket[0].fd, &header, sizeof(header));
Colm Donelan9ea77002019-10-17 14:02:44 +0100165 // We expect 8 as the result here. 0 means EOF, socket is closed. -1 means there been some other kind of error.
166 switch( receiveResult )
Colm Donelana21620d2019-10-11 13:09:49 +0100167 {
Colm Donelan9ea77002019-10-17 14:02:44 +0100168 case 0:
169 // Socket has closed.
170 Close();
Jim Flynnbbfe6032020-07-20 16:57:44 +0100171 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +0000172 std::string("SocketProfilingConnection: Remote socket has closed the connection."),
173 m_Socket[0].fd);
Colm Donelan9ea77002019-10-17 14:02:44 +0100174 case -1:
175 // There's been a socket error. We will presume it's unrecoverable.
176 Close();
Jim Flynnbbfe6032020-07-20 16:57:44 +0100177 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +0000178 std::string("SocketProfilingConnection: Error occured while reading the packet: ") + strerror(errno),
179 m_Socket[0].fd,
180 errno);
Colm Donelan9ea77002019-10-17 14:02:44 +0100181 default:
182 if (receiveResult < 8)
183 {
Jim Flynnbbfe6032020-07-20 16:57:44 +0100184 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +0000185 std::string(
186 "SocketProfilingConnection: The received packet did not contains a valid PIPE header."),
187 m_Socket[0].fd);
Colm Donelan9ea77002019-10-17 14:02:44 +0100188 }
189 break;
Colm Donelana21620d2019-10-11 13:09:49 +0100190 }
Matteo Martincigh8d9590e2019-10-15 09:35:29 +0100191
Colm Donelana21620d2019-10-11 13:09:49 +0100192 // stream_metadata_identifier is the first 4 bytes
193 uint32_t metadataIdentifier = 0;
194 std::memcpy(&metadataIdentifier, header, sizeof(metadataIdentifier));
Matteo Martincigh24e8f922019-09-19 11:57:46 +0100195
Colm Donelana21620d2019-10-11 13:09:49 +0100196 // data_length is the next 4 bytes
197 uint32_t dataLength = 0;
198 std::memcpy(&dataLength, header + 4u, sizeof(dataLength));
Matteo Martincigh24e8f922019-09-19 11:57:46 +0100199
Matteo Martincigh8d9590e2019-10-15 09:35:29 +0100200 std::unique_ptr<unsigned char[]> packetData;
Colm Donelana21620d2019-10-11 13:09:49 +0100201 if (dataLength > 0)
202 {
Matteo Martincigh8d9590e2019-10-15 09:35:29 +0100203 packetData = std::make_unique<unsigned char[]>(dataLength);
Jim Flynnbbfe6032020-07-20 16:57:44 +0100204 long receivedLength = arm::pipe::Read(m_Socket[0].fd, packetData.get(), dataLength);
Jim Flynne11ff892019-10-04 04:25:43 -0700205 if (receivedLength < 0)
206 {
Jim Flynnbbfe6032020-07-20 16:57:44 +0100207 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +0000208 std::string("SocketProfilingConnection: Error occured while reading the packet: ") + strerror(errno),
209 m_Socket[0].fd,
210 errno);
Jim Flynne11ff892019-10-04 04:25:43 -0700211 }
212 if (dataLength != static_cast<uint32_t>(receivedLength))
Matteo Martincigh24e8f922019-09-19 11:57:46 +0100213 {
214 // What do we do here if we can't read in a full packet?
Jim Flynnbbfe6032020-07-20 16:57:44 +0100215 throw arm::pipe::SocketConnectionException(
Sadik Armagana97a0be2020-03-03 10:44:56 +0000216 std::string("SocketProfilingConnection: Invalid PIPE packet."),
217 m_Socket[0].fd);
Matteo Martincigh24e8f922019-09-19 11:57:46 +0100218 }
FinnWilliamsArma0c78712019-09-16 12:06:47 +0100219 }
Colm Donelana21620d2019-10-11 13:09:49 +0100220
Jim Flynnbbfe6032020-07-20 16:57:44 +0100221 return arm::pipe::Packet(metadataIdentifier, dataLength, packetData);
Teresa Charlin9bab4962019-09-06 12:28:35 +0100222}
223
Cathal Corbett5aa9fd72022-02-25 15:33:28 +0000224} // namespace pipe
225} // namespace arm