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