Skip to content

Instantly share code, notes, and snippets.

@RameshKamath
Forked from TheCherno/Instrumentor.h
Created February 7, 2020 05:53
Show Gist options
  • Save RameshKamath/54000346cc02356d8be20110e3f8f593 to your computer and use it in GitHub Desktop.
Save RameshKamath/54000346cc02356d8be20110e3f8f593 to your computer and use it in GitHub Desktop.
Basic Instrumentation Profiler
//
// Basic instrumentation profiler by Cherno
// Usage: include this header file somewhere in your code (eg. precompiled header), and then use like:
//
// Instrumentor::Get().BeginSession("Session Name"); // Begin session
// {
// InstrumentationTimer timer("Profiled Scope Name"); // Place code like this in scopes you'd like to include in profiling
// // Code
// }
// Instrumentor::Get().EndSession(); // End Session
//
// You will probably want to macro-fy this, to switch on/off easily and use things like __FUNCSIG__ for the profile name.
//
#pragma once
#include <string>
#include <chrono>
#include <algorithm>
#include <fstream>
#include <thread>
struct ProfileResult
{
std::string Name;
long long Start, End;
uint32_t ThreadID;
};
struct InstrumentationSession
{
std::string Name;
};
class Instrumentor
{
private:
InstrumentationSession* m_CurrentSession;
std::ofstream m_OutputStream;
int m_ProfileCount;
public:
Instrumentor()
: m_CurrentSession(nullptr), m_ProfileCount(0)
{
}
void BeginSession(const std::string& name, const std::string& filepath = "results.json")
{
m_OutputStream.open(filepath);
WriteHeader();
m_CurrentSession = new InstrumentationSession{ name };
}
void EndSession()
{
WriteFooter();
m_OutputStream.close();
delete m_CurrentSession;
m_CurrentSession = nullptr;
m_ProfileCount = 0;
}
void WriteProfile(const ProfileResult& result)
{
if (m_ProfileCount++ > 0)
m_OutputStream << ",";
std::string name = result.Name;
std::replace(name.begin(), name.end(), '"', '\'');
m_OutputStream << "{";
m_OutputStream << "\"cat\":\"function\",";
m_OutputStream << "\"dur\":" << (result.End - result.Start) << ',';
m_OutputStream << "\"name\":\"" << name << "\",";
m_OutputStream << "\"ph\":\"X\",";
m_OutputStream << "\"pid\":0,";
m_OutputStream << "\"tid\":" << result.ThreadID << ",";
m_OutputStream << "\"ts\":" << result.Start;
m_OutputStream << "}";
m_OutputStream.flush();
}
void WriteHeader()
{
m_OutputStream << "{\"otherData\": {},\"traceEvents\":[";
m_OutputStream.flush();
}
void WriteFooter()
{
m_OutputStream << "]}";
m_OutputStream.flush();
}
static Instrumentor& Get()
{
static Instrumentor instance;
return instance;
}
};
class InstrumentationTimer
{
public:
InstrumentationTimer(const char* name)
: m_Name(name), m_Stopped(false)
{
m_StartTimepoint = std::chrono::high_resolution_clock::now();
}
~InstrumentationTimer()
{
if (!m_Stopped)
Stop();
}
void Stop()
{
auto endTimepoint = std::chrono::high_resolution_clock::now();
long long start = std::chrono::time_point_cast<std::chrono::microseconds>(m_StartTimepoint).time_since_epoch().count();
long long end = std::chrono::time_point_cast<std::chrono::microseconds>(endTimepoint).time_since_epoch().count();
uint32_t threadID = std::hash<std::thread::id>{}(std::this_thread::get_id());
Instrumentor::Get().WriteProfile({ m_Name, start, end, threadID });
m_Stopped = true;
}
private:
const char* m_Name;
std::chrono::time_point<std::chrono::high_resolution_clock> m_StartTimepoint;
bool m_Stopped;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment