Tracer.cpp 4.5 KB

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