blob: 1db80303138100923887e636d545851122bfe7d2 [file] [log] [blame]
Keith Davis3201eea2019-10-24 17:30:41 +01001//
2// Copyright © 2019 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "FileOnlyProfilingConnection.hpp"
7#include "PacketVersionResolver.hpp"
8
9#include <armnn/Exceptions.hpp>
10
11#include <boost/numeric/conversion/cast.hpp>
12#include <iostream>
13#include <thread>
14
15namespace armnn
16{
17
18namespace profiling
19{
20
21FileOnlyProfilingConnection::~FileOnlyProfilingConnection()
22{
23 Close();
24}
25
26bool FileOnlyProfilingConnection::IsOpen() const
27{
28 // This type of connection is always open.
29 return true;
30}
31
32void FileOnlyProfilingConnection::Close()
33{
34 // Dump any unread packets out of the queue.
35 for (unsigned int i = 0; i < m_PacketQueue.size(); i++)
36 {
37 m_PacketQueue.pop();
38 }
39}
40
41bool FileOnlyProfilingConnection::WaitForStreamMeta(const unsigned char* buffer, uint32_t length)
42{
Derek Lamberti1dd75b32019-12-10 21:23:23 +000043 boost::ignore_unused(length);
44
Keith Davis3201eea2019-10-24 17:30:41 +010045 // The first word, stream_metadata_identifer, should always be 0.
46 if (ToUint32(buffer, TargetEndianness::BeWire) != 0)
47 {
48 Fail("Protocol error. The stream_metadata_identifer was not 0.");
49 }
50
51 // Before we interpret the length we need to read the pipe_magic word to determine endianness.
52 if (ToUint32(buffer + 8, TargetEndianness::BeWire) == PIPE_MAGIC)
53 {
54 m_Endianness = TargetEndianness::BeWire;
55 }
56 else if (ToUint32(buffer + 8, TargetEndianness::LeWire) == PIPE_MAGIC)
57 {
58 m_Endianness = TargetEndianness::LeWire;
59 }
60 else
61 {
62 Fail("Protocol read error. Unable to read PIPE_MAGIC value.");
63 }
64 return true;
65}
66
67void FileOnlyProfilingConnection::SendConnectionAck()
68{
69 if (!m_QuietOp)
70 {
71 std::cout << "Sending connection acknowledgement." << std::endl;
72 }
73 std::unique_ptr<unsigned char[]> uniqueNullPtr = nullptr;
Colm Donelan4cace322019-11-20 14:59:12 +000074 {
75 std::lock_guard<std::mutex> lck(m_PacketAvailableMutex);
76 m_PacketQueue.push(Packet(0x10000, 0, uniqueNullPtr));
77 }
78 m_ConditionPacketAvailable.notify_one();
Keith Davis3201eea2019-10-24 17:30:41 +010079}
80
81bool FileOnlyProfilingConnection::SendCounterSelectionPacket()
82{
83 uint32_t uint16_t_size = sizeof(uint16_t);
84 uint32_t uint32_t_size = sizeof(uint32_t);
85
86 uint32_t offset = 0;
87 uint32_t bodySize = uint32_t_size + boost::numeric_cast<uint32_t>(m_IdList.size()) * uint16_t_size;
88
89 auto uniqueData = std::make_unique<unsigned char[]>(bodySize);
90 unsigned char* data = reinterpret_cast<unsigned char*>(uniqueData.get());
91
92 // Copy capturePeriod
93 WriteUint32(data, offset, m_Options.m_CapturePeriod);
94
95 // Copy m_IdList
96 offset += uint32_t_size;
97 for (const uint16_t& id : m_IdList)
98 {
99 WriteUint16(data, offset, id);
100 offset += uint16_t_size;
101 }
102
Colm Donelan4cace322019-11-20 14:59:12 +0000103 {
104 std::lock_guard<std::mutex> lck(m_PacketAvailableMutex);
105 m_PacketQueue.push(Packet(0x40000, bodySize, uniqueData));
106 }
107 m_ConditionPacketAvailable.notify_one();
Keith Davis3201eea2019-10-24 17:30:41 +0100108
109 return true;
110}
111
112bool FileOnlyProfilingConnection::WritePacket(const unsigned char* buffer, uint32_t length)
113{
114 BOOST_ASSERT(buffer);
115
116 // Read Header and determine case
117 uint32_t outgoingHeaderAsWords[2];
118 PackageActivity packageActivity = GetPackageActivity(buffer, outgoingHeaderAsWords);
119
120 switch (packageActivity)
121 {
122 case PackageActivity::StreamMetaData:
123 {
124 if (!WaitForStreamMeta(buffer, length))
125 {
126 return EXIT_FAILURE;
127 }
128
129 SendConnectionAck();
130 break;
131 }
132 case PackageActivity::CounterDirectory:
133 {
134 std::unique_ptr<unsigned char[]> uniqueCounterData = std::make_unique<unsigned char[]>(length - 8);
135
136 std::memcpy(uniqueCounterData.get(), buffer + 8, length - 8);
137
138 Packet directoryPacket(outgoingHeaderAsWords[0], length - 8, uniqueCounterData);
139
140 armnn::profiling::PacketVersionResolver packetVersionResolver;
141 DirectoryCaptureCommandHandler directoryCaptureCommandHandler(
142 0, 2, packetVersionResolver.ResolvePacketVersion(0, 2).GetEncodedValue());
143 directoryCaptureCommandHandler.operator()(directoryPacket);
144 const ICounterDirectory& counterDirectory = directoryCaptureCommandHandler.GetCounterDirectory();
145 for (auto& category : counterDirectory.GetCategories())
146 {
147 // Remember we need to translate the Uid's from our CounterDirectory instance to the parent one.
148 std::vector<uint16_t> translatedCounters;
149 for (auto const& copyUid : category->m_Counters)
150 {
151 translatedCounters.emplace_back(directoryCaptureCommandHandler.TranslateUIDCopyToOriginal(copyUid));
152 }
153 m_IdList.insert(std::end(m_IdList), std::begin(translatedCounters), std::end(translatedCounters));
154 }
155 SendCounterSelectionPacket();
156 break;
157 }
158 default:
159 {
160 break;
161 }
162 }
163 return true;
164}
165
166Packet FileOnlyProfilingConnection::ReadPacket(uint32_t timeout)
167{
Colm Donelan4cace322019-11-20 14:59:12 +0000168 std::unique_lock<std::mutex> lck(m_PacketAvailableMutex);
Finn Williams09ad6f92019-12-19 17:05:18 +0000169
170 // Here we are using m_PacketQueue.empty() as a predicate variable
171 // The conditional variable will wait until packetQueue is not empty or until a timeout
172 if(!m_ConditionPacketAvailable.wait_for(lck,
173 std::chrono::milliseconds(timeout),
174 [&]{return !m_PacketQueue.empty();}))
Keith Davis3201eea2019-10-24 17:30:41 +0100175 {
Finn Williams09ad6f92019-12-19 17:05:18 +0000176 throw armnn::TimeoutException("Thread has timed out as per requested time limit");
Keith Davis3201eea2019-10-24 17:30:41 +0100177 }
Finn Williams09ad6f92019-12-19 17:05:18 +0000178
Keith Davis3201eea2019-10-24 17:30:41 +0100179 Packet returnedPacket = std::move(m_PacketQueue.front());
180 m_PacketQueue.pop();
181 return returnedPacket;
182}
183
184PackageActivity FileOnlyProfilingConnection::GetPackageActivity(const unsigned char* buffer, uint32_t headerAsWords[2])
185{
186 headerAsWords[0] = ToUint32(buffer, m_Endianness);
187 headerAsWords[1] = ToUint32(buffer + 4, m_Endianness);
188 if (headerAsWords[0] == 0x20000) // Packet family = 0 Packet Id = 2
189 {
190 return PackageActivity::CounterDirectory;
191 }
192 else if (headerAsWords[0] == 0) // Packet family = 0 Packet Id = 0
193 {
194 return PackageActivity::StreamMetaData;
195 }
196 else
197 {
198 return PackageActivity::Unknown;
199 }
200}
201
202uint32_t FileOnlyProfilingConnection::ToUint32(const unsigned char* data, TargetEndianness endianness)
203{
204 // Extract the first 4 bytes starting at data and push them into a 32bit integer based on the
205 // specified endianness.
206 if (endianness == TargetEndianness::BeWire)
207 {
208 return static_cast<uint32_t>(data[0]) << 24 | static_cast<uint32_t>(data[1]) << 16 |
209 static_cast<uint32_t>(data[2]) << 8 | static_cast<uint32_t>(data[3]);
210 }
211 else
212 {
213 return static_cast<uint32_t>(data[3]) << 24 | static_cast<uint32_t>(data[2]) << 16 |
214 static_cast<uint32_t>(data[1]) << 8 | static_cast<uint32_t>(data[0]);
215 }
216}
217
218void FileOnlyProfilingConnection::Fail(const std::string& errorMessage)
219{
220 Close();
221 throw RuntimeException(errorMessage);
222}
223
224} // namespace profiling
225
226} // namespace armnn