Changeset View
Changeset View
Standalone View
Standalone View
source/ps/Profiler2.cpp
/* Copyright (C) 2021 Wildfire Games. | /* Copyright (C) 2022 Wildfire Games. | ||||||||||
* | * | ||||||||||
* Permission is hereby granted, free of charge, to any person obtaining | * Permission is hereby granted, free of charge, to any person obtaining | ||||||||||
* a copy of this software and associated documentation files (the | * a copy of this software and associated documentation files (the | ||||||||||
* "Software"), to deal in the Software without restriction, including | * "Software"), to deal in the Software without restriction, including | ||||||||||
* without limitation the rights to use, copy, modify, merge, publish, | * without limitation the rights to use, copy, modify, merge, publish, | ||||||||||
* distribute, sublicense, and/or sell copies of the Software, and to | * distribute, sublicense, and/or sell copies of the Software, and to | ||||||||||
* permit persons to whom the Software is furnished to do so, subject to | * permit persons to whom the Software is furnished to do so, subject to | ||||||||||
* the following conditions: | * the following conditions: | ||||||||||
Show All 25 Lines | |||||||||||
#include <fstream> | #include <fstream> | ||||||||||
#include <iomanip> | #include <iomanip> | ||||||||||
#include <map> | #include <map> | ||||||||||
#include <set> | #include <set> | ||||||||||
#include <tuple> | #include <tuple> | ||||||||||
#include <unordered_map> | #include <unordered_map> | ||||||||||
CProfiler2 g_Profiler2; | CProfiler2* g_Profiler2 = nullptr; | ||||||||||
const size_t CProfiler2::MAX_ATTRIBUTE_LENGTH = 256; | const size_t CProfiler2::MAX_ATTRIBUTE_LENGTH = 256; | ||||||||||
// TODO: what's a good size? | // TODO: what's a good size? | ||||||||||
const size_t CProfiler2::BUFFER_SIZE = 4 * 1024 * 1024; | const size_t CProfiler2::BUFFER_SIZE = 4 * 1024 * 1024; | ||||||||||
const size_t CProfiler2::HOLD_BUFFER_SIZE = 128 * 1024; | const size_t CProfiler2::HOLD_BUFFER_SIZE = 128 * 1024; | ||||||||||
// A human-recognisable pattern (for debugging) followed by random bytes (for uniqueness) | // A human-recognisable pattern (for debugging) followed by random bytes (for uniqueness) | ||||||||||
const u8 CProfiler2::RESYNC_MAGIC[8] = {0x11, 0x22, 0x33, 0x44, 0xf4, 0x93, 0xbe, 0x15}; | const u8 CProfiler2::RESYNC_MAGIC[8] = {0x11, 0x22, 0x33, 0x44, 0xf4, 0x93, 0xbe, 0x15}; | ||||||||||
thread_local CProfiler2::ThreadStorage* CProfiler2::m_CurrentStorage = nullptr; | thread_local CProfiler2::ThreadStorage* CProfiler2::m_CurrentStorage = nullptr; | ||||||||||
CProfiler2::CProfiler2() : | CProfiler2::CProfiler2() | ||||||||||
Stan: Could make it use the singleton class like https://github. | |||||||||||
m_Initialised(false), m_FrameNumber(0), m_MgContext(NULL), m_GPU(NULL) | |||||||||||
{ | { | ||||||||||
ENSURE(Threading::IsMainThread()); | |||||||||||
ENSURE(!g_Profiler2); | |||||||||||
RegisterCurrentThread("main"); | |||||||||||
g_Profiler2 = this; | |||||||||||
} | } | ||||||||||
CProfiler2::~CProfiler2() | CProfiler2::~CProfiler2() | ||||||||||
{ | { | ||||||||||
if (m_Initialised) | ENSURE(Threading::IsMainThread()); | ||||||||||
Shutdown(); | // Must shutdown GPU before profiler. | ||||||||||
ENSURE(!m_GPU); | |||||||||||
g_Profiler2 = nullptr; | |||||||||||
if (m_MgContext) | |||||||||||
{ | |||||||||||
mg_stop(m_MgContext); | |||||||||||
} | |||||||||||
Not Done Inline Actions
Stan: | |||||||||||
} | } | ||||||||||
/** | /** | ||||||||||
* Mongoose callback. Run in an arbitrary thread (possibly concurrently with other requests). | * Mongoose callback. Run in an arbitrary thread (possibly concurrently with other requests). | ||||||||||
*/ | */ | ||||||||||
static void* MgCallback(mg_event event, struct mg_connection *conn, const struct mg_request_info *request_info) | static void* MgCallback(mg_event event, struct mg_connection *conn, const struct mg_request_info *request_info) | ||||||||||
{ | { | ||||||||||
CProfiler2* profiler = (CProfiler2*)request_info->user_data; | CProfiler2* profiler = (CProfiler2*)request_info->user_data; | ||||||||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | case MG_INIT_SSL: | ||||||||||
return NULL; | return NULL; | ||||||||||
default: | default: | ||||||||||
debug_warn(L"Invalid Mongoose event type"); | debug_warn(L"Invalid Mongoose event type"); | ||||||||||
return NULL; | return NULL; | ||||||||||
} | } | ||||||||||
}; | }; | ||||||||||
void CProfiler2::Initialise() | |||||||||||
{ | |||||||||||
ENSURE(!m_Initialised); | |||||||||||
m_Initialised = true; | |||||||||||
RegisterCurrentThread("main"); | |||||||||||
} | |||||||||||
void CProfiler2::InitialiseGPU() | void CProfiler2::InitialiseGPU() | ||||||||||
{ | { | ||||||||||
ENSURE(!m_GPU); | ENSURE(!m_GPU); | ||||||||||
m_GPU = new CProfiler2GPU(*this); | m_GPU = new CProfiler2GPU(*this); | ||||||||||
} | } | ||||||||||
void CProfiler2::EnableHTTP() | void CProfiler2::EnableHTTP() | ||||||||||
{ | { | ||||||||||
ENSURE(m_Initialised); | |||||||||||
LOGMESSAGERENDER("Starting profiler2 HTTP server"); | LOGMESSAGERENDER("Starting profiler2 HTTP server"); | ||||||||||
// Ignore multiple enablings | // Ignore multiple enablings | ||||||||||
if (m_MgContext) | if (m_MgContext) | ||||||||||
return; | return; | ||||||||||
CStr listeningPort = "8000"; | CStr listeningPort = "8000"; | ||||||||||
CStr listeningServer = "127.0.0.1"; | CStr listeningServer = "127.0.0.1"; | ||||||||||
Show All 12 Lines | const char* options[] = { | ||||||||||
nullptr | nullptr | ||||||||||
}; | }; | ||||||||||
m_MgContext = mg_start(MgCallback, this, options); | m_MgContext = mg_start(MgCallback, this, options); | ||||||||||
ENSURE(m_MgContext); | ENSURE(m_MgContext); | ||||||||||
} | } | ||||||||||
void CProfiler2::EnableGPU() | void CProfiler2::EnableGPU() | ||||||||||
{ | { | ||||||||||
ENSURE(m_Initialised); | |||||||||||
if (!m_GPU) | if (!m_GPU) | ||||||||||
{ | { | ||||||||||
LOGMESSAGERENDER("Starting profiler2 GPU mode"); | LOGMESSAGERENDER("Starting profiler2 GPU mode"); | ||||||||||
InitialiseGPU(); | InitialiseGPU(); | ||||||||||
} | } | ||||||||||
} | } | ||||||||||
void CProfiler2::ShutdownGPU() | void CProfiler2::ShutdownGPU() | ||||||||||
Show All 22 Lines | void CProfiler2::Toggle() | ||||||||||
} | } | ||||||||||
else if (!m_GPU && !m_MgContext) | else if (!m_GPU && !m_MgContext) | ||||||||||
{ | { | ||||||||||
EnableGPU(); | EnableGPU(); | ||||||||||
EnableHTTP(); | EnableHTTP(); | ||||||||||
} | } | ||||||||||
} | } | ||||||||||
void CProfiler2::Shutdown() | |||||||||||
{ | |||||||||||
ENSURE(m_Initialised); | |||||||||||
ENSURE(!m_GPU); // must shutdown GPU before profiler | |||||||||||
if (m_MgContext) | |||||||||||
{ | |||||||||||
mg_stop(m_MgContext); | |||||||||||
m_MgContext = NULL; | |||||||||||
} | |||||||||||
// the destructor is not called for the main thread | |||||||||||
// we have to call it manually to avoid memory leaks | |||||||||||
ENSURE(Threading::IsMainThread()); | |||||||||||
m_Initialised = false; | |||||||||||
} | |||||||||||
void CProfiler2::RecordGPUFrameStart() | void CProfiler2::RecordGPUFrameStart() | ||||||||||
{ | { | ||||||||||
if (m_GPU) | if (m_GPU) | ||||||||||
m_GPU->FrameStart(); | m_GPU->FrameStart(); | ||||||||||
} | } | ||||||||||
void CProfiler2::RecordGPUFrameEnd() | void CProfiler2::RecordGPUFrameEnd() | ||||||||||
{ | { | ||||||||||
Show All 10 Lines | |||||||||||
void CProfiler2::RecordGPURegionLeave(const char* id) | void CProfiler2::RecordGPURegionLeave(const char* id) | ||||||||||
{ | { | ||||||||||
if (m_GPU) | if (m_GPU) | ||||||||||
m_GPU->RegionLeave(id); | m_GPU->RegionLeave(id); | ||||||||||
} | } | ||||||||||
void CProfiler2::RegisterCurrentThread(const std::string& name) | void CProfiler2::RegisterCurrentThread(const std::string& name) | ||||||||||
{ | { | ||||||||||
ENSURE(m_Initialised); | |||||||||||
// Must not register a thread more than once. | // Must not register a thread more than once. | ||||||||||
ENSURE(m_CurrentStorage == nullptr); | ENSURE(m_CurrentStorage == nullptr); | ||||||||||
m_CurrentStorage = new ThreadStorage(*this, name); | m_CurrentStorage = new ThreadStorage(*this, name); | ||||||||||
AddThreadStorage(m_CurrentStorage); | AddThreadStorage(m_CurrentStorage); | ||||||||||
RecordSyncMarker(); | RecordSyncMarker(); | ||||||||||
RecordEvent("thread start"); | RecordEvent("thread start"); | ||||||||||
▲ Show 20 Lines • Show All 655 Lines • ▼ Show 20 Lines | for (std::unique_ptr<ThreadStorage>& storage : m_Threads) | ||||||||||
first_time = false; | first_time = false; | ||||||||||
} | } | ||||||||||
stream << "\n]});\n"; | stream << "\n]});\n"; | ||||||||||
} | } | ||||||||||
CProfile2SpikeRegion::CProfile2SpikeRegion(const char* name, double spikeLimit) : | CProfile2SpikeRegion::CProfile2SpikeRegion(const char* name, double spikeLimit) : | ||||||||||
m_Name(name), m_Limit(spikeLimit), m_PushedHold(true) | m_Name(name), m_Limit(spikeLimit), m_PushedHold(true) | ||||||||||
{ | { | ||||||||||
if (g_Profiler2.HoldLevel() < 8 && g_Profiler2.HoldType() != CProfiler2::ThreadStorage::BUFFER_AGGREGATE) | if (g_Profiler2->HoldLevel() < 8 && g_Profiler2->HoldType() != CProfiler2::ThreadStorage::BUFFER_AGGREGATE) | ||||||||||
g_Profiler2.HoldMessages(CProfiler2::ThreadStorage::BUFFER_SPIKE); | g_Profiler2->HoldMessages(CProfiler2::ThreadStorage::BUFFER_SPIKE); | ||||||||||
else | else | ||||||||||
m_PushedHold = false; | m_PushedHold = false; | ||||||||||
COMPILER_FENCE; | COMPILER_FENCE; | ||||||||||
g_Profiler2.RecordRegionEnter(m_Name); | g_Profiler2->RecordRegionEnter(m_Name); | ||||||||||
m_StartTime = g_Profiler2.GetTime(); | m_StartTime = g_Profiler2->GetTime(); | ||||||||||
} | } | ||||||||||
CProfile2SpikeRegion::~CProfile2SpikeRegion() | CProfile2SpikeRegion::~CProfile2SpikeRegion() | ||||||||||
{ | { | ||||||||||
double time = g_Profiler2.GetTime(); | double time = g_Profiler2->GetTime(); | ||||||||||
g_Profiler2.RecordRegionLeave(); | g_Profiler2->RecordRegionLeave(); | ||||||||||
bool shouldWrite = time - m_StartTime > m_Limit; | bool shouldWrite = time - m_StartTime > m_Limit; | ||||||||||
if (m_PushedHold) | if (m_PushedHold) | ||||||||||
g_Profiler2.StopHoldingMessages(shouldWrite); | g_Profiler2->StopHoldingMessages(shouldWrite); | ||||||||||
} | } | ||||||||||
CProfile2AggregatedRegion::CProfile2AggregatedRegion(const char* name, double spikeLimit) : | CProfile2AggregatedRegion::CProfile2AggregatedRegion(const char* name, double spikeLimit) : | ||||||||||
m_Name(name), m_Limit(spikeLimit), m_PushedHold(true) | m_Name(name), m_Limit(spikeLimit), m_PushedHold(true) | ||||||||||
{ | { | ||||||||||
if (g_Profiler2.HoldLevel() < 8 && g_Profiler2.HoldType() != CProfiler2::ThreadStorage::BUFFER_AGGREGATE) | if (g_Profiler2->HoldLevel() < 8 && g_Profiler2->HoldType() != CProfiler2::ThreadStorage::BUFFER_AGGREGATE) | ||||||||||
g_Profiler2.HoldMessages(CProfiler2::ThreadStorage::BUFFER_AGGREGATE); | g_Profiler2->HoldMessages(CProfiler2::ThreadStorage::BUFFER_AGGREGATE); | ||||||||||
else | else | ||||||||||
m_PushedHold = false; | m_PushedHold = false; | ||||||||||
COMPILER_FENCE; | COMPILER_FENCE; | ||||||||||
g_Profiler2.RecordRegionEnter(m_Name); | g_Profiler2->RecordRegionEnter(m_Name); | ||||||||||
m_StartTime = g_Profiler2.GetTime(); | m_StartTime = g_Profiler2->GetTime(); | ||||||||||
} | } | ||||||||||
CProfile2AggregatedRegion::~CProfile2AggregatedRegion() | CProfile2AggregatedRegion::~CProfile2AggregatedRegion() | ||||||||||
{ | { | ||||||||||
double time = g_Profiler2.GetTime(); | double time = g_Profiler2->GetTime(); | ||||||||||
g_Profiler2.RecordRegionLeave(); | g_Profiler2->RecordRegionLeave(); | ||||||||||
bool shouldWrite = time - m_StartTime > m_Limit; | bool shouldWrite = time - m_StartTime > m_Limit; | ||||||||||
if (m_PushedHold) | if (m_PushedHold) | ||||||||||
g_Profiler2.StopHoldingMessages(shouldWrite, true); | g_Profiler2->StopHoldingMessages(shouldWrite, true); | ||||||||||
} | } |
Wildfire Games · Phabricator
Could make it use the singleton class like https://github.com/0ad/0ad/blob/f314b5b9d2be852a4d25f4b9ef40ab4acf4f2002/source/scriptinterface/ScriptEngine.h