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