//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //============================================================================= #include "stdafx.h" #include "tier1/utldict.h" #include #include #include "perf_counters.h" #if 1 class CPerfTracker : public IPerfTracker { public: CPerfTracker() { m_hProcessorTimeCounter = NULL; m_dwProcessID = 0; if ( PdhOpenQuery( NULL, 0, &m_hQuery ) != ERROR_SUCCESS ) m_hQuery = NULL; SYSTEM_INFO info; GetSystemInfo( &info ); m_nProcessors = (int)info.dwNumberOfProcessors; } ~CPerfTracker() { if ( m_hQuery ) PdhCloseQuery( m_hQuery ); } virtual void Init( unsigned long dwProcessID ) { Term(); m_dwProcessID = dwProcessID; char instanceName[512]; if ( GetInstanceNameFromProcessID( m_dwProcessID, instanceName, sizeof( instanceName ) ) ) { // Create a counter to watch this process' time. char str[512]; V_snprintf( str, sizeof( str ), "\\Process(%s)\\%% Processor Time", instanceName ); if ( PdhAddCounter( m_hQuery, str, 0, &m_hProcessorTimeCounter ) != ERROR_SUCCESS ) { m_hProcessorTimeCounter = NULL; } V_snprintf( str, sizeof( str ), "\\Process(%s)\\Private Bytes", instanceName ); if ( PdhAddCounter( m_hQuery, str, 0, &m_hPrivateBytesCounter ) != ERROR_SUCCESS ) { m_hPrivateBytesCounter = NULL; } } } void Term() { if ( m_hProcessorTimeCounter ) PdhRemoveCounter( m_hProcessorTimeCounter ); if ( m_hPrivateBytesCounter ) PdhRemoveCounter( m_hPrivateBytesCounter ); m_hProcessorTimeCounter = NULL; m_hPrivateBytesCounter = NULL; } virtual void Release() { delete this; } virtual unsigned long GetProcessID() { return m_dwProcessID; } virtual void GetPerfData( int &processorPercentage, int &memoryUsageMegabytes ) { processorPercentage = 101; memoryUsageMegabytes = 0; // Collect query data.. PDH_STATUS ret = PdhCollectQueryData( m_hQuery ); if ( ret != ERROR_SUCCESS ) return; // Check processor usage. DWORD dwType; PDH_FMT_COUNTERVALUE counterValue; if ( PdhGetFormattedCounterValue( m_hProcessorTimeCounter, PDH_FMT_LONG | PDH_FMT_NOCAP100, &dwType, &counterValue ) == ERROR_SUCCESS ) processorPercentage = counterValue.longValue / m_nProcessors; else processorPercentage = 101; // Check memory usage. if ( PdhGetFormattedCounterValue( m_hPrivateBytesCounter, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &dwType, &counterValue ) == ERROR_SUCCESS ) memoryUsageMegabytes = (int)(counterValue.doubleValue / (1024.0 * 1024.0)); else memoryUsageMegabytes = 0; } private: bool GetInstanceNameFromProcessID( DWORD processID, char *instanceName, int instanceNameLen ) { instanceName[0] = 0; bool bRet = false; // This refreshes the object list. If we don't do this, it won't get new process IDs correctly. DWORD dummy = 0; PdhEnumObjects( NULL, NULL, NULL, &dummy, PERF_DETAIL_NOVICE, true ); // Find out how much data we need. DWORD counterListLen=2, instanceListLen=2; char *counterList = new char[counterListLen]; char *instanceList = new char[instanceListLen]; PDH_STATUS stat = PdhEnumObjectItems( NULL, NULL, "Process", counterList, &counterListLen, instanceList, &instanceListLen, PERF_DETAIL_NOVICE, 0 ); if ( stat == PDH_MORE_DATA ) { delete [] counterList; delete [] instanceList; char *counterList = new char[counterListLen]; char *instanceList = new char[instanceListLen]; stat = PdhEnumObjectItems( NULL, NULL, "Process", counterList, &counterListLen, instanceList, &instanceListLen, PERF_DETAIL_NOVICE, 0 ); if ( stat == ERROR_SUCCESS ) { // We need the # of each one.. CUtlDict counts; // The instance name list is a bunch of strings terminated with nulls. The final one has two nulls after it. // Walk through the list and get the process ID associated with each instance name. const char *pCur = instanceList; while ( *pCur ) { int index = counts.Find( pCur ); if ( index == counts.InvalidIndex() ) counts.Insert( pCur, 1 ); else counts[index]++; pCur += strlen( pCur ) + 1; } // Each instance (like "vrad") might have multiple versions, like if you're running multiple vrad processes at the same time. for ( int i=counts.First(); i != counts.InvalidIndex(); i=counts.Next( i ) ) { const char *pInstanceName = counts.GetElementName( i ); int nInstances = counts[i]; for ( int iInstance=0; iInstance < nInstances; iInstance++ ) { char testInstanceName[256], fullObjectName[256]; V_snprintf( testInstanceName, sizeof( testInstanceName ), "%s#%d", pInstanceName, iInstance ); V_snprintf( fullObjectName, sizeof( fullObjectName ), "\\Process(%s)\\ID Process", testInstanceName ); HCOUNTER hCounter = NULL; stat = PdhAddCounter( m_hQuery, fullObjectName, 0, &hCounter ); if ( stat == ERROR_SUCCESS ) { stat = PdhCollectQueryData( m_hQuery ); if ( stat == ERROR_SUCCESS ) { DWORD dwType; PDH_FMT_COUNTERVALUE counterValue; stat = PdhGetFormattedCounterValue( hCounter, PDH_FMT_LONG, &dwType, &counterValue ); if ( stat == 0 && counterValue.longValue == (long)processID ) { // Finall! We found it. V_strncpy( instanceName, testInstanceName, instanceNameLen ); bRet = true; PdhRemoveCounter( hCounter ); break; } } PdhRemoveCounter( hCounter ); } } if ( bRet ) break; } } delete [] counterList; delete [] instanceList; } return bRet; } private: DWORD m_dwProcessID; PDH_HQUERY m_hQuery; HCOUNTER m_hProcessorTimeCounter; HCOUNTER m_hPrivateBytesCounter; int m_nProcessors; }; IPerfTracker* CreatePerfTracker() { return new CPerfTracker; } #else #include // --------------------------------------------------------------------------------------------------------------------- // // NOTE: THIS IS THE OLD, UGLY WAY TO DO IT. // --------------------------------------------------------------------------------------------------------------------- // class CPerfTracker { public: CPerfTracker(); void Init( unsigned long dwProcessID ); unsigned long GetProcessID(); // Get the percentage of CPU time that the process is using. int GetCPUPercentage(); private: DWORD m_dwProcessID; LONGLONG m_lnOldValue; LARGE_INTEGER m_OldPerfTime100nSec; int m_nProcessors; }; #define TOTALBYTES 100*1024 #define BYTEINCREMENT 10*1024 #define SYSTEM_OBJECT_INDEX 2 // 'System' object #define PROCESS_OBJECT_INDEX 230 // 'Process' object #define PROCESSOR_OBJECT_INDEX 238 // 'Processor' object #define TOTAL_PROCESSOR_TIME_COUNTER_INDEX 240 // '% Total processor time' counter (valid in WinNT under 'System' object) #define PROCESSOR_TIME_COUNTER_INDEX 6 // '% processor time' counter (for Win2K/XP) // // The performance data is accessed through the registry key // HKEY_PEFORMANCE_DATA. // However, although we use the registry to collect performance data, // the data is not stored in the registry database. // Instead, calling the registry functions with the HKEY_PEFORMANCE_DATA key // causes the system to collect the data from the appropriate system // object managers. // // QueryPerformanceData allocates memory block for getting the // performance data. // // void QueryPerformanceData(PERF_DATA_BLOCK **pPerfData, DWORD dwObjectIndex, DWORD dwCounterIndex) { // // Since i want to use the same allocated area for each query, // i declare CBuffer as static. // The allocated is changed only when RegQueryValueEx return ERROR_MORE_DATA // static CUtlVector Buffer; if ( Buffer.Count() == 0 ) Buffer.SetSize( TOTALBYTES ); DWORD BufferSize = Buffer.Count(); LONG lRes; char keyName[32]; V_snprintf(keyName, sizeof(keyName), "%d",dwObjectIndex); memset( Buffer.Base(), 0, Buffer.Count() ); while( (lRes = RegQueryValueEx( HKEY_PERFORMANCE_DATA, keyName, NULL, NULL, (LPBYTE)Buffer.Base(), &BufferSize )) == ERROR_MORE_DATA ) { // Get a buffer that is big enough. BufferSize += BYTEINCREMENT; Buffer.SetSize( BufferSize ); } *pPerfData = (PPERF_DATA_BLOCK)Buffer.Base(); } /***************************************************************** * * * Functions used to navigate through the performance data. * * * *****************************************************************/ inline PPERF_OBJECT_TYPE FirstObject( PPERF_DATA_BLOCK PerfData ) { return( (PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength) ); } inline PPERF_OBJECT_TYPE NextObject( PPERF_OBJECT_TYPE PerfObj ) { return( (PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength) ); } inline PPERF_COUNTER_DEFINITION FirstCounter( PPERF_OBJECT_TYPE PerfObj ) { return( (PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength) ); } inline PPERF_COUNTER_DEFINITION NextCounter( PPERF_COUNTER_DEFINITION PerfCntr ) { return( (PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength) ); } inline PPERF_INSTANCE_DEFINITION FirstInstance( PPERF_OBJECT_TYPE PerfObj ) { return( (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength) ); } inline PPERF_INSTANCE_DEFINITION NextInstance( PPERF_INSTANCE_DEFINITION PerfInst ) { PPERF_COUNTER_BLOCK PerfCntrBlk; PerfCntrBlk = (PPERF_COUNTER_BLOCK)((PBYTE)PerfInst + PerfInst->ByteLength); return( (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfCntrBlk + PerfCntrBlk->ByteLength) ); } template T GetCounterValueForProcessID(PPERF_OBJECT_TYPE pPerfObj, DWORD dwCounterIndex, DWORD dwProcessID) { unsigned long PROC_ID_COUNTER = 784; BOOL bProcessIDExist = FALSE; PPERF_COUNTER_DEFINITION pPerfCntr = NULL; PPERF_COUNTER_DEFINITION pTheRequestedPerfCntr = NULL; PPERF_COUNTER_DEFINITION pProcIDPerfCntr = NULL; PPERF_INSTANCE_DEFINITION pPerfInst = NULL; PPERF_COUNTER_BLOCK pCounterBlock = NULL; // Get the first counter. pPerfCntr = FirstCounter( pPerfObj ); for( DWORD j=0; j < pPerfObj->NumCounters; j++ ) { if (pPerfCntr->CounterNameTitleIndex == PROC_ID_COUNTER) { pProcIDPerfCntr = pPerfCntr; if (pTheRequestedPerfCntr) break; } if (pPerfCntr->CounterNameTitleIndex == dwCounterIndex) { pTheRequestedPerfCntr = pPerfCntr; if (pProcIDPerfCntr) break; } // Get the next counter. pPerfCntr = NextCounter( pPerfCntr ); } if( pPerfObj->NumInstances == PERF_NO_INSTANCES ) { pCounterBlock = (PPERF_COUNTER_BLOCK) ((LPBYTE) pPerfObj + pPerfObj->DefinitionLength); } else { pPerfInst = FirstInstance( pPerfObj ); for( int k=0; k < pPerfObj->NumInstances; k++ ) { pCounterBlock = (PPERF_COUNTER_BLOCK) ((LPBYTE) pPerfInst + pPerfInst->ByteLength); if (pCounterBlock) { DWORD processID = *(DWORD*)((LPBYTE) pCounterBlock + pProcIDPerfCntr->CounterOffset); if (processID == dwProcessID) { bProcessIDExist = TRUE; break; } } // Get the next instance. pPerfInst = NextInstance( pPerfInst ); } } if (bProcessIDExist && pCounterBlock) { T *lnValue = NULL; lnValue = (T*)((LPBYTE) pCounterBlock + pTheRequestedPerfCntr->CounterOffset); return *lnValue; } return -1; } template T GetCounterValueForProcessID(PERF_DATA_BLOCK **pPerfData, DWORD dwObjectIndex, DWORD dwCounterIndex, DWORD dwProcessID) { QueryPerformanceData(pPerfData, dwObjectIndex, dwCounterIndex); PPERF_OBJECT_TYPE pPerfObj = NULL; T lnValue = {0}; // Get the first object type. pPerfObj = FirstObject( *pPerfData ); // Look for the given object index for( DWORD i=0; i < (*pPerfData)->NumObjectTypes; i++ ) { if (pPerfObj->ObjectNameTitleIndex == dwObjectIndex) { lnValue = GetCounterValueForProcessID(pPerfObj, dwCounterIndex, dwProcessID); break; } pPerfObj = NextObject( pPerfObj ); } return lnValue; } // ------------------------------------------------------------------------------------------- // // CPerfTracker implementation. // ------------------------------------------------------------------------------------------- // CPerfTracker::CPerfTracker() { Init( 0 ); SYSTEM_INFO info; GetSystemInfo( &info ); m_nProcessors = (int)info.dwNumberOfProcessors; } void CPerfTracker::Init( unsigned long dwProcessID ) { m_dwProcessID = dwProcessID; m_lnOldValue = 0; } unsigned long CPerfTracker::GetProcessID() { return m_dwProcessID; } int CPerfTracker::GetCPUPercentage() { DWORD dwObjectIndex = PROCESS_OBJECT_INDEX; DWORD dwCpuUsageIndex = PROCESSOR_TIME_COUNTER_INDEX; PPERF_DATA_BLOCK pPerfData = NULL; LONGLONG lnNewValue = GetCounterValueForProcessID( &pPerfData, dwObjectIndex, dwCpuUsageIndex, m_dwProcessID ); LARGE_INTEGER NewPerfTime100nSec = pPerfData->PerfTime100nSec; if ( m_lnOldValue == 0 ) { m_lnOldValue = lnNewValue; m_OldPerfTime100nSec = NewPerfTime100nSec; return 0; } LONGLONG lnValueDelta = lnNewValue - m_lnOldValue; double DeltaPerfTime100nSec = (double)NewPerfTime100nSec.QuadPart - (double)m_OldPerfTime100nSec.QuadPart; m_lnOldValue = lnNewValue; m_OldPerfTime100nSec = NewPerfTime100nSec; double a = (double)lnValueDelta / DeltaPerfTime100nSec; int CpuUsage = (int) (a*100); if (CpuUsage < 0) return 0; return CpuUsage / m_nProcessors; } #endif