//====== 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 #include #include #ifdef _PS3 #include "ps3/ps3_helpers.h" #endif #if defined( PLATFORM_WINDOWS_PC ) #define WIN_32_LEAN_AND_MEAN #include // 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 }