Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

499 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "stdafx.h"
  7. #include "tier1/utldict.h"
  8. #include <pdh.h>
  9. #include <pdhmsg.h>
  10. #include "perf_counters.h"
  11. #if 1
  12. class CPerfTracker : public IPerfTracker
  13. {
  14. public:
  15. CPerfTracker()
  16. {
  17. m_hProcessorTimeCounter = NULL;
  18. m_dwProcessID = 0;
  19. if ( PdhOpenQuery( NULL, 0, &m_hQuery ) != ERROR_SUCCESS )
  20. m_hQuery = NULL;
  21. SYSTEM_INFO info;
  22. GetSystemInfo( &info );
  23. m_nProcessors = (int)info.dwNumberOfProcessors;
  24. }
  25. ~CPerfTracker()
  26. {
  27. if ( m_hQuery )
  28. PdhCloseQuery( m_hQuery );
  29. }
  30. virtual void Init( unsigned long dwProcessID )
  31. {
  32. Term();
  33. m_dwProcessID = dwProcessID;
  34. char instanceName[512];
  35. if ( GetInstanceNameFromProcessID( m_dwProcessID, instanceName, sizeof( instanceName ) ) )
  36. {
  37. // Create a counter to watch this process' time.
  38. char str[512];
  39. V_snprintf( str, sizeof( str ), "\\Process(%s)\\%% Processor Time", instanceName );
  40. if ( PdhAddCounter( m_hQuery, str, 0, &m_hProcessorTimeCounter ) != ERROR_SUCCESS )
  41. {
  42. m_hProcessorTimeCounter = NULL;
  43. }
  44. V_snprintf( str, sizeof( str ), "\\Process(%s)\\Private Bytes", instanceName );
  45. if ( PdhAddCounter( m_hQuery, str, 0, &m_hPrivateBytesCounter ) != ERROR_SUCCESS )
  46. {
  47. m_hPrivateBytesCounter = NULL;
  48. }
  49. }
  50. }
  51. void Term()
  52. {
  53. if ( m_hProcessorTimeCounter )
  54. PdhRemoveCounter( m_hProcessorTimeCounter );
  55. if ( m_hPrivateBytesCounter )
  56. PdhRemoveCounter( m_hPrivateBytesCounter );
  57. m_hProcessorTimeCounter = NULL;
  58. m_hPrivateBytesCounter = NULL;
  59. }
  60. virtual void Release()
  61. {
  62. delete this;
  63. }
  64. virtual unsigned long GetProcessID()
  65. {
  66. return m_dwProcessID;
  67. }
  68. virtual void GetPerfData( int &processorPercentage, int &memoryUsageMegabytes )
  69. {
  70. processorPercentage = 101;
  71. memoryUsageMegabytes = 0;
  72. // Collect query data..
  73. PDH_STATUS ret = PdhCollectQueryData( m_hQuery );
  74. if ( ret != ERROR_SUCCESS )
  75. return;
  76. // Check processor usage.
  77. DWORD dwType;
  78. PDH_FMT_COUNTERVALUE counterValue;
  79. if ( PdhGetFormattedCounterValue( m_hProcessorTimeCounter, PDH_FMT_LONG | PDH_FMT_NOCAP100, &dwType, &counterValue ) == ERROR_SUCCESS )
  80. processorPercentage = counterValue.longValue / m_nProcessors;
  81. else
  82. processorPercentage = 101;
  83. // Check memory usage.
  84. if ( PdhGetFormattedCounterValue( m_hPrivateBytesCounter, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &dwType, &counterValue ) == ERROR_SUCCESS )
  85. memoryUsageMegabytes = (int)(counterValue.doubleValue / (1024.0 * 1024.0));
  86. else
  87. memoryUsageMegabytes = 0;
  88. }
  89. private:
  90. bool GetInstanceNameFromProcessID( DWORD processID, char *instanceName, int instanceNameLen )
  91. {
  92. instanceName[0] = 0;
  93. bool bRet = false;
  94. // This refreshes the object list. If we don't do this, it won't get new process IDs correctly.
  95. DWORD dummy = 0;
  96. PdhEnumObjects( NULL, NULL, NULL, &dummy, PERF_DETAIL_NOVICE, true );
  97. // Find out how much data we need.
  98. DWORD counterListLen=2, instanceListLen=2;
  99. char *counterList = new char[counterListLen];
  100. char *instanceList = new char[instanceListLen];
  101. PDH_STATUS stat = PdhEnumObjectItems( NULL, NULL, "Process", counterList, &counterListLen, instanceList, &instanceListLen, PERF_DETAIL_NOVICE, 0 );
  102. if ( stat == PDH_MORE_DATA )
  103. {
  104. delete [] counterList;
  105. delete [] instanceList;
  106. char *counterList = new char[counterListLen];
  107. char *instanceList = new char[instanceListLen];
  108. stat = PdhEnumObjectItems( NULL, NULL, "Process", counterList, &counterListLen, instanceList, &instanceListLen, PERF_DETAIL_NOVICE, 0 );
  109. if ( stat == ERROR_SUCCESS )
  110. {
  111. // We need the # of each one..
  112. CUtlDict<int,int> counts;
  113. // The instance name list is a bunch of strings terminated with nulls. The final one has two nulls after it.
  114. // Walk through the list and get the process ID associated with each instance name.
  115. const char *pCur = instanceList;
  116. while ( *pCur )
  117. {
  118. int index = counts.Find( pCur );
  119. if ( index == counts.InvalidIndex() )
  120. counts.Insert( pCur, 1 );
  121. else
  122. counts[index]++;
  123. pCur += strlen( pCur ) + 1;
  124. }
  125. // Each instance (like "vrad") might have multiple versions, like if you're running multiple vrad processes at the same time.
  126. for ( int i=counts.First(); i != counts.InvalidIndex(); i=counts.Next( i ) )
  127. {
  128. const char *pInstanceName = counts.GetElementName( i );
  129. int nInstances = counts[i];
  130. for ( int iInstance=0; iInstance < nInstances; iInstance++ )
  131. {
  132. char testInstanceName[256], fullObjectName[256];
  133. V_snprintf( testInstanceName, sizeof( testInstanceName ), "%s#%d", pInstanceName, iInstance );
  134. V_snprintf( fullObjectName, sizeof( fullObjectName ), "\\Process(%s)\\ID Process", testInstanceName );
  135. HCOUNTER hCounter = NULL;
  136. stat = PdhAddCounter( m_hQuery, fullObjectName, 0, &hCounter );
  137. if ( stat == ERROR_SUCCESS )
  138. {
  139. stat = PdhCollectQueryData( m_hQuery );
  140. if ( stat == ERROR_SUCCESS )
  141. {
  142. DWORD dwType;
  143. PDH_FMT_COUNTERVALUE counterValue;
  144. stat = PdhGetFormattedCounterValue( hCounter, PDH_FMT_LONG, &dwType, &counterValue );
  145. if ( stat == 0 && counterValue.longValue == (long)processID )
  146. {
  147. // Finall! We found it.
  148. V_strncpy( instanceName, testInstanceName, instanceNameLen );
  149. bRet = true;
  150. PdhRemoveCounter( hCounter );
  151. break;
  152. }
  153. }
  154. PdhRemoveCounter( hCounter );
  155. }
  156. }
  157. if ( bRet )
  158. break;
  159. }
  160. }
  161. delete [] counterList;
  162. delete [] instanceList;
  163. }
  164. return bRet;
  165. }
  166. private:
  167. DWORD m_dwProcessID;
  168. PDH_HQUERY m_hQuery;
  169. HCOUNTER m_hProcessorTimeCounter;
  170. HCOUNTER m_hPrivateBytesCounter;
  171. int m_nProcessors;
  172. };
  173. IPerfTracker* CreatePerfTracker()
  174. {
  175. return new CPerfTracker;
  176. }
  177. #else
  178. #include <winperf.h>
  179. // --------------------------------------------------------------------------------------------------------------------- //
  180. // NOTE: THIS IS THE OLD, UGLY WAY TO DO IT.
  181. // --------------------------------------------------------------------------------------------------------------------- //
  182. class CPerfTracker
  183. {
  184. public:
  185. CPerfTracker();
  186. void Init( unsigned long dwProcessID );
  187. unsigned long GetProcessID();
  188. // Get the percentage of CPU time that the process is using.
  189. int GetCPUPercentage();
  190. private:
  191. DWORD m_dwProcessID;
  192. LONGLONG m_lnOldValue;
  193. LARGE_INTEGER m_OldPerfTime100nSec;
  194. int m_nProcessors;
  195. };
  196. #define TOTALBYTES 100*1024
  197. #define BYTEINCREMENT 10*1024
  198. #define SYSTEM_OBJECT_INDEX 2 // 'System' object
  199. #define PROCESS_OBJECT_INDEX 230 // 'Process' object
  200. #define PROCESSOR_OBJECT_INDEX 238 // 'Processor' object
  201. #define TOTAL_PROCESSOR_TIME_COUNTER_INDEX 240 // '% Total processor time' counter (valid in WinNT under 'System' object)
  202. #define PROCESSOR_TIME_COUNTER_INDEX 6 // '% processor time' counter (for Win2K/XP)
  203. //
  204. // The performance data is accessed through the registry key
  205. // HKEY_PEFORMANCE_DATA.
  206. // However, although we use the registry to collect performance data,
  207. // the data is not stored in the registry database.
  208. // Instead, calling the registry functions with the HKEY_PEFORMANCE_DATA key
  209. // causes the system to collect the data from the appropriate system
  210. // object managers.
  211. //
  212. // QueryPerformanceData allocates memory block for getting the
  213. // performance data.
  214. //
  215. //
  216. void QueryPerformanceData(PERF_DATA_BLOCK **pPerfData, DWORD dwObjectIndex, DWORD dwCounterIndex)
  217. {
  218. //
  219. // Since i want to use the same allocated area for each query,
  220. // i declare CBuffer as static.
  221. // The allocated is changed only when RegQueryValueEx return ERROR_MORE_DATA
  222. //
  223. static CUtlVector<char> Buffer;
  224. if ( Buffer.Count() == 0 )
  225. Buffer.SetSize( TOTALBYTES );
  226. DWORD BufferSize = Buffer.Count();
  227. LONG lRes;
  228. char keyName[32];
  229. V_snprintf(keyName, sizeof(keyName), "%d",dwObjectIndex);
  230. memset( Buffer.Base(), 0, Buffer.Count() );
  231. while( (lRes = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
  232. keyName,
  233. NULL,
  234. NULL,
  235. (LPBYTE)Buffer.Base(),
  236. &BufferSize )) == ERROR_MORE_DATA )
  237. {
  238. // Get a buffer that is big enough.
  239. BufferSize += BYTEINCREMENT;
  240. Buffer.SetSize( BufferSize );
  241. }
  242. *pPerfData = (PPERF_DATA_BLOCK)Buffer.Base();
  243. }
  244. /*****************************************************************
  245. * *
  246. * Functions used to navigate through the performance data. *
  247. * *
  248. *****************************************************************/
  249. inline PPERF_OBJECT_TYPE FirstObject( PPERF_DATA_BLOCK PerfData )
  250. {
  251. return( (PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength) );
  252. }
  253. inline PPERF_OBJECT_TYPE NextObject( PPERF_OBJECT_TYPE PerfObj )
  254. {
  255. return( (PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength) );
  256. }
  257. inline PPERF_COUNTER_DEFINITION FirstCounter( PPERF_OBJECT_TYPE PerfObj )
  258. {
  259. return( (PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength) );
  260. }
  261. inline PPERF_COUNTER_DEFINITION NextCounter( PPERF_COUNTER_DEFINITION PerfCntr )
  262. {
  263. return( (PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength) );
  264. }
  265. inline PPERF_INSTANCE_DEFINITION FirstInstance( PPERF_OBJECT_TYPE PerfObj )
  266. {
  267. return( (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength) );
  268. }
  269. inline PPERF_INSTANCE_DEFINITION NextInstance( PPERF_INSTANCE_DEFINITION PerfInst )
  270. {
  271. PPERF_COUNTER_BLOCK PerfCntrBlk;
  272. PerfCntrBlk = (PPERF_COUNTER_BLOCK)((PBYTE)PerfInst + PerfInst->ByteLength);
  273. return( (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfCntrBlk + PerfCntrBlk->ByteLength) );
  274. }
  275. template<class T>
  276. T GetCounterValueForProcessID(PPERF_OBJECT_TYPE pPerfObj, DWORD dwCounterIndex, DWORD dwProcessID)
  277. {
  278. unsigned long PROC_ID_COUNTER = 784;
  279. BOOL bProcessIDExist = FALSE;
  280. PPERF_COUNTER_DEFINITION pPerfCntr = NULL;
  281. PPERF_COUNTER_DEFINITION pTheRequestedPerfCntr = NULL;
  282. PPERF_COUNTER_DEFINITION pProcIDPerfCntr = NULL;
  283. PPERF_INSTANCE_DEFINITION pPerfInst = NULL;
  284. PPERF_COUNTER_BLOCK pCounterBlock = NULL;
  285. // Get the first counter.
  286. pPerfCntr = FirstCounter( pPerfObj );
  287. for( DWORD j=0; j < pPerfObj->NumCounters; j++ )
  288. {
  289. if (pPerfCntr->CounterNameTitleIndex == PROC_ID_COUNTER)
  290. {
  291. pProcIDPerfCntr = pPerfCntr;
  292. if (pTheRequestedPerfCntr)
  293. break;
  294. }
  295. if (pPerfCntr->CounterNameTitleIndex == dwCounterIndex)
  296. {
  297. pTheRequestedPerfCntr = pPerfCntr;
  298. if (pProcIDPerfCntr)
  299. break;
  300. }
  301. // Get the next counter.
  302. pPerfCntr = NextCounter( pPerfCntr );
  303. }
  304. if( pPerfObj->NumInstances == PERF_NO_INSTANCES )
  305. {
  306. pCounterBlock = (PPERF_COUNTER_BLOCK) ((LPBYTE) pPerfObj + pPerfObj->DefinitionLength);
  307. }
  308. else
  309. {
  310. pPerfInst = FirstInstance( pPerfObj );
  311. for( int k=0; k < pPerfObj->NumInstances; k++ )
  312. {
  313. pCounterBlock = (PPERF_COUNTER_BLOCK) ((LPBYTE) pPerfInst + pPerfInst->ByteLength);
  314. if (pCounterBlock)
  315. {
  316. DWORD processID = *(DWORD*)((LPBYTE) pCounterBlock + pProcIDPerfCntr->CounterOffset);
  317. if (processID == dwProcessID)
  318. {
  319. bProcessIDExist = TRUE;
  320. break;
  321. }
  322. }
  323. // Get the next instance.
  324. pPerfInst = NextInstance( pPerfInst );
  325. }
  326. }
  327. if (bProcessIDExist && pCounterBlock)
  328. {
  329. T *lnValue = NULL;
  330. lnValue = (T*)((LPBYTE) pCounterBlock + pTheRequestedPerfCntr->CounterOffset);
  331. return *lnValue;
  332. }
  333. return -1;
  334. }
  335. template<class T>
  336. T GetCounterValueForProcessID(PERF_DATA_BLOCK **pPerfData, DWORD dwObjectIndex, DWORD dwCounterIndex, DWORD dwProcessID)
  337. {
  338. QueryPerformanceData(pPerfData, dwObjectIndex, dwCounterIndex);
  339. PPERF_OBJECT_TYPE pPerfObj = NULL;
  340. T lnValue = {0};
  341. // Get the first object type.
  342. pPerfObj = FirstObject( *pPerfData );
  343. // Look for the given object index
  344. for( DWORD i=0; i < (*pPerfData)->NumObjectTypes; i++ )
  345. {
  346. if (pPerfObj->ObjectNameTitleIndex == dwObjectIndex)
  347. {
  348. lnValue = GetCounterValueForProcessID<T>(pPerfObj, dwCounterIndex, dwProcessID);
  349. break;
  350. }
  351. pPerfObj = NextObject( pPerfObj );
  352. }
  353. return lnValue;
  354. }
  355. // ------------------------------------------------------------------------------------------- //
  356. // CPerfTracker implementation.
  357. // ------------------------------------------------------------------------------------------- //
  358. CPerfTracker::CPerfTracker()
  359. {
  360. Init( 0 );
  361. SYSTEM_INFO info;
  362. GetSystemInfo( &info );
  363. m_nProcessors = (int)info.dwNumberOfProcessors;
  364. }
  365. void CPerfTracker::Init( unsigned long dwProcessID )
  366. {
  367. m_dwProcessID = dwProcessID;
  368. m_lnOldValue = 0;
  369. }
  370. unsigned long CPerfTracker::GetProcessID()
  371. {
  372. return m_dwProcessID;
  373. }
  374. int CPerfTracker::GetCPUPercentage()
  375. {
  376. DWORD dwObjectIndex = PROCESS_OBJECT_INDEX;
  377. DWORD dwCpuUsageIndex = PROCESSOR_TIME_COUNTER_INDEX;
  378. PPERF_DATA_BLOCK pPerfData = NULL;
  379. LONGLONG lnNewValue = GetCounterValueForProcessID<LONGLONG>( &pPerfData, dwObjectIndex, dwCpuUsageIndex, m_dwProcessID );
  380. LARGE_INTEGER NewPerfTime100nSec = pPerfData->PerfTime100nSec;
  381. if ( m_lnOldValue == 0 )
  382. {
  383. m_lnOldValue = lnNewValue;
  384. m_OldPerfTime100nSec = NewPerfTime100nSec;
  385. return 0;
  386. }
  387. LONGLONG lnValueDelta = lnNewValue - m_lnOldValue;
  388. double DeltaPerfTime100nSec = (double)NewPerfTime100nSec.QuadPart - (double)m_OldPerfTime100nSec.QuadPart;
  389. m_lnOldValue = lnNewValue;
  390. m_OldPerfTime100nSec = NewPerfTime100nSec;
  391. double a = (double)lnValueDelta / DeltaPerfTime100nSec;
  392. int CpuUsage = (int) (a*100);
  393. if (CpuUsage < 0)
  394. return 0;
  395. return CpuUsage / m_nProcessors;
  396. }
  397. #endif