1 //
2 // Copyright © 2019, 2022-2023 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include "ProfilingService.hpp"
7
8 #include <common/include/Logging.hpp>
9 #include <common/include/NumericCast.hpp>
10 #include <common/include/ProfilingGuid.hpp>
11 #include <common/include/SocketConnectionException.hpp>
12
13 #if defined(ARMNN_BUILD_BARE_METAL) || defined(ARMNN_EXECUTE_NETWORK_STATIC)
14 #include <common/include/IgnoreUnused.hpp>
15 #endif
16
17
18 #include <fmt/format.h>
19
20 namespace arm
21 {
22
23 namespace pipe
24 {
25
ResetExternalProfilingOptions(const arm::pipe::ProfilingOptions & options,bool resetProfilingService)26 void ProfilingService::ResetExternalProfilingOptions(const arm::pipe::ProfilingOptions& options,
27 bool resetProfilingService)
28 {
29 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
30 // Update the profiling options
31 m_Options = options;
32 m_TimelineReporting = options.m_TimelineEnabled;
33 m_ConnectionAcknowledgedCommandHandler.setTimelineEnabled(options.m_TimelineEnabled);
34
35 // Check if the profiling service needs to be reset
36 if (resetProfilingService)
37 {
38 // Reset the profiling service
39 Reset();
40 }
41 #else
42 IgnoreUnused(options);
43 IgnoreUnused(resetProfilingService);
44 #endif // ARMNN_BUILD_BARE_METAL || ARMNN_EXECUTE_NETWORK_STATIC
45 }
46
IsProfilingEnabled() const47 bool ProfilingService::IsProfilingEnabled() const
48 {
49 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
50 return m_Options.m_EnableProfiling;
51 #else
52 return false;
53 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
54 }
55
ConfigureProfilingService(const ProfilingOptions & options,bool resetProfilingService)56 ProfilingState ProfilingService::ConfigureProfilingService(
57 const ProfilingOptions& options,
58 bool resetProfilingService)
59 {
60 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
61 ResetExternalProfilingOptions(options, resetProfilingService);
62 ProfilingState currentState = m_StateMachine.GetCurrentState();
63 if (options.m_EnableProfiling)
64 {
65 switch (currentState)
66 {
67 case ProfilingState::Uninitialised:
68 Update(); // should transition to NotConnected
69 Update(); // will either stay in NotConnected because there is no server
70 // or will enter WaitingForAck.
71 currentState = m_StateMachine.GetCurrentState();
72 if (currentState == ProfilingState::WaitingForAck)
73 {
74 Update(); // poke it again to send out the metadata packet
75 }
76 currentState = m_StateMachine.GetCurrentState();
77 return currentState;
78 case ProfilingState::NotConnected:
79 Update(); // will either stay in NotConnected because there is no server
80 // or will enter WaitingForAck
81 currentState = m_StateMachine.GetCurrentState();
82 if (currentState == ProfilingState::WaitingForAck)
83 {
84 Update(); // poke it again to send out the metadata packet
85 }
86 currentState = m_StateMachine.GetCurrentState();
87 return currentState;
88 default:
89 return currentState;
90 }
91 }
92 else
93 {
94 // Make sure profiling is shutdown
95 switch (currentState)
96 {
97 case ProfilingState::Uninitialised:
98 case ProfilingState::NotConnected:
99 return currentState;
100 default:
101 Stop();
102 return m_StateMachine.GetCurrentState();
103 }
104 }
105 #else
106 IgnoreUnused(options);
107 IgnoreUnused(resetProfilingService);
108 return ProfilingState::Uninitialised;
109 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
110 }
111
Update()112 void ProfilingService::Update()
113 {
114 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
115 if (!m_Options.m_EnableProfiling)
116 {
117 // Don't run if profiling is disabled
118 return;
119 }
120
121 ProfilingState currentState = m_StateMachine.GetCurrentState();
122 switch (currentState)
123 {
124 case ProfilingState::Uninitialised:
125
126 // Initialize the profiling service
127 Initialize();
128
129 // Move to the next state
130 m_StateMachine.TransitionToState(ProfilingState::NotConnected);
131 break;
132 case ProfilingState::NotConnected:
133 // Stop the command thread (if running)
134 m_CommandHandler.Stop();
135
136 // Stop the send thread (if running)
137 m_SendThread.Stop(false);
138
139 // Stop the periodic counter capture thread (if running)
140 m_PeriodicCounterCapture.Stop();
141
142 // Reset any existing profiling connection
143 m_ProfilingConnection.reset();
144
145 try
146 {
147 // Setup the profiling connection
148 ARM_PIPE_ASSERT(m_ProfilingConnectionFactory);
149 m_ProfilingConnection = m_ProfilingConnectionFactory->GetProfilingConnection(m_Options);
150 }
151 catch (const arm::pipe::ProfilingException& e)
152 {
153 ARM_PIPE_LOG(warning) << "An error has occurred when creating the profiling connection: "
154 << e.what();
155 }
156 catch (const arm::pipe::SocketConnectionException& e)
157 {
158 ARM_PIPE_LOG(warning) << "An error has occurred when creating the profiling connection ["
159 << e.what() << "] on socket [" << e.GetSocketFd() << "].";
160 }
161
162 // Move to the next state
163 m_StateMachine.TransitionToState(m_ProfilingConnection
164 ? ProfilingState::WaitingForAck // Profiling connection obtained, wait for ack
165 : ProfilingState::NotConnected); // Profiling connection failed, stay in the
166 // "NotConnected" state
167 break;
168 case ProfilingState::WaitingForAck:
169 ARM_PIPE_ASSERT(m_ProfilingConnection);
170
171 // Start the command thread
172 m_CommandHandler.Start(*m_ProfilingConnection);
173
174 // Start the send thread, while in "WaitingForAck" state it'll send out a "Stream MetaData" packet waiting for
175 // a valid "Connection Acknowledged" packet confirming the connection
176 m_SendThread.Start(*m_ProfilingConnection);
177
178 // The connection acknowledged command handler will automatically transition the state to "Active" once a
179 // valid "Connection Acknowledged" packet has been received
180
181 break;
182 case ProfilingState::Active:
183
184 // The period counter capture thread is started by the Periodic Counter Selection command handler upon
185 // request by an external profiling service
186
187 break;
188 default:
189 throw arm::pipe::ProfilingException(fmt::format("Unknown profiling service state: {}",
190 static_cast<int>(currentState)));
191 }
192 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
193 }
194
Disconnect()195 void ProfilingService::Disconnect()
196 {
197 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
198 ProfilingState currentState = m_StateMachine.GetCurrentState();
199 switch (currentState)
200 {
201 case ProfilingState::Uninitialised:
202 case ProfilingState::NotConnected:
203 case ProfilingState::WaitingForAck:
204 return; // NOP
205 case ProfilingState::Active:
206 // Stop the command thread (if running)
207 Stop();
208
209 break;
210 default:
211 throw arm::pipe::ProfilingException(fmt::format("Unknown profiling service state: {}",
212 static_cast<int>(currentState)));
213 }
214 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
215 }
216
217 // Store a profiling context returned from a backend that support profiling, and register its counters
AddBackendProfilingContext(const std::string & backendId,std::shared_ptr<IBackendProfilingContext> profilingContext)218 void ProfilingService::AddBackendProfilingContext(
219 const std::string& backendId,
220 std::shared_ptr<IBackendProfilingContext> profilingContext)
221 {
222 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
223 ARM_PIPE_ASSERT(profilingContext != nullptr);
224 // Register the backend counters
225 m_MaxGlobalCounterId = profilingContext->RegisterCounters(m_MaxGlobalCounterId);
226 m_BackendProfilingContexts.emplace(backendId, std::move(profilingContext));
227 #else
228 IgnoreUnused(backendId);
229 IgnoreUnused(profilingContext);
230 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
231 }
GetCounterDirectory() const232 const ICounterDirectory& ProfilingService::GetCounterDirectory() const
233 {
234 return m_CounterDirectory;
235 }
236
GetCounterRegistry()237 ICounterRegistry& ProfilingService::GetCounterRegistry()
238 {
239 return m_CounterDirectory;
240 }
241
GetCurrentState() const242 ProfilingState ProfilingService::GetCurrentState() const
243 {
244 return m_StateMachine.GetCurrentState();
245 }
246
GetCounterCount() const247 uint16_t ProfilingService::GetCounterCount() const
248 {
249 return m_CounterDirectory.GetCounterCount();
250 }
251
IsCounterRegistered(uint16_t counterUid) const252 bool ProfilingService::IsCounterRegistered(uint16_t counterUid) const
253 {
254 return m_CounterDirectory.IsCounterRegistered(counterUid);
255 }
256
GetAbsoluteCounterValue(uint16_t counterUid) const257 uint32_t ProfilingService::GetAbsoluteCounterValue(uint16_t counterUid) const
258 {
259 CheckCounterUid(counterUid);
260 std::atomic<uint32_t>* counterValuePtr = m_CounterIndex.at(counterUid);
261 ARM_PIPE_ASSERT(counterValuePtr);
262 return counterValuePtr->load(std::memory_order::memory_order_relaxed);
263 }
264
GetDeltaCounterValue(uint16_t counterUid)265 uint32_t ProfilingService::GetDeltaCounterValue(uint16_t counterUid)
266 {
267 CheckCounterUid(counterUid);
268 std::atomic<uint32_t>* counterValuePtr = m_CounterIndex.at(counterUid);
269 ARM_PIPE_ASSERT(counterValuePtr);
270 const uint32_t counterValue = counterValuePtr->load(std::memory_order::memory_order_relaxed);
271 SubtractCounterValue(counterUid, counterValue);
272 return counterValue;
273 }
274
GetCounterMappings() const275 const ICounterMappings& ProfilingService::GetCounterMappings() const
276 {
277 return m_CounterIdMap;
278 }
279
GetCounterMappingRegistry()280 IRegisterCounterMapping& ProfilingService::GetCounterMappingRegistry()
281 {
282 return m_CounterIdMap;
283 }
284
IsCategoryRegistered(const std::string & categoryName) const285 bool ProfilingService::IsCategoryRegistered(const std::string& categoryName) const
286 {
287 return m_CounterDirectory.IsCategoryRegistered(categoryName);
288 }
289
IsCounterRegistered(const std::string & counterName) const290 bool ProfilingService::IsCounterRegistered(const std::string& counterName) const
291 {
292 return m_CounterDirectory.IsCounterRegistered(counterName);
293 }
294
GetCaptureData()295 CaptureData ProfilingService::GetCaptureData()
296 {
297 return m_Holder.GetCaptureData();
298 }
299
SetCaptureData(uint32_t capturePeriod,const std::vector<uint16_t> & counterIds,const std::set<std::string> & activeBackends)300 void ProfilingService::SetCaptureData(uint32_t capturePeriod,
301 const std::vector<uint16_t>& counterIds,
302 const std::set<std::string>& activeBackends)
303 {
304 m_Holder.SetCaptureData(capturePeriod, counterIds, activeBackends);
305 }
306
SetCounterValue(uint16_t counterUid,uint32_t value)307 void ProfilingService::SetCounterValue(uint16_t counterUid, uint32_t value)
308 {
309 CheckCounterUid(counterUid);
310 std::atomic<uint32_t>* counterValuePtr = m_CounterIndex.at(counterUid);
311 ARM_PIPE_ASSERT(counterValuePtr);
312 counterValuePtr->store(value, std::memory_order::memory_order_relaxed);
313 }
314
AddCounterValue(uint16_t counterUid,uint32_t value)315 uint32_t ProfilingService::AddCounterValue(uint16_t counterUid, uint32_t value)
316 {
317 CheckCounterUid(counterUid);
318 std::atomic<uint32_t>* counterValuePtr = m_CounterIndex.at(counterUid);
319 ARM_PIPE_ASSERT(counterValuePtr);
320 return counterValuePtr->fetch_add(value, std::memory_order::memory_order_relaxed);
321 }
322
SubtractCounterValue(uint16_t counterUid,uint32_t value)323 uint32_t ProfilingService::SubtractCounterValue(uint16_t counterUid, uint32_t value)
324 {
325 CheckCounterUid(counterUid);
326 std::atomic<uint32_t>* counterValuePtr = m_CounterIndex.at(counterUid);
327 ARM_PIPE_ASSERT(counterValuePtr);
328 return counterValuePtr->fetch_sub(value, std::memory_order::memory_order_relaxed);
329 }
330
IncrementCounterValue(uint16_t counterUid)331 uint32_t ProfilingService::IncrementCounterValue(uint16_t counterUid)
332 {
333 CheckCounterUid(counterUid);
334 std::atomic<uint32_t>* counterValuePtr = m_CounterIndex.at(counterUid);
335 ARM_PIPE_ASSERT(counterValuePtr);
336 return counterValuePtr->operator++(std::memory_order::memory_order_relaxed);
337 }
338
GetSendTimelinePacket() const339 std::unique_ptr<ISendTimelinePacket> ProfilingService::GetSendTimelinePacket() const
340 {
341 return m_TimelinePacketWriterFactory.GetSendTimelinePacket();
342 }
343
Initialize()344 void ProfilingService::Initialize()
345 {
346 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
347 m_Initialiser.InitialiseProfilingService(*this);
348 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
349 }
350
InitializeCounterValue(uint16_t counterUid)351 void ProfilingService::InitializeCounterValue(uint16_t counterUid)
352 {
353 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
354 // Increase the size of the counter index if necessary
355 if (counterUid >= m_CounterIndex.size())
356 {
357 m_CounterIndex.resize(arm::pipe::numeric_cast<size_t>(counterUid) + 1);
358 }
359
360 // Create a new atomic counter and add it to the list
361 m_CounterValues.emplace_back(0);
362
363 // Register the new counter to the counter index for quick access
364 std::atomic<uint32_t>* counterValuePtr = &(m_CounterValues.back());
365 m_CounterIndex.at(counterUid) = counterValuePtr;
366 #else
367 IgnoreUnused(counterUid);
368 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
369 }
370
Reset()371 void ProfilingService::Reset()
372 {
373 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
374 // Stop the profiling service...
375 Stop();
376
377 // ...then delete all the counter data and configuration...
378 m_CounterIndex.clear();
379 m_CounterValues.clear();
380 m_CounterDirectory.Clear();
381 m_CounterIdMap.Reset();
382 m_BufferManager.Reset();
383
384 // ...finally reset the profiling state machine
385 m_StateMachine.Reset();
386 m_BackendProfilingContexts.clear();
387 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
388 }
389
Stop()390 void ProfilingService::Stop()
391 {
392 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
393 { // only lock when we are updating the inference completed variable
394 std::unique_lock<std::mutex> lck(m_ServiceActiveMutex);
395 m_ServiceActive = false;
396 }
397 // The order in which we reset/stop the components is not trivial!
398 // First stop the producing threads
399 // Command Handler first as it is responsible for launching then Periodic Counter capture thread
400 m_CommandHandler.Stop();
401 m_PeriodicCounterCapture.Stop();
402 // The the consuming thread
403 m_SendThread.Stop(false);
404
405 // ...then close and destroy the profiling connection...
406 if (m_ProfilingConnection != nullptr && m_ProfilingConnection->IsOpen())
407 {
408 m_ProfilingConnection->Close();
409 }
410 m_ProfilingConnection.reset();
411
412 // ...then move to the "NotConnected" state
413 m_StateMachine.TransitionToState(ProfilingState::NotConnected);
414 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
415 }
416
CheckCounterUid(uint16_t counterUid) const417 inline void ProfilingService::CheckCounterUid(uint16_t counterUid) const
418 {
419 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
420 if (!IsCounterRegistered(counterUid))
421 {
422 throw arm::pipe::InvalidArgumentException(fmt::format("Counter UID {} is not registered", counterUid));
423 }
424 #else
425 IgnoreUnused(counterUid);
426 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
427 }
428
NotifyBackendsForTimelineReporting()429 void ProfilingService::NotifyBackendsForTimelineReporting()
430 {
431 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
432 BackendProfilingContext::iterator it = m_BackendProfilingContexts.begin();
433 while (it != m_BackendProfilingContexts.end())
434 {
435 auto& backendProfilingContext = it->second;
436 backendProfilingContext->EnableTimelineReporting(m_TimelineReporting);
437 // Increment the Iterator to point to next entry
438 it++;
439 }
440 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
441 }
442
NotifyProfilingServiceActive()443 void ProfilingService::NotifyProfilingServiceActive()
444 {
445 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
446 { // only lock when we are updating the inference completed variable
447 std::unique_lock<std::mutex> lck(m_ServiceActiveMutex);
448 m_ServiceActive = true;
449 }
450 m_ServiceActiveConditionVariable.notify_one();
451 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
452 }
453
WaitForProfilingServiceActivation(unsigned int timeout)454 void ProfilingService::WaitForProfilingServiceActivation(unsigned int timeout)
455 {
456 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
457 std::unique_lock<std::mutex> lck(m_ServiceActiveMutex);
458
459 auto start = std::chrono::high_resolution_clock::now();
460 // Here we we will go back to sleep after a spurious wake up if
461 // m_InferenceCompleted is not yet true.
462 if (!m_ServiceActiveConditionVariable.wait_for(lck,
463 std::chrono::milliseconds(timeout),
464 [&]{return m_ServiceActive == true;}))
465 {
466 if (m_ServiceActive == true)
467 {
468 return;
469 }
470 auto finish = std::chrono::high_resolution_clock::now();
471 std::chrono::duration<double, std::milli> elapsed = finish - start;
472 std::stringstream ss;
473 ss << "Timed out waiting on profiling service activation for " << elapsed.count() << " ms";
474 ARM_PIPE_LOG(warning) << ss.str();
475 }
476 #else
477 IgnoreUnused(timeout);
478 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
479 }
480
~ProfilingService()481 ProfilingService::~ProfilingService()
482 {
483 #if !defined(ARMNN_BUILD_BARE_METAL) && !defined(ARMNN_EXECUTE_NETWORK_STATIC)
484 Stop();
485 #endif // ARMNN_BUILD_BARE_METAL && ARMNN_EXECUTE_NETWORK_STATIC
486 }
487
488 } // namespace pipe
489
490 } // namespace arm
491