Tracer.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Util/Tracer.h>
  6. #include <AnKi/Util/HighRezTimer.h>
  7. #include <AnKi/Util/HashMap.h>
  8. #include <AnKi/Util/List.h>
  9. namespace anki
  10. {
  11. class Tracer::Chunk : public IntrusiveListEnabled<Chunk>
  12. {
  13. public:
  14. Array<TracerEvent, EVENTS_PER_CHUNK> m_events;
  15. U32 m_eventCount = 0;
  16. Array<TracerCounter, COUNTERS_PER_CHUNK> m_counters;
  17. U32 m_counterCount = 0;
  18. };
  19. /// Thread local storage.
  20. class alignas(ANKI_CACHE_LINE_SIZE) Tracer::ThreadLocal
  21. {
  22. public:
  23. ThreadId m_tid = 0;
  24. Chunk* m_currentChunk = nullptr;
  25. IntrusiveList<Chunk> m_allChunks;
  26. SpinLock m_currentChunkLock;
  27. };
  28. thread_local Tracer::ThreadLocal* Tracer::m_threadLocal = nullptr;
  29. Tracer::~Tracer()
  30. {
  31. LockGuard<Mutex> lock(m_allThreadLocalMtx);
  32. for(ThreadLocal* tlocal : m_allThreadLocal)
  33. {
  34. m_alloc.deleteInstance(tlocal);
  35. }
  36. m_allThreadLocal.destroy(m_alloc);
  37. }
  38. Tracer::ThreadLocal& Tracer::getThreadLocal()
  39. {
  40. ThreadLocal* out = m_threadLocal;
  41. if(ANKI_UNLIKELY(out == nullptr))
  42. {
  43. out = m_alloc.newInstance<ThreadLocal>();
  44. out->m_tid = Thread::getCurrentThreadId();
  45. m_threadLocal = out;
  46. // Store it
  47. LockGuard<Mutex> lock(m_allThreadLocalMtx);
  48. m_allThreadLocal.emplaceBack(m_alloc, out);
  49. }
  50. return *out;
  51. }
  52. Tracer::Chunk& Tracer::getOrCreateChunk(ThreadLocal& tlocal)
  53. {
  54. Chunk* out;
  55. if(tlocal.m_currentChunk && tlocal.m_currentChunk->m_eventCount < EVENTS_PER_CHUNK
  56. && tlocal.m_currentChunk->m_counterCount < COUNTERS_PER_CHUNK)
  57. {
  58. // There is a chunk and it has enough space
  59. out = tlocal.m_currentChunk;
  60. }
  61. else
  62. {
  63. // Create a new
  64. out = m_alloc.newInstance<Chunk>();
  65. tlocal.m_currentChunk = out;
  66. tlocal.m_allChunks.pushBack(out);
  67. }
  68. return *out;
  69. }
  70. TracerEventHandle Tracer::beginEvent()
  71. {
  72. TracerEventHandle out;
  73. if(m_enabled)
  74. {
  75. out.m_start = HighRezTimer::getCurrentTime();
  76. }
  77. else
  78. {
  79. out.m_start = 0.0;
  80. }
  81. return out;
  82. }
  83. void Tracer::endEvent(const char* eventName, TracerEventHandle event)
  84. {
  85. if(!m_enabled || event.m_start == 0.0)
  86. {
  87. return;
  88. }
  89. // Get the time before the lock and everything
  90. const Second duration = HighRezTimer::getCurrentTime() - event.m_start;
  91. if(duration == 0.0)
  92. {
  93. return;
  94. }
  95. ThreadLocal& tlocal = getThreadLocal();
  96. // Write the event
  97. LockGuard<SpinLock> lock(tlocal.m_currentChunkLock);
  98. Chunk& chunk = getOrCreateChunk(tlocal);
  99. TracerEvent& writeEvent = chunk.m_events[chunk.m_eventCount++];
  100. writeEvent.m_name = eventName;
  101. writeEvent.m_start = event.m_start;
  102. writeEvent.m_duration = duration;
  103. // Write counter as well. In ns
  104. TracerCounter& writeCounter = chunk.m_counters[chunk.m_counterCount++];
  105. writeCounter.m_name = eventName;
  106. writeCounter.m_value = U64(writeEvent.m_duration * 1000000000.0);
  107. }
  108. void Tracer::addCustomEvent(const char* eventName, Second start, Second duration)
  109. {
  110. ANKI_ASSERT(eventName && start >= 0.0 && duration >= 0.0);
  111. if(!m_enabled || duration == 0.0)
  112. {
  113. return;
  114. }
  115. ThreadLocal& tlocal = getThreadLocal();
  116. // Write the event
  117. LockGuard<SpinLock> lock(tlocal.m_currentChunkLock);
  118. Chunk& chunk = getOrCreateChunk(tlocal);
  119. TracerEvent& writeEvent = chunk.m_events[chunk.m_eventCount++];
  120. writeEvent.m_name = eventName;
  121. writeEvent.m_start = start;
  122. writeEvent.m_duration = duration;
  123. // Write counter as well. In ns
  124. TracerCounter& writeCounter = chunk.m_counters[chunk.m_counterCount++];
  125. writeCounter.m_name = eventName;
  126. writeCounter.m_value = U64(duration * 1000000000.0);
  127. }
  128. void Tracer::incrementCounter(const char* counterName, U64 value)
  129. {
  130. if(!m_enabled)
  131. {
  132. return;
  133. }
  134. ThreadLocal& tlocal = getThreadLocal();
  135. LockGuard<SpinLock> lock(tlocal.m_currentChunkLock);
  136. Chunk& chunk = getOrCreateChunk(tlocal);
  137. TracerCounter& writeTo = chunk.m_counters[chunk.m_counterCount++];
  138. writeTo.m_name = counterName;
  139. writeTo.m_value = value;
  140. }
  141. void Tracer::flush(TracerFlushCallback callback, void* callbackUserData)
  142. {
  143. ANKI_ASSERT(callback);
  144. LockGuard<Mutex> lock(m_allThreadLocalMtx);
  145. for(ThreadLocal* tlocal : m_allThreadLocal)
  146. {
  147. LockGuard<SpinLock> lock2(tlocal->m_currentChunkLock);
  148. while(!tlocal->m_allChunks.isEmpty())
  149. {
  150. Chunk* chunk = tlocal->m_allChunks.popFront();
  151. callback(callbackUserData, tlocal->m_tid, WeakArray<TracerEvent>(&chunk->m_events[0], chunk->m_eventCount),
  152. WeakArray<TracerCounter>(&chunk->m_counters[0], chunk->m_counterCount));
  153. m_alloc.deleteInstance(chunk);
  154. }
  155. tlocal->m_currentChunk = nullptr;
  156. }
  157. }
  158. } // end namespace anki