Skip to content

Instantly share code, notes, and snippets.

@TheCherno
Last active October 17, 2025 07:36
Show Gist options
  • Save TheCherno/31f135eea6ee729ab5f26a6908eb3a5e to your computer and use it in GitHub Desktop.
Save TheCherno/31f135eea6ee729ab5f26a6908eb3a5e to your computer and use it in GitHub Desktop.

Revisions

  1. TheCherno revised this gist Nov 20, 2019. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions Instrumentor.h
    Original file line number Diff line number Diff line change
    @@ -3,12 +3,12 @@

    // Usage: include this header file somewhere in your code (eg. precompiled header), and then use like:
    //
    // Instrumentor::Get().BeginSession("Session Name"); // Begin session
    // 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
    // InstrumentationTimer timer("Profiled Scope Name"); // Place code like this in scopes you'd like to include in profiling
    // // Code
    // }
    // Instrumentor::Get().EndSession(); // End Session
    // 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.
    //
    @@ -96,8 +96,8 @@ class Instrumentor

    static Instrumentor& Get()
    {
    static Instrumentor* instance = new Instrumentor();
    return *instance;
    static Instrumentor instance;
    return instance;
    }
    };

    @@ -130,6 +130,6 @@ class InstrumentationTimer
    }
    private:
    const char* m_Name;
    std::chrono::time_point<std::chrono::steady_clock> m_StartTimepoint;
    std::chrono::time_point<std::chrono::high_resolution_clock> m_StartTimepoint;
    bool m_Stopped;
    };
  2. TheCherno created this gist Nov 16, 2019.
    135 changes: 135 additions & 0 deletions Instrumentor.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,135 @@
    //
    // 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 = new Instrumentor();
    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::steady_clock> m_StartTimepoint;
    bool m_Stopped;
    };