• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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