summaryrefslogtreecommitdiff
path: root/utils/vmpi/vmpi_service/perf_counters.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/vmpi/vmpi_service/perf_counters.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'utils/vmpi/vmpi_service/perf_counters.cpp')
-rw-r--r--utils/vmpi/vmpi_service/perf_counters.cpp500
1 files changed, 500 insertions, 0 deletions
diff --git a/utils/vmpi/vmpi_service/perf_counters.cpp b/utils/vmpi/vmpi_service/perf_counters.cpp
new file mode 100644
index 0000000..3d98d2a
--- /dev/null
+++ b/utils/vmpi/vmpi_service/perf_counters.cpp
@@ -0,0 +1,500 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "stdafx.h"
+#include "tier1/utldict.h"
+#include <pdh.h>
+#include <pdhmsg.h>
+#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<int,int> 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 <winperf.h>
+
+// --------------------------------------------------------------------------------------------------------------------- //
+// 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<char> 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<class T>
+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<class T>
+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<T>(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<LONGLONG>( &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 \ No newline at end of file