ArmNN  NotReleased
DirectoryCaptureCommandHandler.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2019 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
6 
7 #include <armnn/BackendId.hpp>
8 #include "ProfilingUtils.hpp"
9 
10 #include <atomic>
11 #include <iostream>
12 
13 namespace armnn
14 {
15 
16 namespace profiling
17 {
18 
19 // Utils
20 uint32_t uint16_t_size = sizeof(uint16_t);
21 uint32_t uint32_t_size = sizeof(uint32_t);
22 
23 void DirectoryCaptureCommandHandler::ParseData(const armnn::profiling::Packet& packet)
24 {
25  uint16_t categoryRecordCount;
26  uint16_t counterSetRecordCount;
27  uint16_t deviceRecordCount;
28 
29  uint32_t offset = 0;
30 
31  if (packet.GetLength() < 8)
32  {
33  std::cout << "Counter directory packet received." << std::endl;
34  return;
35  }
36 
37  const unsigned char* data = packet.GetData();
38  // Body header word 0:
39  // 0:15 [16] reserved: all zeros
40  offset += uint16_t_size;
41  // 16:31 [16] device_records_count: number of entries in the device_records_pointer_table
42  deviceRecordCount = profiling::ReadUint16(data, offset);
43  offset += uint16_t_size;
44 
45  // Body header word 1:
46  // 0:31 [32] device_records_pointer_table_offset: offset to the device_records_pointer_table
47  // The offset is always zero here, as the device record pointer table field is always the first item in the pool
48  offset += uint32_t_size;
49 
50  // Body header word 2:
51  // 0:15 [16] reserved: all zeros
52  offset += uint16_t_size;
53  // 16:31 [16] counter_set_count: number of entries in the counter_set_pointer_table
54  counterSetRecordCount = profiling::ReadUint16(data, offset);
55  offset += uint16_t_size;
56 
57  // Body header word 3:
58  // 0:31 [32] counter_set_pointer_table_offset: offset to the counter_set_pointer_table
59  // counterPointerTableSetOffset = profiling::ReadUint32(data, offset);
60  offset += uint32_t_size;
61 
62  // Body header word 4:
63  // 0:15 [16] reserved: all zeros
64  offset += uint16_t_size;
65  // 16:31 [16] categories_count: number of entries in the categories_pointer_table
66  categoryRecordCount = profiling::ReadUint16(data, offset);
67  offset += uint16_t_size;
68 
69  // Body header word 5:
70  // 0:31 [32] categories_pointer_table_offset: offset to the categories_pointer_table
71  // categoriesPointerTableOffset = profiling::ReadUint32(data, offset);
72  offset += uint32_t_size;
73 
74  std::vector<uint32_t> deviceRecordOffsets(deviceRecordCount);
75  std::vector<uint32_t> counterSetOffsets(counterSetRecordCount);
76  std::vector<uint32_t> categoryOffsets(categoryRecordCount);
77 
78  for (uint32_t i = 0; i < deviceRecordCount; ++i)
79  {
80  deviceRecordOffsets[i] = profiling::ReadUint32(data, offset);
81  offset += uint32_t_size;
82  }
83 
84  for (uint32_t i = 0; i < counterSetRecordCount; ++i)
85  {
86  counterSetOffsets[i] = profiling::ReadUint32(data, offset);
87  offset += uint32_t_size;
88  }
89 
90  for (uint32_t i = 0; i < categoryRecordCount; ++i)
91  {
92  categoryOffsets[i] = profiling::ReadUint32(data, offset);
93  offset += uint32_t_size;
94  }
95 
96  for (uint32_t deviceIndex = 0; deviceIndex < deviceRecordCount; ++deviceIndex)
97  {
98  uint32_t deviceRecordOffset = offset + deviceRecordOffsets[deviceIndex];
99  // Device record word 0:
100  // 0:15 [16] cores: the number of individual streams of counters for one or more cores of some device
101  uint16_t deviceCores = profiling::ReadUint16(data, deviceRecordOffset);
102  // 16:31 [16] deviceUid: the unique identifier for the device
103  deviceRecordOffset += uint16_t_size;
104  uint16_t deviceUid = profiling::ReadUint16(data, deviceRecordOffset);
105  deviceRecordOffset += uint16_t_size;
106 
107  // Device record word 1:
108  // Offset from the beginning of the device record pool to the name field.
109  uint32_t nameOffset = profiling::ReadUint32(data, deviceRecordOffset);
110 
111  deviceRecordOffset += uint32_t_size;
112  deviceRecordOffset += uint32_t_size;
113  deviceRecordOffset += nameOffset;
114 
115  const std::string& deviceName = GetStringNameFromBuffer(data, deviceRecordOffset);
116  const Device* registeredDevice = m_CounterDirectory.RegisterDevice(deviceName, deviceCores);
117  m_UidTranslation[registeredDevice->m_Uid] = deviceUid;
118  }
119 
120  for (uint32_t counterSetIndex = 0; counterSetIndex < counterSetRecordCount; ++counterSetIndex)
121  {
122  uint32_t counterSetOffset = offset + counterSetOffsets[counterSetIndex];
123 
124  // Counter set record word 0:
125  // 0:15 [16] count: the number of counters which can be active in this set at any one time
126  uint16_t counterSetCount = profiling::ReadUint16(data, counterSetOffset);
127  counterSetOffset += uint16_t_size;
128 
129  // 16:31 [16] deviceUid: the unique identifier for the counter_set
130  uint16_t counterSetUid = profiling::ReadUint16(data, counterSetOffset);
131  counterSetOffset += uint16_t_size;
132 
133  // Counter set record word 1:
134  // 0:31 [32] name_offset: offset from the beginning of the counter set pool to the name field
135  // The offset is always zero here, as the name field is always the first (and only) item in the pool
136  counterSetOffset += uint32_t_size;
137  counterSetOffset += uint32_t_size;
138 
139  auto counterSet =
140  m_CounterDirectory.RegisterCounterSet(GetStringNameFromBuffer(data, counterSetOffset), counterSetCount);
141  m_UidTranslation[counterSet->m_Uid] = counterSetUid;
142  }
143  ReadCategoryRecords(data, offset, categoryOffsets);
144 }
145 
146 void DirectoryCaptureCommandHandler::ReadCategoryRecords(const unsigned char* const data,
147  uint32_t offset,
148  std::vector<uint32_t> categoryOffsets)
149 {
150  uint32_t categoryRecordCount = static_cast<uint32_t>(categoryOffsets.size());
151 
152  for (uint32_t categoryIndex = 0; categoryIndex < categoryRecordCount; ++categoryIndex)
153  {
154  uint32_t categoryRecordOffset = offset + categoryOffsets[categoryIndex];
155 
156  // Category record word 0:
157  // 0:15 The deviceUid of a counter_set the category is associated with.
158  // Set to zero if the category is NOT associated with a counter set.
159  uint16_t counterSetUid = profiling::ReadUint16(data, categoryRecordOffset);
160  categoryRecordOffset += uint16_t_size;
161 
162  // 16:31 The deviceUid of a device element which identifies some hardware device that the category belongs to.
163  // Set to zero if the category is NOT associated with a device
164  uint16_t deviceUid = profiling::ReadUint16(data, categoryRecordOffset);
165 
166  categoryRecordOffset += uint16_t_size;
167 
168  // Category record word 1:
169  // 0:15 Reserved, value 0x0000.
170  categoryRecordOffset += uint16_t_size;
171  // 16:31 Number of events belonging to this category.
172  uint32_t eventCount = profiling::ReadUint16(data, categoryRecordOffset);
173  categoryRecordOffset += uint16_t_size;
174 
175  // Category record word 2
176  // 0:31 Offset from the beginning of the category data pool to the event_pointer_table
177  uint32_t eventPointerTableOffset = profiling::ReadUint32(data, categoryRecordOffset);
178  categoryRecordOffset += uint32_t_size;
179 
180  // Category record word 3
181  // 0:31 Offset from the beginning of the category data pool to the name field.
182  uint32_t nameOffset = profiling::ReadUint32(data, categoryRecordOffset);
183  categoryRecordOffset += uint32_t_size;
184 
185  std::vector<uint32_t> eventRecordsOffsets(eventCount);
186 
187  eventPointerTableOffset += categoryRecordOffset;
188 
189  for (uint32_t eventIndex = 0; eventIndex < eventCount; ++eventIndex)
190  {
191  eventRecordsOffsets[eventIndex] =
192  profiling::ReadUint32(data, eventPointerTableOffset + uint32_t_size * eventIndex);
193  }
194 
195  const std::vector<CounterDirectoryEventRecord>& eventRecords =
196  ReadEventRecords(data, categoryRecordOffset, eventRecordsOffsets);
197  categoryRecordOffset += uint32_t_size;
198 
199  const Category* category = m_CounterDirectory.RegisterCategory(
200  GetStringNameFromBuffer(data, categoryRecordOffset + nameOffset), deviceUid, counterSetUid);
201  for (auto& counter : eventRecords)
202  {
203  const Counter* registeredCounter = m_CounterDirectory.RegisterCounter(armnn::profiling::BACKEND_ID,
204  counter.m_CounterUid,
205  category->m_Name,
206  counter.m_CounterClass,
207  counter.m_CounterInterpolation,
208  counter.m_CounterMultiplier,
209  counter.m_CounterName,
210  counter.m_CounterDescription,
211  counter.m_CounterUnits);
212  m_UidTranslation[registeredCounter->m_Uid] = counter.m_CounterUid;
213  }
214  }
215 }
216 
217 std::vector<CounterDirectoryEventRecord> DirectoryCaptureCommandHandler::ReadEventRecords(
218  const unsigned char* data, uint32_t offset, std::vector<uint32_t> eventRecordsOffsets)
219 {
220  uint32_t eventCount = static_cast<uint32_t>(eventRecordsOffsets.size());
221 
222  std::vector<CounterDirectoryEventRecord> eventRecords(eventCount);
223  for (unsigned long i = 0; i < eventCount; ++i)
224  {
225  uint32_t eventRecordOffset = eventRecordsOffsets[i] + offset;
226 
227  // Event record word 0:
228  // 0:15 [16] count_uid: unique ID for the counter. Must be unique across all counters in all categories
229  eventRecords[i].m_CounterUid = profiling::ReadUint16(data, eventRecordOffset);
230  eventRecordOffset += uint16_t_size;
231  // 16:31 [16] max_counter_uid: if the device this event is associated with has more than one core and there
232  // is one of these counters per core this value will be set to
233  // (counter_uid + cores (from device_record)) - 1.
234  // If there is only a single core then this value will be the same as
235  // the counter_uid value
236  eventRecords[i].m_MaxCounterUid = profiling::ReadUint16(data, eventRecordOffset);
237  eventRecordOffset += uint16_t_size;
238 
239  // Event record word 1:
240  // 0:15 [16] counter_set: UID of the counter_set this event is associated with. Set to zero if the event
241  // is NOT associated with a counter_set
242  eventRecords[i].m_DeviceUid = profiling::ReadUint16(data, eventRecordOffset);
243  eventRecordOffset += uint16_t_size;
244 
245  // 16:31 [16] device: UID of the device this event is associated with. Set to zero if the event is NOT
246  // associated with a device
247  eventRecords[i].m_CounterSetUid = profiling::ReadUint16(data, eventRecordOffset);
248  eventRecordOffset += uint16_t_size;
249 
250  // Event record word 2:
251  // 0:15 [16] interpolation: type describing how to interpolate each data point in a stream of data points
252  eventRecords[i].m_CounterClass = profiling::ReadUint16(data, eventRecordOffset);
253  eventRecordOffset += uint16_t_size;
254 
255  // 16:31 [16] class: type describing how to treat each data point in a stream of data points
256  eventRecords[i].m_CounterInterpolation = profiling::ReadUint16(data, eventRecordOffset);
257  eventRecordOffset += uint16_t_size;
258 
259  // Event record word 3-4:
260  // 0:63 [64] multiplier: internal data stream is represented as integer values, this allows scaling of
261  // those values as if they are fixed point numbers. Zero is not a valid value
262  uint32_t multiplier[2] = { 0u, 0u };
263 
264  multiplier[0] = profiling::ReadUint32(data, eventRecordOffset);
265  eventRecordOffset += uint32_t_size;
266  multiplier[1] = profiling::ReadUint32(data, eventRecordOffset);
267  eventRecordOffset += uint32_t_size;
268 
269  std::memcpy(&eventRecords[i].m_CounterMultiplier, &multiplier, sizeof(multiplier));
270 
271  // Event record word 5:
272  // 0:31 [32] name_eventRecordOffset: eventRecordOffset from the
273  // beginning of the event record pool to the name field
274  // The eventRecordOffset is always zero here, as the name field is always the first item in the pool
275  eventRecordOffset += uint32_t_size;
276 
277  // Event record word 6:
278  // 0:31 [32] description_eventRecordOffset: eventRecordOffset from the
279  // beginning of the event record pool to the description field
280  // The size of the name buffer in bytes
281  uint32_t descriptionOffset = profiling::ReadUint32(data, eventRecordOffset);
282  eventRecordOffset += uint32_t_size;
283 
284  // Event record word 7:
285  // 0:31 [32] units_eventRecordOffset: (optional) eventRecordOffset from the
286  // beginning of the event record pool to the units field.
287  // An eventRecordOffset value of zero indicates this field is not provided
288  uint32_t unitsOffset = profiling::ReadUint32(data, eventRecordOffset);
289  eventRecordOffset += uint32_t_size;
290  eventRecordOffset += uint32_t_size;
291 
292  eventRecords[i].m_CounterName = GetStringNameFromBuffer(data, eventRecordOffset);
293 
294  eventRecords[i].m_CounterDescription = GetStringNameFromBuffer(data, eventRecordOffset + descriptionOffset);
295 
296  eventRecords[i].m_CounterUnits = GetStringNameFromBuffer(data, eventRecordOffset + unitsOffset);
297  }
298 
299  return eventRecords;
300 }
301 
303 {
304  if (!m_QuietOperation) // Are we supposed to print to stdout?
305  {
306  std::cout << "Counter directory packet received." << std::endl;
307  }
308 
309  // The ArmNN counter directory is static per ArmNN instance. Ensure we don't parse it a second time.
310  if (!ParsedCounterDirectory())
311  {
312  ParseData(packet);
313  m_AlreadyParsed = true;
314  }
315 
316  if (!m_QuietOperation)
317  {
318  armnn::profiling::PrintCounterDirectory(m_CounterDirectory);
319  }
320 }
321 
323 {
324  return m_CounterDirectory;
325 }
326 
327 std::string DirectoryCaptureCommandHandler::GetStringNameFromBuffer(const unsigned char* const data, uint32_t offset)
328 {
329  std::string deviceName;
330  uint8_t nextChar = profiling::ReadUint8(data, offset);
331 
332  while (isprint(nextChar))
333  {
334  deviceName += static_cast<char>(nextChar);
335  offset++;
336  nextChar = profiling::ReadUint8(data, offset);
337  }
338 
339  return deviceName;
340 }
341 
342 } // namespace profiling
343 
344 } // namespace armnn
void PrintCounterDirectory(ICounterDirectory &counterDirectory)
const Category * RegisterCategory(const std::string &categoryName, const Optional< uint16_t > &deviceUid=EmptyOptional(), const Optional< uint16_t > &counterSetUid=EmptyOptional()) override
uint16_t ReadUint16(const IPacketBufferPtr &packetBuffer, unsigned int offset)
void operator()(const armnn::profiling::Packet &packet) override
const Device * RegisterDevice(const std::string &deviceName, uint16_t cores=0, const Optional< std::string > &parentCategoryName=EmptyOptional()) override
const Counter * RegisterCounter(const BackendId &backendId, const uint16_t uid, const std::string &parentCategoryName, uint16_t counterClass, uint16_t interpolation, double multiplier, const std::string &name, const std::string &description, const Optional< std::string > &units=EmptyOptional(), const Optional< uint16_t > &numberOfCores=EmptyOptional(), const Optional< uint16_t > &deviceUid=EmptyOptional(), const Optional< uint16_t > &counterSetUid=EmptyOptional()) override
uint32_t GetLength() const
Definition: Packet.hpp:74
uint32_t ReadUint32(const IPacketBufferPtr &packetBuffer, unsigned int offset)
uint8_t ReadUint8(const IPacketBufferPtr &packetBuffer, unsigned int offset)
const unsigned char * GetData() const
Definition: Packet.hpp:75
const CounterSet * RegisterCounterSet(const std::string &counterSetName, uint16_t count=0, const Optional< std::string > &parentCategoryName=EmptyOptional()) override