Counter Strike : Global Offensive Source Code
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.

572 lines
17 KiB

  1. //========= Copyright � 1996-2004, Valve LLC, All rights reserved. ============
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include "stdafx.h"
  8. #ifdef _WIN32
  9. #include "winperfcounter.h"
  10. #ifdef _WIN32
  11. #include <pdh.h>
  12. #include <pdhmsg.h>
  13. #endif
  14. #ifdef GC
  15. #include "gclogger.h"
  16. using namespace GCSDK;
  17. #endif
  18. //-----------------------------------------------------------------------------
  19. // Purpose: threaded reading & access of PDH data
  20. //-----------------------------------------------------------------------------
  21. class CWinPerfCounterReadThread : public CThread
  22. {
  23. public:
  24. CWinPerfCounterReadThread( CWinPerfCountersPriv *pPrivData ) : m_pPrivData( pPrivData ), m_pPerfCounterMap( NULL )
  25. {
  26. }
  27. void SetPerfCounterMap( const PerfCounter_t *pPerfCounterMap )
  28. {
  29. m_pPerfCounterMap = pPerfCounterMap;
  30. }
  31. virtual int Run();
  32. private:
  33. CWinPerfCountersPriv *m_pPrivData;
  34. const PerfCounter_t *m_pPerfCounterMap;
  35. };
  36. //-----------------------------------------------------------------------------
  37. // Purpose: Pimpl pattern - Hides all the Pdh* stuff from the global include
  38. //-----------------------------------------------------------------------------
  39. class CWinPerfCountersPriv
  40. {
  41. #pragma warning(suppress : 4355) // warning C4355: 'this' : used in base member initializer list
  42. CWinPerfCountersPriv( int nCounters ) : m_threadQueryPerfCounters( this )
  43. {
  44. m_ppdhHCounters = NULL;
  45. #ifdef _WIN32
  46. m_ppdhHCounters = new PDH_HCOUNTER[nCounters];
  47. #endif
  48. }
  49. ~CWinPerfCountersPriv()
  50. {
  51. delete[] m_ppdhHCounters;
  52. }
  53. #ifdef DBGFLAG_VALIDATE
  54. //-----------------------------------------------------------------------------
  55. // Purpose: Run a global validation pass on all of our data structures and memory
  56. // allocations.
  57. // Input: validator - Our global validator object
  58. // pchName - Our name (typically a member var in our container)
  59. //-----------------------------------------------------------------------------
  60. void CWinPerfCountersPriv::Validate( CValidator &validator, const char *pchName )
  61. {
  62. VALIDATE_SCOPE();
  63. AUTO_LOCK( m_mutexDataAccess );
  64. if ( m_ppdhHCounters )
  65. validator.ClaimArrayMemory( m_ppdhHCounters );
  66. ValidateObj( m_vecData );
  67. ValidateObj( m_vecDataInFlight );
  68. }
  69. #endif // DBGFLAG_VALIDATE
  70. friend class CWinPerfCounters;
  71. friend class CWinPerfCounterReadThread;
  72. protected:
  73. #ifdef _WIN32
  74. PDH_HQUERY m_pdhHQuery;
  75. PDH_HCOUNTER *m_ppdhHCounters;
  76. CUtlVector<uint32> m_vecData, m_vecDataInFlight;
  77. CThreadMutex m_mutexDataAccess;
  78. CThreadEvent m_eventStartQuery;
  79. volatile bool m_bThreadRunning;
  80. CWinPerfCounterReadThread m_threadQueryPerfCounters;
  81. #endif
  82. };
  83. //-----------------------------------------------------------------------------
  84. // Purpose: c'tor
  85. //-----------------------------------------------------------------------------
  86. CWinPerfCounters::CWinPerfCounters( )
  87. {
  88. m_pPrivData = NULL;
  89. m_bInited = false;
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose: d'tor
  93. //-----------------------------------------------------------------------------
  94. CWinPerfCounters::~CWinPerfCounters()
  95. {
  96. Shutdown();
  97. }
  98. //-----------------------------------------------------------------------------
  99. // Purpose: registers the interesting counters via the Pdh (perf data handling) API
  100. //-----------------------------------------------------------------------------
  101. bool CWinPerfCounters::Init( const PerfCounter_t *counterMap, int nCounters )
  102. {
  103. VPROF_BUDGET( "CWinPerfCounters::Init", VPROF_BUDGETGROUP_STEAM );
  104. Assert( nCounters > 0 );
  105. Assert( counterMap );
  106. Assert( !m_pPrivData );
  107. bool bRet = false;
  108. #ifdef _WIN32
  109. m_pPerfCounterMap = counterMap;
  110. m_nCounters = nCounters;
  111. m_pPrivData = new CWinPerfCountersPriv( nCounters );
  112. AUTO_LOCK( m_pPrivData->m_mutexDataAccess );
  113. m_pPrivData->m_threadQueryPerfCounters.SetPerfCounterMap( counterMap );
  114. m_pPrivData->m_vecData.SetSize( m_nCounters );
  115. m_pPrivData->m_vecDataInFlight.SetSize( m_nCounters );
  116. m_pPrivData->m_bThreadRunning = true;
  117. bRet = true;
  118. PDH_STATUS pdhStat = PdhOpenQuery( NULL, NULL, &m_pPrivData->m_pdhHQuery );
  119. AssertMsg( ERROR_SUCCESS == pdhStat, "CWinPerfCounters::Init(): Failed to initalize performance data gathering.\n" );
  120. for ( int i = 0; i < m_nCounters; ++i )
  121. {
  122. pdhStat = PdhAddCounter( m_pPrivData->m_pdhHQuery, m_pPerfCounterMap[i].m_rgchPerfObject, NULL, &(m_pPrivData->m_ppdhHCounters[i]) );
  123. if ( ERROR_SUCCESS != pdhStat && NULL != m_pPerfCounterMap[i].m_rgchPerfObjectAlternative )
  124. {
  125. // first counter name didn't work; try our backup if we have one
  126. pdhStat = PdhAddCounter( m_pPrivData->m_pdhHQuery, m_pPerfCounterMap[i].m_rgchPerfObjectAlternative, NULL, &(m_pPrivData->m_ppdhHCounters[i]) );
  127. }
  128. m_pPrivData->m_vecData[i] = 0;
  129. m_pPrivData->m_vecDataInFlight[i] = 0;
  130. if ( ERROR_SUCCESS != pdhStat )
  131. {
  132. char rgchCounterNames[ 1024 ];
  133. if ( NULL != m_pPerfCounterMap[ i ].m_rgchPerfObjectAlternative )
  134. {
  135. Q_snprintf( rgchCounterNames, Q_ARRAYSIZE( rgchCounterNames ), "%s or %s",
  136. m_pPerfCounterMap[ i ].m_rgchPerfObject,
  137. m_pPerfCounterMap[ i ].m_rgchPerfObjectAlternative );
  138. }
  139. else
  140. {
  141. Q_snprintf( rgchCounterNames, Q_ARRAYSIZE( rgchCounterNames ), "%s", m_pPerfCounterMap[ i ].m_rgchPerfObject );
  142. }
  143. if ( m_pPerfCounterMap[i].m_bAssertOnFailure )
  144. AssertMsg1( false, "CWinPerfCounters::Init():Failed to add %s to performance monitoring.",
  145. rgchCounterNames );
  146. else
  147. {
  148. #ifdef GC
  149. EmitError( SPEW_GC, "CWinPerfCounters::Init():Failed to add %s to performance monitoring.\n", rgchCounterNames );
  150. #else
  151. Warning( "CWinPerfCounters::Init():Failed to add %s to performance monitoring.\n", rgchCounterNames );
  152. #endif
  153. pdhStat = ERROR_SUCCESS;
  154. }
  155. }
  156. bRet &= ( ERROR_SUCCESS == pdhStat );
  157. }
  158. m_bInited = true;
  159. Assert( m_pPrivData->m_vecDataInFlight.Count() == m_nCounters );
  160. Assert( m_pPrivData->m_vecData.Count() == m_nCounters );
  161. // take a sample immediately
  162. TakeSample();
  163. #endif
  164. return bRet;
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose: Performs the read of the perf counters being monitored
  168. //-----------------------------------------------------------------------------
  169. bool CWinPerfCounters::TakeSample()
  170. {
  171. #ifdef _WIN32
  172. if ( m_bInited ) // this gets called before InitOnServerRunning
  173. {
  174. if ( !m_pPrivData->m_threadQueryPerfCounters.IsAlive() )
  175. {
  176. m_pPrivData->m_threadQueryPerfCounters.Start();
  177. }
  178. m_pPrivData->m_eventStartQuery.Set();
  179. return true;
  180. }
  181. #endif
  182. return false;
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose: pokes the measured values into the provided stats struct
  186. //-----------------------------------------------------------------------------
  187. bool CWinPerfCounters::WriteStats( void *pStatsStruct )
  188. {
  189. bool bRet = false;
  190. #ifdef _WIN32
  191. AUTO_LOCK( m_pPrivData->m_mutexDataAccess );
  192. // pull out our already retrieved data
  193. for ( int i = 0; i < m_nCounters; ++i )
  194. {
  195. //Get destination
  196. byte *dst = (byte *) pStatsStruct + m_pPerfCounterMap[i].m_statsOffset;
  197. *(int32 *)dst = m_pPrivData->m_vecData[i];
  198. //perform rollup if necessary
  199. if ( m_pPerfCounterMap[i].m_bCounterRequiresRollup == true && ( i + 1 < m_nCounters ) )
  200. {
  201. while( m_pPerfCounterMap[i].m_rgchPerfObject == m_pPerfCounterMap[i+1].m_rgchPerfObject )
  202. {
  203. ++i;
  204. *(int32 *)dst += m_pPrivData->m_vecData[i];
  205. if ( m_nCounters == i ) break;
  206. }
  207. }
  208. }
  209. bRet = true;
  210. #endif // _WIN32
  211. return bRet;
  212. }
  213. //-----------------------------------------------------------------------------
  214. // Purpose: collects data on a thread
  215. //-----------------------------------------------------------------------------
  216. int CWinPerfCounterReadThread::Run()
  217. {
  218. while ( m_pPrivData->m_bThreadRunning )
  219. {
  220. // wait to be signalled
  221. m_pPrivData->m_eventStartQuery.Wait();
  222. if ( !m_pPrivData->m_bThreadRunning )
  223. break;
  224. PDH_STATUS pdhStat = PdhCollectQueryData( m_pPrivData->m_pdhHQuery );
  225. // pull out all the stats
  226. PDH_FMT_COUNTERVALUE pdhCounterVal;
  227. for ( int i = 0; i < m_pPrivData->m_vecDataInFlight.Count(); ++i )
  228. {
  229. DWORD fmt = PDH_FMT_LONG;
  230. switch ( m_pPerfCounterMap[i].m_eFmt )
  231. {
  232. case k_EFormatInt:
  233. fmt = PDH_FMT_LONG;
  234. break;
  235. case k_EFormatFloat:
  236. fmt = PDH_FMT_DOUBLE;
  237. break;
  238. default:
  239. Assert(false);
  240. }
  241. pdhStat = PdhGetFormattedCounterValue( m_pPrivData->m_ppdhHCounters[i], fmt, NULL, &pdhCounterVal );
  242. byte *dst = (byte *)&m_pPrivData->m_vecDataInFlight[i];
  243. switch ( m_pPerfCounterMap[i].m_eFmt )
  244. {
  245. case k_EFormatInt:
  246. *(int32 *)dst = ERROR_SUCCESS == pdhStat ? pdhCounterVal.longValue : (int) m_pPerfCounterMap[i].m_fUnsetValue;
  247. break;
  248. case k_EFormatFloat:
  249. *(float *)dst = ERROR_SUCCESS == pdhStat ? (float) pdhCounterVal.doubleValue : (float) m_pPerfCounterMap[i].m_fUnsetValue;
  250. break;
  251. default:
  252. Assert(false);
  253. }
  254. }
  255. // swap in the new data
  256. AUTO_LOCK( m_pPrivData->m_mutexDataAccess );
  257. m_pPrivData->m_vecData.Swap( m_pPrivData->m_vecDataInFlight );
  258. }
  259. return 0;
  260. }
  261. //-----------------------------------------------------------------------------
  262. // Purpose: closes the Pdh Query (which frees system resources)
  263. //-----------------------------------------------------------------------------
  264. void CWinPerfCounters::Shutdown()
  265. {
  266. if ( m_pPrivData )
  267. {
  268. m_pPrivData->m_bThreadRunning = false;
  269. if ( m_pPrivData->m_threadQueryPerfCounters.IsAlive() )
  270. {
  271. m_pPrivData->m_eventStartQuery.Set();
  272. m_pPrivData->m_threadQueryPerfCounters.Join( 200 );
  273. }
  274. #ifdef _WIN32
  275. if (m_bInited)
  276. {
  277. PdhCloseQuery( m_pPrivData->m_pdhHQuery );
  278. m_bInited = false;
  279. }
  280. #endif
  281. }
  282. m_pPerfCounterMap = NULL;
  283. SAFE_DELETE( m_pPrivData );
  284. }
  285. #ifdef DBGFLAG_VALIDATE
  286. //-----------------------------------------------------------------------------
  287. // Purpose: Run a global validation pass on all of our data structures and memory
  288. // allocations.
  289. // Input: validator - Our global validator object
  290. // pchName - Our name (typically a member var in our container)
  291. //-----------------------------------------------------------------------------
  292. void CWinPerfCounters::Validate( CValidator &validator, const char *pchName )
  293. {
  294. VALIDATE_SCOPE();
  295. ValidatePtr( m_pPrivData );
  296. }
  297. #endif // DBGFLAG_VALIDATE
  298. //-----------------------------------------------------------------------------
  299. // Purpose:
  300. //-----------------------------------------------------------------------------
  301. CWinNetworkPerfCounters::CWinNetworkPerfCounters( )
  302. {
  303. m_unNumInterfaces = 0;
  304. m_bInited = false;
  305. }
  306. //-----------------------------------------------------------------------------
  307. // Purpose:
  308. //-----------------------------------------------------------------------------
  309. CWinNetworkPerfCounters::~CWinNetworkPerfCounters()
  310. {
  311. }
  312. //-----------------------------------------------------------------------------
  313. // Purpose:
  314. //-----------------------------------------------------------------------------
  315. bool CWinNetworkPerfCounters::Init()
  316. {
  317. // Enumerate all network interfaces and create counters for each
  318. bool bRet = false;
  319. HQUERY hQuery;
  320. PDH_STATUS pdhStatus = PdhOpenQuery( NULL, 1, & hQuery );
  321. if ( pdhStatus != ERROR_SUCCESS )
  322. {
  323. return false;
  324. }
  325. CUtlBuffer bufCounterList;
  326. DWORD dwCounterListSize = 0;
  327. CUtlBuffer bufInstanceList;
  328. DWORD dwInstanceListSize = 0;
  329. LPTSTR pszThisInstance = NULL;
  330. // Determine the required buffer size for the data.
  331. pdhStatus = PdhEnumObjectItems
  332. (
  333. NULL, // reserved
  334. NULL, // local machine
  335. TEXT("Network Interface"), // object to enumerate
  336. NULL, // pass in NULL buffers
  337. & dwCounterListSize, // an 0 length to get
  338. NULL,// required size
  339. & dwInstanceListSize, // of the buffers in chars
  340. PERF_DETAIL_WIZARD, // counter detail level
  341. 0
  342. );
  343. // Note: old MSDN example tests against ERROR_SUCCESS (works on Win2k, fails on XP,
  344. // new (.NET) MSDN example tests against PDH_MORE_DATA (works on XP, fails on Win2k Server).
  345. // A usenet post has code that tests against both.
  346. if ( pdhStatus != ERROR_SUCCESS
  347. && pdhStatus != PDH_MORE_DATA
  348. ) //lint !e650 !e737 constant out of range for '!=' (code from MSDN)
  349. {
  350. // failed to determine buffer size
  351. return bRet;
  352. }
  353. else
  354. {
  355. // Allocate the buffers and try the call again.
  356. bufCounterList.EnsureCapacity( dwCounterListSize * sizeof(TCHAR) );
  357. bufInstanceList.EnsureCapacity( dwInstanceListSize * sizeof (TCHAR) );
  358. pdhStatus = PdhEnumObjectItems
  359. (
  360. NULL, // reserved
  361. NULL, // local machine
  362. TEXT("Network Interface"), // object to enumerate
  363. (LPTSTR) bufCounterList.Base(),
  364. & dwCounterListSize,
  365. (LPTSTR) bufInstanceList.Base(),
  366. & dwInstanceListSize,
  367. PERF_DETAIL_WIZARD, // counter detail level
  368. 0
  369. );
  370. if ( pdhStatus != ERROR_SUCCESS )
  371. {
  372. return bRet;
  373. }
  374. else
  375. {
  376. // If the machine has multiple network cards with identical names then we need
  377. // to count them and append '#n' to the name, beginning with #1 for the first
  378. // duplicate.
  379. typedef CUtlDict< uint > mapIdenticalInstanceCount_t;
  380. mapIdenticalInstanceCount_t mapIdenticalInstanceCount;
  381. // Walk the return instance list.
  382. for (
  383. pszThisInstance = (LPTSTR) bufInstanceList.Base();
  384. * pszThisInstance != 0;
  385. pszThisInstance += lstrlen( pszThisInstance ) + 1
  386. )
  387. {
  388. // reached our limit
  389. if ( m_unNumInterfaces >= sm_unMaxNetworkInterfacesToMeasure )
  390. break;
  391. CUtlString sThisInstance;
  392. // If the machine has multiple network cards with identical names then we need
  393. // to count them and append '#n' to the name, beginning with #1 for the first
  394. // duplicate.
  395. // note 11/20/2012 I'm not sure this is true anymore, Windows might finally
  396. // be giving us the instance names we actually need. Which is good because sometimes
  397. // it's reall " 2" or " _2" on the end. . .
  398. int iDict = mapIdenticalInstanceCount.Find( pszThisInstance );
  399. if ( iDict == mapIdenticalInstanceCount.InvalidIndex() )
  400. {
  401. mapIdenticalInstanceCount.Insert( pszThisInstance, 1 );
  402. sThisInstance = pszThisInstance;
  403. }
  404. else
  405. {
  406. mapIdenticalInstanceCount[iDict]++;
  407. sThisInstance.Format( "%s #%d", pszThisInstance, mapIdenticalInstanceCount[iDict] );
  408. }
  409. CUtlString sBytesSentCounterName;
  410. sBytesSentCounterName.Format( "\\Network Interface(%s)\\Bytes Sent/sec", sThisInstance.String() );
  411. CUtlString sBytesRecvCounterName;
  412. sBytesRecvCounterName.Format( "\\Network Interface(%s)\\Bytes Received/sec", sThisInstance.String() );
  413. PerfCounter_t &BytesSentCounter = m_rgPerfCounterInfo[ 2 * m_unNumInterfaces ];
  414. PerfCounter_t &BytesReceivedCounter = m_rgPerfCounterInfo[ 2 * m_unNumInterfaces + 1 ];
  415. BytesSentCounter.m_rgchPerfObject = strdup( sBytesSentCounterName.Get() );
  416. BytesSentCounter.m_rgchPerfObjectAlternative = NULL;
  417. BytesSentCounter.m_statsOffset = offsetof( Stats_t, m_rgunNetworkBytesSentStats ) + ( m_unNumInterfaces * sizeof( uint32 ) );
  418. BytesSentCounter.m_eFmt = k_EFormatInt;
  419. BytesSentCounter.m_fUnsetValue = 0;
  420. BytesSentCounter.m_bAssertOnFailure = false;
  421. BytesSentCounter.m_bCounterRequiresRollup = false;
  422. BytesReceivedCounter.m_rgchPerfObject = strdup( sBytesRecvCounterName.Get() );
  423. BytesReceivedCounter.m_rgchPerfObjectAlternative = NULL;
  424. BytesReceivedCounter.m_statsOffset = offsetof( Stats_t, m_rgunNetworkBytesReceivedStats ) + ( m_unNumInterfaces * sizeof( uint32 ) );
  425. BytesReceivedCounter.m_eFmt = k_EFormatInt;
  426. BytesReceivedCounter.m_fUnsetValue = 0;
  427. BytesReceivedCounter.m_bAssertOnFailure = false;
  428. BytesReceivedCounter.m_bCounterRequiresRollup = false;
  429. m_unNumInterfaces++;
  430. }
  431. bRet = m_PerfCounters.Init( m_rgPerfCounterInfo, m_unNumInterfaces*2 );
  432. m_bInited = bRet;
  433. }
  434. }
  435. PdhCloseQuery( hQuery );
  436. return bRet;
  437. }
  438. //-----------------------------------------------------------------------------
  439. // Purpose:
  440. //-----------------------------------------------------------------------------
  441. bool CWinNetworkPerfCounters::TakeSample()
  442. {
  443. if ( m_bInited )
  444. return m_PerfCounters.TakeSample();
  445. else
  446. return false;
  447. }
  448. //-----------------------------------------------------------------------------
  449. // Purpose:
  450. //-----------------------------------------------------------------------------
  451. bool CWinNetworkPerfCounters::WriteStats( uint64 *pu64BytesSentPerSec, uint64 *pu64BytesRecvPerSec )
  452. {
  453. if ( !m_bInited )
  454. return false;
  455. bool bRet = false;
  456. if ( m_PerfCounters.WriteStats( &m_Stats ) )
  457. {
  458. *pu64BytesSentPerSec = 0;
  459. *pu64BytesRecvPerSec = 0;
  460. for ( uint32 i=0; i < m_unNumInterfaces; ++i )
  461. {
  462. *pu64BytesSentPerSec += m_Stats.m_rgunNetworkBytesSentStats[i];
  463. *pu64BytesRecvPerSec += m_Stats.m_rgunNetworkBytesReceivedStats[i];
  464. }
  465. bRet = true;
  466. }
  467. return bRet;
  468. }
  469. //-----------------------------------------------------------------------------
  470. // Purpose:
  471. //-----------------------------------------------------------------------------
  472. void CWinNetworkPerfCounters::Shutdown()
  473. {
  474. if ( m_bInited )
  475. m_PerfCounters.Shutdown();
  476. }
  477. #ifdef DBGFLAG_VALIDATE
  478. //-----------------------------------------------------------------------------
  479. // Purpose:
  480. //-----------------------------------------------------------------------------
  481. void CWinNetworkPerfCounters::Validate( CValidator &validator, const char *pchName )
  482. {
  483. ValidateObj( m_PerfCounters );
  484. for ( uint32 i=0; i < m_unNumInterfaces; ++i )
  485. {
  486. validator.ClaimMemory( (void*) m_rgPerfCounterInfo[2*i].m_rgchPerfObject );
  487. validator.ClaimMemory( (void*) m_rgPerfCounterInfo[2*i + 1].m_rgchPerfObject );
  488. }
  489. }
  490. #endif // DBGFLAG_VALIDATE
  491. #endif // _WIN32