blob: 6f7101b254f892853c621165a07806854b677adf [file] [log] [blame]
Teresa Charlin9bab4962019-09-06 12:28:35 +01001//
2// Copyright © 2019 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "SocketProfilingConnection.hpp"
7
Colm Donelana21620d2019-10-11 13:09:49 +01008#include <cerrno>
Teresa Charlin9bab4962019-09-06 12:28:35 +01009#include <fcntl.h>
Colm Donelana21620d2019-10-11 13:09:49 +010010#include <sys/ioctl.h>
Teresa Charlin9bab4962019-09-06 12:28:35 +010011#include <sys/socket.h>
12#include <sys/un.h>
Teresa Charlin9bab4962019-09-06 12:28:35 +010013#include <string>
14
15namespace armnn
16{
17namespace profiling
18{
19
20SocketProfilingConnection::SocketProfilingConnection()
21{
22 memset(m_Socket, 0, sizeof(m_Socket));
23 // Note: we're using Linux specific SOCK_CLOEXEC flag.
24 m_Socket[0].fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
25 if (m_Socket[0].fd == -1)
26 {
Colm Donelana21620d2019-10-11 13:09:49 +010027 throw armnn::RuntimeException(std::string("Socket construction failed: ") + strerror(errno));
Teresa Charlin9bab4962019-09-06 12:28:35 +010028 }
29
30 // Connect to the named unix domain socket.
31 struct sockaddr_un server{};
32 memset(&server, 0, sizeof(sockaddr_un));
33 // As m_GatorNamespace begins with a null character we need to ignore that when getting its length.
34 memcpy(server.sun_path, m_GatorNamespace, strlen(m_GatorNamespace + 1) + 1);
35 server.sun_family = AF_UNIX;
36 if (0 != connect(m_Socket[0].fd, reinterpret_cast<const sockaddr*>(&server), sizeof(sockaddr_un)))
37 {
38 close(m_Socket[0].fd);
Colm Donelana21620d2019-10-11 13:09:49 +010039 throw armnn::RuntimeException(std::string("Cannot connect to stream socket: ") + strerror(errno));
Teresa Charlin9bab4962019-09-06 12:28:35 +010040 }
41
42 // Our socket will only be interested in polling reads.
43 m_Socket[0].events = POLLIN;
44
45 // Make the socket non blocking.
46 const int currentFlags = fcntl(m_Socket[0].fd, F_GETFL);
47 if (0 != fcntl(m_Socket[0].fd, F_SETFL, currentFlags | O_NONBLOCK))
48 {
49 close(m_Socket[0].fd);
Colm Donelana21620d2019-10-11 13:09:49 +010050 throw armnn::RuntimeException(std::string("Failed to set socket as non blocking: ") + strerror(errno));
Teresa Charlin9bab4962019-09-06 12:28:35 +010051 }
52}
53
Matteo Martincigh54fb9572019-10-02 12:50:57 +010054bool SocketProfilingConnection::IsOpen() const
Teresa Charlin9bab4962019-09-06 12:28:35 +010055{
Matteo Martincigh24e8f922019-09-19 11:57:46 +010056 return m_Socket[0].fd > 0;
Teresa Charlin9bab4962019-09-06 12:28:35 +010057}
58
59void SocketProfilingConnection::Close()
60{
Matteo Martincigh24e8f922019-09-19 11:57:46 +010061 if (close(m_Socket[0].fd) != 0)
FinnWilliamsArma0c78712019-09-16 12:06:47 +010062 {
Colm Donelana21620d2019-10-11 13:09:49 +010063 throw armnn::RuntimeException(std::string("Cannot close stream socket: ") + strerror(errno));
FinnWilliamsArma0c78712019-09-16 12:06:47 +010064 }
Matteo Martincigh24e8f922019-09-19 11:57:46 +010065
66 memset(m_Socket, 0, sizeof(m_Socket));
Teresa Charlin9bab4962019-09-06 12:28:35 +010067}
68
Matteo Martincigh24e8f922019-09-19 11:57:46 +010069bool SocketProfilingConnection::WritePacket(const unsigned char* buffer, uint32_t length)
Teresa Charlin9bab4962019-09-06 12:28:35 +010070{
Matteo Martincigh24e8f922019-09-19 11:57:46 +010071 if (buffer == nullptr || length == 0)
FinnWilliamsArma0c78712019-09-16 12:06:47 +010072 {
73 return false;
74 }
Matteo Martincigh24e8f922019-09-19 11:57:46 +010075
76 return write(m_Socket[0].fd, buffer, length) != -1;
Teresa Charlin9bab4962019-09-06 12:28:35 +010077}
78
79Packet SocketProfilingConnection::ReadPacket(uint32_t timeout)
80{
Colm Donelana21620d2019-10-11 13:09:49 +010081 // Is there currently at least a headers worth of data waiting to be read?
82 int bytes_available;
83 ioctl(m_Socket[0].fd, FIONREAD, &bytes_available);
84 if (bytes_available >= 8)
FinnWilliamsArma0c78712019-09-16 12:06:47 +010085 {
Colm Donelana21620d2019-10-11 13:09:49 +010086 // Yes there is. Read it:
87 return ReceivePacket();
88 }
89 else
90 {
91 // Poll for data on the socket or until timeout occurs
92 int pollResult = poll(m_Socket, 1, static_cast<int>(timeout));
Matteo Martincigh24e8f922019-09-19 11:57:46 +010093
Colm Donelana21620d2019-10-11 13:09:49 +010094 switch (pollResult)
FinnWilliamsArma0c78712019-09-16 12:06:47 +010095 {
Colm Donelana21620d2019-10-11 13:09:49 +010096 case -1: // Error
97 throw armnn::RuntimeException(std::string("Read failure from socket: ") + strerror(errno));
98
99 case 0: // Timeout
100 throw TimeoutException("Timeout while reading from socket");
101
102 default: // Normal poll return but it could still contain an error signal
103
104 // Check if the socket reported an error
105 if (m_Socket[0].revents & (POLLNVAL | POLLERR | POLLHUP))
106 {
107 throw armnn::RuntimeException(std::string("Socket reported an error: ") + strerror(errno));
108 }
109
110 // Check if there is data to read
111 if (!(m_Socket[0].revents & (POLLIN)))
112 {
113 // This is a very odd case. The file descriptor was woken up but no data was written.
114 throw armnn::RuntimeException("Poll resulted in : no data to read");
115 }
116
117 return ReceivePacket();
FinnWilliamsArma0c78712019-09-16 12:06:47 +0100118 }
Colm Donelana21620d2019-10-11 13:09:49 +0100119 }
120}
FinnWilliamsArma0c78712019-09-16 12:06:47 +0100121
Colm Donelana21620d2019-10-11 13:09:49 +0100122Packet SocketProfilingConnection::ReceivePacket()
123{
124 char header[8] = {};
125 if (8 != recv(m_Socket[0].fd, &header, sizeof(header), 0))
126 {
127 // What do we do here if there's not a valid 8 byte header to read?
128 throw armnn::RuntimeException("The received packet did not contains a valid MIPE header");
129 }
130 // stream_metadata_identifier is the first 4 bytes
131 uint32_t metadataIdentifier = 0;
132 std::memcpy(&metadataIdentifier, header, sizeof(metadataIdentifier));
Matteo Martincigh24e8f922019-09-19 11:57:46 +0100133
Colm Donelana21620d2019-10-11 13:09:49 +0100134 // data_length is the next 4 bytes
135 uint32_t dataLength = 0;
136 std::memcpy(&dataLength, header + 4u, sizeof(dataLength));
Matteo Martincigh24e8f922019-09-19 11:57:46 +0100137
Matteo Martincigh67ef2a52019-10-10 13:29:02 +0100138 std::unique_ptr<unsigned char[]> packetData;
Colm Donelana21620d2019-10-11 13:09:49 +0100139 if (dataLength > 0)
140 {
Matteo Martincigh67ef2a52019-10-10 13:29:02 +0100141 packetData = std::make_unique<unsigned char[]>(dataLength);
Jim Flynne11ff892019-10-04 04:25:43 -0700142 ssize_t receivedLength = recv(m_Socket[0].fd, packetData.get(), dataLength, 0);
143 if (receivedLength < 0)
144 {
145 throw armnn::RuntimeException(std::string("Error occured on recv: ") + strerror(errno));
146 }
147 if (dataLength != static_cast<uint32_t>(receivedLength))
Matteo Martincigh24e8f922019-09-19 11:57:46 +0100148 {
149 // What do we do here if we can't read in a full packet?
Colm Donelana21620d2019-10-11 13:09:49 +0100150 throw armnn::RuntimeException("Invalid MIPE packet");
Matteo Martincigh24e8f922019-09-19 11:57:46 +0100151 }
FinnWilliamsArma0c78712019-09-16 12:06:47 +0100152 }
Colm Donelana21620d2019-10-11 13:09:49 +0100153
154 return Packet(metadataIdentifier, dataLength, packetData);
Teresa Charlin9bab4962019-09-06 12:28:35 +0100155}
156
157} // namespace profiling
158} // namespace armnn