Source code of Windows XP (NT5)
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.

724 lines
21 KiB

  1. /*++ BUILD Version: 0001 // Increment this if a change has global effects
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. p5ctrs.c
  5. Abstract:
  6. This file implements the Extensible Objects for the P5 object type
  7. Created:
  8. Russ Blake 24 Feb 93
  9. Revision History
  10. --*/
  11. //
  12. // Include Files
  13. //
  14. #include <nt.h>
  15. #include <ntrtl.h>
  16. #include <nturtl.h>
  17. #include <windows.h>
  18. #include <string.h>
  19. #include <winperf.h>
  20. #include "p5ctrmsg.h" // error message definition
  21. #include "p5ctrnam.h"
  22. #include "p5msg.h"
  23. #include "perfutil.h"
  24. #include "pentdata.h"
  25. #include "..\pstat.h"
  26. //
  27. // References to constants which initialize the Object type definitions
  28. //
  29. extern P5_DATA_DEFINITION P5DataDefinition;
  30. //
  31. // P5 data structures
  32. //
  33. DWORD dwOpenCount = 0; // count of "Open" threads
  34. BOOL bInitOK = FALSE; // true = DLL initialized OK
  35. BOOL bP6notP5 = FALSE; // true for P6 processors, false for P5 CPUs
  36. HANDLE DriverHandle; // handle of opened device driver
  37. UCHAR NumberOfProcessors;
  38. #define INFSIZE 60000
  39. ULONG Buffer[INFSIZE/4];
  40. //
  41. // Function Prototypes
  42. //
  43. // these are used to insure that the data collection functions
  44. // accessed by Perflib will have the correct calling format.
  45. //
  46. PM_OPEN_PROC OpenP5PerformanceData;
  47. PM_COLLECT_PROC CollectP5PerformanceData;
  48. PM_CLOSE_PROC CloseP5PerformanceData;
  49. static
  50. ULONG
  51. InitPerfInfo()
  52. /*++
  53. Routine Description:
  54. Initialize data for perf measurements
  55. Arguments:
  56. None
  57. Return Value:
  58. Number of system processors (0 if error)
  59. Revision History:
  60. 10-21-91 Initial code
  61. --*/
  62. {
  63. UNICODE_STRING DriverName;
  64. NTSTATUS status;
  65. OBJECT_ATTRIBUTES ObjA;
  66. IO_STATUS_BLOCK IOSB;
  67. SYSTEM_BASIC_INFORMATION BasicInfo;
  68. PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PPerfInfo;
  69. SYSTEM_PROCESSOR_INFORMATION CpuInfo;
  70. int i;
  71. //
  72. // Init Nt performance interface
  73. //
  74. NtQuerySystemInformation(
  75. SystemBasicInformation,
  76. &BasicInfo,
  77. sizeof(BasicInfo),
  78. NULL
  79. );
  80. NumberOfProcessors = BasicInfo.NumberOfProcessors;
  81. if (NumberOfProcessors > MAX_PROCESSORS) {
  82. return(0);
  83. }
  84. //
  85. // Open PStat driver
  86. //
  87. RtlInitUnicodeString(&DriverName, L"\\Device\\PStat");
  88. InitializeObjectAttributes(
  89. &ObjA,
  90. &DriverName,
  91. OBJ_CASE_INSENSITIVE,
  92. 0,
  93. 0 );
  94. status = NtOpenFile (
  95. &DriverHandle, // return handle
  96. SYNCHRONIZE | FILE_READ_DATA, // desired access
  97. &ObjA, // Object
  98. &IOSB, // io status block
  99. FILE_SHARE_READ | FILE_SHARE_WRITE, // share access
  100. FILE_SYNCHRONOUS_IO_ALERT // open options
  101. );
  102. if (!NT_SUCCESS(status)) {
  103. return 0;
  104. }
  105. NtQuerySystemInformation (
  106. SystemProcessorInformation,
  107. &CpuInfo,
  108. sizeof(CpuInfo),
  109. NULL);
  110. if ((CpuInfo.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) &&
  111. (CpuInfo.ProcessorLevel == 6)) {
  112. // then this is a P6 so set the global flag
  113. bP6notP5 = TRUE;
  114. }
  115. return(NumberOfProcessors);
  116. }
  117. static
  118. long
  119. GetPerfRegistryInitialization
  120. (
  121. HKEY *phKeyDriverPerf,
  122. DWORD *pdwFirstCounter,
  123. DWORD *pdwFirstHelp
  124. )
  125. {
  126. long status;
  127. DWORD size;
  128. DWORD type;
  129. // get counter and help index base values from registry
  130. // Open key to registry entry
  131. // read First Counter and First Help values
  132. // update static data strucutures by adding base to
  133. // offset value in structure.
  134. status = RegOpenKeyEx (
  135. HKEY_LOCAL_MACHINE,
  136. "SYSTEM\\CurrentControlSet\\Services\\PStat\\Performance",
  137. 0L,
  138. KEY_ALL_ACCESS,
  139. phKeyDriverPerf);
  140. if (status != ERROR_SUCCESS) {
  141. REPORT_ERROR_DATA (P5PERF_UNABLE_OPEN_DRIVER_KEY, LOG_USER,
  142. &status, sizeof(status));
  143. // this is fatal, if we can't get the base values of the
  144. // counter or help names, then the names won't be available
  145. // to the requesting application so there's not much
  146. // point in continuing.
  147. return(status);
  148. }
  149. size = sizeof (DWORD);
  150. status = RegQueryValueEx(
  151. *phKeyDriverPerf,
  152. "First Counter",
  153. 0L,
  154. &type,
  155. (LPBYTE)pdwFirstCounter,
  156. &size);
  157. if (status != ERROR_SUCCESS) {
  158. REPORT_ERROR_DATA (P5PERF_UNABLE_READ_FIRST_COUNTER, LOG_USER,
  159. &status, sizeof(status));
  160. // this is fatal, if we can't get the base values of the
  161. // counter or help names, then the names won't be available
  162. // to the requesting application so there's not much
  163. // point in continuing.
  164. return(status);
  165. }
  166. size = sizeof (DWORD);
  167. status = RegQueryValueEx(
  168. *phKeyDriverPerf,
  169. "First Help",
  170. 0L,
  171. &type,
  172. (LPBYTE)pdwFirstHelp,
  173. &size);
  174. if (status != ERROR_SUCCESS) {
  175. REPORT_ERROR_DATA (P5PERF_UNABLE_READ_FIRST_HELP, LOG_USER,
  176. &status, sizeof(status));
  177. // this is fatal, if we can't get the base values of the
  178. // counter or help names, then the names won't be available
  179. // to the requesting application so there's not much
  180. // point in continuing.
  181. }
  182. return(status);
  183. }
  184. DWORD APIENTRY
  185. OpenP5PerformanceData(
  186. LPWSTR lpDeviceNames
  187. )
  188. /*++
  189. Routine Description:
  190. This routine will open the driver which gets performance data on the
  191. P5. This routine also initializes the data structures used to
  192. pass data back to the registry
  193. Arguments:
  194. Pointer to object ID of each device to be opened (P5)
  195. Return Value:
  196. None.
  197. --*/
  198. {
  199. DWORD ctr;
  200. LONG status;
  201. HKEY hKeyDriverPerf;
  202. DWORD dwFirstCounter;
  203. DWORD dwFirstHelp;
  204. PPERF_COUNTER_DEFINITION pPerfCounterDef;
  205. P5_COUNTER_DATA p5Data;
  206. //
  207. // Since WINLOGON is multi-threaded and will call this routine in
  208. // order to service remote performance queries, this library
  209. // must keep track of how many times it has been opened (i.e.
  210. // how many threads have accessed it). The registry routines will
  211. // limit access to the initialization routine to only one thread
  212. // at a time so synchronization (i.e. reentrancy) should not be
  213. // a problem
  214. //
  215. if (!dwOpenCount) {
  216. // open Eventlog interface
  217. hEventLog = MonOpenEventLog();
  218. // open device driver to retrieve performance values
  219. NumberOfProcessors = (UCHAR)InitPerfInfo();
  220. // log error if unsuccessful
  221. if (!NumberOfProcessors) {
  222. REPORT_ERROR (P5PERF_OPEN_FILE_ERROR, LOG_USER);
  223. // this is fatal, if we can't get data then there's no
  224. // point in continuing.
  225. status = GetLastError(); // return error
  226. goto OpenExitPoint;
  227. }
  228. status = GetPerfRegistryInitialization(&hKeyDriverPerf,
  229. &dwFirstCounter,
  230. &dwFirstHelp);
  231. if (status == ERROR_SUCCESS) {
  232. // initialize P5 data
  233. P5DataDefinition.P5PerfObject.ObjectNameTitleIndex +=
  234. dwFirstCounter;
  235. P5DataDefinition.P5PerfObject.ObjectHelpTitleIndex +=
  236. dwFirstHelp;
  237. pPerfCounterDef = &P5DataDefinition.Data_read;
  238. for (ctr=0;
  239. ctr < P5DataDefinition.P5PerfObject.NumCounters;
  240. ctr++, pPerfCounterDef++) {
  241. pPerfCounterDef->CounterNameTitleIndex += dwFirstCounter;
  242. pPerfCounterDef->CounterHelpTitleIndex += dwFirstHelp;
  243. }
  244. // initialize P6 data
  245. P6DataDefinition.P6PerfObject.ObjectNameTitleIndex +=
  246. dwFirstCounter;
  247. P6DataDefinition.P6PerfObject.ObjectHelpTitleIndex +=
  248. dwFirstHelp;
  249. pPerfCounterDef = &P6DataDefinition.StoreBufferBlocks;
  250. for (ctr=0;
  251. ctr < P6DataDefinition.P6PerfObject.NumCounters;
  252. ctr++, pPerfCounterDef++) {
  253. pPerfCounterDef->CounterNameTitleIndex += dwFirstCounter;
  254. pPerfCounterDef->CounterHelpTitleIndex += dwFirstHelp;
  255. }
  256. RegCloseKey (hKeyDriverPerf); // close key to registry
  257. bInitOK = TRUE; // ok to use this function
  258. }
  259. }
  260. dwOpenCount++; // increment OPEN counter
  261. status = ERROR_SUCCESS; // for successful exit
  262. OpenExitPoint:
  263. return status;
  264. }
  265. static
  266. void
  267. UpdateInternalStats()
  268. {
  269. IO_STATUS_BLOCK IOSB;
  270. // clear the buffer first
  271. memset (Buffer, 0, sizeof(Buffer));
  272. // get the stat's from the driver
  273. NtDeviceIoControlFile(
  274. DriverHandle,
  275. (HANDLE) NULL, // event
  276. (PIO_APC_ROUTINE) NULL,
  277. (PVOID) NULL,
  278. &IOSB,
  279. PSTAT_READ_STATS,
  280. Buffer, // input buffer
  281. INFSIZE,
  282. NULL, // output buffer
  283. 0
  284. );
  285. }
  286. DWORD APIENTRY
  287. CollectP5PerformanceData(
  288. IN LPWSTR lpValueName,
  289. IN OUT LPVOID *lppData,
  290. IN OUT LPDWORD lpcbTotalBytes,
  291. IN OUT LPDWORD lpNumObjectTypes
  292. )
  293. /*++
  294. Routine Description:
  295. This routine will return the data for the P5 counters.
  296. Arguments:
  297. IN LPWSTR lpValueName
  298. pointer to a wide character string passed by registry.
  299. IN OUT LPVOID *lppData
  300. IN: pointer to the address of the buffer to receive the completed
  301. PerfDataBlock and subordinate structures. This routine will
  302. append its data to the buffer starting at the point referenced
  303. by *lppData.
  304. OUT: points to the first byte after the data structure added by this
  305. routine. This routine updated the value at lppdata after appending
  306. its data.
  307. IN OUT LPDWORD lpcbTotalBytes
  308. IN: the address of the DWORD that tells the size in bytes of the
  309. buffer referenced by the lppData argument
  310. OUT: the number of bytes added by this routine is writted to the
  311. DWORD pointed to by this argument
  312. IN OUT LPDWORD NumObjectTypes
  313. IN: the address of the DWORD to receive the number of objects added
  314. by this routine
  315. OUT: the number of objects added by this routine is writted to the
  316. DWORD pointed to by this argument
  317. Return Value:
  318. ERROR_MORE_DATA if buffer passed is too small to hold data
  319. any error conditions encountered are reported to the event log if
  320. event logging is enabled.
  321. ERROR_SUCCESS if success or any other error. Errors, however are
  322. also reported to the event log.
  323. --*/
  324. {
  325. // Variables for reformating the data
  326. DWORD CurProc;
  327. DWORD SpaceNeeded;
  328. DWORD dwQueryType;
  329. pPSTATS pPentStats;
  330. DWORD cReg0; // pperf Register 0
  331. DWORD cReg1; // pperf Register 1
  332. DWORD dwDerivedIndex;
  333. PVOID pCounterData;
  334. WCHAR ProcessorNameBuffer[11];
  335. UNICODE_STRING ProcessorName;
  336. PP5_DATA_DEFINITION pP5DataDefinition;
  337. PP5_COUNTER_DATA pP5Data;
  338. PP6_DATA_DEFINITION pP6DataDefinition;
  339. PP6_COUNTER_DATA pP6Data;
  340. PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
  341. UpdateInternalStats(); // get stats as early as possible
  342. pPentStats = (pPSTATS)((LPBYTE)Buffer + sizeof(ULONG));
  343. //
  344. // before doing anything else, see if Open went OK
  345. //
  346. if (!bInitOK) {
  347. // unable to continue because open failed.
  348. *lpcbTotalBytes = (DWORD) 0;
  349. *lpNumObjectTypes = (DWORD) 0;
  350. return ERROR_SUCCESS; // yes, this is a successful exit
  351. }
  352. // see if this is a foreign (i.e. non-NT) computer data request
  353. //
  354. dwQueryType = GetQueryType(lpValueName);
  355. if ((dwQueryType == QUERY_FOREIGN) ||
  356. (dwQueryType == QUERY_COSTLY)) {
  357. // this routine does not service requests for data from
  358. // Non-NT computers nor is this a "costly" counter
  359. *lpcbTotalBytes = (DWORD) 0;
  360. *lpNumObjectTypes = (DWORD) 0;
  361. return ERROR_SUCCESS;
  362. }
  363. if (dwQueryType == QUERY_ITEMS){
  364. // both p5 & p6 counters use the same object id
  365. if ( !(IsNumberInUnicodeList(
  366. P5DataDefinition.P5PerfObject.ObjectNameTitleIndex,
  367. lpValueName))) {
  368. // request received for data object not provided by this routine
  369. *lpcbTotalBytes = (DWORD) 0;
  370. *lpNumObjectTypes = (DWORD) 0;
  371. return ERROR_SUCCESS;
  372. }
  373. }
  374. if (bP6notP5) {
  375. pP6DataDefinition = (P6_DATA_DEFINITION *) *lppData;
  376. SpaceNeeded = sizeof(P6_DATA_DEFINITION) +
  377. NumberOfProcessors *
  378. (sizeof(PERF_INSTANCE_DEFINITION) +
  379. (MAX_INSTANCE_NAME+1) * sizeof(WCHAR) +
  380. sizeof(P6_COUNTER_DATA));
  381. } else {
  382. pP5DataDefinition = (P5_DATA_DEFINITION *) *lppData;
  383. SpaceNeeded = sizeof(P5_DATA_DEFINITION) +
  384. NumberOfProcessors *
  385. (sizeof(PERF_INSTANCE_DEFINITION) +
  386. (MAX_INSTANCE_NAME+1) * sizeof(WCHAR) +
  387. sizeof(P5_COUNTER_DATA));
  388. }
  389. if (*lpcbTotalBytes < SpaceNeeded) {
  390. *lpcbTotalBytes = (DWORD) 0;
  391. *lpNumObjectTypes = (DWORD) 0;
  392. return ERROR_MORE_DATA;
  393. }
  394. // ******************************************************************
  395. // **** ****
  396. // **** If here, then the data request includes this performance ****
  397. // **** object and there's enough room for the data so continue ****
  398. // **** ****
  399. // ******************************************************************
  400. //
  401. // Copy the (constant and initialized) Object Type and counter definitions
  402. // to the caller's data buffer
  403. //
  404. if (bP6notP5) {
  405. memmove(pP6DataDefinition,
  406. &P6DataDefinition,
  407. sizeof(P6_DATA_DEFINITION));
  408. pP6DataDefinition->P6PerfObject.NumInstances = NumberOfProcessors;
  409. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  410. &pP6DataDefinition[1];
  411. } else {
  412. memmove(pP5DataDefinition,
  413. &P5DataDefinition,
  414. sizeof(P5_DATA_DEFINITION));
  415. pP5DataDefinition->P5PerfObject.NumInstances = NumberOfProcessors;
  416. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  417. &pP5DataDefinition[1];
  418. }
  419. //
  420. // Format and collect P5 data from the system for each processor
  421. //
  422. for (CurProc = 0;
  423. CurProc < NumberOfProcessors;
  424. CurProc++, pPentStats++) {
  425. // get the index of the two counters returned by the pentium
  426. // performance register interface device driver
  427. cReg0 = pPentStats->EventId[0];
  428. cReg1 = pPentStats->EventId[1];
  429. // build the processor intstance structure
  430. ProcessorName.Length = 0;
  431. ProcessorName.MaximumLength = 11;
  432. ProcessorName.Buffer = ProcessorNameBuffer;
  433. // convert processor instance to a string for use as the instance
  434. // name
  435. RtlIntegerToUnicodeString(CurProc, 10, &ProcessorName);
  436. // initialize the instance structure and return a pointer to the
  437. // base of the data block for this instance
  438. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  439. &pCounterData,
  440. 0,
  441. 0,
  442. CurProc,
  443. &ProcessorName);
  444. if (bP6notP5) {
  445. // do P6 data
  446. pP6Data = (PP6_COUNTER_DATA)pCounterData;
  447. // define the length of the data
  448. pP6Data->CounterBlock.ByteLength = sizeof(P6_COUNTER_DATA);
  449. // clear area so unused counters are 0
  450. memset((PVOID) &pP6Data->llStoreBufferBlocks, // start with 1st data field
  451. 0,
  452. sizeof(P6_COUNTER_DATA) - sizeof(PERF_COUNTER_BLOCK));
  453. // load the 64bit values in the appropriate counter fields
  454. // all other values will remain zeroed
  455. if ((cReg0 < P6IndexMax) &&
  456. (P6IndexToData[cReg0] != PENT_INDEX_NOT_USED)) {
  457. *(LONGLONG *)((LPBYTE)pP6Data + P6IndexToData[cReg0]) =
  458. (pPentStats->Counters[0] & 0x000000FFFFFFFFFF);
  459. }
  460. if ((cReg1 < P6IndexMax) &&
  461. (P6IndexToData[cReg1] != PENT_INDEX_NOT_USED)) {
  462. *(LONGLONG *)((LPBYTE)pP6Data + P6IndexToData[cReg1]) =
  463. (pPentStats->Counters[1] & 0x000000FFFFFFFFFF);
  464. }
  465. // set the instance pointer to the first byte after this instance's
  466. // counter data
  467. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  468. ((PBYTE) pP6Data +
  469. sizeof(P6_COUNTER_DATA));
  470. } else {
  471. // do P5 data
  472. pP5Data = (PP5_COUNTER_DATA)pCounterData;
  473. // define the length of the data
  474. pP5Data->CounterBlock.ByteLength = sizeof(P5_COUNTER_DATA);
  475. // clear area so unused counters are 0
  476. memset((PVOID) &pP5Data->llData_read, // start with 1st data field
  477. 0,
  478. sizeof(P5_COUNTER_DATA) - sizeof(PERF_COUNTER_BLOCK));
  479. // load the 64bit values in the appropriate counter fields
  480. // all other values will remain zeroed
  481. if ((cReg0 < P5IndexMax) &&
  482. (P5IndexToData[cReg0] != PENT_INDEX_NOT_USED)) {
  483. // only the low order 40 bits are valid so mask off the
  484. // others to prevent spurious values
  485. *(LONGLONG *)((LPBYTE)pP5Data + P5IndexToData[cReg0]) =
  486. (pPentStats->Counters[0] & 0x000000FFFFFFFFFF);
  487. }
  488. if ((cReg1 < P5IndexMax) &&
  489. (P5IndexToData[cReg1] != PENT_INDEX_NOT_USED)) {
  490. // only the low order 40 bits are valid so mask off the
  491. // others to prevent spurious values
  492. *(LONGLONG *)((LPBYTE)pP5Data + P5IndexToData[cReg1]) =
  493. (pPentStats->Counters[1] & 0x000000FFFFFFFFFF);
  494. }
  495. // see if the selected counters are part of a derived counter and
  496. // update if necessary
  497. if ((cReg0 < P5IndexMax) && (cReg1 < P5IndexMax) &&
  498. (dwDerivedp5Counters[cReg0] && dwDerivedp5Counters[cReg1])) {
  499. for (dwDerivedIndex = 0;
  500. dwDerivedIndex < dwP5DerivedCountersCount;
  501. dwDerivedIndex++) {
  502. if ((cReg0 == P5DerivedCounters[dwDerivedIndex].dwCR0Index) &&
  503. (cReg1 == P5DerivedCounters[dwDerivedIndex].dwCR1Index)) {
  504. *(DWORD *)((LPBYTE)pP5Data +
  505. P5DerivedCounters[dwDerivedIndex].dwCR0FieldOffset) =
  506. (DWORD)(pPentStats->Counters[0] & 0x00000000FFFFFFFF);
  507. *(DWORD *)((LPBYTE)pP5Data +
  508. P5DerivedCounters[dwDerivedIndex].dwCR1FieldOffset) =
  509. (DWORD)(pPentStats->Counters[1] & 0x00000000FFFFFFFF);
  510. break; // out of loop
  511. }
  512. }
  513. }
  514. // set the instance pointer to the first byte after this instance's
  515. // counter data
  516. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  517. ((PBYTE) pP5Data +
  518. sizeof(P5_COUNTER_DATA));
  519. }
  520. }
  521. // update arguments for return
  522. // update the object's length in the object def structure
  523. if (bP6notP5) {
  524. *lpcbTotalBytes = (DWORD)((PBYTE)pPerfInstanceDefinition -
  525. (PBYTE)pP6DataDefinition);
  526. pP6DataDefinition->P6PerfObject.TotalByteLength = *lpcbTotalBytes;
  527. } else {
  528. // return the size of this object's data
  529. *lpcbTotalBytes = (DWORD)((PBYTE)pPerfInstanceDefinition -
  530. (PBYTE)pP5DataDefinition);
  531. pP5DataDefinition->P5PerfObject.TotalByteLength = *lpcbTotalBytes;
  532. }
  533. // return the pointer to the next available byte in the data block
  534. *lppData = (PBYTE) pPerfInstanceDefinition;
  535. // return the number of objects returned in this data block
  536. *lpNumObjectTypes = PENT_NUM_PERF_OBJECT_TYPES;
  537. // always return success, unless there was not enough room in the
  538. // buffer passed in by the caller
  539. return ERROR_SUCCESS;
  540. }
  541. DWORD APIENTRY
  542. CloseP5PerformanceData(
  543. )
  544. /*++
  545. Routine Description:
  546. This routine closes the open handles to P5 device performance counters
  547. Arguments:
  548. None.
  549. Return Value:
  550. ERROR_SUCCESS
  551. --*/
  552. {
  553. if (!(--dwOpenCount)) { // when this is the last thread...
  554. CloseHandle(DriverHandle);
  555. MonCloseEventLog();
  556. }
  557. return ERROR_SUCCESS;
  558. }
  559.