Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

676 lines
19 KiB

/*++ BUILD Version: 0001 // Increment this if a change has global effects
Copyright (c) 1992 Microsoft Corporation
Module Name:
p5ctrs.c
Abstract:
This file implements the Extensible Objects for the P5 object type
Created:
Russ Blake 24 Feb 93
Revision History
--*/
//
// Include Files
//
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <string.h>
#include <winperf.h>
#include "p5ctrmsg.h" // error message definition
#include "p5ctrnam.h"
#include "p5msg.h"
#include "perfutil.h"
#include "p5data.h"
#include "..\pstat.h"
//
// References to constants which initialize the Object type definitions
//
extern P5_DATA_DEFINITION P5DataDefinition;
//
// P5 data structures
//
DWORD dwOpenCount = 0; // count of "Open" threads
BOOL bInitOK = FALSE; // true = DLL initialized OK
HANDLE DriverHandle; // handle of opened device driver
UCHAR NumberOfProcessors;
#define INFSIZE 60000
ULONG Buffer[INFSIZE/4];
//
// Function Prototypes
//
// these are used to insure that the data collection functions
// accessed by Perflib will have the correct calling format.
//
DWORD APIENTRY OpenP5PerformanceData(LPWSTR);
DWORD APIENTRY CollectP5PerformanceData(LPWSTR, LPVOID *, LPDWORD, LPDWORD);
DWORD APIENTRY CloseP5PerformanceData(void);
ULONG
InitPerfInfo()
/*++
Routine Description:
Initialize data for perf measurements
Arguments:
None
Return Value:
Number of system processors (0 if error)
Revision History:
10-21-91 Initial code
--*/
{
UNICODE_STRING DriverName;
NTSTATUS status;
OBJECT_ATTRIBUTES ObjA;
IO_STATUS_BLOCK IOSB;
SYSTEM_BASIC_INFORMATION BasicInfo;
PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION PPerfInfo;
int i;
//
// Init Nt performance interface
//
NtQuerySystemInformation(
SystemBasicInformation,
&BasicInfo,
sizeof(BasicInfo),
NULL
);
NumberOfProcessors = BasicInfo.NumberOfProcessors;
if (NumberOfProcessors > MAX_PROCESSORS) {
return(0);
}
//
// Open P5Stat driver
//
RtlInitUnicodeString(&DriverName, L"\\Device\\P5Stat");
InitializeObjectAttributes(
&ObjA,
&DriverName,
OBJ_CASE_INSENSITIVE,
0,
0 );
status = NtOpenFile (
&DriverHandle, // return handle
SYNCHRONIZE | FILE_READ_DATA, // desired access
&ObjA, // Object
&IOSB, // io status block
FILE_SHARE_READ | FILE_SHARE_WRITE, // share access
FILE_SYNCHRONOUS_IO_ALERT // open options
);
if (!NT_SUCCESS(status)) {
return 0;
}
return(NumberOfProcessors);
}
long
GetPerfRegistryInitialization
(
HKEY *phKeyDriverPerf,
DWORD *pdwFirstCounter,
DWORD *pdwFirstHelp
)
{
long status;
DWORD size;
DWORD type;
// get counter and help index base values from registry
// Open key to registry entry
// read First Counter and First Help values
// update static data strucutures by adding base to
// offset value in structure.
status = RegOpenKeyEx (
HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Services\\P5Stat\\Performance",
0L,
KEY_ALL_ACCESS,
phKeyDriverPerf);
if (status != ERROR_SUCCESS) {
REPORT_ERROR_DATA (P5PERF_UNABLE_OPEN_DRIVER_KEY, LOG_USER,
&status, sizeof(status));
// this is fatal, if we can't get the base values of the
// counter or help names, then the names won't be available
// to the requesting application so there's not much
// point in continuing.
return(status);
}
size = sizeof (DWORD);
status = RegQueryValueEx(
*phKeyDriverPerf,
"First Counter",
0L,
&type,
(LPBYTE)pdwFirstCounter,
&size);
if (status != ERROR_SUCCESS) {
REPORT_ERROR_DATA (P5PERF_UNABLE_READ_FIRST_COUNTER, LOG_USER,
&status, sizeof(status));
// this is fatal, if we can't get the base values of the
// counter or help names, then the names won't be available
// to the requesting application so there's not much
// point in continuing.
return(status);
}
size = sizeof (DWORD);
status = RegQueryValueEx(
*phKeyDriverPerf,
"First Help",
0L,
&type,
(LPBYTE)pdwFirstHelp,
&size);
if (status != ERROR_SUCCESS) {
REPORT_ERROR_DATA (P5PERF_UNABLE_READ_FIRST_HELP, LOG_USER,
&status, sizeof(status));
// this is fatal, if we can't get the base values of the
// counter or help names, then the names won't be available
// to the requesting application so there's not much
// point in continuing.
}
return(status);
//
// NOTE: the initialization program could also retrieve
// LastCounter and LastHelp if they wanted to do
// bounds checking on the new number. e.g.
//
// counter->CounterNameTitleIndex += dwFirstCounter;
// if (counter->CounterNameTitleIndex > dwLastCounter) {
// LogErrorToEventLog (INDEX_OUT_OF_BOUNDS);
// }
}
DWORD APIENTRY
OpenP5PerformanceData(
LPWSTR lpDeviceNames
)
/*++
Routine Description:
This routine will open the driver which gets performance data on the
P5. This routine also initializes the data structures used to
pass data back to the registry
Arguments:
Pointer to object ID of each device to be opened (P5)
Return Value:
None.
--*/
{
DWORD ctr;
LONG status;
TCHAR szMappedObject[] = TEXT("P5_COUNTER_BLOCK");
HKEY hKeyDriverPerf;
DWORD dwFirstCounter;
DWORD dwFirstHelp;
PPERF_COUNTER_DEFINITION pPerfCounterDef;
//
// Since SCREG 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
//
if (!dwOpenCount) {
// open Eventlog interface
hEventLog = MonOpenEventLog();
// open shared memory used by device driver to pass performance values
NumberOfProcessors = (UCHAR)InitPerfInfo();
// log error if unsuccessful
if (!NumberOfProcessors) {
REPORT_ERROR (P5PERF_OPEN_FILE_ERROR, LOG_USER);
// this is fatal, if we can't get data then there's no
// point in continuing.
status = GetLastError(); // return error
goto OpenExitPoint;
}
status = GetPerfRegistryInitialization(&hKeyDriverPerf,
&dwFirstCounter,
&dwFirstHelp);
if (status == ERROR_SUCCESS) {
P5DataDefinition.P5PerfObject.ObjectNameTitleIndex +=
dwFirstCounter;
P5DataDefinition.P5PerfObject.ObjectHelpTitleIndex +=
dwFirstHelp;
pPerfCounterDef = &P5DataDefinition.Data_read;
for (ctr=0;
ctr < P5DataDefinition.P5PerfObject.NumCounters;
ctr++, pPerfCounterDef++) {
pPerfCounterDef->CounterNameTitleIndex += dwFirstCounter;
pPerfCounterDef->CounterHelpTitleIndex += dwFirstHelp;
}
RegCloseKey (hKeyDriverPerf); // close key to registry
bInitOK = TRUE; // ok to use this function
}
}
dwOpenCount++; // increment OPEN counter
status = ERROR_SUCCESS; // for successful exit
OpenExitPoint:
return status;
}
void UpdateInternalStats()
{
IO_STATUS_BLOCK IOSB;
NtDeviceIoControlFile(
DriverHandle,
(HANDLE) NULL, // event
(PIO_APC_ROUTINE) NULL,
(PVOID) NULL,
&IOSB,
P5STAT_READ_STATS,
Buffer, // input buffer
INFSIZE,
NULL, // output buffer
0
);
}
DWORD APIENTRY
CollectP5PerformanceData(
IN LPWSTR lpValueName,
IN OUT LPVOID *lppData,
IN OUT LPDWORD lpcbTotalBytes,
IN OUT LPDWORD lpNumObjectTypes
)
/*++
Routine Description:
This routine will return the data for the P5 counters.
Arguments:
IN LPWSTR lpValueName
pointer to a wide character string passed by registry.
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
Return Value:
ERROR_MORE_DATA if buffer passed is too small to hold data
any error conditions encountered are reported to the event log if
event logging is enabled.
ERROR_SUCCESS if success or any other error. Errors, however are
also reported to the event log.
--*/
{
// Variables for reformating the data
DWORD CurProc;
DWORD SpaceNeeded;
DWORD TotalLen; // Length of the total return block
DWORD dwQueryType;
pP5STATS pP5Stats;
DWORD cReg0; // pperf Register 0
DWORD cReg1; // pperf Register 1
WCHAR ProcessorNameBuffer[11];
UNICODE_STRING ProcessorName;
PLARGE_INTEGER pliCounters;
PDWORD pdwCounters;
PPERF_COUNTER_BLOCK pPerfCounterBlock;
PP5_DATA_DEFINITION pP5DataDefinition;
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
// The folllowing macro is used to update counters which are
// arithmetic derivatives of other counters.
#define UpdateDerivedCounters(Numerator, Denominator, oCounter) \
\
else if (cReg1 == (Denominator >> 1) - 1 && \
cReg0 == (Numerator >> 1) - 1) \
{ \
pdwCounters = (PDWORD) ((PBYTE) pPerfCounterBlock + oCounter); \
*pdwCounters++ = pliCounters[cReg0].LowPart; \
*pdwCounters = pliCounters[cReg1].LowPart; \
} \
else if (cReg0 == (Denominator >> 1) - 1 && \
cReg1 == (Numerator >> 1) - 1) \
{ \
pdwCounters = (PDWORD) ((PBYTE) pPerfCounterBlock + oCounter); \
*pdwCounters++ = pliCounters[cReg1].LowPart; \
*pdwCounters = pliCounters[cReg0].LowPart; \
} \
UpdateInternalStats(); // get stats as early as possible
pP5Stats = (pP5STATS) ((PUCHAR) Buffer + sizeof(ULONG));
//
// before doing anything else, see if Open went OK
//
if (!bInitOK) {
// unable to continue because open failed.
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS; // yes, this is a successful exit
}
// see if this is a foreign (i.e. non-NT) computer data request
//
dwQueryType = GetQueryType(lpValueName);
if (dwQueryType == QUERY_FOREIGN) {
// this routine does not service requests for data from
// Non-NT computers
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS;
}
if (dwQueryType == QUERY_ITEMS){
if ( !(IsNumberInUnicodeList(
P5DataDefinition.P5PerfObject.ObjectNameTitleIndex,
lpValueName))) {
// request received for data object not provided by this routine
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS;
}
}
pP5DataDefinition = (P5_DATA_DEFINITION *) *lppData;
SpaceNeeded = sizeof(P5_DATA_DEFINITION) +
NumberOfProcessors *
(sizeof(PERF_INSTANCE_DEFINITION) +
(MAX_INSTANCE_NAME+1) * sizeof(WCHAR) +
SIZE_OF_P5_PERFORMANCE_DATA);
if (*lpcbTotalBytes < SpaceNeeded) {
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_MORE_DATA;
}
//
// Copy the (constant, initialized) Object Type and counter definitions
// to the caller's data buffer
//
memmove(pP5DataDefinition,
&P5DataDefinition,
sizeof(P5_DATA_DEFINITION));
TotalLen = sizeof(P5_DATA_DEFINITION);
pP5DataDefinition->P5PerfObject.NumInstances = NumberOfProcessors;
//
// Format and collect P5 data from shared memory
//
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pP5DataDefinition[1];
for (CurProc = 0;
CurProc < NumberOfProcessors;
CurProc++, pP5Stats++) {
ProcessorName.Length = 0;
ProcessorName.MaximumLength = 11;
ProcessorName.Buffer = ProcessorNameBuffer;
RtlIntegerToUnicodeString(CurProc, 10, &ProcessorName);
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
0,
0,
CurProc,
&ProcessorName);
/* TotalLen += (PBYTE) pPerfCounterBlock -
(PBYTE) pPerfInstanceDefinition +
SIZE_OF_P5_PERFORMANCE_DATA;
*/
pPerfCounterBlock->ByteLength = SIZE_OF_P5_PERFORMANCE_DATA;
pliCounters = (PLARGE_INTEGER) (&pPerfCounterBlock[1]);
// clear area so unused counters are 0
memset((PVOID) pliCounters,
0,
SIZE_OF_P5_PERFORMANCE_DATA - sizeof(PERF_COUNTER_BLOCK));
// get the index of the two counters returned by the p5 driver
cReg0 = pP5Stats->CESR & 0x3f;
cReg1 = (pP5Stats->CESR >> 16) & 0x3f;
// load the 64bit values in the appropriate counter fields
// all other values will remain zeroed
pliCounters[cReg0].QuadPart = pP5Stats->P5Counters[0].QuadPart;
pliCounters[cReg1].QuadPart = pP5Stats->P5Counters[1].QuadPart;
// Derived counters which end up as percentages use only the DWORD
// values: hopefully these do not wrap twice between recorded/displayed
// intervals.
if (cReg1 == (CODE_READ >> 1) - 1 &&
cReg0 == (CODE_CACHE_MISS >> 1) - 1)
{
pdwCounters = (PDWORD) ((PBYTE) pPerfCounterBlock +
PCT_CODE_READ_MISS_OFFSET);
*pdwCounters++ = pliCounters[cReg0].LowPart;
*pdwCounters = pliCounters[cReg1].LowPart;
}
else if (cReg0 == (CODE_READ >> 1) - 1 &&
cReg1 == (CODE_CACHE_MISS >> 1) - 1)
{
pdwCounters = (PDWORD) ((PBYTE) pPerfCounterBlock +
PCT_CODE_READ_MISS_OFFSET);
*pdwCounters++ = pliCounters[cReg1].LowPart;
*pdwCounters = pliCounters[cReg0].LowPart;
}
UpdateDerivedCounters(DATA_RW_MISS,
DATA_RW,
PCT_DATA_RW_MISS_OFFSET)
UpdateDerivedCounters(BRANCHES,
INSTRUCTIONS_EXECUTED,
PCT_BRANCHES_OFFSET)
UpdateDerivedCounters(CODE_TLB_MISS,
CODE_READ,
PCT_CODE_TLB_MISS_OFFSET)
UpdateDerivedCounters(DATA_READ_MISS,
DATA_READ,
PCT_DATA_READ_MISS_OFFSET)
UpdateDerivedCounters(DATA_WRITE_MISS,
DATA_WRITE,
PCT_DATA_WRITE_MISS_OFFSET)
UpdateDerivedCounters(DATA_TLB_MISS,
DATA_RW,
PCT_DATA_TLB_MISS_OFFSET)
UpdateDerivedCounters(DATA_CACHE_SNOOP_HITS,
DATA_CACHE_SNOOPS,
PCT_DATA_SNOOP_HITS_OFFSET)
UpdateDerivedCounters(SEGMENT_CACHE_HITS,
SEGMENT_CACHE_ACCESSES,
PCT_SEGMENT_CACHE_HITS_OFFSET)
UpdateDerivedCounters(BTB_HITS,
BRANCHES,
PCT_BTB_HITS_OFFSET)
UpdateDerivedCounters(INSTRUCTIONS_EXECUTED_IN_VPIPE,
INSTRUCTIONS_EXECUTED,
PCT_VPIPE_INST_OFFSET)
// update pointers for next instance
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
((PBYTE) pPerfCounterBlock +
SIZE_OF_P5_PERFORMANCE_DATA);
}
// update arguments for return
*lpcbTotalBytes = (DWORD)((PBYTE)pPerfInstanceDefinition -
(PBYTE)pP5DataDefinition);
pP5DataDefinition->P5PerfObject.TotalByteLength = *lpcbTotalBytes;
*lppData = (PBYTE) pPerfInstanceDefinition;
*lpNumObjectTypes = P5_NUM_PERF_OBJECT_TYPES;
return ERROR_SUCCESS;
}
DWORD APIENTRY
CloseP5PerformanceData(
)
/*++
Routine Description:
This routine closes the open handles to P5 device performance counters
Arguments:
None.
Return Value:
ERROR_SUCCESS
--*/
{
if (!(--dwOpenCount)) { // when this is the last thread...
CloseHandle(DriverHandle);
MonCloseEventLog();
}
return ERROR_SUCCESS;
}