blob: 24f2cc7ede794bad8c83eb46d54ec49dada8519e [file] [log] [blame]
Keith Davis3201eea2019-10-24 17:30:41 +01001//
Jim Flynnbbfe6032020-07-20 16:57:44 +01002// Copyright © 2019 Arm Ltd and Contributors. All rights reserved.
Keith Davis3201eea2019-10-24 17:30:41 +01003// SPDX-License-Identifier: MIT
4//
5
6#include "FileOnlyProfilingConnection.hpp"
7#include "PacketVersionResolver.hpp"
8
9#include <armnn/Exceptions.hpp>
Finn Williamse09fc822020-04-29 13:17:30 +010010#include <common/include/Constants.hpp>
Jim Flynn01d02812020-04-29 21:12:13 +010011#include <common/include/ProfilingException.hpp>
Keith Davis3201eea2019-10-24 17:30:41 +010012
Jim Flynn4e755a52020-03-29 17:48:26 +010013#include <algorithm>
Keith Davis3201eea2019-10-24 17:30:41 +010014#include <boost/numeric/conversion/cast.hpp>
15#include <iostream>
16#include <thread>
17
18namespace armnn
19{
20
21namespace profiling
22{
23
Jim Flynn01d02812020-04-29 21:12:13 +010024std::vector<uint32_t> StreamMetaDataProcessor::GetHeadersAccepted()
25{
26 std::vector<uint32_t> headers;
27 headers.push_back(m_MetaDataPacketHeader);
28 return headers;
29}
30
Jim Flynnbbfe6032020-07-20 16:57:44 +010031void StreamMetaDataProcessor::HandlePacket(const arm::pipe::Packet& packet)
Jim Flynn01d02812020-04-29 21:12:13 +010032{
33 if (packet.GetHeader() != m_MetaDataPacketHeader)
34 {
Jim Flynnbbfe6032020-07-20 16:57:44 +010035 throw arm::pipe::ProfilingException("StreamMetaDataProcessor can only handle Stream Meta Data Packets");
Jim Flynn01d02812020-04-29 21:12:13 +010036 }
37 // determine the endianness of the protocol
38 TargetEndianness endianness;
Jim Flynnbbfe6032020-07-20 16:57:44 +010039 if (ToUint32(packet.GetData(),TargetEndianness::BeWire) == arm::pipe::PIPE_MAGIC)
Jim Flynn01d02812020-04-29 21:12:13 +010040 {
41 endianness = TargetEndianness::BeWire;
42 }
Jim Flynnbbfe6032020-07-20 16:57:44 +010043 else if (ToUint32(packet.GetData(), TargetEndianness::LeWire) == arm::pipe::PIPE_MAGIC)
Jim Flynn01d02812020-04-29 21:12:13 +010044 {
45 endianness = TargetEndianness::LeWire;
46 }
47 else
48 {
Jim Flynnbbfe6032020-07-20 16:57:44 +010049 throw arm::pipe::ProfilingException("Protocol read error. Unable to read the PIPE_MAGIC value.");
Jim Flynn01d02812020-04-29 21:12:13 +010050 }
51 m_FileOnlyProfilingConnection->SetEndianess(endianness);
52 // send back the acknowledgement
53 std::unique_ptr<unsigned char[]> uniqueNullPtr = nullptr;
Jim Flynnbbfe6032020-07-20 16:57:44 +010054 arm::pipe::Packet returnPacket(0x10000, 0, uniqueNullPtr);
Jim Flynn01d02812020-04-29 21:12:13 +010055 m_FileOnlyProfilingConnection->ReturnPacket(returnPacket);
56}
57
58uint32_t StreamMetaDataProcessor::ToUint32(const unsigned char* data, TargetEndianness endianness)
59{
60 // Extract the first 4 bytes starting at data and push them into a 32bit integer based on the
61 // specified endianness.
62 if (endianness == TargetEndianness::BeWire)
63 {
64 return static_cast<uint32_t>(data[0]) << 24 | static_cast<uint32_t>(data[1]) << 16 |
65 static_cast<uint32_t>(data[2]) << 8 | static_cast<uint32_t>(data[3]);
66 }
67 else
68 {
69 return static_cast<uint32_t>(data[3]) << 24 | static_cast<uint32_t>(data[2]) << 16 |
70 static_cast<uint32_t>(data[1]) << 8 | static_cast<uint32_t>(data[0]);
71 }
72}
73
Keith Davis3201eea2019-10-24 17:30:41 +010074FileOnlyProfilingConnection::~FileOnlyProfilingConnection()
75{
Jim Flynn01d02812020-04-29 21:12:13 +010076 try
77 {
78 Close();
79 }
80 catch (...)
81 {
82 // do nothing
83 }
Keith Davis3201eea2019-10-24 17:30:41 +010084}
85
86bool FileOnlyProfilingConnection::IsOpen() const
87{
88 // This type of connection is always open.
89 return true;
90}
91
92void FileOnlyProfilingConnection::Close()
93{
94 // Dump any unread packets out of the queue.
Jim Flynn4e755a52020-03-29 17:48:26 +010095 size_t initialSize = m_PacketQueue.size();
96 for (size_t i = 0; i < initialSize; ++i)
Keith Davis3201eea2019-10-24 17:30:41 +010097 {
98 m_PacketQueue.pop();
99 }
Jim Flynn4e755a52020-03-29 17:48:26 +0100100 // dispose of the processing thread
101 m_KeepRunning.store(false);
102 if (m_LocalHandlersThread.joinable())
103 {
104 // make sure the thread wakes up and sees it has to stop
105 m_ConditionPacketReadable.notify_one();
106 m_LocalHandlersThread.join();
107 }
Keith Davis3201eea2019-10-24 17:30:41 +0100108}
109
Keith Davis3201eea2019-10-24 17:30:41 +0100110bool FileOnlyProfilingConnection::WritePacket(const unsigned char* buffer, uint32_t length)
111{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100112 ARMNN_ASSERT(buffer);
Jim Flynnbbfe6032020-07-20 16:57:44 +0100113 arm::pipe::Packet packet = ReceivePacket(buffer, length);
Jim Flynn4e755a52020-03-29 17:48:26 +0100114 ForwardPacketToHandlers(packet);
Keith Davis3201eea2019-10-24 17:30:41 +0100115 return true;
116}
117
Jim Flynnbbfe6032020-07-20 16:57:44 +0100118void FileOnlyProfilingConnection::ReturnPacket(arm::pipe::Packet& packet)
Jim Flynn01d02812020-04-29 21:12:13 +0100119{
120 {
121 std::lock_guard<std::mutex> lck(m_PacketAvailableMutex);
122 m_PacketQueue.push(std::move(packet));
123 }
124 m_ConditionPacketAvailable.notify_one();
125}
126
Jim Flynnbbfe6032020-07-20 16:57:44 +0100127arm::pipe::Packet FileOnlyProfilingConnection::ReadPacket(uint32_t timeout)
Keith Davis3201eea2019-10-24 17:30:41 +0100128{
Colm Donelan4cace322019-11-20 14:59:12 +0000129 std::unique_lock<std::mutex> lck(m_PacketAvailableMutex);
Finn Williams09ad6f92019-12-19 17:05:18 +0000130
131 // Here we are using m_PacketQueue.empty() as a predicate variable
132 // The conditional variable will wait until packetQueue is not empty or until a timeout
Jim Flynn01d02812020-04-29 21:12:13 +0100133 if (!m_ConditionPacketAvailable.wait_for(lck,
134 std::chrono::milliseconds(timeout),
135 [&]{return !m_PacketQueue.empty();}))
Keith Davis3201eea2019-10-24 17:30:41 +0100136 {
Jim Flynnbbfe6032020-07-20 16:57:44 +0100137 arm::pipe::Packet empty;
Jim Flynn01d02812020-04-29 21:12:13 +0100138 return empty;
Keith Davis3201eea2019-10-24 17:30:41 +0100139 }
Finn Williams09ad6f92019-12-19 17:05:18 +0000140
Jim Flynnbbfe6032020-07-20 16:57:44 +0100141 arm::pipe::Packet returnedPacket = std::move(m_PacketQueue.front());
Keith Davis3201eea2019-10-24 17:30:41 +0100142 m_PacketQueue.pop();
143 return returnedPacket;
144}
145
Keith Davis3201eea2019-10-24 17:30:41 +0100146void FileOnlyProfilingConnection::Fail(const std::string& errorMessage)
147{
148 Close();
149 throw RuntimeException(errorMessage);
150}
151
Jim Flynn4e755a52020-03-29 17:48:26 +0100152/// Adds a local packet handler to the FileOnlyProfilingConnection. Invoking this will start
153/// a processing thread that will ensure that processing of packets will happen on a separate
154/// thread from the profiling services send thread and will therefore protect against the
155/// profiling message buffer becoming exhausted because packet handling slows the dispatch.
156void FileOnlyProfilingConnection::AddLocalPacketHandler(ILocalPacketHandlerSharedPtr localPacketHandler)
157{
158 m_PacketHandlers.push_back(std::move(localPacketHandler));
159 ILocalPacketHandlerSharedPtr localCopy = m_PacketHandlers.back();
160 localCopy->SetConnection(this);
161 if (localCopy->GetHeadersAccepted().empty())
162 {
163 //this is a universal handler
164 m_UniversalHandlers.push_back(localCopy);
165 }
166 else
167 {
168 for (uint32_t header : localCopy->GetHeadersAccepted())
169 {
170 auto iter = m_IndexedHandlers.find(header);
171 if (iter == m_IndexedHandlers.end())
172 {
173 std::vector<ILocalPacketHandlerSharedPtr> handlers;
174 handlers.push_back(localCopy);
175 m_IndexedHandlers.emplace(std::make_pair(header, handlers));
176 }
177 else
178 {
179 iter->second.push_back(localCopy);
180 }
181 }
182 }
183}
184
185void FileOnlyProfilingConnection::StartProcessingThread()
186{
187 // check if the thread has already started
188 if (m_IsRunning.load())
189 {
190 return;
191 }
192 // make sure if there was one running before it is joined
193 if (m_LocalHandlersThread.joinable())
194 {
195 m_LocalHandlersThread.join();
196 }
197 m_IsRunning.store(true);
198 m_KeepRunning.store(true);
199 m_LocalHandlersThread = std::thread(&FileOnlyProfilingConnection::ServiceLocalHandlers, this);
200}
201
Jim Flynnbbfe6032020-07-20 16:57:44 +0100202void FileOnlyProfilingConnection::ForwardPacketToHandlers(arm::pipe::Packet& packet)
Jim Flynn4e755a52020-03-29 17:48:26 +0100203{
204 if (m_PacketHandlers.empty())
205 {
206 return;
207 }
Jim Flynn01d02812020-04-29 21:12:13 +0100208 if (!m_KeepRunning.load())
Jim Flynn4e755a52020-03-29 17:48:26 +0100209 {
210 return;
211 }
212 {
213 std::unique_lock<std::mutex> readableListLock(m_ReadableMutex);
Jim Flynn01d02812020-04-29 21:12:13 +0100214 if (!m_KeepRunning.load())
Jim Flynn4e755a52020-03-29 17:48:26 +0100215 {
216 return;
217 }
218 m_ReadableList.push(std::move(packet));
219 }
220 m_ConditionPacketReadable.notify_one();
221}
222
223void FileOnlyProfilingConnection::ServiceLocalHandlers()
224{
225 do
226 {
Jim Flynnbbfe6032020-07-20 16:57:44 +0100227 arm::pipe::Packet returnedPacket;
Jim Flynn4e755a52020-03-29 17:48:26 +0100228 bool readPacket = false;
229 { // only lock while we are taking the packet off the incoming list
230 std::unique_lock<std::mutex> lck(m_ReadableMutex);
231 if (m_Timeout < 0)
232 {
233 m_ConditionPacketReadable.wait(lck,
234 [&] { return !m_ReadableList.empty(); });
235 }
236 else
237 {
238 m_ConditionPacketReadable.wait_for(lck,
239 std::chrono::milliseconds(std::max(m_Timeout, 1000)),
240 [&] { return !m_ReadableList.empty(); });
241 }
242 if (m_KeepRunning.load())
243 {
244 if (!m_ReadableList.empty())
245 {
246 returnedPacket = std::move(m_ReadableList.front());
247 m_ReadableList.pop();
248 readPacket = true;
249 }
250 }
251 else
252 {
253 ClearReadableList();
254 }
255 }
256 if (m_KeepRunning.load() && readPacket)
257 {
258 DispatchPacketToHandlers(returnedPacket);
259 }
260 } while (m_KeepRunning.load());
261 // make sure the readable list is cleared
262 ClearReadableList();
263 m_IsRunning.store(false);
264}
265
266void FileOnlyProfilingConnection::ClearReadableList()
267{
268 // make sure the incoming packet queue gets emptied
269 size_t initialSize = m_ReadableList.size();
270 for (size_t i = 0; i < initialSize; ++i)
271 {
272 m_ReadableList.pop();
273 }
274}
275
Jim Flynnbbfe6032020-07-20 16:57:44 +0100276void FileOnlyProfilingConnection::DispatchPacketToHandlers(const arm::pipe::Packet& packet)
Jim Flynn4e755a52020-03-29 17:48:26 +0100277{
278 for (auto& delegate : m_UniversalHandlers)
279 {
280 delegate->HandlePacket(packet);
281 }
282 auto iter = m_IndexedHandlers.find(packet.GetHeader());
283 if (iter != m_IndexedHandlers.end())
284 {
Jim Flynn01d02812020-04-29 21:12:13 +0100285 for (auto& delegate : iter->second)
Jim Flynn4e755a52020-03-29 17:48:26 +0100286 {
Jim Flynn01d02812020-04-29 21:12:13 +0100287 try
288 {
289 delegate->HandlePacket(packet);
290 }
Jim Flynnbbfe6032020-07-20 16:57:44 +0100291 catch (const arm::pipe::ProfilingException& ex)
Jim Flynn01d02812020-04-29 21:12:13 +0100292 {
293 Fail(ex.what());
294 }
295 catch (const std::exception& ex)
296 {
297 Fail(ex.what());
298 }
299 catch (...)
300 {
301 Fail("handler failed");
302 }
Jim Flynn4e755a52020-03-29 17:48:26 +0100303 }
304 }
305}
306
Keith Davis3201eea2019-10-24 17:30:41 +0100307} // namespace profiling
308
309} // namespace armnn