CoreTracer.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. // Copyright (C) 2009-present, 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/Core/CoreTracer.h>
  6. #include <AnKi/Util/DynamicArray.h>
  7. #include <AnKi/Util/Tracer.h>
  8. #include <AnKi/Util/System.h>
  9. #include <AnKi/Math/Functions.h>
  10. namespace anki {
  11. BoolCVar g_tracingEnabledCVar(CVarSubsystem::kCore, "Tracing", false, "Enable or disable tracing");
  12. #if ANKI_OS_ANDROID
  13. BoolCVar g_streamlineEnabledCVar(CVarSubsystem::kCore, "StreamlineAnnotations", false, "Enable or disable Streamline annotations");
  14. #endif
  15. #if ANKI_TRACING_ENABLED
  16. static void getSpreadsheetColumnName(U32 column, Array<char, 3>& arr)
  17. {
  18. U32 major = column / 26;
  19. U32 minor = column % 26;
  20. if(major)
  21. {
  22. arr[0] = char('A' + (major - 1));
  23. arr[1] = char('A' + minor);
  24. }
  25. else
  26. {
  27. arr[0] = char('A' + minor);
  28. arr[1] = '\0';
  29. }
  30. arr[2] = '\0';
  31. }
  32. class CoreTracer::ThreadWorkItem : public IntrusiveListEnabled<ThreadWorkItem>
  33. {
  34. public:
  35. CoreDynamicArray<TracerEvent> m_events;
  36. CoreDynamicArray<TracerCounter> m_counters;
  37. ThreadId m_tid;
  38. U64 m_frame;
  39. };
  40. class CoreTracer::PerFrameCounters : public IntrusiveListEnabled<PerFrameCounters>
  41. {
  42. public:
  43. CoreDynamicArray<TracerCounter> m_counters;
  44. U64 m_frame;
  45. };
  46. CoreTracer::CoreTracer()
  47. : m_thread("Tracer")
  48. {
  49. }
  50. CoreTracer::~CoreTracer()
  51. {
  52. // Stop thread
  53. {
  54. LockGuard<Mutex> lock(m_mtx);
  55. m_quit = true;
  56. m_cvar.notifyOne();
  57. }
  58. [[maybe_unused]] Error err = m_thread.join();
  59. // Finalize trace file
  60. if(m_traceJsonFile.isOpen())
  61. {
  62. err = m_traceJsonFile.writeText("{}\n]\n");
  63. }
  64. // Write counter file
  65. err = writeCountersOnShutdown();
  66. // Cleanup
  67. while(!m_frameCounters.isEmpty())
  68. {
  69. PerFrameCounters* frame = m_frameCounters.popBack();
  70. deleteInstance(CoreMemoryPool::getSingleton(), frame);
  71. }
  72. while(!m_workItems.isEmpty())
  73. {
  74. ThreadWorkItem* item = m_workItems.popBack();
  75. deleteInstance(CoreMemoryPool::getSingleton(), item);
  76. }
  77. // Destroy the tracer
  78. Tracer::freeSingleton();
  79. }
  80. Error CoreTracer::init(CString directory)
  81. {
  82. Tracer::allocateSingleton();
  83. if(Tracer::getSingleton().getEnabled() != g_tracingEnabledCVar.get())
  84. {
  85. // Change the value inside the if because setEnabled prints a message
  86. Tracer::getSingleton().setEnabled(g_tracingEnabledCVar.get());
  87. }
  88. # if ANKI_OS_ANDROID
  89. if(Tracer::getSingleton().getStreamlineEnabled())
  90. {
  91. Tracer::getSingleton().setStreamlineEnabled(g_streamlineEnabledCVar.get());
  92. }
  93. # endif
  94. m_thread.start(this, [](ThreadCallbackInfo& info) -> Error {
  95. return static_cast<CoreTracer*>(info.m_userData)->threadWorker();
  96. });
  97. std::tm tm = getLocalTime();
  98. CoreString fname;
  99. fname.sprintf("%s/%d%02d%02d-%02d%02d_", directory.cstr(), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
  100. m_traceJsonFilename.sprintf("%strace.json", fname.cstr());
  101. m_countersCsvFilename.sprintf("%scounters.csv", fname.cstr());
  102. return Error::kNone;
  103. }
  104. Error CoreTracer::threadWorker()
  105. {
  106. Error err = Error::kNone;
  107. Bool quit = false;
  108. while(!err && !quit)
  109. {
  110. ThreadWorkItem* item = nullptr;
  111. // Get some work
  112. {
  113. // Wait for something
  114. LockGuard<Mutex> lock(m_mtx);
  115. while(m_workItems.isEmpty() && !m_quit)
  116. {
  117. m_cvar.wait(m_mtx);
  118. }
  119. // Get some work
  120. if(!m_workItems.isEmpty())
  121. {
  122. item = m_workItems.popFront();
  123. }
  124. else if(m_quit)
  125. {
  126. quit = true;
  127. }
  128. }
  129. // Do some work using the frame and delete it
  130. if(item)
  131. {
  132. err = writeEvents(*item);
  133. if(!err)
  134. {
  135. gatherCounters(*item);
  136. }
  137. deleteInstance(CoreMemoryPool::getSingleton(), item);
  138. }
  139. }
  140. return err;
  141. }
  142. Error CoreTracer::writeEvents(ThreadWorkItem& item)
  143. {
  144. if(item.m_events.getSize() == 0)
  145. {
  146. return Error::kNone;
  147. }
  148. if(!m_traceJsonFile.isOpen())
  149. {
  150. ANKI_CHECK(m_traceJsonFile.open(m_traceJsonFilename, FileOpenFlag::kWrite));
  151. ANKI_CHECK(m_traceJsonFile.writeText("[\n"));
  152. ANKI_CORE_LOGI("Trace file created: %s", m_traceJsonFilename.cstr());
  153. }
  154. // First sort them to fix overlaping in chrome
  155. std::sort(item.m_events.getBegin(), item.m_events.getEnd(), [](const TracerEvent& a, TracerEvent& b) {
  156. return (a.m_start != b.m_start) ? a.m_start < b.m_start : a.m_duration > b.m_duration;
  157. });
  158. // Write events
  159. for(const TracerEvent& event : item.m_events)
  160. {
  161. const I64 startMicroSec = I64(event.m_start * 1000000.0);
  162. const I64 durMicroSec = I64(event.m_duration * 1000000.0);
  163. // Do a hack
  164. const ThreadId tid = (event.m_name == "tGpuFrameTime") ? 1 : item.m_tid;
  165. ANKI_CHECK(m_traceJsonFile.writeTextf("{\"name\": \"%s\", \"cat\": \"PERF\", \"ph\": \"X\", "
  166. "\"pid\": 1, \"tid\": %" PRIu64 ", \"ts\": %" PRIi64 ", \"dur\": %" PRIi64 "},\n",
  167. event.m_name.cstr(), tid, startMicroSec, durMicroSec));
  168. }
  169. // Store counters
  170. // TODO
  171. return Error::kNone;
  172. }
  173. void CoreTracer::gatherCounters(ThreadWorkItem& item)
  174. {
  175. // Sort
  176. std::sort(item.m_counters.getBegin(), item.m_counters.getEnd(), [](const TracerCounter& a, const TracerCounter& b) {
  177. return a.m_name < b.m_name;
  178. });
  179. // Merge same
  180. CoreDynamicArray<TracerCounter> mergedCounters;
  181. for(U32 i = 0; i < item.m_counters.getSize(); ++i)
  182. {
  183. if(mergedCounters.getSize() == 0 || mergedCounters.getBack().m_name != item.m_counters[i].m_name)
  184. {
  185. // New
  186. mergedCounters.emplaceBack(item.m_counters[i]);
  187. }
  188. else
  189. {
  190. // Merge
  191. mergedCounters.getBack().m_value += item.m_counters[i].m_value;
  192. }
  193. }
  194. ANKI_ASSERT(mergedCounters.getSize() > 0 && mergedCounters.getSize() <= item.m_counters.getSize());
  195. // Add missing counter names
  196. Bool addedCounterName = false;
  197. for(U32 i = 0; i < mergedCounters.getSize(); ++i)
  198. {
  199. const TracerCounter& counter = mergedCounters[i];
  200. Bool found = false;
  201. for(const CoreString& name : m_counterNames)
  202. {
  203. if(name == counter.m_name)
  204. {
  205. found = true;
  206. break;
  207. }
  208. }
  209. if(!found)
  210. {
  211. m_counterNames.emplaceBack(counter.m_name);
  212. addedCounterName = true;
  213. }
  214. }
  215. if(addedCounterName)
  216. {
  217. std::sort(m_counterNames.getBegin(), m_counterNames.getEnd());
  218. }
  219. // Get a per-frame structure
  220. if(m_frameCounters.isEmpty() || m_frameCounters.getBack().m_frame != item.m_frame)
  221. {
  222. // Create new frame
  223. PerFrameCounters* newPerFrame = newInstance<PerFrameCounters>(CoreMemoryPool::getSingleton());
  224. newPerFrame->m_counters = std::move(mergedCounters);
  225. newPerFrame->m_frame = item.m_frame;
  226. m_frameCounters.pushBack(newPerFrame);
  227. }
  228. else
  229. {
  230. // Merge counters to existing frame
  231. PerFrameCounters& frame = m_frameCounters.getBack();
  232. ANKI_ASSERT(frame.m_frame == item.m_frame);
  233. for(const TracerCounter& newCounter : mergedCounters)
  234. {
  235. Bool found = false;
  236. for(TracerCounter& existingCounter : frame.m_counters)
  237. {
  238. if(newCounter.m_name == existingCounter.m_name)
  239. {
  240. existingCounter.m_value += newCounter.m_value;
  241. found = true;
  242. break;
  243. }
  244. }
  245. if(!found)
  246. {
  247. frame.m_counters.emplaceBack(newCounter);
  248. }
  249. }
  250. }
  251. }
  252. void CoreTracer::flushFrame(U64 frame)
  253. {
  254. struct Ctx
  255. {
  256. U64 m_frame;
  257. CoreTracer* m_self;
  258. };
  259. Ctx ctx;
  260. ctx.m_frame = frame;
  261. ctx.m_self = this;
  262. Tracer::getSingleton().flush(
  263. [](void* ud, ThreadId tid, ConstWeakArray<TracerEvent> events, ConstWeakArray<TracerCounter> counters) {
  264. Ctx& ctx = *static_cast<Ctx*>(ud);
  265. CoreTracer& self = *ctx.m_self;
  266. ThreadWorkItem* item = newInstance<ThreadWorkItem>(CoreMemoryPool::getSingleton());
  267. item->m_tid = tid;
  268. item->m_frame = ctx.m_frame;
  269. if(events.getSize() > 0)
  270. {
  271. item->m_events.resize(events.getSize());
  272. memcpy(&item->m_events[0], &events[0], events.getSizeInBytes());
  273. }
  274. if(counters.getSize() > 0)
  275. {
  276. item->m_counters.resize(counters.getSize());
  277. memcpy(&item->m_counters[0], &counters[0], counters.getSizeInBytes());
  278. }
  279. LockGuard<Mutex> lock(self.m_mtx);
  280. self.m_workItems.pushBack(item);
  281. self.m_cvar.notifyOne();
  282. },
  283. &ctx);
  284. if(Tracer::getSingleton().getEnabled() != g_tracingEnabledCVar.get())
  285. {
  286. Tracer::getSingleton().setEnabled(g_tracingEnabledCVar.get());
  287. }
  288. # if ANKI_OS_ANDROID
  289. if(Tracer::getSingleton().getStreamlineEnabled() != g_streamlineEnabledCVar.get())
  290. {
  291. Tracer::getSingleton().setStreamlineEnabled(g_streamlineEnabledCVar.get());
  292. }
  293. # endif
  294. }
  295. Error CoreTracer::writeCountersOnShutdown()
  296. {
  297. if(m_frameCounters.getSize() == 0)
  298. {
  299. return Error::kNone;
  300. }
  301. File countersCsvFile;
  302. ANKI_CHECK(countersCsvFile.open(m_countersCsvFilename, FileOpenFlag::kWrite));
  303. ANKI_CORE_LOGI("Counter file created: %s", m_countersCsvFilename.cstr());
  304. // Write the header
  305. ANKI_CHECK(countersCsvFile.writeText("Frame"));
  306. for(U32 i = 0; i < m_counterNames.getSize(); ++i)
  307. {
  308. ANKI_CHECK(countersCsvFile.writeTextf(",%s", m_counterNames[i].cstr()));
  309. }
  310. ANKI_CHECK(countersCsvFile.writeText("\n"));
  311. // Write each frame
  312. for(const PerFrameCounters& frame : m_frameCounters)
  313. {
  314. ANKI_CHECK(countersCsvFile.writeTextf("%" PRIu64, frame.m_frame));
  315. for(U32 j = 0; j < m_counterNames.getSize(); ++j)
  316. {
  317. // Find value
  318. U64 value = 0;
  319. for(const TracerCounter& counter : frame.m_counters)
  320. {
  321. if(counter.m_name == m_counterNames[j])
  322. {
  323. value = counter.m_value;
  324. break;
  325. }
  326. }
  327. ANKI_CHECK(countersCsvFile.writeTextf(",%" PRIu64, value));
  328. }
  329. ANKI_CHECK(countersCsvFile.writeText("\n"));
  330. }
  331. // Write some statistics
  332. Array<const char*, 2> funcs = {"SUM", "AVERAGE"};
  333. for(const char* func : funcs)
  334. {
  335. ANKI_CHECK(countersCsvFile.writeText(func));
  336. for(U32 i = 0; i < m_frameCounters.getSize(); ++i)
  337. {
  338. Array<char, 3> columnName;
  339. getSpreadsheetColumnName(i + 1, columnName);
  340. ANKI_CHECK(countersCsvFile.writeTextf(",=%s(%s2:%s%zu)", func, &columnName[0], &columnName[0], m_frameCounters.getSize() + 1));
  341. }
  342. ANKI_CHECK(countersCsvFile.writeText("\n"));
  343. }
  344. return Error::kNone;
  345. }
  346. #endif
  347. } // end namespace anki