331 lines
8.4 KiB
C++
331 lines
8.4 KiB
C++
|
//====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//
|
||
|
//=============================================================================//
|
||
|
|
||
|
#include "tier0/platform.h"
|
||
|
#include "tier0/miniprofiler.h"
|
||
|
#include "tier0/cache_hints.h"
|
||
|
#include "tier0/dbg.h"
|
||
|
#include "tier0/threadtools.h"
|
||
|
#include <time.h>
|
||
|
#include <string.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#ifdef _PS3
|
||
|
#include "ps3/ps3_helpers.h"
|
||
|
#endif
|
||
|
|
||
|
#if defined( PLATFORM_WINDOWS_PC )
|
||
|
#define WIN_32_LEAN_AND_MEAN
|
||
|
#include <windows.h> // Currently needed for LARGE_INTEGER
|
||
|
#endif
|
||
|
|
||
|
// NOTE: This has to be the last file included!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
#ifdef IS_WINDOWS_PC
|
||
|
CTHREADLOCALPTR( CMiniProfiler ) s_pLastMiniProfilerTS;
|
||
|
#else
|
||
|
CMiniProfiler *s_pLastMiniProfilerTS;
|
||
|
#endif
|
||
|
|
||
|
static CLinkedMiniProfiler *s_pDummyList = NULL;
|
||
|
|
||
|
class CRootMiniProfiler : public CLinkedMiniProfiler
|
||
|
{
|
||
|
public:
|
||
|
CRootMiniProfiler( void ) : CLinkedMiniProfiler( "DummyRoot", &s_pDummyList )
|
||
|
{
|
||
|
s_pLastMiniProfilerTS = this;
|
||
|
}
|
||
|
};
|
||
|
CRootMiniProfiler g_rootMiniProfiler;
|
||
|
|
||
|
#if defined( STATIC_LINK ) || defined( _LINUX )
|
||
|
// in static link scenario, we don't need any extra linkage specified where we define our variables
|
||
|
#undef MINIPROFILER_DLL_LINKAGE
|
||
|
#define MINIPROFILER_DLL_LINKAGE
|
||
|
#else
|
||
|
extern "C"
|
||
|
{
|
||
|
#endif
|
||
|
MINIPROFILER_DLL_LINKAGE CMiniProfiler *g_pRootMiniProfiler = &g_rootMiniProfiler;
|
||
|
MINIPROFILER_DLL_LINKAGE CLinkedMiniProfiler *g_pGlobalMiniProfilers = NULL;
|
||
|
MINIPROFILER_DLL_LINKAGE CLinkedMiniProfiler *g_pAssertMiniProfilers = NULL;
|
||
|
MINIPROFILER_DLL_LINKAGE CMiniProfiler *g_pLastMiniProfiler = &g_rootMiniProfiler;
|
||
|
MINIPROFILER_DLL_LINKAGE uint32 g_nMiniProfilerFrame = 0;
|
||
|
#if defined( STATIC_LINK ) || defined( _LINUX )
|
||
|
#else
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int64 GetHardwareClockReliably()
|
||
|
{
|
||
|
int64 res = 0;
|
||
|
#if ENABLE_MINI_PROFILER && IS_WINDOWS_PC
|
||
|
QueryPerformanceCounter( ( LARGE_INTEGER* )&res );
|
||
|
#endif
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
CMiniProfiler* PushMiniProfilerTS( CMiniProfiler *pProfiler )
|
||
|
{
|
||
|
CMiniProfiler *pLast = s_pLastMiniProfilerTS;
|
||
|
if ( !pLast )
|
||
|
{
|
||
|
pLast = &g_rootMiniProfiler;
|
||
|
}
|
||
|
s_pLastMiniProfilerTS = pProfiler;
|
||
|
return pLast;
|
||
|
}
|
||
|
|
||
|
|
||
|
CThreadFastMutex g_ProfilerListMutex;
|
||
|
//CInterlockedInt g_LinkedMiniProfilerIdCount;
|
||
|
|
||
|
void AppendMiniProfilerToList( CLinkedMiniProfiler *pProfiler, CLinkedMiniProfiler **ppList )
|
||
|
{
|
||
|
#if ENABLE_MINI_PROFILER
|
||
|
g_ProfilerListMutex.Lock();
|
||
|
//pProfiler->m_nId = g_LinkedMiniProfilerIdCount++;
|
||
|
if( IsDebug() )
|
||
|
{
|
||
|
int nProfilerCount = 0; NOTE_UNUSED( nProfilerCount );
|
||
|
CLinkedMiniProfiler *pTest;
|
||
|
for( pTest = *ppList; pTest; pTest = pTest->m_pNext )
|
||
|
{
|
||
|
nProfilerCount++;
|
||
|
if( pTest == pProfiler )
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if( pProfiler == pTest && !pProfiler->m_ppPrev )
|
||
|
DebuggerBreak(); // the miniprofiler is not yet added to any list (pprev == 0) but it's already in this list (pTest == pProfiler)
|
||
|
if( !pTest && pProfiler->m_ppPrev )
|
||
|
DebuggerBreak(); // the profiler is not in this list, but it's already in some list (pNext!=0)
|
||
|
}
|
||
|
|
||
|
if( !pProfiler->m_ppPrev )
|
||
|
{
|
||
|
// the profiler is not yet in any list
|
||
|
|
||
|
if ( ppList )
|
||
|
{
|
||
|
pProfiler->m_pNext = *ppList;
|
||
|
pProfiler->m_ppPrev = ppList;
|
||
|
if( pProfiler->m_pNext )
|
||
|
{
|
||
|
pProfiler->m_pNext->m_ppPrev = &pProfiler->m_pNext;
|
||
|
}
|
||
|
*ppList = pProfiler;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( !ppList )
|
||
|
{
|
||
|
// Warning: unhooking something that wasn't removed from the previous list?
|
||
|
if( IsDebug() )
|
||
|
{
|
||
|
DebuggerBreak();
|
||
|
}
|
||
|
pProfiler->m_pNext = NULL;
|
||
|
pProfiler->m_ppPrev = NULL;
|
||
|
}
|
||
|
}
|
||
|
g_ProfilerListMutex.Unlock();
|
||
|
#endif // ENABLE_MINI_PROFILER
|
||
|
}
|
||
|
|
||
|
|
||
|
void RemoveMiniProfilerFromList( CLinkedMiniProfiler *pProfiler )
|
||
|
{
|
||
|
#if ENABLE_MINI_PROFILER
|
||
|
g_ProfilerListMutex.Lock();
|
||
|
// We need to remove miniprofiler from the list properly. This is an issue because we unload DLLs sometimes.
|
||
|
if ( pProfiler->m_ppPrev )
|
||
|
{
|
||
|
*pProfiler->m_ppPrev = pProfiler->m_pNext; // that's it: we just remove this object from the list by linking previous object with the next
|
||
|
}
|
||
|
if ( pProfiler->m_pNext )
|
||
|
{
|
||
|
pProfiler->m_pNext->m_ppPrev = pProfiler->m_ppPrev;
|
||
|
}
|
||
|
|
||
|
// unhook the profiler from the list completely, so that we don't try to do it twice
|
||
|
pProfiler->m_ppPrev = NULL;
|
||
|
pProfiler->m_pNext = NULL;
|
||
|
|
||
|
g_ProfilerListMutex.Unlock();
|
||
|
#endif // ENABLE_MINI_PROFILER
|
||
|
}
|
||
|
|
||
|
#if ENABLE_HARDWARE_PROFILER
|
||
|
DLL_CLASS_EXPORT void CMiniProfiler::Publish(const char *szMessage, ...)
|
||
|
{
|
||
|
#ifdef _X360
|
||
|
if(m_numCalls >= 100 || m_numTimeBaseTicks > 50) // 500 timebase ticks is 1 microsecond
|
||
|
{
|
||
|
char szBuf[256];
|
||
|
va_list args;
|
||
|
va_start(args, szMessage);
|
||
|
vsnprintf(szBuf, sizeof(szBuf), szMessage, args);
|
||
|
PIXAddNamedCounter(float(INT32(m_numTimeBaseTicks-m_numTimeBaseTicksInCallees))*0.02f, "Ex:%s,mcs", szBuf);
|
||
|
PIXAddNamedCounter(float(INT32(m_numTimeBaseTicks))*0.02f, "%s,mcs", szBuf);
|
||
|
if(m_numCalls)
|
||
|
PIXAddNamedCounter((float)(64*INT32(m_numTimeBaseTicks)/INT32(m_numCalls)), "%s,ticks", szBuf);
|
||
|
PIXAddNamedCounter((float)(INT32(m_numCalls)), "%s,calls", szBuf);
|
||
|
}
|
||
|
#endif
|
||
|
Reset();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
void CLinkedMiniProfiler::Publish(uint nHistoryMax)
|
||
|
{
|
||
|
#if ENABLE_HARDWARE_PROFILER
|
||
|
if(nHistoryMax != m_nHistoryMax)
|
||
|
{
|
||
|
PurgeHistory();
|
||
|
delete[]m_pHistory;
|
||
|
m_nHistoryMax = nHistoryMax;
|
||
|
if(nHistoryMax)
|
||
|
m_pHistory = new CMiniProfiler[nHistoryMax];
|
||
|
else
|
||
|
m_pHistory = NULL;
|
||
|
m_nHistoryLength = 0;
|
||
|
m_nFrameHistoryBegins = g_nMiniProfilerFrame;
|
||
|
}
|
||
|
else
|
||
|
if(m_nHistoryLength >= nHistoryMax)
|
||
|
{
|
||
|
PurgeHistory();
|
||
|
}
|
||
|
if(m_pHistory)
|
||
|
m_pHistory[m_nHistoryLength++] = *this;
|
||
|
|
||
|
CMiniProfiler::Publish(m_szName);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
#if ENABLE_HARDWARE_PROFILER
|
||
|
static char g_szFileName[128] = "";
|
||
|
static FILE *g_pPurgeFile = NULL;
|
||
|
#endif
|
||
|
|
||
|
void CLinkedMiniProfiler::PurgeHistory()
|
||
|
{
|
||
|
#if ENABLE_HARDWARE_PROFILER
|
||
|
if(m_nHistoryLength && g_pPurgeFile)
|
||
|
{
|
||
|
size_t len = (strlen(m_szName) + 3) & ~3;
|
||
|
fwrite(&len, sizeof(len), 1, g_pPurgeFile);
|
||
|
fwrite(m_szName, len, 1, g_pPurgeFile);
|
||
|
fwrite(&m_nFrameHistoryBegins, sizeof(m_nFrameHistoryBegins), 1, g_pPurgeFile);
|
||
|
fwrite(&m_nHistoryLength, sizeof(m_nHistoryLength), 1, g_pPurgeFile);
|
||
|
fwrite(m_pHistory, sizeof(CMiniProfiler), m_nHistoryLength, g_pPurgeFile);
|
||
|
m_nHistoryLength = 0; // reset the history, nothing else
|
||
|
m_nFrameHistoryBegins = g_nMiniProfilerFrame;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
extern "C"
|
||
|
MINIPROFILER_DLL_LINKAGE void PublishAll( CLinkedMiniProfiler*pList, uint32 nHistoryMax )
|
||
|
{
|
||
|
#if ENABLE_HARDWARE_PROFILER
|
||
|
for(CLinkedMiniProfiler *prof = pList; prof; prof = prof->m_pNext)
|
||
|
prof->Publish(nHistoryMax);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void MicroProfilerAddTS( CMicroProfiler *pProfiler, uint64 numTimeBaseTicks )
|
||
|
{
|
||
|
#if ENABLE_MICRO_PROFILER > 0
|
||
|
ThreadInterlockedExchangeAdd64( ( int64* )&pProfiler->m_numTimeBaseTicks, numTimeBaseTicks );
|
||
|
ThreadInterlockedIncrement( ( int32* )&pProfiler->m_numCalls );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void PopMiniProfilerTS( CMiniProfiler *pProfiler )
|
||
|
{
|
||
|
s_pLastMiniProfilerTS = pProfiler;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
static void GetPerformanceFrequency( int64 *pFreqOut )
|
||
|
{
|
||
|
#ifdef PLATFORM_POSIX
|
||
|
*pFreqOut = 2000000000;
|
||
|
#elif defined( _PS3 )
|
||
|
*pFreqOut = 3200000000ll;
|
||
|
#else
|
||
|
QueryPerformanceFrequency( ( LARGE_INTEGER* ) pFreqOut );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
DLL_EXPORT void PublishAllMiniProfilers(int nHistoryMax)
|
||
|
{
|
||
|
#if ENABLE_HARDWARE_PROFILER
|
||
|
if(nHistoryMax >= 0/*cv_phys_enable_PIX_counters.GetBool()*/)
|
||
|
{
|
||
|
if(nHistoryMax && !g_pPurgeFile)
|
||
|
{
|
||
|
if(!g_szFileName[0])
|
||
|
{
|
||
|
tm lt;
|
||
|
Plat_GetLocalTime( < );
|
||
|
//SYSTEMTIME st;
|
||
|
//GetLocalTime(&st);
|
||
|
//sprintf(g_szFileName, "D:\\mp%02d-%02d-%02d-%02d-%02d-%02d.dmp", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
|
||
|
sprintf(g_szFileName, "miniprofile%02d%02d-%02d_%02d_%02d.prf", /*lt->tm_year+1900, */lt.tm_mon+1, lt.tm_mday, lt.tm_hour, lt.tm_min, lt.tm_sec);
|
||
|
}
|
||
|
g_pPurgeFile = fopen(g_szFileName, "ab");
|
||
|
if(g_pPurgeFile)
|
||
|
{
|
||
|
int nVersion = 0x0101;
|
||
|
fwrite(&nVersion, 4, 1, g_pPurgeFile);
|
||
|
#ifdef _X360
|
||
|
int nFrequency = 49875; // ticks per millisecond
|
||
|
#else
|
||
|
// even though this is not correct on older computers, I think most modern computers have the multimedia clock that has the same frequency as the CPU
|
||
|
int64 nActualFrequency = GetCPUInformation().m_Speed;
|
||
|
int nFrequency = int((nActualFrequency+500) / 1000);
|
||
|
#endif
|
||
|
fwrite(&nFrequency, 4, 1, g_pPurgeFile);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PublishAll(g_pPhysicsMiniProfilers,nHistoryMax);
|
||
|
PublishAll(g_pOtherMiniProfilers,nHistoryMax);
|
||
|
g_rootMiniProfiler.Reset();
|
||
|
g_nMiniProfilerFrame ++;
|
||
|
|
||
|
if(g_pPurgeFile)
|
||
|
{
|
||
|
if(nHistoryMax)
|
||
|
fflush(g_pPurgeFile);
|
||
|
else
|
||
|
{
|
||
|
Msg("Closing profile: '%s'\n", g_szFileName);
|
||
|
fclose(g_pPurgeFile);
|
||
|
g_pPurgeFile = NULL;
|
||
|
g_szFileName[0] = '\0';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|