789 lines
26 KiB
C
789 lines
26 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
perfcpu.c
|
||
|
||
Abstract:
|
||
|
||
This file implements an Performance Object that presents
|
||
System Processor performance object data
|
||
|
||
Created:
|
||
|
||
Bob Watson 22-Oct-1996
|
||
|
||
Revision History
|
||
|
||
|
||
--*/
|
||
|
||
//
|
||
// Include Files
|
||
//
|
||
#include <nt.h>
|
||
#include <ntrtl.h>
|
||
#include <nturtl.h>
|
||
#include <windows.h>
|
||
#include <assert.h>
|
||
#include <winperf.h>
|
||
#include <ntprfctr.h>
|
||
#include <perfutil.h>
|
||
#include "perfos.h"
|
||
#include "perfosmc.h"
|
||
#include "datacpu.h"
|
||
|
||
DWORD dwCpuOpenCount = 0; // count of "Open" threads
|
||
|
||
// variables local to this module.
|
||
static SYSTEM_INTERRUPT_INFORMATION *pProcessorInterruptInformation = NULL;
|
||
static DWORD dwInterruptInfoBufferSize = 0;
|
||
|
||
static SYSTEM_PROCESSOR_IDLE_INFORMATION *pProcessorIdleInformation = NULL;
|
||
static DWORD dwProcessorIdleBufferSize = 0;
|
||
|
||
static UCHAR *pProcessorBuffer = NULL;
|
||
static ULONG ProcessorBufSize = 0;
|
||
|
||
BOOL bPerfCpuUseIdleData = FALSE;
|
||
BOOL bPerfCpuIdleDataTested = FALSE;
|
||
|
||
|
||
DWORD APIENTRY
|
||
OpenProcessorObject (
|
||
LPWSTR lpDeviceNames
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will initialize the data structures used to pass
|
||
data back to the registry
|
||
|
||
Arguments:
|
||
|
||
Pointer to object ID of each device to be opened (PerfGen)
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
DWORD status = ERROR_SUCCESS;
|
||
//
|
||
// Since WINLOGON is multi-threaded and will call this routine in
|
||
// order to service remote performance queries, this library
|
||
// must keep track of how many times it has been opened (i.e.
|
||
// how many threads have accessed it). the registry routines will
|
||
// limit access to the initialization routine to only one thread
|
||
// at a time so synchronization (i.e. reentrancy) should not be
|
||
// a problem
|
||
//
|
||
|
||
UNREFERENCED_PARAMETER (lpDeviceNames);
|
||
|
||
if (!dwCpuOpenCount) {
|
||
dwInterruptInfoBufferSize = (ULONG)BasicInfo.NumberOfProcessors *
|
||
sizeof (SYSTEM_INTERRUPT_INFORMATION);
|
||
|
||
pProcessorInterruptInformation = ALLOCMEM (hLibHeap,
|
||
HEAP_ZERO_MEMORY, dwInterruptInfoBufferSize);
|
||
|
||
if (pProcessorInterruptInformation == NULL) {
|
||
status = ERROR_OUTOFMEMORY;
|
||
goto OpenExitPoint;
|
||
}
|
||
|
||
ProcessorBufSize = BasicInfo.NumberOfProcessors *
|
||
sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
|
||
|
||
pProcessorBuffer = ALLOCMEM(hLibHeap, HEAP_ZERO_MEMORY,
|
||
ProcessorBufSize);
|
||
if (pProcessorBuffer == NULL) {
|
||
status = ERROR_OUTOFMEMORY;
|
||
goto OpenExitPoint;
|
||
}
|
||
|
||
dwProcessorIdleBufferSize = BasicInfo.NumberOfProcessors *
|
||
sizeof(SYSTEM_PROCESSOR_IDLE_INFORMATION);
|
||
|
||
pProcessorIdleInformation = ALLOCMEM(hLibHeap, HEAP_ZERO_MEMORY,
|
||
dwProcessorIdleBufferSize);
|
||
if (pProcessorIdleInformation == NULL) {
|
||
status = ERROR_OUTOFMEMORY;
|
||
goto OpenExitPoint;
|
||
}
|
||
|
||
}
|
||
dwCpuOpenCount++; // increment OPEN counter
|
||
|
||
status = ERROR_SUCCESS; // for successful exit
|
||
|
||
OpenExitPoint:
|
||
if (status == ERROR_OUTOFMEMORY) {
|
||
if (pProcessorInterruptInformation) {
|
||
FREEMEM (hLibHeap, 0, pProcessorInterruptInformation);
|
||
pProcessorInterruptInformation = NULL;
|
||
}
|
||
if (pProcessorBuffer) {
|
||
FREEMEM (hLibHeap, 0, pProcessorBuffer);
|
||
pProcessorBuffer = NULL;
|
||
}
|
||
dwInterruptInfoBufferSize = 0;
|
||
ProcessorBufSize = 0;
|
||
dwProcessorIdleBufferSize = 0;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
DWORD APIENTRY
|
||
CollectProcessorObjectData (
|
||
IN OUT LPVOID *lppData,
|
||
IN OUT LPDWORD lpcbTotalBytes,
|
||
IN OUT LPDWORD lpNumObjectTypes
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will return the data for the processor object
|
||
|
||
Arguments:
|
||
|
||
IN OUT LPVOID *lppData
|
||
IN: pointer to the address of the buffer to receive the completed
|
||
PerfDataBlock and subordinate structures. This routine will
|
||
append its data to the buffer starting at the point referenced
|
||
by *lppData.
|
||
OUT: points to the first byte after the data structure added by this
|
||
routine. This routine updated the value at lppdata after appending
|
||
its data.
|
||
|
||
IN OUT LPDWORD lpcbTotalBytes
|
||
IN: the address of the DWORD that tells the size in bytes of the
|
||
buffer referenced by the lppData argument
|
||
OUT: the number of bytes added by this routine is writted to the
|
||
DWORD pointed to by this argument
|
||
|
||
IN OUT LPDWORD NumObjectTypes
|
||
IN: the address of the DWORD to receive the number of objects added
|
||
by this routine
|
||
OUT: the number of objects added by this routine is writted to the
|
||
DWORD pointed to by this argument
|
||
|
||
Returns:
|
||
|
||
0 if successful, else Win 32 error code of failure
|
||
|
||
--*/
|
||
{
|
||
LONG lReturn = ERROR_SUCCESS;
|
||
DWORD TotalLen; // Length of the total return block
|
||
|
||
DWORD dwBufferSize;
|
||
DWORD dwReturnedBufferSize = 0;
|
||
|
||
PPROCESSOR_DATA_DEFINITION pProcessorDataDefinition = NULL;
|
||
PPROCESSOR_COUNTER_DATA pPCD;
|
||
PEX_PROCESSOR_DATA_DEFINITION pExProcessorDataDefinition = NULL;
|
||
PEX_PROCESSOR_COUNTER_DATA pExPCD;
|
||
|
||
PROCESSOR_COUNTER_DATA pcdTotalData;
|
||
EX_PROCESSOR_COUNTER_DATA pexcdTotalData;
|
||
|
||
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
|
||
|
||
ULONG CurProc;
|
||
|
||
UNICODE_STRING ProcessorName;
|
||
WCHAR ProcessorNameBuffer[512];
|
||
|
||
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *pProcessorInformation = NULL;
|
||
SYSTEM_PROCESSOR_IDLE_INFORMATION *pProcIdleInformation = NULL;
|
||
|
||
SYSTEM_INTERRUPT_INFORMATION *pThisProcessorInterruptInformation = NULL;
|
||
DWORD dwInterruptInfoBufferSize;
|
||
NTSTATUS ntStatus;
|
||
|
||
//
|
||
// Check for sufficient space for processor data
|
||
//
|
||
|
||
// check for QUADWORD alignment of incoming pointer
|
||
assert (((ULONG_PTR)(*lppData) & 0x00000007) == 0);
|
||
|
||
if (!bPerfCpuIdleDataTested) {
|
||
// call this function once to see if this info is available from the system
|
||
//
|
||
// get system idle information by processor
|
||
//
|
||
dwBufferSize = dwProcessorIdleBufferSize;
|
||
|
||
ntStatus = NtQuerySystemInformation(
|
||
SystemProcessorIdleInformation,
|
||
pProcessorIdleInformation,
|
||
dwBufferSize,
|
||
&dwReturnedBufferSize
|
||
);
|
||
|
||
if (NT_SUCCESS(ntStatus)) {
|
||
bPerfCpuUseIdleData = TRUE;
|
||
} else {
|
||
memset (pProcessorIdleInformation, 0, dwProcessorIdleBufferSize);
|
||
}
|
||
|
||
bPerfCpuIdleDataTested = TRUE;
|
||
}
|
||
|
||
if (bPerfCpuUseIdleData) {
|
||
pExProcessorDataDefinition = (EX_PROCESSOR_DATA_DEFINITION *) *lppData;
|
||
|
||
TotalLen =
|
||
sizeof(EX_PROCESSOR_DATA_DEFINITION) + // object def header
|
||
((sizeof (PERF_INSTANCE_DEFINITION) + // plus an instance for
|
||
((MAX_INSTANCE_NAME + 1) * sizeof(WCHAR)) +
|
||
sizeof (PROCESSOR_COUNTER_DATA)) * // each processor and
|
||
(BasicInfo.NumberOfProcessors + 1)); // the "total" instance
|
||
TotalLen = QWORD_MULTIPLE(TotalLen);
|
||
|
||
if ( *lpcbTotalBytes < TotalLen ) {
|
||
lReturn = ERROR_MORE_DATA;
|
||
*lpcbTotalBytes = (DWORD) 0;
|
||
*lpNumObjectTypes = (DWORD) 0;
|
||
goto COLLECT_BAIL_OUT;
|
||
}
|
||
} else {
|
||
pProcessorDataDefinition = (PROCESSOR_DATA_DEFINITION *) *lppData;
|
||
|
||
TotalLen =
|
||
sizeof(PROCESSOR_DATA_DEFINITION) + // object def header
|
||
((sizeof (PERF_INSTANCE_DEFINITION) + // plus an instance for
|
||
((MAX_INSTANCE_NAME + 1) * sizeof(WCHAR)) +
|
||
sizeof (PROCESSOR_COUNTER_DATA)) * // each processor and
|
||
(BasicInfo.NumberOfProcessors + 1)); // the "total" instance
|
||
|
||
if ( *lpcbTotalBytes < TotalLen ) {
|
||
lReturn = ERROR_MORE_DATA;
|
||
*lpcbTotalBytes = (DWORD) 0;
|
||
*lpNumObjectTypes = (DWORD) 0;
|
||
goto COLLECT_BAIL_OUT;
|
||
}
|
||
}
|
||
//
|
||
// Get processor data from system
|
||
//
|
||
|
||
if ( ProcessorBufSize ) {
|
||
ntStatus = NtQuerySystemInformation(
|
||
SystemProcessorPerformanceInformation,
|
||
pProcessorBuffer,
|
||
ProcessorBufSize,
|
||
&dwReturnedBufferSize
|
||
);
|
||
|
||
if (!NT_SUCCESS(ntStatus)) {
|
||
// clear buffer & log error
|
||
ReportEvent (hEventLog,
|
||
EVENTLOG_WARNING_TYPE,
|
||
0,
|
||
PERFOS_UNABLE_QUERY_PROCSSOR_INFO,
|
||
NULL,
|
||
0,
|
||
sizeof(DWORD),
|
||
NULL,
|
||
(LPVOID)&ntStatus);
|
||
|
||
memset (pProcessorBuffer, 0, ProcessorBufSize);
|
||
}
|
||
}
|
||
|
||
//
|
||
// get system interrupt information by processor
|
||
//
|
||
dwInterruptInfoBufferSize = (ULONG)BasicInfo.NumberOfProcessors *
|
||
sizeof (SYSTEM_INTERRUPT_INFORMATION);
|
||
|
||
ntStatus = NtQuerySystemInformation(
|
||
SystemInterruptInformation,
|
||
pProcessorInterruptInformation,
|
||
dwInterruptInfoBufferSize,
|
||
&dwReturnedBufferSize
|
||
);
|
||
|
||
if (!NT_SUCCESS(ntStatus)) {
|
||
// clear buffer & log error
|
||
ReportEvent (hEventLog,
|
||
EVENTLOG_WARNING_TYPE,
|
||
0,
|
||
PERFOS_UNABLE_QUERY_INTERRUPT_INFO,
|
||
NULL,
|
||
0,
|
||
sizeof(DWORD),
|
||
NULL,
|
||
(LPVOID)&ntStatus);
|
||
|
||
memset (pProcessorInterruptInformation, 0,
|
||
(BasicInfo.NumberOfProcessors *
|
||
sizeof (SYSTEM_INTERRUPT_INFORMATION)));
|
||
}
|
||
|
||
if (bPerfCpuUseIdleData) {
|
||
//
|
||
// get system idle information by processor
|
||
//
|
||
dwBufferSize = dwProcessorIdleBufferSize;
|
||
|
||
ntStatus = NtQuerySystemInformation(
|
||
SystemProcessorIdleInformation,
|
||
pProcessorIdleInformation,
|
||
dwBufferSize,
|
||
&dwReturnedBufferSize
|
||
);
|
||
|
||
if (!NT_SUCCESS(ntStatus)) {
|
||
// it worked once before or this flag wouldn't be set
|
||
// so report the error.
|
||
ReportEvent (hEventLog,
|
||
EVENTLOG_WARNING_TYPE,
|
||
0,
|
||
PERFOS_UNABLE_QUERY_IDLE_INFO,
|
||
NULL,
|
||
0,
|
||
sizeof(DWORD),
|
||
NULL,
|
||
(LPVOID)&ntStatus);
|
||
|
||
memset (pProcessorIdleInformation, 0, dwProcessorIdleBufferSize);
|
||
}
|
||
} else {
|
||
memset (pProcessorIdleInformation, 0, dwProcessorIdleBufferSize);
|
||
}
|
||
|
||
// clear the pointers to trap unassigned ones below
|
||
pPCD = NULL;
|
||
pExPCD = NULL;
|
||
|
||
if ((!bPerfCpuUseIdleData) && (pProcessorDataDefinition != NULL)) {
|
||
// use the original format of the structure
|
||
// clear the "Total" instance
|
||
memset (&pcdTotalData, 0, sizeof (pcdTotalData));
|
||
|
||
// Define processor data block
|
||
//
|
||
|
||
memcpy (pProcessorDataDefinition,
|
||
&ProcessorDataDefinition,
|
||
sizeof(PROCESSOR_DATA_DEFINITION));
|
||
|
||
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
|
||
&pProcessorDataDefinition[1];
|
||
|
||
pProcessorInformation = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *)
|
||
pProcessorBuffer;
|
||
|
||
// point to the first processor in the returned array of interrupt
|
||
// information. data is returned as an array of structures.
|
||
|
||
pThisProcessorInterruptInformation = pProcessorInterruptInformation;
|
||
pProcIdleInformation = pProcessorIdleInformation;
|
||
|
||
for ( CurProc = 0;
|
||
CurProc < (ULONG) BasicInfo.NumberOfProcessors;
|
||
CurProc++ ) {
|
||
|
||
//
|
||
// Define processor instance 0;
|
||
// More could be defined like this
|
||
//
|
||
|
||
ProcessorName.Length = 0;
|
||
ProcessorName.MaximumLength = sizeof(ProcessorNameBuffer);
|
||
ProcessorName.Buffer = ProcessorNameBuffer;
|
||
|
||
RtlIntegerToUnicodeString(CurProc, 10, &ProcessorName);
|
||
|
||
MonBuildInstanceDefinition(pPerfInstanceDefinition,
|
||
(PVOID *) &pPCD,
|
||
0,
|
||
0,
|
||
(DWORD)-1,
|
||
ProcessorNameBuffer);
|
||
|
||
// test for Quadword Alignment
|
||
assert (((ULONG_PTR)(pPCD) & 0x00000007) == 0);
|
||
//
|
||
// Format and collect processor data. While doing so,
|
||
// accumulate totals in the System Object Type data block.
|
||
// Pointers to these were initialized in QuerySystemData.
|
||
//
|
||
|
||
pPCD->CounterBlock.ByteLength = sizeof (PROCESSOR_COUNTER_DATA);
|
||
|
||
pcdTotalData.ProcessorTime +=
|
||
pPCD->ProcessorTime =
|
||
pProcessorInformation->IdleTime.QuadPart;
|
||
pcdTotalData.UserTime +=
|
||
pPCD->UserTime =
|
||
pProcessorInformation->UserTime.QuadPart;
|
||
// kernel time is total kernel time less the time spent in the
|
||
// idle thread for that processor
|
||
pcdTotalData.KernelTime +=
|
||
pPCD->KernelTime =
|
||
pProcessorInformation->KernelTime.QuadPart -
|
||
pPCD->ProcessorTime;
|
||
|
||
pcdTotalData.Interrupts +=
|
||
pPCD->Interrupts = pProcessorInformation->InterruptCount;
|
||
pcdTotalData.DpcTime +=
|
||
pPCD->DpcTime = pProcessorInformation->DpcTime.QuadPart;
|
||
pcdTotalData.InterruptTime +=
|
||
pPCD->InterruptTime =
|
||
pProcessorInformation->InterruptTime.QuadPart;
|
||
|
||
pcdTotalData.DpcCountRate +=
|
||
pPCD->DpcCountRate =
|
||
pThisProcessorInterruptInformation->DpcCount;
|
||
|
||
pcdTotalData.DpcRate +=
|
||
pPCD->DpcRate =
|
||
pThisProcessorInterruptInformation->DpcRate;
|
||
|
||
//
|
||
// Advance to next processor
|
||
//
|
||
|
||
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pPCD[1];
|
||
|
||
// point to next processor's data in return array(s)
|
||
pProcessorInformation++;
|
||
pThisProcessorInterruptInformation++;
|
||
pProcIdleInformation++;
|
||
}
|
||
|
||
// do the total instance now
|
||
ProcessorName.Length = (WORD)((lstrlenW (wszTotal) + 1) * sizeof (WCHAR));
|
||
ProcessorName.MaximumLength = (WORD)(sizeof (ProcessorNameBuffer));
|
||
lstrcpyW (ProcessorNameBuffer, wszTotal);
|
||
ProcessorName.Buffer = ProcessorNameBuffer;
|
||
|
||
MonBuildInstanceDefinition(pPerfInstanceDefinition,
|
||
(PVOID *) &pPCD,
|
||
0,
|
||
0,
|
||
(DWORD)-1,
|
||
ProcessorNameBuffer);
|
||
|
||
// define the size
|
||
pcdTotalData.CounterBlock.ByteLength = sizeof (PROCESSOR_COUNTER_DATA);
|
||
|
||
// adjust the total values of the time fields to the number of
|
||
// processors to "normalize" the values
|
||
|
||
pcdTotalData.ProcessorTime /= BasicInfo.NumberOfProcessors;
|
||
pcdTotalData.UserTime /= BasicInfo.NumberOfProcessors;
|
||
pcdTotalData.KernelTime /= BasicInfo.NumberOfProcessors;
|
||
|
||
pcdTotalData.DpcTime /= BasicInfo.NumberOfProcessors;
|
||
pcdTotalData.InterruptTime /= BasicInfo.NumberOfProcessors;
|
||
|
||
// these fields are OK as totals
|
||
//
|
||
// pcdTotalData.Interrupts
|
||
// pcdTotalData.DpcCountRate
|
||
// pcdTotalData.DpcRate
|
||
|
||
// copy total data to buffer
|
||
memcpy (pPCD, &pcdTotalData, sizeof (pcdTotalData));
|
||
|
||
// adjust local buffer pointer
|
||
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pPCD[1];
|
||
|
||
//
|
||
// Now we know how large an area we used for the
|
||
// processor definition, so we can update the offset
|
||
// to the next object definition
|
||
//
|
||
|
||
pProcessorDataDefinition->ProcessorObjectType.NumInstances =
|
||
BasicInfo.NumberOfProcessors + 1;
|
||
|
||
*lppData = (LPVOID)pPerfInstanceDefinition;
|
||
|
||
// round up buffer to the nearest QUAD WORD
|
||
|
||
*lppData = ALIGN_ON_QWORD (*lppData);
|
||
|
||
*lpcbTotalBytes =
|
||
pProcessorDataDefinition->ProcessorObjectType.TotalByteLength =
|
||
(DWORD)((LPBYTE) pPerfInstanceDefinition -
|
||
(LPBYTE) pProcessorDataDefinition);
|
||
}
|
||
|
||
if ((bPerfCpuUseIdleData) && (pExProcessorDataDefinition != NULL)) {
|
||
// use the new extended structure
|
||
// clear the "Total" instance
|
||
memset (&pexcdTotalData, 0, sizeof (pexcdTotalData));
|
||
|
||
// Define processor data block
|
||
//
|
||
|
||
memcpy (pExProcessorDataDefinition,
|
||
&ExProcessorDataDefinition,
|
||
sizeof(EX_PROCESSOR_DATA_DEFINITION));
|
||
|
||
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
|
||
&pExProcessorDataDefinition[1];
|
||
|
||
pProcessorInformation = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *)
|
||
pProcessorBuffer;
|
||
|
||
// point to the first processor in the returned array of interrupt
|
||
// information. data is returned as an array of structures.
|
||
|
||
pThisProcessorInterruptInformation = pProcessorInterruptInformation;
|
||
pProcIdleInformation = pProcessorIdleInformation;
|
||
|
||
for ( CurProc = 0;
|
||
CurProc < (ULONG) BasicInfo.NumberOfProcessors;
|
||
CurProc++ ) {
|
||
|
||
//
|
||
// Define processor instance 0;
|
||
// More could be defined like this
|
||
//
|
||
|
||
ProcessorName.Length = 0;
|
||
ProcessorName.MaximumLength = sizeof(ProcessorNameBuffer);
|
||
ProcessorName.Buffer = ProcessorNameBuffer;
|
||
|
||
RtlIntegerToUnicodeString(CurProc, 10, &ProcessorName);
|
||
|
||
MonBuildInstanceDefinition(pPerfInstanceDefinition,
|
||
(PVOID *) &pExPCD,
|
||
0,
|
||
0,
|
||
(DWORD)-1,
|
||
ProcessorNameBuffer);
|
||
|
||
// test for Quadword Alignment
|
||
assert (((ULONG_PTR)(pExPCD) & 0x00000007) == 0);
|
||
//
|
||
// Format and collect processor data. While doing so,
|
||
// accumulate totals in the System Object Type data block.
|
||
// Pointers to these were initialized in QuerySystemData.
|
||
//
|
||
|
||
pExPCD->CounterBlock.ByteLength = sizeof (EX_PROCESSOR_COUNTER_DATA);
|
||
|
||
pexcdTotalData.ProcessorTime +=
|
||
pExPCD->ProcessorTime =
|
||
pProcessorInformation->IdleTime.QuadPart;
|
||
pexcdTotalData.UserTime +=
|
||
pExPCD->UserTime =
|
||
pProcessorInformation->UserTime.QuadPart;
|
||
// kernel time is total kernel time less the time spent in the
|
||
// idle thread for that processor
|
||
pexcdTotalData.KernelTime +=
|
||
pExPCD->KernelTime =
|
||
pProcessorInformation->KernelTime.QuadPart -
|
||
pExPCD->ProcessorTime;
|
||
|
||
pexcdTotalData.Interrupts +=
|
||
pExPCD->Interrupts = pProcessorInformation->InterruptCount;
|
||
pexcdTotalData.DpcTime +=
|
||
pExPCD->DpcTime = pProcessorInformation->DpcTime.QuadPart;
|
||
pexcdTotalData.InterruptTime +=
|
||
pExPCD->InterruptTime =
|
||
pProcessorInformation->InterruptTime.QuadPart;
|
||
|
||
pexcdTotalData.DpcCountRate +=
|
||
pExPCD->DpcCountRate =
|
||
pThisProcessorInterruptInformation->DpcCount;
|
||
|
||
pexcdTotalData.DpcRate +=
|
||
pExPCD->DpcRate =
|
||
pThisProcessorInterruptInformation->DpcRate;
|
||
|
||
// fill in the system idle info
|
||
|
||
pexcdTotalData.IdleTime +=
|
||
pExPCD->IdleTime =
|
||
pProcIdleInformation->IdleTime;
|
||
pexcdTotalData.C1Time +=
|
||
pExPCD->C1Time =
|
||
pProcIdleInformation->C1Time;
|
||
pexcdTotalData.C2Time +=
|
||
pExPCD->C2Time =
|
||
pProcIdleInformation->C2Time;
|
||
pexcdTotalData.C3Time +=
|
||
pExPCD->C3Time =
|
||
pProcIdleInformation->C3Time;
|
||
pexcdTotalData.C1Transitions +=
|
||
pExPCD->C1Transitions =
|
||
pProcIdleInformation->C1Transitions;
|
||
pexcdTotalData.C2Transitions +=
|
||
pExPCD->C2Transitions =
|
||
pProcIdleInformation->C2Transitions;
|
||
pexcdTotalData.C3Transitions +=
|
||
pExPCD->C3Transitions =
|
||
pProcIdleInformation->C3Transitions;
|
||
|
||
//
|
||
// Advance to next processor
|
||
//
|
||
|
||
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pExPCD[1];
|
||
|
||
// point to next processor's data in return array(s)
|
||
pProcessorInformation++;
|
||
pThisProcessorInterruptInformation++;
|
||
pProcIdleInformation++;
|
||
}
|
||
|
||
// do the total instance now
|
||
ProcessorName.Length = (WORD)((lstrlenW (wszTotal) + 1) * sizeof (WCHAR));
|
||
ProcessorName.MaximumLength = (WORD)(sizeof (ProcessorNameBuffer));
|
||
lstrcpyW (ProcessorNameBuffer, wszTotal);
|
||
ProcessorName.Buffer = ProcessorNameBuffer;
|
||
|
||
MonBuildInstanceDefinition(pPerfInstanceDefinition,
|
||
(PVOID *) &pExPCD,
|
||
0,
|
||
0,
|
||
(DWORD)-1,
|
||
ProcessorNameBuffer);
|
||
|
||
// define the size
|
||
pexcdTotalData.CounterBlock.ByteLength = sizeof (PROCESSOR_COUNTER_DATA);
|
||
|
||
// adjust the total values of the time fields to the number of
|
||
// processors to "normalize" the values
|
||
|
||
pexcdTotalData.ProcessorTime /= BasicInfo.NumberOfProcessors;
|
||
pexcdTotalData.UserTime /= BasicInfo.NumberOfProcessors;
|
||
pexcdTotalData.KernelTime /= BasicInfo.NumberOfProcessors;
|
||
pexcdTotalData.IdleTime /= BasicInfo.NumberOfProcessors;
|
||
pexcdTotalData.C1Time /= BasicInfo.NumberOfProcessors;
|
||
pexcdTotalData.C2Time /= BasicInfo.NumberOfProcessors;
|
||
pexcdTotalData.C3Time /= BasicInfo.NumberOfProcessors;
|
||
|
||
pexcdTotalData.DpcTime /= BasicInfo.NumberOfProcessors;
|
||
pexcdTotalData.InterruptTime /= BasicInfo.NumberOfProcessors;
|
||
|
||
// these fields are OK as totals
|
||
//
|
||
// pexcdTotalData.Interrupts
|
||
// pexcdTotalData.DpcCountRate
|
||
// pexcdTotalData.DpcRate
|
||
|
||
// copy total data to buffer
|
||
memcpy (pExPCD, &pexcdTotalData, sizeof (pexcdTotalData));
|
||
|
||
// adjust local buffer pointer
|
||
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pExPCD[1];
|
||
|
||
//
|
||
// Now we know how large an area we used for the
|
||
// processor definition, so we can update the offset
|
||
// to the next object definition
|
||
//
|
||
|
||
pExProcessorDataDefinition->ProcessorObjectType.NumInstances =
|
||
BasicInfo.NumberOfProcessors + 1;
|
||
|
||
*lppData = (LPVOID)pPerfInstanceDefinition;
|
||
|
||
// round up buffer to the nearest QUAD WORD
|
||
|
||
*lppData = ALIGN_ON_QWORD (*lppData);
|
||
|
||
*lpcbTotalBytes =
|
||
pExProcessorDataDefinition->ProcessorObjectType.TotalByteLength =
|
||
(DWORD)((LPBYTE) pPerfInstanceDefinition -
|
||
(LPBYTE) pExProcessorDataDefinition);
|
||
}
|
||
|
||
if ((pExProcessorDataDefinition == NULL) && (pProcessorDataDefinition == NULL)) {
|
||
// then no data buffer found to use
|
||
lReturn = ERROR_SUCCESS;
|
||
*lpcbTotalBytes = (DWORD) 0;
|
||
*lpNumObjectTypes = (DWORD) 0;
|
||
goto COLLECT_BAIL_OUT;
|
||
}
|
||
|
||
#if DBG
|
||
if (*lpcbTotalBytes > TotalLen ) {
|
||
DbgPrint ("\nPERFOS: Processor Perf Ctr. Instance Size Underestimated:");
|
||
DbgPrint ("\nPERFOS: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
|
||
}
|
||
#endif
|
||
|
||
*lpNumObjectTypes = 1;
|
||
|
||
return ERROR_SUCCESS;
|
||
|
||
COLLECT_BAIL_OUT:
|
||
|
||
return lReturn;
|
||
}
|
||
|
||
#pragma warning (disable : 4706)
|
||
DWORD APIENTRY
|
||
CloseProcessorObject (
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine closes the open handles
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
|
||
Return Value:
|
||
|
||
ERROR_SUCCESS
|
||
|
||
--*/
|
||
|
||
{
|
||
if (dwCpuOpenCount > 0) {
|
||
if (!(--dwCpuOpenCount)) { // when this is the last thread...
|
||
// close stuff here
|
||
if (hLibHeap != NULL) {
|
||
if (pProcessorInterruptInformation != NULL) {
|
||
FREEMEM (hLibHeap, 0, pProcessorInterruptInformation);
|
||
pProcessorInterruptInformation = NULL;
|
||
}
|
||
|
||
if (pProcessorBuffer != NULL) {
|
||
FREEMEM (hLibHeap, 0, pProcessorBuffer);
|
||
pProcessorBuffer = NULL;
|
||
}
|
||
if (pProcessorIdleInformation != NULL) {
|
||
FREEMEM (hLibHeap, 0, pProcessorIdleInformation);
|
||
pProcessorIdleInformation = NULL;
|
||
}
|
||
dwInterruptInfoBufferSize = 0;
|
||
ProcessorBufSize = 0;
|
||
dwProcessorIdleBufferSize = 0;
|
||
}
|
||
}
|
||
} else {
|
||
// if the open count is 0, then these should have been deleted
|
||
assert (pProcessorBuffer == NULL);
|
||
assert (pProcessorInterruptInformation == NULL);
|
||
assert (pProcessorIdleInformation == NULL);
|
||
}
|
||
|
||
return ERROR_SUCCESS;
|
||
|
||
}
|
||
#pragma warning (default : 4706)
|