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.

665 lines
15 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #ifdef _WIN32
  9. #include <windows.h>
  10. #pragma warning( disable : 4530 ) // warning: exception handler -GX option
  11. #include "tier0/valve_off.h"
  12. #include "tier0/pmelib.h"
  13. #if _MSC_VER >=1300
  14. #else
  15. #include "winioctl.h"
  16. #endif
  17. #include "tier0/valve_on.h"
  18. #include "tier0/ioctlcodes.h"
  19. // NOTE: This has to be the last file included!
  20. #include "tier0/memdbgon.h"
  21. PME* PME::_singleton = 0;
  22. // Single interface.
  23. PME* PME::Instance()
  24. {
  25. if (_singleton == 0)
  26. {
  27. _singleton = new PME;
  28. }
  29. return _singleton;
  30. }
  31. //---------------------------------------------------------------------------
  32. // Open the device driver and detect the processor
  33. //---------------------------------------------------------------------------
  34. HRESULT PME::Init( void )
  35. {
  36. OSVERSIONINFO OS;
  37. if ( bDriverOpen )
  38. return E_DRIVER_ALREADY_OPEN;
  39. switch( vendor )
  40. {
  41. case INTEL:
  42. case AMD:
  43. break;
  44. default:
  45. bDriverOpen = FALSE; // not an Intel or Athlon processor so return false
  46. return E_UNKNOWN_CPU_VENDOR;
  47. }
  48. //-----------------------------------------------------------------------
  49. // Get the operating system version
  50. //-----------------------------------------------------------------------
  51. OS.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
  52. GetVersionEx( &OS );
  53. if ( OS.dwPlatformId == VER_PLATFORM_WIN32_NT )
  54. {
  55. hFile = CreateFile( // WINDOWS NT
  56. "\\\\.\\GDPERF",
  57. GENERIC_READ,
  58. 0,
  59. NULL,
  60. OPEN_EXISTING,
  61. FILE_ATTRIBUTE_NORMAL,
  62. NULL);
  63. }
  64. else
  65. {
  66. hFile = CreateFile( // WINDOWS 95
  67. "\\\\.\\GDPERF.VXD",
  68. GENERIC_READ,
  69. 0,
  70. NULL,
  71. OPEN_EXISTING,
  72. FILE_ATTRIBUTE_NORMAL,
  73. NULL);
  74. }
  75. if (hFile == INVALID_HANDLE_VALUE )
  76. return E_CANT_OPEN_DRIVER;
  77. bDriverOpen = TRUE;
  78. //-------------------------------------------------------------------
  79. // We have successfully opened the device driver, get the family
  80. // of the processor.
  81. //-------------------------------------------------------------------
  82. //-------------------------------------------------------------------
  83. // We need to write to counter 0 on the pro family to enable both
  84. // of the performance counters. We write to both so they start in a
  85. // known state. For the pentium this is not necessary.
  86. //-------------------------------------------------------------------
  87. if (vendor == INTEL && version.Family == PENTIUMPRO_FAMILY)
  88. {
  89. SelectP5P6PerformanceEvent(P6_CLOCK, 0, TRUE, TRUE);
  90. SelectP5P6PerformanceEvent(P6_CLOCK, 1, TRUE, TRUE);
  91. }
  92. return S_OK;
  93. }
  94. //---------------------------------------------------------------------------
  95. // Close the device driver
  96. //---------------------------------------------------------------------------
  97. HRESULT PME::Close(void)
  98. {
  99. if (bDriverOpen == false) // driver is not going
  100. return E_DRIVER_NOT_OPEN;
  101. bDriverOpen = false;
  102. if (hFile) // if we have no driver handle, return FALSE
  103. {
  104. BOOL result = CloseHandle(hFile);
  105. hFile = NULL;
  106. return result ? S_OK : HRESULT_FROM_WIN32( GetLastError() );
  107. }
  108. else
  109. return E_DRIVER_NOT_OPEN;
  110. }
  111. //---------------------------------------------------------------------------
  112. // Select the event to monitor with counter 0
  113. //
  114. HRESULT PME::SelectP5P6PerformanceEvent(uint32 dw_event, uint32 dw_counter,
  115. bool b_user, bool b_kernel)
  116. {
  117. HRESULT hr = S_OK;
  118. if (dw_counter>1) // is the counter valid
  119. return E_BAD_COUNTER;
  120. if (bDriverOpen == false) // driver is not going
  121. return E_DRIVER_NOT_OPEN;
  122. if ( ((dw_event>>28)&0xF) != (uint32)version.Family)
  123. {
  124. return E_ILLEGAL_OPERATION; // this operation is not for this processor
  125. }
  126. if ( (((dw_event & 0x300)>>8) & (dw_counter+1)) == 0 )
  127. {
  128. return E_ILLEGAL_OPERATION; // this operation is not for this counter
  129. }
  130. switch(version.Family)
  131. {
  132. case PENTIUM_FAMILY:
  133. {
  134. uint64 i64_cesr;
  135. int i_kernel_bit,i_user_bit;
  136. BYTE u1_event = (BYTE)((dw_event & (0x3F0000))>>16);
  137. if (dw_counter==0) // the kernel and user mode bits depend on
  138. { // counter being used.
  139. i_kernel_bit = 6;
  140. i_user_bit = 7;
  141. }
  142. else
  143. {
  144. i_kernel_bit = 22;
  145. i_user_bit = 23;
  146. }
  147. ReadMSR(0x11, &i64_cesr); // get current P5 event select (cesr)
  148. // top 32bits of cesr are not valid so ignore them
  149. i64_cesr &= ((dw_counter == 0)?0xffff0000:0x0000ffff);
  150. WriteMSR(0x11,i64_cesr); // stop the counter
  151. WriteMSR((dw_counter==0)?0x12:0x13,0ui64); // clear the p.counter
  152. // set the user and kernel mode bits
  153. i64_cesr |= ( b_user?(1<<7):0 ) | ( b_kernel?(1<<6):0 );
  154. // is this the special P5 value that signals count clocks??
  155. if (u1_event == 0x3f)
  156. {
  157. WriteMSR(0x11, i64_cesr|0x100); // Count clocks
  158. }
  159. else
  160. {
  161. WriteMSR(0x11, i64_cesr|u1_event); // Count events
  162. }
  163. }
  164. break;
  165. case PENTIUMPRO_FAMILY:
  166. {
  167. BYTE u1_event = (BYTE)((dw_event & (0xFF0000))>>16);
  168. BYTE u1_mask = (BYTE)((dw_event & 0xFF));
  169. // Event select 0 and 1 are identical.
  170. hr = WriteMSR((dw_counter==0)?0x186:0x187,
  171. uint64((u1_event | (b_user?(1<<16):0) | (b_kernel?(1<<17):0) | (1<<22) | (1<<18) | (u1_mask<<8)) )
  172. );
  173. }
  174. break;
  175. case PENTIUM4_FAMILY:
  176. // use the p4 path
  177. break;
  178. default:
  179. return E_UNKNOWN_CPU;
  180. }
  181. return hr;
  182. }
  183. //---------------------------------------------------------------------------
  184. // Read model specific register
  185. //---------------------------------------------------------------------------
  186. HRESULT PME::ReadMSR(uint32 dw_reg, int64 * pi64_value)
  187. {
  188. DWORD dw_ret_len;
  189. if (bDriverOpen == false) // driver is not going
  190. return E_DRIVER_NOT_OPEN;
  191. BOOL result = DeviceIoControl
  192. (
  193. hFile, // Handle to device
  194. (DWORD) IOCTL_READ_MSR, // IO Control code for Read
  195. &dw_reg, // Input Buffer to driver.
  196. sizeof(uint32), // Length of input buffer.
  197. pi64_value, // Output Buffer from driver.
  198. sizeof(int64), // Length of output buffer in bytes.
  199. &dw_ret_len, // Bytes placed in output buffer.
  200. NULL // NULL means wait till op. completes
  201. );
  202. HRESULT hr = result ? S_OK : HRESULT_FROM_WIN32( GetLastError() );
  203. if (hr == S_OK && dw_ret_len != sizeof(int64))
  204. hr = E_BAD_DATA;
  205. return hr;
  206. }
  207. HRESULT PME::ReadMSR(uint32 dw_reg, uint64 * pi64_value)
  208. {
  209. DWORD dw_ret_len;
  210. if (bDriverOpen == false) // driver is not going
  211. return E_DRIVER_NOT_OPEN;
  212. BOOL result = DeviceIoControl
  213. (
  214. hFile, // Handle to device
  215. (DWORD) IOCTL_READ_MSR, // IO Control code for Read
  216. &dw_reg, // Input Buffer to driver.
  217. sizeof(uint32), // Length of input buffer.
  218. pi64_value, // Output Buffer from driver.
  219. sizeof(uint64), // Length of output buffer in bytes.
  220. &dw_ret_len, // Bytes placed in output buffer.
  221. NULL // NULL means wait till op. completes
  222. );
  223. HRESULT hr = result ? S_OK : HRESULT_FROM_WIN32( GetLastError() );
  224. if (hr == S_OK && dw_ret_len != sizeof(uint64))
  225. hr = E_BAD_DATA;
  226. return hr;
  227. }
  228. //---------------------------------------------------------------------------
  229. // Write model specific register
  230. //---------------------------------------------------------------------------
  231. HRESULT PME::WriteMSR(uint32 dw_reg, const int64 & i64_value)
  232. {
  233. DWORD dw_buffer[3];
  234. DWORD dw_ret_len;
  235. if (bDriverOpen == false) // driver is not going
  236. return E_DRIVER_NOT_OPEN;
  237. dw_buffer[0] = dw_reg; // setup the 12 byte input
  238. *((int64*)(&dw_buffer[1]))= i64_value;
  239. BOOL result = DeviceIoControl
  240. (
  241. hFile, // Handle to device
  242. (DWORD) IOCTL_WRITE_MSR, // IO Control code for Read
  243. dw_buffer, // Input Buffer to driver.
  244. 12, // Length of Input buffer
  245. NULL, // Buffer from driver, None for WRMSR
  246. 0, // Length of output buffer in bytes.
  247. &dw_ret_len, // Bytes placed in DataBuffer.
  248. NULL // NULL means wait till op. completes.
  249. );
  250. HRESULT hr = result ? S_OK : HRESULT_FROM_WIN32( GetLastError() );
  251. if (hr == S_OK && dw_ret_len != 0)
  252. hr = E_BAD_DATA;
  253. return hr;
  254. }
  255. HRESULT PME::WriteMSR(uint32 dw_reg, const uint64 & i64_value)
  256. {
  257. DWORD dw_buffer[3];
  258. DWORD dw_ret_len;
  259. if (bDriverOpen == false) // driver is not going
  260. return E_DRIVER_NOT_OPEN;
  261. dw_buffer[0] = dw_reg; // setup the 12 byte input
  262. *((uint64*)(&dw_buffer[1]))= i64_value;
  263. BOOL result = DeviceIoControl
  264. (
  265. hFile, // Handle to device
  266. (DWORD) IOCTL_WRITE_MSR, // IO Control code for Read
  267. dw_buffer, // Input Buffer to driver.
  268. 12, // Length of Input buffer
  269. NULL, // Buffer from driver, None for WRMSR
  270. 0, // Length of output buffer in bytes.
  271. &dw_ret_len, // Bytes placed in DataBuffer.
  272. NULL // NULL means wait till op. completes.
  273. );
  274. //E_POINTER
  275. HRESULT hr = result ? S_OK : HRESULT_FROM_WIN32( GetLastError() );
  276. if (hr == S_OK && dw_ret_len != 0)
  277. hr = E_BAD_DATA;
  278. return hr;
  279. }
  280. #pragma hdrstop
  281. //---------------------------------------------------------------------------
  282. // Return the frequency of the processor in Hz.
  283. //
  284. double PME::GetCPUClockSpeedFast(void)
  285. {
  286. int64 i64_perf_start, i64_perf_freq, i64_perf_end;
  287. int64 i64_clock_start,i64_clock_end;
  288. double d_loop_period, d_clock_freq;
  289. //-----------------------------------------------------------------------
  290. // Query the performance of the Windows high resolution timer.
  291. //-----------------------------------------------------------------------
  292. QueryPerformanceFrequency((LARGE_INTEGER*)&i64_perf_freq);
  293. //-----------------------------------------------------------------------
  294. // Query the current value of the Windows high resolution timer.
  295. //-----------------------------------------------------------------------
  296. QueryPerformanceCounter((LARGE_INTEGER*)&i64_perf_start);
  297. i64_perf_end = 0;
  298. //-----------------------------------------------------------------------
  299. // Time of loop of 250000 windows cycles with RDTSC
  300. //-----------------------------------------------------------------------
  301. RDTSC(i64_clock_start);
  302. while(i64_perf_end<i64_perf_start+250000)
  303. {
  304. QueryPerformanceCounter((LARGE_INTEGER*)&i64_perf_end);
  305. }
  306. RDTSC(i64_clock_end);
  307. //-----------------------------------------------------------------------
  308. // Caclulate the frequency of the RDTSC timer and therefore calculate
  309. // the frequency of the processor.
  310. //-----------------------------------------------------------------------
  311. i64_clock_end -= i64_clock_start;
  312. d_loop_period = ((double)(i64_perf_freq)) / 250000.0;
  313. d_clock_freq = ((double)(i64_clock_end & 0xffffffff))*d_loop_period;
  314. return (float)d_clock_freq;
  315. }
  316. // takes 1 second
  317. double PME::GetCPUClockSpeedSlow(void)
  318. {
  319. if (m_CPUClockSpeed != 0)
  320. return m_CPUClockSpeed;
  321. unsigned long start_ms, stop_ms;
  322. unsigned long start_tsc,stop_tsc;
  323. // boosting priority helps with noise. its optional and i dont think
  324. // it helps all that much
  325. PME * pme = PME::Instance();
  326. pme->SetProcessPriority(ProcessPriorityHigh);
  327. // wait for millisecond boundary
  328. start_ms = GetTickCount() + 5;
  329. while (start_ms <= GetTickCount());
  330. // read timestamp (you could use QueryPerformanceCounter in hires mode if you want)
  331. #ifdef COMPILER_MSVC64
  332. RDTSC(start_tsc);
  333. #else
  334. __asm
  335. {
  336. rdtsc
  337. mov dword ptr [start_tsc+0],eax
  338. mov dword ptr [start_tsc+4],edx
  339. }
  340. #endif
  341. // wait for end
  342. stop_ms = start_ms + 1000; // longer wait gives better resolution
  343. while (stop_ms > GetTickCount());
  344. // read timestamp (you could use QueryPerformanceCounter in hires mode if you want)
  345. #ifdef COMPILER_MSVC64
  346. RDTSC(stop_tsc);
  347. #else
  348. __asm
  349. {
  350. rdtsc
  351. mov dword ptr [stop_tsc+0],eax
  352. mov dword ptr [stop_tsc+4],edx
  353. }
  354. #endif
  355. // normalize priority
  356. pme->SetProcessPriority(ProcessPriorityNormal);
  357. // return clock speed
  358. // optionally here you could round to known clocks, like speeds that are multimples
  359. // of 100, 133, 166, etc.
  360. m_CPUClockSpeed = ((stop_tsc - start_tsc) * 1000.0) / (double)(stop_ms - start_ms);
  361. return m_CPUClockSpeed;
  362. }
  363. const unsigned short cccr_escr_map[NCOUNTERS][8] =
  364. {
  365. {
  366. 0x3B2,
  367. 0x3B4,
  368. 0x3AA,
  369. 0x3B6,
  370. 0x3AC,
  371. 0x3C8,
  372. 0x3A2,
  373. 0x3A0,
  374. },
  375. {
  376. 0x3B2,
  377. 0x3B4,
  378. 0x3AA,
  379. 0x3B6,
  380. 0x3AC,
  381. 0x3C8,
  382. 0x3A2,
  383. 0x3A0,
  384. },
  385. {
  386. 0x3B3,
  387. 0x3B5,
  388. 0x3AB,
  389. 0x3B7,
  390. 0x3AD,
  391. 0x3C9,
  392. 0x3A3,
  393. 0x3A1,
  394. },
  395. {
  396. 0x3B3,
  397. 0x3B5,
  398. 0x3AB,
  399. 0x3B7,
  400. 0x3AD,
  401. 0x3C9,
  402. 0x3A3,
  403. 0x3A1,
  404. },
  405. {
  406. 0x3C0,
  407. 0x3C4,
  408. 0x3C2,
  409. },
  410. {
  411. 0x3C0,
  412. 0x3C4,
  413. 0x3C2,
  414. },
  415. {
  416. 0x3C1,
  417. 0x3C5,
  418. 0x3C3,
  419. },
  420. {
  421. 0x3C1,
  422. 0x3C5,
  423. 0x3C3,
  424. },
  425. {
  426. 0x3A6,
  427. 0x3A4,
  428. 0x3AE,
  429. 0x3B0,
  430. 0,
  431. 0x3A8,
  432. },
  433. {
  434. 0x3A6,
  435. 0x3A4,
  436. 0x3AE,
  437. 0x3B0,
  438. 0,
  439. 0x3A8,
  440. },
  441. {
  442. 0x3A7,
  443. 0x3A5,
  444. 0x3AF,
  445. 0x3B1,
  446. 0,
  447. 0x3A9,
  448. },
  449. {
  450. 0x3A7,
  451. 0x3A5,
  452. 0x3AF,
  453. 0x3B1,
  454. 0,
  455. 0x3A9,
  456. },
  457. {
  458. 0x3BA,
  459. 0x3CA,
  460. 0x3BC,
  461. 0x3BE,
  462. 0x3B8,
  463. 0x3CC,
  464. 0x3E0,
  465. },
  466. {
  467. 0x3BA,
  468. 0x3CA,
  469. 0x3BC,
  470. 0x3BE,
  471. 0x3B8,
  472. 0x3CC,
  473. 0x3E0,
  474. },
  475. {
  476. 0x3BB,
  477. 0x3CB,
  478. 0x3BD,
  479. 0,
  480. 0x3B9,
  481. 0x3CD,
  482. 0x3E1,
  483. },
  484. {
  485. 0x3BB,
  486. 0x3CB,
  487. 0x3BD,
  488. 0,
  489. 0x3B9,
  490. 0x3CD,
  491. 0x3E1,
  492. },
  493. {
  494. 0x3BA,
  495. 0x3CA,
  496. 0x3BC,
  497. 0x3BE,
  498. 0x3B8,
  499. 0x3CC,
  500. 0x3E0,
  501. },
  502. {
  503. 0x3BB,
  504. 0x3CB,
  505. 0x3BD,
  506. 0,
  507. 0x3B9,
  508. 0x3CD,
  509. 0x3E1,
  510. },
  511. };
  512. #ifdef DBGFLAG_VALIDATE
  513. //-----------------------------------------------------------------------------
  514. // Purpose: Ensure that all of our internal structures are consistent, and
  515. // account for all memory that we've allocated.
  516. // Input: validator - Our global validator object
  517. // pchName - Our name (typically a member var in our container)
  518. //-----------------------------------------------------------------------------
  519. void PME::Validate( CValidator &validator, tchar *pchName )
  520. {
  521. validator.Push( _T("PME"), this, pchName );
  522. validator.ClaimMemory( this );
  523. validator.ClaimMemory( cache );
  524. validator.ClaimMemory( ( void * ) vendor_name.c_str( ) );
  525. validator.ClaimMemory( ( void * ) brand.c_str( ) );
  526. validator.Pop( );
  527. }
  528. #endif // DBGFLAG_VALIDATE
  529. #pragma warning( default : 4530 ) // warning: exception handler -GX option
  530. #endif