Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/ps/Profiler2.cpp
Show All 33 Lines | |||||
#include <map> | #include <map> | ||||
#include <unordered_map> | #include <unordered_map> | ||||
CProfiler2 g_Profiler2; | CProfiler2 g_Profiler2; | ||||
// 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; | |||||
CProfiler2::CProfiler2() : | CProfiler2::CProfiler2() : | ||||
m_Initialised(false), m_FrameNumber(0), m_MgContext(NULL), m_GPU(NULL) | m_Initialised(false), m_FrameNumber(0), m_MgContext(NULL), m_GPU(NULL) | ||||
{ | { | ||||
} | } | ||||
CProfiler2::~CProfiler2() | CProfiler2::~CProfiler2() | ||||
{ | { | ||||
if (m_Initialised) | if (m_Initialised) | ||||
▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | default: | ||||
debug_warn(L"Invalid Mongoose event type"); | debug_warn(L"Invalid Mongoose event type"); | ||||
return NULL; | return NULL; | ||||
} | } | ||||
}; | }; | ||||
void CProfiler2::Initialise() | void CProfiler2::Initialise() | ||||
{ | { | ||||
ENSURE(!m_Initialised); | ENSURE(!m_Initialised); | ||||
int err = pthread_key_create(&m_TLS, &CProfiler2::TLSDtor); | |||||
ENSURE(err == 0); | |||||
m_Initialised = true; | m_Initialised = true; | ||||
RegisterCurrentThread("main"); | RegisterCurrentThread("main"); | ||||
} | } | ||||
void CProfiler2::InitialiseGPU() | void CProfiler2::InitialiseGPU() | ||||
{ | { | ||||
ENSURE(!m_GPU); | ENSURE(!m_GPU); | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | void CProfiler2::Shutdown() | ||||
{ | { | ||||
mg_stop(m_MgContext); | mg_stop(m_MgContext); | ||||
m_MgContext = NULL; | m_MgContext = NULL; | ||||
} | } | ||||
// the destructor is not called for the main thread | // the destructor is not called for the main thread | ||||
// we have to call it manually to avoid memory leaks | // we have to call it manually to avoid memory leaks | ||||
ENSURE(ThreadUtil::IsMainThread()); | ENSURE(ThreadUtil::IsMainThread()); | ||||
void * dataptr = pthread_getspecific(m_TLS); | |||||
TLSDtor(dataptr); | |||||
int err = pthread_key_delete(m_TLS); | |||||
ENSURE(err == 0); | |||||
m_Initialised = false; | m_Initialised = false; | ||||
} | } | ||||
void CProfiler2::RecordGPUFrameStart() | void CProfiler2::RecordGPUFrameStart() | ||||
{ | { | ||||
if (m_GPU) | if (m_GPU) | ||||
m_GPU->FrameStart(); | m_GPU->FrameStart(); | ||||
} | } | ||||
Show All 11 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); | ||||
} | } | ||||
/** | |||||
* Called by pthreads when a registered thread is destroyed. | |||||
*/ | |||||
void CProfiler2::TLSDtor(void* data) | |||||
{ | |||||
ThreadStorage* storage = (ThreadStorage*)data; | |||||
storage->GetProfiler().RemoveThreadStorage(storage); | |||||
delete (ThreadStorage*)data; | |||||
} | |||||
void CProfiler2::RegisterCurrentThread(const std::string& name) | void CProfiler2::RegisterCurrentThread(const std::string& name) | ||||
{ | { | ||||
ENSURE(m_Initialised); | ENSURE(m_Initialised); | ||||
ENSURE(pthread_getspecific(m_TLS) == NULL); // mustn't register a thread more than once | // Must not register a thread more than once. | ||||
ENSURE(m_CurrentStorage == nullptr); | |||||
ThreadStorage* storage = new ThreadStorage(*this, name); | m_CurrentStorage = new ThreadStorage(*this, name); | ||||
int err = pthread_setspecific(m_TLS, storage); | AddThreadStorage(m_CurrentStorage); | ||||
ENSURE(err == 0); | |||||
RecordSyncMarker(); | RecordSyncMarker(); | ||||
RecordEvent("thread start"); | RecordEvent("thread start"); | ||||
AddThreadStorage(storage); | |||||
} | } | ||||
void CProfiler2::AddThreadStorage(ThreadStorage* storage) | void CProfiler2::AddThreadStorage(ThreadStorage* storage) | ||||
{ | { | ||||
std::lock_guard<std::mutex> lock(m_Mutex); | std::lock_guard<std::mutex> lock(m_Mutex); | ||||
m_Threads.push_back(storage); | m_Threads.push_back(std::unique_ptr<ThreadStorage>(storage)); | ||||
} | } | ||||
void CProfiler2::RemoveThreadStorage(ThreadStorage* storage) | void CProfiler2::RemoveThreadStorage(ThreadStorage* storage) | ||||
{ | { | ||||
std::lock_guard<std::mutex> lock(m_Mutex); | std::lock_guard<std::mutex> lock(m_Mutex); | ||||
m_Threads.erase(std::find(m_Threads.begin(), m_Threads.end(), storage)); | m_Threads.erase(std::find_if(m_Threads.begin(), m_Threads.end(), [storage](const std::unique_ptr<ThreadStorage>& s) { return s.get() == storage; })); | ||||
} | } | ||||
CProfiler2::ThreadStorage::ThreadStorage(CProfiler2& profiler, const std::string& name) : | CProfiler2::ThreadStorage::ThreadStorage(CProfiler2& profiler, const std::string& name) : | ||||
m_Profiler(profiler), m_Name(name), m_BufferPos0(0), m_BufferPos1(0), m_LastTime(timer_Time()), m_HeldDepth(0) | m_Profiler(profiler), m_Name(name), m_BufferPos0(0), m_BufferPos1(0), m_LastTime(timer_Time()), m_HeldDepth(0) | ||||
{ | { | ||||
m_Buffer = new u8[BUFFER_SIZE]; | m_Buffer = new u8[BUFFER_SIZE]; | ||||
memset(m_Buffer, ITEM_NOP, BUFFER_SIZE); | memset(m_Buffer, ITEM_NOP, BUFFER_SIZE); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 406 Lines • ▼ Show 20 Lines | |||||
void CProfiler2::ConstructJSONOverview(std::ostream& stream) | void CProfiler2::ConstructJSONOverview(std::ostream& stream) | ||||
{ | { | ||||
TIMER(L"profile2 overview"); | TIMER(L"profile2 overview"); | ||||
std::lock_guard<std::mutex> lock(m_Mutex); | std::lock_guard<std::mutex> lock(m_Mutex); | ||||
stream << "{\"threads\":["; | stream << "{\"threads\":["; | ||||
for (size_t i = 0; i < m_Threads.size(); ++i) | bool first_time = true; | ||||
for (std::unique_ptr<ThreadStorage>& storage : m_Threads) | |||||
{ | { | ||||
if (i != 0) | if (!first_time) | ||||
stream << ","; | stream << ","; | ||||
stream << "{\"name\":\"" << CStr(m_Threads[i]->GetName()).EscapeToPrintableASCII() << "\"}"; | stream << "{\"name\":\"" << CStr(storage->GetName()).EscapeToPrintableASCII() << "\"}"; | ||||
first_time = false; | |||||
} | } | ||||
stream << "]}"; | stream << "]}"; | ||||
} | } | ||||
/** | /** | ||||
* Given a buffer and a visitor class (with functions OnEvent, OnEnter, OnLeave, OnAttribute), | * Given a buffer and a visitor class (with functions OnEvent, OnEnter, OnLeave, OnAttribute), | ||||
* calls the visitor for every item in the buffer. | * calls the visitor for every item in the buffer. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 156 Lines • ▼ Show 20 Lines | const char* CProfiler2::ConstructJSONResponse(std::ostream& stream, const std::string& thread) | ||||
std::string buffer; | std::string buffer; | ||||
{ | { | ||||
TIMER(L"profile2 get buffer"); | TIMER(L"profile2 get buffer"); | ||||
std::lock_guard<std::mutex> lock(m_Mutex); // lock against changes to m_Threads or deletions of ThreadStorage | std::lock_guard<std::mutex> lock(m_Mutex); // lock against changes to m_Threads or deletions of ThreadStorage | ||||
ThreadStorage* storage = NULL; | auto it = std::find_if(m_Threads.begin(), m_Threads.end(), [&thread](std::unique_ptr<ThreadStorage>& storage) { | ||||
for (size_t i = 0; i < m_Threads.size(); ++i) | return storage->GetName() == thread; | ||||
{ | }); | ||||
if (m_Threads[i]->GetName() == thread) | |||||
{ | |||||
storage = m_Threads[i]; | |||||
break; | |||||
} | |||||
} | |||||
if (!storage) | if (it == m_Threads.end()) | ||||
return "cannot find named thread"; | return "cannot find named thread"; | ||||
stream << "{\"events\":[\n"; | stream << "{\"events\":[\n"; | ||||
stream << "[\n"; | stream << "[\n"; | ||||
buffer = storage->GetBuffer(); | buffer = (*it)->GetBuffer(); | ||||
} | } | ||||
BufferVisitor_Dump visitor(stream); | BufferVisitor_Dump visitor(stream); | ||||
RunBufferVisitor(buffer, visitor); | RunBufferVisitor(buffer, visitor); | ||||
stream << "null]\n]}"; | stream << "null]\n]}"; | ||||
return NULL; | return NULL; | ||||
} | } | ||||
void CProfiler2::SaveToFile() | void CProfiler2::SaveToFile() | ||||
{ | { | ||||
OsPath path = psLogDir()/"profile2.jsonp"; | OsPath path = psLogDir()/"profile2.jsonp"; | ||||
std::ofstream stream(OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc); | std::ofstream stream(OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc); | ||||
ENSURE(stream.good()); | ENSURE(stream.good()); | ||||
std::vector<ThreadStorage*> threads; | |||||
{ | |||||
std::lock_guard<std::mutex> lock(m_Mutex); | |||||
threads = m_Threads; | |||||
} | |||||
stream << "profileDataCB({\"threads\": [\n"; | stream << "profileDataCB({\"threads\": [\n"; | ||||
for (size_t i = 0; i < threads.size(); ++i) | bool first_time = true; | ||||
for (std::unique_ptr<ThreadStorage>& storage : m_Threads) | |||||
{ | { | ||||
if (i != 0) | if (!first_time) | ||||
stream << ",\n"; | stream << ",\n"; | ||||
stream << "{\"name\":\"" << CStr(threads[i]->GetName()).EscapeToPrintableASCII() << "\",\n"; | stream << "{\"name\":\"" << CStr(storage->GetName()).EscapeToPrintableASCII() << "\",\n"; | ||||
stream << "\"data\": "; | stream << "\"data\": "; | ||||
ConstructJSONResponse(stream, threads[i]->GetName()); | ConstructJSONResponse(stream, storage->GetName()); | ||||
stream << "\n}"; | stream << "\n}"; | ||||
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) | ||||
Show All 37 Lines |
Wildfire Games · Phabricator