178 lines
5.3 KiB
C++
178 lines
5.3 KiB
C++
#ifndef INSTRUMENTOR_HPP
|
|
#define INSTRUMENTOR_HPP
|
|
|
|
#include "open_engine/logging.hpp"
|
|
|
|
#include <iomanip>
|
|
#include <mutex>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <chrono>
|
|
#include <algorithm>
|
|
#include <fstream>
|
|
#include <thread>
|
|
|
|
namespace OpenEngine {
|
|
|
|
using FloatingPointMicroseconds = std::chrono::duration<double, std::micro>;
|
|
|
|
struct ProfileResult
|
|
{
|
|
std::string name;
|
|
FloatingPointMicroseconds start;
|
|
std::chrono::microseconds elapsed_time;
|
|
std::thread::id thread_id;
|
|
};
|
|
|
|
struct InstrumentationSession
|
|
{
|
|
const char* name;
|
|
};
|
|
|
|
class Instrumentor
|
|
{
|
|
public:
|
|
~Instrumentor()
|
|
{
|
|
EndSession();
|
|
};
|
|
|
|
void BeginSession(const char* name, const std::string& filepath = "results.json")
|
|
{
|
|
std::lock_guard lock(mutex);
|
|
if (current_session) {
|
|
if (Logger::GetCoreLogger())
|
|
OE_CORE_ERROR("Instrumentor::BeginSession({}), when session {} already exists", name, current_session->name);
|
|
InternalEndSession();
|
|
}
|
|
|
|
output_stream.open(filepath);
|
|
if(output_stream.is_open()) {
|
|
current_session = new InstrumentationSession(name);
|
|
WriteHeader();
|
|
} else {
|
|
if (Logger::GetCoreLogger())
|
|
OE_CORE_ERROR("Instrumentor could not open results file: {}", filepath);
|
|
}
|
|
};
|
|
|
|
void EndSession()
|
|
{
|
|
std::lock_guard lock(mutex);
|
|
InternalEndSession();
|
|
};
|
|
|
|
void WriteProfile(const ProfileResult& result)
|
|
{
|
|
std::stringstream json;
|
|
|
|
std::string name = result.name;
|
|
std::replace(name.begin(), name.end(), '"', '\'');
|
|
|
|
json << std::setprecision(3) << std::fixed;
|
|
json << ",{";
|
|
json << "\"cat\":\"function\",";
|
|
json << "\"dur\":" << (result.elapsed_time.count()) << ',';
|
|
json << "\"name\":\"" << name << "\",";
|
|
json << "\"ph\":\"X\",";
|
|
json << "\"pid\":0,";
|
|
json << "\"tid\":" << result.thread_id << ",";
|
|
json << "\"ts\":" << result.start.count();
|
|
json << "}";
|
|
|
|
std::lock_guard lock(mutex);
|
|
|
|
if (current_session) {
|
|
output_stream << json.str();
|
|
output_stream.flush();
|
|
}
|
|
};
|
|
|
|
static Instrumentor& Get()
|
|
{
|
|
static Instrumentor instance;
|
|
return instance;
|
|
};
|
|
|
|
private:
|
|
void WriteHeader()
|
|
{
|
|
output_stream << "{\"otherData\": {},\"traceEvents\":[{}";
|
|
};
|
|
|
|
void WriteFooter()
|
|
{
|
|
output_stream << "]}";
|
|
};
|
|
|
|
void InternalEndSession() {
|
|
if (current_session) {
|
|
WriteFooter();
|
|
output_stream.close();
|
|
delete current_session;
|
|
current_session = nullptr;
|
|
}
|
|
}
|
|
|
|
Instrumentor()
|
|
{
|
|
};
|
|
|
|
private:
|
|
std::mutex mutex;
|
|
InstrumentationSession* current_session = nullptr;
|
|
std::ofstream output_stream;
|
|
int profile_count = 0;
|
|
};
|
|
|
|
class InstrumentationTimer
|
|
{
|
|
public:
|
|
InstrumentationTimer(const char* name)
|
|
: name(name), stopped(false)
|
|
{
|
|
start_timepoint = std::chrono::high_resolution_clock::now();
|
|
};
|
|
|
|
~InstrumentationTimer()
|
|
{
|
|
if (!stopped)
|
|
Stop();
|
|
};
|
|
|
|
void Stop()
|
|
{
|
|
auto end_timepoint = std::chrono::high_resolution_clock::now();
|
|
|
|
auto start = FloatingPointMicroseconds{start_timepoint.time_since_epoch()};
|
|
auto elapsed_time = std::chrono::time_point_cast<std::chrono::microseconds>(end_timepoint).time_since_epoch()
|
|
- std::chrono::time_point_cast<std::chrono::microseconds>(start_timepoint).time_since_epoch();
|
|
|
|
uint32_t thread_id = std::hash<std::thread::id>{}(std::this_thread::get_id());
|
|
Instrumentor::Get().WriteProfile({ name, start, elapsed_time, std::this_thread::get_id() });
|
|
|
|
stopped = true;
|
|
};
|
|
|
|
private:
|
|
const char* name;
|
|
std::chrono::time_point<std::chrono::high_resolution_clock> start_timepoint;
|
|
bool stopped;
|
|
};
|
|
}
|
|
|
|
#define OE_PROFILE 1
|
|
#if OE_PROFILE
|
|
#define OE_PROFILE_BEGIN_SESSION(name, filepath) ::OpenEngine::Instrumentor::Get().BeginSession(name, filepath)
|
|
#define OE_PROFILE_END_SESSION() ::OpenEngine::Instrumentor::Get().EndSession()
|
|
#define OE_PROFILE_SCOPE(name) ::OpenEngine::InstrumentationTimer timer##__LINE__(name);
|
|
#define OE_PROFILE_FUNCTION() OE_PROFILE_SCOPE(__PRETTY_FUNCTION__)
|
|
#else
|
|
#define OE_PROFILE_BEGIN_SESSION(name, filepath)
|
|
#define OE_PROFILE_END_SESSION()
|
|
#define OE_PROFILE_SCOPE(name)
|
|
#define OE_PROFILE_FUNCTION()
|
|
#endif
|
|
|
|
#endif // INSTRUMENTOR_HPP
|