add tracing for performance profiling
This commit is contained in:
@@ -4,12 +4,14 @@ add_subdirectory(random)
|
||||
|
||||
SET(HDRS
|
||||
${HDRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tracing.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/utilityRandom.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
SET(SRCS
|
||||
${SRCS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tracing.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/utilityRandom.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
279
src/lib/utility/tracing.cpp
Normal file
279
src/lib/utility/tracing.cpp
Normal file
@@ -0,0 +1,279 @@
|
||||
#include "tracing.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
std::shared_ptr<Tracer> Tracer::s_instance;
|
||||
long long int Tracer::s_nextTraceId = 0;
|
||||
|
||||
Tracer* Tracer::getInstance()
|
||||
{
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = std::shared_ptr<Tracer>(new Tracer());
|
||||
}
|
||||
|
||||
return s_instance.get();
|
||||
}
|
||||
|
||||
std::shared_ptr<TraceEvent> Tracer::startEvent(const std::string& eventName)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
const std::thread::id id = std::this_thread::get_id();
|
||||
|
||||
std::shared_ptr<TraceEvent> event =
|
||||
std::make_shared<TraceEvent>(eventName, s_nextTraceId++, m_startedEvents[id].size());
|
||||
|
||||
m_events[id].push_back(event);
|
||||
m_startedEvents[id].push(event.get());
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void Tracer::finishEvent(std::shared_ptr<TraceEvent> event)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
const std::thread::id id = std::this_thread::get_id();
|
||||
|
||||
m_startedEvents[id].pop();
|
||||
}
|
||||
|
||||
void Tracer::printTraces()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
size_t unfinishEvents = 0;
|
||||
for (auto& p : m_startedEvents)
|
||||
{
|
||||
unfinishEvents += p.second.size();
|
||||
}
|
||||
|
||||
if (unfinishEvents > 0)
|
||||
{
|
||||
std::cout << "TRACING: Trace events are still running." << std::endl;
|
||||
return;
|
||||
}
|
||||
else if (m_events.empty())
|
||||
{
|
||||
std::cout << "TRACING: No trace events collected." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
std::cout << "TRACING\n--------------------------\n" << std::endl;
|
||||
|
||||
std::cout << "HISTORY:\n\n";
|
||||
std::cout << " time name function";
|
||||
std::cout << " location\n";
|
||||
std::cout << "-----------------------------------------------------------------";
|
||||
std::cout << "------------------------------------------------------------\n";
|
||||
|
||||
for (auto& p : m_events)
|
||||
{
|
||||
std::cout << "thread: " << p.first << std::endl;
|
||||
|
||||
for (const std::shared_ptr<TraceEvent>& event : p.second)
|
||||
{
|
||||
std::cout.width(8 + 2 * event->depth);
|
||||
std::cout << std::right /*<< std::setprecision(3)*/ << std::fixed << event->time;
|
||||
|
||||
std::cout.width(17 - 2 * event->depth);
|
||||
std::cout << " ";
|
||||
|
||||
std::cout.width(25);
|
||||
std::cout << std::left << event->eventName;
|
||||
|
||||
std::cout.width(50);
|
||||
std::cout << (event->functionName + "()") << event->locationName << std::endl;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "\nREPORT:\n\n";
|
||||
std::cout << " time count name function";
|
||||
std::cout << " location\n";
|
||||
std::cout << "-----------------------------------------------------------------";
|
||||
std::cout << "------------------------------------------------------------\n";
|
||||
|
||||
struct AccumulatedTraceEvent
|
||||
{
|
||||
TraceEvent* event;
|
||||
size_t count;
|
||||
float time;
|
||||
};
|
||||
|
||||
std::map<std::string, AccumulatedTraceEvent> accumulatedEvents;
|
||||
|
||||
for (auto& p : m_events)
|
||||
{
|
||||
for (const std::shared_ptr<TraceEvent>& event : p.second)
|
||||
{
|
||||
const std::string name = event->eventName + event->functionName + event->locationName;
|
||||
|
||||
std::pair<std::map<std::string, AccumulatedTraceEvent>::iterator, bool> p =
|
||||
accumulatedEvents.emplace(name, AccumulatedTraceEvent());
|
||||
|
||||
AccumulatedTraceEvent* acc = &p.first->second;
|
||||
if (p.second)
|
||||
{
|
||||
acc->event = event.get();
|
||||
acc->time = event->time;
|
||||
acc->count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
acc->time += event->time;
|
||||
acc->count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::multiset<AccumulatedTraceEvent,
|
||||
std::function<bool(const AccumulatedTraceEvent&, const AccumulatedTraceEvent&)>> sortedEvents(
|
||||
[](const AccumulatedTraceEvent& a, const AccumulatedTraceEvent& b)
|
||||
{
|
||||
return a.time > b.time;
|
||||
}
|
||||
);
|
||||
|
||||
for (const std::pair<std::string, AccumulatedTraceEvent>& p : accumulatedEvents)
|
||||
{
|
||||
sortedEvents.insert(p.second);
|
||||
}
|
||||
|
||||
for (const AccumulatedTraceEvent& acc : sortedEvents)
|
||||
{
|
||||
std::cout.width(8);
|
||||
std::cout << std::right/* << std::setprecision(3)*/ << std::fixed << acc.time;
|
||||
|
||||
std::cout.width(10);
|
||||
std::cout << acc.count << " ";
|
||||
|
||||
std::cout.width(25);
|
||||
std::cout << std::left << acc.event->eventName;
|
||||
|
||||
std::cout.width(50);
|
||||
std::cout << (acc.event->functionName + "()") << acc.event->locationName << std::endl;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
m_events.clear();
|
||||
}
|
||||
|
||||
Tracer::Tracer()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<AccumulatingTracer> AccumulatingTracer::s_instance;
|
||||
long long int AccumulatingTracer::s_nextTraceId = 0;
|
||||
|
||||
AccumulatingTracer* AccumulatingTracer::getInstance()
|
||||
{
|
||||
if (!s_instance)
|
||||
{
|
||||
s_instance = std::shared_ptr<AccumulatingTracer>(new AccumulatingTracer());
|
||||
}
|
||||
|
||||
return s_instance.get();
|
||||
}
|
||||
|
||||
std::shared_ptr<TraceEvent> AccumulatingTracer::startEvent(const std::string& eventName)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
const std::thread::id id = std::this_thread::get_id();
|
||||
|
||||
std::shared_ptr<TraceEvent> event =
|
||||
std::make_shared<TraceEvent>(eventName, s_nextTraceId++, m_startedEvents[id].size());
|
||||
|
||||
m_startedEvents[id].push(event.get());
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void AccumulatingTracer::finishEvent(std::shared_ptr<TraceEvent> event)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
const std::thread::id id = std::this_thread::get_id();
|
||||
|
||||
m_startedEvents[id].pop();
|
||||
|
||||
const std::string name = event->eventName + event->functionName + event->locationName;
|
||||
|
||||
std::pair<std::map<std::string, AccumulatedTraceEvent>::iterator, bool> p =
|
||||
m_accumulatedEvents.emplace(name, AccumulatedTraceEvent());
|
||||
|
||||
AccumulatedTraceEvent* acc = &p.first->second;
|
||||
if (p.second)
|
||||
{
|
||||
acc->event = TraceEvent(*(event.get()));
|
||||
acc->time = event->time;
|
||||
acc->count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
acc->time += event->time;
|
||||
acc->count++;
|
||||
}
|
||||
}
|
||||
|
||||
void AccumulatingTracer::printTraces()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
for (auto& p : m_startedEvents)
|
||||
{
|
||||
if (!p.second.empty())
|
||||
{
|
||||
std::cout << "TRACING: Trace events are still running." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\nREPORT:\n\n";
|
||||
std::cout << " time count name function";
|
||||
std::cout << " location\n";
|
||||
std::cout << "-----------------------------------------------------------------";
|
||||
std::cout << "------------------------------------------------------------\n";
|
||||
|
||||
std::multiset<AccumulatedTraceEvent,
|
||||
std::function<bool (const AccumulatedTraceEvent&, const AccumulatedTraceEvent&)>> sortedEvents(
|
||||
[](const AccumulatedTraceEvent& a, const AccumulatedTraceEvent& b)
|
||||
{
|
||||
return a.time > b.time;
|
||||
}
|
||||
);
|
||||
|
||||
for (const std::pair<std::string, AccumulatedTraceEvent>& p : m_accumulatedEvents)
|
||||
{
|
||||
sortedEvents.insert(p.second);
|
||||
}
|
||||
|
||||
for (const AccumulatedTraceEvent& acc : sortedEvents)
|
||||
{
|
||||
std::cout.width(8);
|
||||
std::cout << std::right/* << std::setprecision(3)*/ << std::fixed << acc.time;
|
||||
|
||||
std::cout.width(10);
|
||||
std::cout << acc.count << " ";
|
||||
|
||||
std::cout.width(25);
|
||||
std::cout << std::left << acc.event.eventName;
|
||||
|
||||
std::cout.width(50);
|
||||
std::cout << (acc.event.functionName + "()") << acc.event.locationName << std::endl;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
m_accumulatedEvents.clear();
|
||||
}
|
||||
|
||||
AccumulatingTracer::AccumulatingTracer()
|
||||
{
|
||||
}
|
||||
156
src/lib/utility/tracing.h
Normal file
156
src/lib/utility/tracing.h
Normal file
@@ -0,0 +1,156 @@
|
||||
#ifndef TRACING_H
|
||||
#define TRACING_H
|
||||
|
||||
|
||||
//#define TRACING_ENABLED
|
||||
//#define USE_ACCUMULATED_TRACING
|
||||
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <stack>
|
||||
#include <thread>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QFileInfo>
|
||||
|
||||
struct TraceEvent
|
||||
{
|
||||
public:
|
||||
TraceEvent()
|
||||
: eventName("")
|
||||
, id(0)
|
||||
, depth(0)
|
||||
, time(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
TraceEvent(const std::string& eventName, long long int id, size_t depth)
|
||||
: eventName(eventName)
|
||||
, id(id)
|
||||
, depth(depth)
|
||||
, time(0.0)
|
||||
{
|
||||
}
|
||||
|
||||
std::string eventName;
|
||||
long long int id;
|
||||
size_t depth;
|
||||
|
||||
std::string functionName;
|
||||
std::string locationName;
|
||||
|
||||
double time;
|
||||
};
|
||||
|
||||
|
||||
class Tracer
|
||||
{
|
||||
public:
|
||||
static Tracer* getInstance();
|
||||
|
||||
std::shared_ptr<TraceEvent> startEvent(const std::string& eventName);
|
||||
void finishEvent(std::shared_ptr<TraceEvent> event);
|
||||
|
||||
void printTraces();
|
||||
|
||||
private:
|
||||
static std::shared_ptr<Tracer> s_instance;
|
||||
static long long int s_nextTraceId;
|
||||
|
||||
Tracer();
|
||||
Tracer(const Tracer&) = delete;
|
||||
void operator=(const Tracer&) = delete;
|
||||
|
||||
std::map<std::thread::id, std::vector<std::shared_ptr<TraceEvent>>> m_events;
|
||||
std::map<std::thread::id, std::stack<TraceEvent*>> m_startedEvents;
|
||||
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
|
||||
class AccumulatingTracer
|
||||
{
|
||||
public:
|
||||
static AccumulatingTracer* getInstance();
|
||||
|
||||
std::shared_ptr<TraceEvent> startEvent(const std::string& eventName);
|
||||
void finishEvent(std::shared_ptr<TraceEvent> event);
|
||||
|
||||
void printTraces();
|
||||
|
||||
private:
|
||||
struct AccumulatedTraceEvent
|
||||
{
|
||||
TraceEvent event;
|
||||
size_t count;
|
||||
double time;
|
||||
};
|
||||
|
||||
static std::shared_ptr<AccumulatingTracer> s_instance;
|
||||
static long long int s_nextTraceId;
|
||||
|
||||
AccumulatingTracer();
|
||||
AccumulatingTracer(const AccumulatingTracer&) = delete;
|
||||
void operator=(const AccumulatingTracer&) = delete;
|
||||
|
||||
std::map<std::string, AccumulatedTraceEvent> m_accumulatedEvents;
|
||||
std::map<std::thread::id, std::stack<TraceEvent*>> m_startedEvents;
|
||||
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
|
||||
template <typename TracerType>
|
||||
class ScopedTrace
|
||||
{
|
||||
public:
|
||||
ScopedTrace(const std::string& eventName, const std::string& fileName, int lineNumber, const std::string& functionName);
|
||||
~ScopedTrace();
|
||||
|
||||
private:
|
||||
std::shared_ptr<TraceEvent> m_event;
|
||||
QDateTime m_timeStamp;
|
||||
};
|
||||
|
||||
template <typename TracerType>
|
||||
ScopedTrace<TracerType>::ScopedTrace(
|
||||
const std::string& eventName, const std::string& fileName, int lineNumber, const std::string& functionName)
|
||||
{
|
||||
m_event = TracerType::getInstance()->startEvent(eventName);
|
||||
m_event->functionName = functionName;
|
||||
m_event->locationName = QFileInfo(QString::fromStdString(fileName)).fileName().toStdString() + ":" + std::to_string(lineNumber);
|
||||
|
||||
m_timeStamp = QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
template <typename TracerType>
|
||||
ScopedTrace<TracerType>::~ScopedTrace()
|
||||
{
|
||||
m_event->time = m_timeStamp.msecsTo(QDateTime::currentDateTime());
|
||||
TracerType::getInstance()->finishEvent(m_event);
|
||||
}
|
||||
|
||||
|
||||
#ifdef TRACING_ENABLED
|
||||
#ifdef USE_ACCUMULATED_TRACING
|
||||
#define TRACE(__name__) \
|
||||
ScopedTrace<AccumulatingTracer> __trace__(std::string(__name__), __FILE__, __LINE__, __FUNCTION__)
|
||||
|
||||
#define PRINT_TRACES() \
|
||||
AccumulatingTracer::getInstance()->printTraces()
|
||||
#else
|
||||
#define TRACE(__name__) \
|
||||
ScopedTrace<Tracer> __trace__(std::string(__name__), __FILE__, __LINE__, __FUNCTION__)
|
||||
|
||||
#define PRINT_TRACES() \
|
||||
Tracer::getInstance()->printTraces()
|
||||
#endif
|
||||
|
||||
|
||||
#else
|
||||
#define TRACE(__name__)
|
||||
#define PRINT_TRACES()
|
||||
#endif
|
||||
|
||||
#endif // TRACING_H
|
||||
Reference in New Issue
Block a user