|
|
/*++
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)
|