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.

788 lines
26 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. perfcpu.c
  5. Abstract:
  6. This file implements an Performance Object that presents
  7. System Processor performance object data
  8. Created:
  9. Bob Watson 22-Oct-1996
  10. Revision History
  11. --*/
  12. //
  13. // Include Files
  14. //
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #include <windows.h>
  19. #include <assert.h>
  20. #include <winperf.h>
  21. #include <ntprfctr.h>
  22. #include <perfutil.h>
  23. #include "perfos.h"
  24. #include "perfosmc.h"
  25. #include "datacpu.h"
  26. DWORD dwCpuOpenCount = 0; // count of "Open" threads
  27. // variables local to this module.
  28. static SYSTEM_INTERRUPT_INFORMATION *pProcessorInterruptInformation = NULL;
  29. static DWORD dwInterruptInfoBufferSize = 0;
  30. static SYSTEM_PROCESSOR_IDLE_INFORMATION *pProcessorIdleInformation = NULL;
  31. static DWORD dwProcessorIdleBufferSize = 0;
  32. static UCHAR *pProcessorBuffer = NULL;
  33. static ULONG ProcessorBufSize = 0;
  34. BOOL bPerfCpuUseIdleData = FALSE;
  35. BOOL bPerfCpuIdleDataTested = FALSE;
  36. DWORD APIENTRY
  37. OpenProcessorObject (
  38. LPWSTR lpDeviceNames
  39. )
  40. /*++
  41. Routine Description:
  42. This routine will initialize the data structures used to pass
  43. data back to the registry
  44. Arguments:
  45. Pointer to object ID of each device to be opened (PerfGen)
  46. Return Value:
  47. None.
  48. --*/
  49. {
  50. DWORD status = ERROR_SUCCESS;
  51. //
  52. // Since WINLOGON is multi-threaded and will call this routine in
  53. // order to service remote performance queries, this library
  54. // must keep track of how many times it has been opened (i.e.
  55. // how many threads have accessed it). the registry routines will
  56. // limit access to the initialization routine to only one thread
  57. // at a time so synchronization (i.e. reentrancy) should not be
  58. // a problem
  59. //
  60. UNREFERENCED_PARAMETER (lpDeviceNames);
  61. if (!dwCpuOpenCount) {
  62. dwInterruptInfoBufferSize = (ULONG)BasicInfo.NumberOfProcessors *
  63. sizeof (SYSTEM_INTERRUPT_INFORMATION);
  64. pProcessorInterruptInformation = ALLOCMEM (hLibHeap,
  65. HEAP_ZERO_MEMORY, dwInterruptInfoBufferSize);
  66. if (pProcessorInterruptInformation == NULL) {
  67. status = ERROR_OUTOFMEMORY;
  68. goto OpenExitPoint;
  69. }
  70. ProcessorBufSize = BasicInfo.NumberOfProcessors *
  71. sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
  72. pProcessorBuffer = ALLOCMEM(hLibHeap, HEAP_ZERO_MEMORY,
  73. ProcessorBufSize);
  74. if (pProcessorBuffer == NULL) {
  75. status = ERROR_OUTOFMEMORY;
  76. goto OpenExitPoint;
  77. }
  78. dwProcessorIdleBufferSize = BasicInfo.NumberOfProcessors *
  79. sizeof(SYSTEM_PROCESSOR_IDLE_INFORMATION);
  80. pProcessorIdleInformation = ALLOCMEM(hLibHeap, HEAP_ZERO_MEMORY,
  81. dwProcessorIdleBufferSize);
  82. if (pProcessorIdleInformation == NULL) {
  83. status = ERROR_OUTOFMEMORY;
  84. goto OpenExitPoint;
  85. }
  86. }
  87. dwCpuOpenCount++; // increment OPEN counter
  88. status = ERROR_SUCCESS; // for successful exit
  89. OpenExitPoint:
  90. if (status == ERROR_OUTOFMEMORY) {
  91. if (pProcessorInterruptInformation) {
  92. FREEMEM (hLibHeap, 0, pProcessorInterruptInformation);
  93. pProcessorInterruptInformation = NULL;
  94. }
  95. if (pProcessorBuffer) {
  96. FREEMEM (hLibHeap, 0, pProcessorBuffer);
  97. pProcessorBuffer = NULL;
  98. }
  99. dwInterruptInfoBufferSize = 0;
  100. ProcessorBufSize = 0;
  101. dwProcessorIdleBufferSize = 0;
  102. }
  103. return status;
  104. }
  105. DWORD APIENTRY
  106. CollectProcessorObjectData (
  107. IN OUT LPVOID *lppData,
  108. IN OUT LPDWORD lpcbTotalBytes,
  109. IN OUT LPDWORD lpNumObjectTypes
  110. )
  111. /*++
  112. Routine Description:
  113. This routine will return the data for the processor object
  114. Arguments:
  115. IN OUT LPVOID *lppData
  116. IN: pointer to the address of the buffer to receive the completed
  117. PerfDataBlock and subordinate structures. This routine will
  118. append its data to the buffer starting at the point referenced
  119. by *lppData.
  120. OUT: points to the first byte after the data structure added by this
  121. routine. This routine updated the value at lppdata after appending
  122. its data.
  123. IN OUT LPDWORD lpcbTotalBytes
  124. IN: the address of the DWORD that tells the size in bytes of the
  125. buffer referenced by the lppData argument
  126. OUT: the number of bytes added by this routine is writted to the
  127. DWORD pointed to by this argument
  128. IN OUT LPDWORD NumObjectTypes
  129. IN: the address of the DWORD to receive the number of objects added
  130. by this routine
  131. OUT: the number of objects added by this routine is writted to the
  132. DWORD pointed to by this argument
  133. Returns:
  134. 0 if successful, else Win 32 error code of failure
  135. --*/
  136. {
  137. LONG lReturn = ERROR_SUCCESS;
  138. DWORD TotalLen; // Length of the total return block
  139. DWORD dwBufferSize;
  140. DWORD dwReturnedBufferSize = 0;
  141. PPROCESSOR_DATA_DEFINITION pProcessorDataDefinition = NULL;
  142. PPROCESSOR_COUNTER_DATA pPCD;
  143. PEX_PROCESSOR_DATA_DEFINITION pExProcessorDataDefinition = NULL;
  144. PEX_PROCESSOR_COUNTER_DATA pExPCD;
  145. PROCESSOR_COUNTER_DATA pcdTotalData;
  146. EX_PROCESSOR_COUNTER_DATA pexcdTotalData;
  147. PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
  148. ULONG CurProc;
  149. UNICODE_STRING ProcessorName;
  150. WCHAR ProcessorNameBuffer[512];
  151. SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *pProcessorInformation = NULL;
  152. SYSTEM_PROCESSOR_IDLE_INFORMATION *pProcIdleInformation = NULL;
  153. SYSTEM_INTERRUPT_INFORMATION *pThisProcessorInterruptInformation = NULL;
  154. DWORD dwInterruptInfoBufferSize;
  155. NTSTATUS ntStatus;
  156. //
  157. // Check for sufficient space for processor data
  158. //
  159. // check for QUADWORD alignment of incoming pointer
  160. assert (((ULONG_PTR)(*lppData) & 0x00000007) == 0);
  161. if (!bPerfCpuIdleDataTested) {
  162. // call this function once to see if this info is available from the system
  163. //
  164. // get system idle information by processor
  165. //
  166. dwBufferSize = dwProcessorIdleBufferSize;
  167. ntStatus = NtQuerySystemInformation(
  168. SystemProcessorIdleInformation,
  169. pProcessorIdleInformation,
  170. dwBufferSize,
  171. &dwReturnedBufferSize
  172. );
  173. if (NT_SUCCESS(ntStatus)) {
  174. bPerfCpuUseIdleData = TRUE;
  175. } else {
  176. memset (pProcessorIdleInformation, 0, dwProcessorIdleBufferSize);
  177. }
  178. bPerfCpuIdleDataTested = TRUE;
  179. }
  180. if (bPerfCpuUseIdleData) {
  181. pExProcessorDataDefinition = (EX_PROCESSOR_DATA_DEFINITION *) *lppData;
  182. TotalLen =
  183. sizeof(EX_PROCESSOR_DATA_DEFINITION) + // object def header
  184. ((sizeof (PERF_INSTANCE_DEFINITION) + // plus an instance for
  185. ((MAX_INSTANCE_NAME + 1) * sizeof(WCHAR)) +
  186. sizeof (PROCESSOR_COUNTER_DATA)) * // each processor and
  187. (BasicInfo.NumberOfProcessors + 1)); // the "total" instance
  188. TotalLen = QWORD_MULTIPLE(TotalLen);
  189. if ( *lpcbTotalBytes < TotalLen ) {
  190. lReturn = ERROR_MORE_DATA;
  191. *lpcbTotalBytes = (DWORD) 0;
  192. *lpNumObjectTypes = (DWORD) 0;
  193. goto COLLECT_BAIL_OUT;
  194. }
  195. } else {
  196. pProcessorDataDefinition = (PROCESSOR_DATA_DEFINITION *) *lppData;
  197. TotalLen =
  198. sizeof(PROCESSOR_DATA_DEFINITION) + // object def header
  199. ((sizeof (PERF_INSTANCE_DEFINITION) + // plus an instance for
  200. ((MAX_INSTANCE_NAME + 1) * sizeof(WCHAR)) +
  201. sizeof (PROCESSOR_COUNTER_DATA)) * // each processor and
  202. (BasicInfo.NumberOfProcessors + 1)); // the "total" instance
  203. if ( *lpcbTotalBytes < TotalLen ) {
  204. lReturn = ERROR_MORE_DATA;
  205. *lpcbTotalBytes = (DWORD) 0;
  206. *lpNumObjectTypes = (DWORD) 0;
  207. goto COLLECT_BAIL_OUT;
  208. }
  209. }
  210. //
  211. // Get processor data from system
  212. //
  213. if ( ProcessorBufSize ) {
  214. ntStatus = NtQuerySystemInformation(
  215. SystemProcessorPerformanceInformation,
  216. pProcessorBuffer,
  217. ProcessorBufSize,
  218. &dwReturnedBufferSize
  219. );
  220. if (!NT_SUCCESS(ntStatus)) {
  221. // clear buffer & log error
  222. ReportEvent (hEventLog,
  223. EVENTLOG_WARNING_TYPE,
  224. 0,
  225. PERFOS_UNABLE_QUERY_PROCSSOR_INFO,
  226. NULL,
  227. 0,
  228. sizeof(DWORD),
  229. NULL,
  230. (LPVOID)&ntStatus);
  231. memset (pProcessorBuffer, 0, ProcessorBufSize);
  232. }
  233. }
  234. //
  235. // get system interrupt information by processor
  236. //
  237. dwInterruptInfoBufferSize = (ULONG)BasicInfo.NumberOfProcessors *
  238. sizeof (SYSTEM_INTERRUPT_INFORMATION);
  239. ntStatus = NtQuerySystemInformation(
  240. SystemInterruptInformation,
  241. pProcessorInterruptInformation,
  242. dwInterruptInfoBufferSize,
  243. &dwReturnedBufferSize
  244. );
  245. if (!NT_SUCCESS(ntStatus)) {
  246. // clear buffer & log error
  247. ReportEvent (hEventLog,
  248. EVENTLOG_WARNING_TYPE,
  249. 0,
  250. PERFOS_UNABLE_QUERY_INTERRUPT_INFO,
  251. NULL,
  252. 0,
  253. sizeof(DWORD),
  254. NULL,
  255. (LPVOID)&ntStatus);
  256. memset (pProcessorInterruptInformation, 0,
  257. (BasicInfo.NumberOfProcessors *
  258. sizeof (SYSTEM_INTERRUPT_INFORMATION)));
  259. }
  260. if (bPerfCpuUseIdleData) {
  261. //
  262. // get system idle information by processor
  263. //
  264. dwBufferSize = dwProcessorIdleBufferSize;
  265. ntStatus = NtQuerySystemInformation(
  266. SystemProcessorIdleInformation,
  267. pProcessorIdleInformation,
  268. dwBufferSize,
  269. &dwReturnedBufferSize
  270. );
  271. if (!NT_SUCCESS(ntStatus)) {
  272. // it worked once before or this flag wouldn't be set
  273. // so report the error.
  274. ReportEvent (hEventLog,
  275. EVENTLOG_WARNING_TYPE,
  276. 0,
  277. PERFOS_UNABLE_QUERY_IDLE_INFO,
  278. NULL,
  279. 0,
  280. sizeof(DWORD),
  281. NULL,
  282. (LPVOID)&ntStatus);
  283. memset (pProcessorIdleInformation, 0, dwProcessorIdleBufferSize);
  284. }
  285. } else {
  286. memset (pProcessorIdleInformation, 0, dwProcessorIdleBufferSize);
  287. }
  288. // clear the pointers to trap unassigned ones below
  289. pPCD = NULL;
  290. pExPCD = NULL;
  291. if ((!bPerfCpuUseIdleData) && (pProcessorDataDefinition != NULL)) {
  292. // use the original format of the structure
  293. // clear the "Total" instance
  294. memset (&pcdTotalData, 0, sizeof (pcdTotalData));
  295. // Define processor data block
  296. //
  297. memcpy (pProcessorDataDefinition,
  298. &ProcessorDataDefinition,
  299. sizeof(PROCESSOR_DATA_DEFINITION));
  300. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  301. &pProcessorDataDefinition[1];
  302. pProcessorInformation = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *)
  303. pProcessorBuffer;
  304. // point to the first processor in the returned array of interrupt
  305. // information. data is returned as an array of structures.
  306. pThisProcessorInterruptInformation = pProcessorInterruptInformation;
  307. pProcIdleInformation = pProcessorIdleInformation;
  308. for ( CurProc = 0;
  309. CurProc < (ULONG) BasicInfo.NumberOfProcessors;
  310. CurProc++ ) {
  311. //
  312. // Define processor instance 0;
  313. // More could be defined like this
  314. //
  315. ProcessorName.Length = 0;
  316. ProcessorName.MaximumLength = sizeof(ProcessorNameBuffer);
  317. ProcessorName.Buffer = ProcessorNameBuffer;
  318. RtlIntegerToUnicodeString(CurProc, 10, &ProcessorName);
  319. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  320. (PVOID *) &pPCD,
  321. 0,
  322. 0,
  323. (DWORD)-1,
  324. ProcessorNameBuffer);
  325. // test for Quadword Alignment
  326. assert (((ULONG_PTR)(pPCD) & 0x00000007) == 0);
  327. //
  328. // Format and collect processor data. While doing so,
  329. // accumulate totals in the System Object Type data block.
  330. // Pointers to these were initialized in QuerySystemData.
  331. //
  332. pPCD->CounterBlock.ByteLength = sizeof (PROCESSOR_COUNTER_DATA);
  333. pcdTotalData.ProcessorTime +=
  334. pPCD->ProcessorTime =
  335. pProcessorInformation->IdleTime.QuadPart;
  336. pcdTotalData.UserTime +=
  337. pPCD->UserTime =
  338. pProcessorInformation->UserTime.QuadPart;
  339. // kernel time is total kernel time less the time spent in the
  340. // idle thread for that processor
  341. pcdTotalData.KernelTime +=
  342. pPCD->KernelTime =
  343. pProcessorInformation->KernelTime.QuadPart -
  344. pPCD->ProcessorTime;
  345. pcdTotalData.Interrupts +=
  346. pPCD->Interrupts = pProcessorInformation->InterruptCount;
  347. pcdTotalData.DpcTime +=
  348. pPCD->DpcTime = pProcessorInformation->DpcTime.QuadPart;
  349. pcdTotalData.InterruptTime +=
  350. pPCD->InterruptTime =
  351. pProcessorInformation->InterruptTime.QuadPart;
  352. pcdTotalData.DpcCountRate +=
  353. pPCD->DpcCountRate =
  354. pThisProcessorInterruptInformation->DpcCount;
  355. pcdTotalData.DpcRate +=
  356. pPCD->DpcRate =
  357. pThisProcessorInterruptInformation->DpcRate;
  358. //
  359. // Advance to next processor
  360. //
  361. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pPCD[1];
  362. // point to next processor's data in return array(s)
  363. pProcessorInformation++;
  364. pThisProcessorInterruptInformation++;
  365. pProcIdleInformation++;
  366. }
  367. // do the total instance now
  368. ProcessorName.Length = (WORD)((lstrlenW (wszTotal) + 1) * sizeof (WCHAR));
  369. ProcessorName.MaximumLength = (WORD)(sizeof (ProcessorNameBuffer));
  370. lstrcpyW (ProcessorNameBuffer, wszTotal);
  371. ProcessorName.Buffer = ProcessorNameBuffer;
  372. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  373. (PVOID *) &pPCD,
  374. 0,
  375. 0,
  376. (DWORD)-1,
  377. ProcessorNameBuffer);
  378. // define the size
  379. pcdTotalData.CounterBlock.ByteLength = sizeof (PROCESSOR_COUNTER_DATA);
  380. // adjust the total values of the time fields to the number of
  381. // processors to "normalize" the values
  382. pcdTotalData.ProcessorTime /= BasicInfo.NumberOfProcessors;
  383. pcdTotalData.UserTime /= BasicInfo.NumberOfProcessors;
  384. pcdTotalData.KernelTime /= BasicInfo.NumberOfProcessors;
  385. pcdTotalData.DpcTime /= BasicInfo.NumberOfProcessors;
  386. pcdTotalData.InterruptTime /= BasicInfo.NumberOfProcessors;
  387. // these fields are OK as totals
  388. //
  389. // pcdTotalData.Interrupts
  390. // pcdTotalData.DpcCountRate
  391. // pcdTotalData.DpcRate
  392. // copy total data to buffer
  393. memcpy (pPCD, &pcdTotalData, sizeof (pcdTotalData));
  394. // adjust local buffer pointer
  395. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pPCD[1];
  396. //
  397. // Now we know how large an area we used for the
  398. // processor definition, so we can update the offset
  399. // to the next object definition
  400. //
  401. pProcessorDataDefinition->ProcessorObjectType.NumInstances =
  402. BasicInfo.NumberOfProcessors + 1;
  403. *lppData = (LPVOID)pPerfInstanceDefinition;
  404. // round up buffer to the nearest QUAD WORD
  405. *lppData = ALIGN_ON_QWORD (*lppData);
  406. *lpcbTotalBytes =
  407. pProcessorDataDefinition->ProcessorObjectType.TotalByteLength =
  408. (DWORD)((LPBYTE) pPerfInstanceDefinition -
  409. (LPBYTE) pProcessorDataDefinition);
  410. }
  411. if ((bPerfCpuUseIdleData) && (pExProcessorDataDefinition != NULL)) {
  412. // use the new extended structure
  413. // clear the "Total" instance
  414. memset (&pexcdTotalData, 0, sizeof (pexcdTotalData));
  415. // Define processor data block
  416. //
  417. memcpy (pExProcessorDataDefinition,
  418. &ExProcessorDataDefinition,
  419. sizeof(EX_PROCESSOR_DATA_DEFINITION));
  420. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  421. &pExProcessorDataDefinition[1];
  422. pProcessorInformation = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *)
  423. pProcessorBuffer;
  424. // point to the first processor in the returned array of interrupt
  425. // information. data is returned as an array of structures.
  426. pThisProcessorInterruptInformation = pProcessorInterruptInformation;
  427. pProcIdleInformation = pProcessorIdleInformation;
  428. for ( CurProc = 0;
  429. CurProc < (ULONG) BasicInfo.NumberOfProcessors;
  430. CurProc++ ) {
  431. //
  432. // Define processor instance 0;
  433. // More could be defined like this
  434. //
  435. ProcessorName.Length = 0;
  436. ProcessorName.MaximumLength = sizeof(ProcessorNameBuffer);
  437. ProcessorName.Buffer = ProcessorNameBuffer;
  438. RtlIntegerToUnicodeString(CurProc, 10, &ProcessorName);
  439. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  440. (PVOID *) &pExPCD,
  441. 0,
  442. 0,
  443. (DWORD)-1,
  444. ProcessorNameBuffer);
  445. // test for Quadword Alignment
  446. assert (((ULONG_PTR)(pExPCD) & 0x00000007) == 0);
  447. //
  448. // Format and collect processor data. While doing so,
  449. // accumulate totals in the System Object Type data block.
  450. // Pointers to these were initialized in QuerySystemData.
  451. //
  452. pExPCD->CounterBlock.ByteLength = sizeof (EX_PROCESSOR_COUNTER_DATA);
  453. pexcdTotalData.ProcessorTime +=
  454. pExPCD->ProcessorTime =
  455. pProcessorInformation->IdleTime.QuadPart;
  456. pexcdTotalData.UserTime +=
  457. pExPCD->UserTime =
  458. pProcessorInformation->UserTime.QuadPart;
  459. // kernel time is total kernel time less the time spent in the
  460. // idle thread for that processor
  461. pexcdTotalData.KernelTime +=
  462. pExPCD->KernelTime =
  463. pProcessorInformation->KernelTime.QuadPart -
  464. pExPCD->ProcessorTime;
  465. pexcdTotalData.Interrupts +=
  466. pExPCD->Interrupts = pProcessorInformation->InterruptCount;
  467. pexcdTotalData.DpcTime +=
  468. pExPCD->DpcTime = pProcessorInformation->DpcTime.QuadPart;
  469. pexcdTotalData.InterruptTime +=
  470. pExPCD->InterruptTime =
  471. pProcessorInformation->InterruptTime.QuadPart;
  472. pexcdTotalData.DpcCountRate +=
  473. pExPCD->DpcCountRate =
  474. pThisProcessorInterruptInformation->DpcCount;
  475. pexcdTotalData.DpcRate +=
  476. pExPCD->DpcRate =
  477. pThisProcessorInterruptInformation->DpcRate;
  478. // fill in the system idle info
  479. pexcdTotalData.IdleTime +=
  480. pExPCD->IdleTime =
  481. pProcIdleInformation->IdleTime;
  482. pexcdTotalData.C1Time +=
  483. pExPCD->C1Time =
  484. pProcIdleInformation->C1Time;
  485. pexcdTotalData.C2Time +=
  486. pExPCD->C2Time =
  487. pProcIdleInformation->C2Time;
  488. pexcdTotalData.C3Time +=
  489. pExPCD->C3Time =
  490. pProcIdleInformation->C3Time;
  491. pexcdTotalData.C1Transitions +=
  492. pExPCD->C1Transitions =
  493. pProcIdleInformation->C1Transitions;
  494. pexcdTotalData.C2Transitions +=
  495. pExPCD->C2Transitions =
  496. pProcIdleInformation->C2Transitions;
  497. pexcdTotalData.C3Transitions +=
  498. pExPCD->C3Transitions =
  499. pProcIdleInformation->C3Transitions;
  500. //
  501. // Advance to next processor
  502. //
  503. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pExPCD[1];
  504. // point to next processor's data in return array(s)
  505. pProcessorInformation++;
  506. pThisProcessorInterruptInformation++;
  507. pProcIdleInformation++;
  508. }
  509. // do the total instance now
  510. ProcessorName.Length = (WORD)((lstrlenW (wszTotal) + 1) * sizeof (WCHAR));
  511. ProcessorName.MaximumLength = (WORD)(sizeof (ProcessorNameBuffer));
  512. lstrcpyW (ProcessorNameBuffer, wszTotal);
  513. ProcessorName.Buffer = ProcessorNameBuffer;
  514. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  515. (PVOID *) &pExPCD,
  516. 0,
  517. 0,
  518. (DWORD)-1,
  519. ProcessorNameBuffer);
  520. // define the size
  521. pexcdTotalData.CounterBlock.ByteLength = sizeof (PROCESSOR_COUNTER_DATA);
  522. // adjust the total values of the time fields to the number of
  523. // processors to "normalize" the values
  524. pexcdTotalData.ProcessorTime /= BasicInfo.NumberOfProcessors;
  525. pexcdTotalData.UserTime /= BasicInfo.NumberOfProcessors;
  526. pexcdTotalData.KernelTime /= BasicInfo.NumberOfProcessors;
  527. pexcdTotalData.IdleTime /= BasicInfo.NumberOfProcessors;
  528. pexcdTotalData.C1Time /= BasicInfo.NumberOfProcessors;
  529. pexcdTotalData.C2Time /= BasicInfo.NumberOfProcessors;
  530. pexcdTotalData.C3Time /= BasicInfo.NumberOfProcessors;
  531. pexcdTotalData.DpcTime /= BasicInfo.NumberOfProcessors;
  532. pexcdTotalData.InterruptTime /= BasicInfo.NumberOfProcessors;
  533. // these fields are OK as totals
  534. //
  535. // pexcdTotalData.Interrupts
  536. // pexcdTotalData.DpcCountRate
  537. // pexcdTotalData.DpcRate
  538. // copy total data to buffer
  539. memcpy (pExPCD, &pexcdTotalData, sizeof (pexcdTotalData));
  540. // adjust local buffer pointer
  541. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pExPCD[1];
  542. //
  543. // Now we know how large an area we used for the
  544. // processor definition, so we can update the offset
  545. // to the next object definition
  546. //
  547. pExProcessorDataDefinition->ProcessorObjectType.NumInstances =
  548. BasicInfo.NumberOfProcessors + 1;
  549. *lppData = (LPVOID)pPerfInstanceDefinition;
  550. // round up buffer to the nearest QUAD WORD
  551. *lppData = ALIGN_ON_QWORD (*lppData);
  552. *lpcbTotalBytes =
  553. pExProcessorDataDefinition->ProcessorObjectType.TotalByteLength =
  554. (DWORD)((LPBYTE) pPerfInstanceDefinition -
  555. (LPBYTE) pExProcessorDataDefinition);
  556. }
  557. if ((pExProcessorDataDefinition == NULL) && (pProcessorDataDefinition == NULL)) {
  558. // then no data buffer found to use
  559. lReturn = ERROR_SUCCESS;
  560. *lpcbTotalBytes = (DWORD) 0;
  561. *lpNumObjectTypes = (DWORD) 0;
  562. goto COLLECT_BAIL_OUT;
  563. }
  564. #if DBG
  565. if (*lpcbTotalBytes > TotalLen ) {
  566. DbgPrint ("\nPERFOS: Processor Perf Ctr. Instance Size Underestimated:");
  567. DbgPrint ("\nPERFOS: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
  568. }
  569. #endif
  570. *lpNumObjectTypes = 1;
  571. return ERROR_SUCCESS;
  572. COLLECT_BAIL_OUT:
  573. return lReturn;
  574. }
  575. #pragma warning (disable : 4706)
  576. DWORD APIENTRY
  577. CloseProcessorObject (
  578. )
  579. /*++
  580. Routine Description:
  581. This routine closes the open handles
  582. Arguments:
  583. None.
  584. Return Value:
  585. ERROR_SUCCESS
  586. --*/
  587. {
  588. if (dwCpuOpenCount > 0) {
  589. if (!(--dwCpuOpenCount)) { // when this is the last thread...
  590. // close stuff here
  591. if (hLibHeap != NULL) {
  592. if (pProcessorInterruptInformation != NULL) {
  593. FREEMEM (hLibHeap, 0, pProcessorInterruptInformation);
  594. pProcessorInterruptInformation = NULL;
  595. }
  596. if (pProcessorBuffer != NULL) {
  597. FREEMEM (hLibHeap, 0, pProcessorBuffer);
  598. pProcessorBuffer = NULL;
  599. }
  600. if (pProcessorIdleInformation != NULL) {
  601. FREEMEM (hLibHeap, 0, pProcessorIdleInformation);
  602. pProcessorIdleInformation = NULL;
  603. }
  604. dwInterruptInfoBufferSize = 0;
  605. ProcessorBufSize = 0;
  606. dwProcessorIdleBufferSize = 0;
  607. }
  608. }
  609. } else {
  610. // if the open count is 0, then these should have been deleted
  611. assert (pProcessorBuffer == NULL);
  612. assert (pProcessorInterruptInformation == NULL);
  613. assert (pProcessorIdleInformation == NULL);
  614. }
  615. return ERROR_SUCCESS;
  616. }
  617. #pragma warning (default : 4706)