• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright © 2019 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "DirectoryCaptureCommandHandler.hpp"
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 
ParseData(const arm::pipe::Packet & packet)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 
ReadCategoryRecords(const unsigned char * const data,uint32_t offset,std::vector<uint32_t> categoryOffsets)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 
ReadEventRecords(const unsigned char * data,uint32_t offset,std::vector<uint32_t> eventRecordsOffsets)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 
operator ()(const arm::pipe::Packet & packet)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 
GetCounterDirectory() const320 const ICounterDirectory& DirectoryCaptureCommandHandler::GetCounterDirectory() const
321 {
322     return m_CounterDirectory;
323 }
324 
GetStringNameFromBuffer(const unsigned char * const data,uint32_t offset)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