Browse Source

More work on the tracer

Panagiotis Christopoulos Charitos 7 years ago
parent
commit
74da9fe436
2 changed files with 174 additions and 10 deletions
  1. 151 1
      src/anki/util/Trace.cpp
  2. 23 9
      src/anki/util/Trace.h

+ 151 - 1
src/anki/util/Trace.cpp

@@ -11,6 +11,11 @@ namespace anki
 
 thread_local Tracer::ThreadLocal Tracer::m_threadLocal;
 
+void Tracer::beginFrame()
+{
+	m_startFrameTime = HighRezTimer::getCurrentTime();
+}
+
 Tracer::ThreadLocal& Tracer::getThreadLocal()
 {
 	ThreadLocal& out = m_threadLocal;
@@ -37,6 +42,8 @@ void Tracer::beginEvent()
 
 void Tracer::endEvent(const char* eventName)
 {
+	ANKI_ASSERT(eventName);
+
 	if(m_enabled)
 	{
 		// Set the time in the event
@@ -53,14 +60,157 @@ void Tracer::endEvent(const char* eventName)
 
 void Tracer::increaseCounter(const char* counterName, U64 value)
 {
+	ANKI_ASSERT(counterName);
+
 	if(m_enabled)
 	{
 		ThreadLocal& threadLocal = getThreadLocal();
 		Counter* counter = threadLocal.m_counterAlloc.newInstance(m_alloc);
 		counter->m_name = counterName;
 		counter->m_value = value;
-		counter->m_frame = m_frame.load();
+		counter->m_frame = m_frame;
+		counter->m_startFrameTime = m_startFrameTime;
+	}
+}
+
+void Tracer::compactCounters(DynamicArrayAuto<Counter>& allCounters)
+{
+	// Gather all the counters
+	DynamicArrayAuto<Counter> uncompactCounters(m_alloc);
+	for(ThreadLocal* threadLocal : m_allThreadLocal)
+	{
+		while(!threadLocal->m_counters.isEmpty())
+		{
+			// Pop counter
+			Counter& inCounter = threadLocal->m_counters.getFront();
+			threadLocal->m_counters.popFront();
+
+			// Copy
+			Counter newCounter = inCounter;
+			uncompactCounters.emplaceBack(newCounter);
+
+			// Delete poped counter
+			threadLocal->m_counterAlloc.deleteInstance(m_alloc, &inCounter);
+		}
+	}
+
+	if(uncompactCounters.getSize() == 0)
+	{
+		// Early exit
+		return;
+	}
+
+	// Sort them
+	std::sort(uncompactCounters.getBegin(), uncompactCounters.getEnd(), [](const Counter& a, const Counter& b) {
+		if(a.m_frame != b.m_frame)
+		{
+			return a.m_frame < b.m_frame;
+		}
+
+		ANKI_ASSERT(a.m_name && a.m_name);
+		return std::strcmp(a.m_name, b.m_name) < 0;
+	});
+
+	// Compact them
+	allCounters.emplaceBack(uncompactCounters[0]);
+	for(U i = 1; i < uncompactCounters.getSize(); ++i)
+	{
+		const Counter& inCounter = uncompactCounters[i];
+		Counter& outCounter = allCounters.getBack();
+		if(CString(inCounter.m_name) != CString(outCounter.m_name) || inCounter.m_frame != outCounter.m_frame)
+		{
+			// Create a new entry
+			allCounters.emplaceBack(inCounter);
+		}
+		else
+		{
+			// Append value to the existing counter
+			outCounter.m_value += inCounter.m_value;
+		}
+	}
+}
+
+Error Tracer::writeTraceJson(CString filename, const DynamicArrayAuto<Counter>& counters)
+{
+	class NEvent : public Event
+	{
+	public:
+		ThreadId m_tid;
+	};
+
+	// Gather all events from all threads
+	DynamicArrayAuto<NEvent> allEvents(m_alloc);
+	for(ThreadLocal* threadLocal : m_allThreadLocal)
+	{
+		while(!threadLocal->m_events.isEmpty())
+		{
+			// Pop event
+			Event& inEvent = threadLocal->m_events.getFront();
+			threadLocal->m_events.popFront();
+
+			// Copy
+			NEvent newEvent;
+			static_cast<Event&>(newEvent) = inEvent;
+			newEvent.m_tid = threadLocal->m_tid;
+			allEvents.emplaceBack(newEvent);
+
+			// Delete poped event
+			threadLocal->m_eventAlloc.deleteInstance(m_alloc, &inEvent);
+		}
 	}
+
+	if(allEvents.getSize() == 0)
+	{
+		// Early exit
+		return Error::NONE;
+	}
+
+	// Sort them
+	std::sort(allEvents.getBegin(), allEvents.getEnd(), [](const NEvent& a, const NEvent& b) {
+		return a.m_timestamp < b.m_timestamp;
+	});
+
+	// Write the events to the file
+	StringAuto newFname(m_alloc);
+	newFname.sprintf("%s_trace.json", filename.cstr());
+	File file;
+	ANKI_CHECK(file.open(newFname.toCString(), FileOpenFlag::WRITE));
+
+	for(const NEvent& event : allEvents)
+	{
+		U64 startMicroSec = U64(event.m_timestamp * 1000000.0);
+		U64 durMicroSec = U64(event.m_duration * 1000000.0);
+
+		if(durMicroSec == 0)
+		{
+			continue;
+		}
+
+		ANKI_CHECK(file.writeText("{\"name\": \"%s\", \"cat\": \"PERF\", \"ph\": \"X\", "
+								  "\"pid\": 1, \"tid\": %llu, \"ts\": %llu, \"dur\": %llu},\n",
+			event.m_name,
+			event.m_tid,
+			startMicroSec,
+			durMicroSec));
+	}
+
+	// Write the counters
+	for(const Counter& counter : counters)
+	{
+		ANKI_CHECK(m_traceFile.writeText("{\"name\": \"%s\", \"cat\": \"PERF\", \"ph\": \"C\", "
+										 "\"pid\": 1, \"ts\": %llu, \"args\": {\"val\": %llu}},\n",
+			counter.m_name,
+			U64(counter.m_startFrameTime * 1000000.0),
+			counter.m_value));
+	}
+
+	return Error::NONE;
+}
+
+Error Tracer::flush(CString filename)
+{
+	// TODO
+	return Error::NONE;
 }
 
 } // end namespace anki

+ 23 - 9
src/anki/util/Trace.h

@@ -37,10 +37,13 @@ public:
 	/// Increase a counter.
 	void increaseCounter(const char* counterName, U64 value);
 
+	/// TODO
+	void beginFrame();
+
 	/// Call it to end the frame.
 	void endFrame()
 	{
-		m_frame.fetchAdd(1);
+		m_frame += 0;
 	}
 
 	Bool getEnabled() const
@@ -68,24 +71,25 @@ private:
 	class Event : public IntrusiveListEnabled<Event>
 	{
 	public:
-		const char* m_name;
-		Second m_timestamp;
-		Second m_duration;
+		const char* m_name ANKI_DBG_NULLIFY;
+		Second m_timestamp ANKI_DBG_NULLIFY;
+		Second m_duration ANKI_DBG_NULLIFY;
 	};
 
 	/// Counter.
 	class Counter : public IntrusiveListEnabled<Counter>
 	{
 	public:
-		const char* m_name;
-		U64 m_value;
-		U64 m_frame;
+		const char* m_name ANKI_DBG_NULLIFY;
+		U64 m_value ANKI_DBG_NULLIFY;
+		U64 m_frame ANKI_DBG_NULLIFY;
+		Second m_startFrameTime;
 	};
 
 	class ThreadLocal
 	{
 	public:
-		ThreadId m_tid;
+		ThreadId m_tid ANKI_DBG_NULLIFY;
 		ObjectAllocatorSameType<Event> m_eventAlloc;
 		ObjectAllocatorSameType<Counter> m_counterAlloc;
 		IntrusiveList<Event> m_events;
@@ -97,10 +101,20 @@ private:
 	DynamicArray<ThreadLocal*> m_allThreadLocal; ///< The Tracer should know about all the ThreadLocal.
 	Mutex m_threadLocalMtx;
 
-	Atomic<U64> m_frame = {0};
+	U64 m_frame = 0;
+	Second m_startFrameTime = 0.0;
 
 	/// Get the thread local ThreadLocal structure.
 	ThreadLocal& getThreadLocal();
+
+	/// TODO
+	void compactCounters(DynamicArrayAuto<Counter>& allCounters);
+
+	/// TODO
+	Error writeCounterCsv(CString filename, const DynamicArrayAuto<Counter>& counters);
+
+	/// TODO
+	Error writeTraceJson(CString filename, const DynamicArrayAuto<Counter>& counters);
 };
 /// @}