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.
416 lines
12 KiB
416 lines
12 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
perfpage.c
|
|
|
|
Abstract:
|
|
|
|
This file implements an Performance Object that presents
|
|
system Page file performance data
|
|
|
|
Created:
|
|
|
|
Bob Watson 22-Oct-1996
|
|
|
|
Revision History
|
|
|
|
|
|
--*/
|
|
|
|
//
|
|
// Include Files
|
|
//
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <winperf.h>
|
|
#include <assert.h>
|
|
#include <ntprfctr.h>
|
|
#define PERF_HEAP hLibHeap
|
|
#include <perfutil.h>
|
|
#include "perfos.h"
|
|
#include "perfosmc.h"
|
|
#include "datapage.h"
|
|
|
|
DWORD dwPageOpenCount = 0; // count of "Open" threads
|
|
|
|
PSYSTEM_PAGEFILE_INFORMATION pSysPageFileInfo = NULL;
|
|
DWORD dwSysPageFileInfoSize = 0; // size of page file info array
|
|
|
|
|
|
DWORD APIENTRY
|
|
OpenPageFileObject (
|
|
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 (!dwPageOpenCount) {
|
|
// allocate the memory for the Page file info
|
|
|
|
dwSysPageFileInfoSize = LARGE_BUFFER_SIZE;
|
|
|
|
pSysPageFileInfo = ALLOCMEM (dwSysPageFileInfoSize);
|
|
|
|
if (pSysPageFileInfo == NULL) {
|
|
status = ERROR_OUTOFMEMORY;
|
|
goto OpenExitPoint;
|
|
}
|
|
}
|
|
|
|
dwPageOpenCount++; // increment OPEN counter
|
|
|
|
status = ERROR_SUCCESS; // for successful exit
|
|
|
|
OpenExitPoint:
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
DWORD APIENTRY
|
|
CollectPageFileObjectData (
|
|
IN OUT LPVOID *lppData,
|
|
IN OUT LPDWORD lpcbTotalBytes,
|
|
IN OUT LPDWORD lpNumObjectTypes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will return the data for the XXX 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
|
|
|
|
--*/
|
|
{
|
|
DWORD TotalLen; // Length of the total return block
|
|
|
|
DWORD PageFileNumber;
|
|
DWORD NumPageFileInstances;
|
|
DWORD dwReturnedBufferSize;
|
|
|
|
NTSTATUS status;
|
|
|
|
PSYSTEM_PAGEFILE_INFORMATION pThisPageFile;
|
|
PPAGEFILE_DATA_DEFINITION pPageFileDataDefinition;
|
|
PPERF_INSTANCE_DEFINITION pPerfInstanceDefinition;
|
|
PPAGEFILE_COUNTER_DATA pPFCD;
|
|
PAGEFILE_COUNTER_DATA TotalPFCD;
|
|
|
|
//
|
|
// Check for sufficient space for the Pagefile object
|
|
// and counter type definition records, + one instance and
|
|
// one set of counter data
|
|
//
|
|
|
|
#ifdef DBG
|
|
STARTTIMING;
|
|
#endif
|
|
TotalLen = sizeof(PAGEFILE_DATA_DEFINITION) +
|
|
sizeof(PERF_INSTANCE_DEFINITION) +
|
|
MAX_PATH * sizeof(WCHAR) +
|
|
sizeof(PAGEFILE_COUNTER_DATA);
|
|
|
|
if ( *lpcbTotalBytes < TotalLen ) {
|
|
*lpcbTotalBytes = (DWORD) 0;
|
|
*lpNumObjectTypes = (DWORD) 0;
|
|
return ERROR_MORE_DATA;
|
|
}
|
|
|
|
while ((status = NtQuerySystemInformation(
|
|
SystemPageFileInformation, // item id
|
|
pSysPageFileInfo, // address of buffer to get data
|
|
dwSysPageFileInfoSize, // size of buffer
|
|
&dwReturnedBufferSize)) == STATUS_INFO_LENGTH_MISMATCH) {
|
|
dwSysPageFileInfoSize += INCREMENT_BUFFER_SIZE;
|
|
FREEMEM(pSysPageFileInfo);
|
|
pSysPageFileInfo = ALLOCMEM (dwSysPageFileInfoSize);
|
|
|
|
if (pSysPageFileInfo == NULL) {
|
|
status = STATUS_NO_MEMORY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
if (hEventLog != NULL) {
|
|
ReportEvent (hEventLog,
|
|
EVENTLOG_ERROR_TYPE,
|
|
0,
|
|
PERFOS_UNABLE_QUERY_PAGEFILE_INFO,
|
|
NULL,
|
|
0,
|
|
sizeof(DWORD),
|
|
NULL,
|
|
&status);
|
|
}
|
|
*lpcbTotalBytes = (DWORD) 0;
|
|
*lpNumObjectTypes = (DWORD) 0;
|
|
return RtlNtStatusToDosError(status);
|
|
}
|
|
#ifdef DBG
|
|
ENDTIMING (("PERFPAGE: %d takes %I64u ms\n", __LINE__, diff));
|
|
#endif
|
|
|
|
pPageFileDataDefinition = (PPAGEFILE_DATA_DEFINITION) *lppData;
|
|
//
|
|
// Define Page File data block
|
|
//
|
|
|
|
memcpy (pPageFileDataDefinition,
|
|
&PagefileDataDefinition,
|
|
sizeof(PAGEFILE_DATA_DEFINITION));
|
|
|
|
// Now load data for each PageFile
|
|
|
|
// clear the total fields
|
|
memset (&TotalPFCD, 0, sizeof(TotalPFCD));
|
|
TotalPFCD.CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof (PAGEFILE_COUNTER_DATA));
|
|
|
|
PageFileNumber = 0;
|
|
NumPageFileInstances = 0;
|
|
|
|
pThisPageFile = pSysPageFileInfo; // initialize pointer to list of pagefiles
|
|
|
|
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
|
|
&pPageFileDataDefinition[1];
|
|
|
|
// the check for NULL pointer is NOT the exit criteria for this loop,
|
|
// merely a check to bail out if the first (or any subsequent) pointer
|
|
// is NULL. Normally the loop will exit when the NextEntryOffset == 0
|
|
|
|
while ( pThisPageFile != NULL ) {
|
|
|
|
// compute the size required for the next instance record
|
|
|
|
TotalLen =
|
|
// current bytes already used
|
|
(DWORD)((LPBYTE)pPerfInstanceDefinition -
|
|
(LPBYTE)pPageFileDataDefinition)
|
|
// + this instance definition
|
|
+ sizeof(PERF_INSTANCE_DEFINITION)
|
|
// + the file (instance) name
|
|
+ QWORD_MULTIPLE(pThisPageFile->PageFileName.Length + sizeof(WCHAR))
|
|
// + the data block
|
|
+ sizeof (PAGEFILE_COUNTER_DATA);
|
|
|
|
TotalLen = QWORD_MULTIPLE(TotalLen+4); // round up to the next quadword
|
|
|
|
if ( *lpcbTotalBytes < TotalLen ) {
|
|
*lpcbTotalBytes = (DWORD) 0;
|
|
*lpNumObjectTypes = (DWORD) 0;
|
|
return ERROR_MORE_DATA;
|
|
}
|
|
|
|
// Build an Instance
|
|
|
|
MonBuildInstanceDefinition(pPerfInstanceDefinition,
|
|
(PVOID *) &pPFCD,
|
|
0,
|
|
0,
|
|
(DWORD)-1,
|
|
pThisPageFile->PageFileName.Buffer);
|
|
|
|
//
|
|
// Format the pagefile data
|
|
//
|
|
|
|
pPFCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof (PAGEFILE_COUNTER_DATA));
|
|
|
|
pPFCD->PercentInUse = pThisPageFile->TotalInUse;
|
|
pPFCD->PeakUsageBase =
|
|
pPFCD->PercentInUseBase = pThisPageFile->TotalSize;
|
|
pPFCD->PeakUsage = pThisPageFile->PeakUsage;
|
|
|
|
// update the total accumulators
|
|
|
|
TotalPFCD.PeakUsageBase =
|
|
TotalPFCD.PercentInUseBase += pThisPageFile->TotalSize;
|
|
TotalPFCD.PeakUsage += pThisPageFile->PeakUsage;
|
|
TotalPFCD.PercentInUse += pThisPageFile->TotalInUse;
|
|
|
|
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
|
|
&pPFCD[1];
|
|
NumPageFileInstances++;
|
|
PageFileNumber++;
|
|
|
|
if (pThisPageFile->NextEntryOffset != 0) {
|
|
pThisPageFile = (PSYSTEM_PAGEFILE_INFORMATION)\
|
|
((BYTE *)pThisPageFile + pThisPageFile->NextEntryOffset);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if (NumPageFileInstances > 0) {
|
|
// compute the size required for the next instance record
|
|
|
|
TotalLen =
|
|
// current bytes already used
|
|
(DWORD)((LPBYTE)pPerfInstanceDefinition -
|
|
(LPBYTE)pPageFileDataDefinition)
|
|
// + this instance definition
|
|
+ sizeof(PERF_INSTANCE_DEFINITION)
|
|
// + the file (instance) name
|
|
+ QWORD_MULTIPLE((lstrlenW (wszTotal) + 1) * sizeof (WCHAR))
|
|
// + the data block
|
|
+ sizeof (PAGEFILE_COUNTER_DATA);
|
|
|
|
TotalLen = QWORD_MULTIPLE(TotalLen+4); // round up to the next quadword
|
|
|
|
if ( *lpcbTotalBytes < TotalLen ) {
|
|
*lpcbTotalBytes = (DWORD) 0;
|
|
*lpNumObjectTypes = (DWORD) 0;
|
|
return ERROR_MORE_DATA;
|
|
}
|
|
|
|
// Build the Total Instance
|
|
|
|
MonBuildInstanceDefinition(pPerfInstanceDefinition,
|
|
(PVOID *)&pPFCD,
|
|
0,
|
|
0,
|
|
(DWORD)-1,
|
|
(LPWSTR)wszTotal);
|
|
|
|
//
|
|
// copy the total data
|
|
//
|
|
|
|
memcpy (pPFCD, &TotalPFCD, sizeof (TotalPFCD));
|
|
|
|
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
|
|
&pPFCD[1];
|
|
NumPageFileInstances++;
|
|
}
|
|
// Note number of PageFile instances
|
|
|
|
pPageFileDataDefinition->PagefileObjectType.NumInstances =
|
|
NumPageFileInstances;
|
|
|
|
//
|
|
// update pointers for return
|
|
//
|
|
|
|
*lpcbTotalBytes =
|
|
pPageFileDataDefinition->PagefileObjectType.TotalByteLength =
|
|
(DWORD) QWORD_MULTIPLE(((LPBYTE) pPerfInstanceDefinition) -
|
|
(LPBYTE) pPageFileDataDefinition);
|
|
* lppData = (LPVOID) (((LPBYTE) pPageFileDataDefinition) + * lpcbTotalBytes);
|
|
|
|
#ifdef DBG
|
|
if (*lpcbTotalBytes > TotalLen ) {
|
|
DbgPrint ("\nPERFOS: Paging File Perf Ctr. Instance Size Underestimated:");
|
|
DbgPrint ("\nPERFOS: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
|
|
}
|
|
#endif
|
|
|
|
*lpNumObjectTypes = 1;
|
|
|
|
#ifdef DBG
|
|
ENDTIMING (("PERFPAGE: %d takes %I64u ms total\n", __LINE__, diff));
|
|
#endif
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
DWORD APIENTRY
|
|
ClosePageFileObject (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine closes the open handles to the Signal Gen counters.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
if (dwPageOpenCount > 0) {
|
|
if (!(--dwPageOpenCount)) { // when this is the last thread...
|
|
// close stuff here
|
|
if (hLibHeap != NULL) {
|
|
if (pSysPageFileInfo != NULL) {
|
|
FREEMEM (pSysPageFileInfo);
|
|
pSysPageFileInfo = NULL;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// if open count == 0, then this should be null
|
|
assert (pSysPageFileInfo == NULL);
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|