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.
 
 
 
 
 
 

626 lines
20 KiB

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
perfsrv.c
Abstract:
This file implements a Performance Object that presents
Server Performance object data
Created:
Bob Watson 22-Oct-1996
Revision History
--*/
//
// Include Files
//
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntddnfs.h>
#include <windows.h>
#include <assert.h>
#include <lmerr.h>
#include <lmapibuf.h>
#include <lmwksta.h>
#include <srvfsctl.h>
#include <winperf.h>
#include <ntprfctr.h>
#include <assert.h>
#include <perfutil.h>
#include "perfnet.h"
#include "netsvcmc.h"
#include "datasrv.h"
#include "datasrvq.h"
#define MAX_SRVQ_NAME_LENGTH 16
static HANDLE hSrv = NULL;
static SRV_QUEUE_STATISTICS *pSrvQueueStatistics = NULL;
static DWORD dwDataBufferLength = 0L;
static SYSTEM_BASIC_INFORMATION BasicInfo;
static BOOL bSrvQOk = TRUE;
DWORD APIENTRY
OpenServerObject (
IN LPWSTR lpValueName
)
{
STRING DeviceName;
UNICODE_STRING DeviceNameU;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS status;
UNREFERENCED_PARAMETER (lpValueName);
// open the handle to the server for data collection
//
// Get access to the Server for it's data
//
RtlInitString(&DeviceName, SERVER_DEVICE_NAME);
DeviceNameU.Buffer = NULL;
status = RtlAnsiStringToUnicodeString(&DeviceNameU, &DeviceName, TRUE);
if (NT_SUCCESS(status)) {
InitializeObjectAttributes(&ObjectAttributes,
&DeviceNameU,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
status = NtOpenFile(&hSrv,
SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
0,
FILE_SYNCHRONOUS_IO_NONALERT
);
}
if (!NT_SUCCESS(status)) {
hSrv = NULL;
bSrvQOk = FALSE;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE,
0,
PERFNET_UNABLE_OPEN_SERVER,
NULL,
0,
sizeof(DWORD),
NULL,
(LPVOID)&status);
}
if (DeviceNameU.Buffer) {
RtlFreeUnicodeString(&DeviceNameU);
}
return (DWORD)RtlNtStatusToDosError(status);
}
DWORD APIENTRY
OpenServerQueueObject (
IN LPWSTR szValueName
)
{
NTSTATUS status;
UNREFERENCED_PARAMETER (szValueName);
//
// collect basic and static processor data
//
status = NtQuerySystemInformation(
SystemBasicInformation,
&BasicInfo,
sizeof(SYSTEM_BASIC_INFORMATION),
NULL
);
assert (NT_SUCCESS(status));
if (!NT_SUCCESS(status)) {
// all we really want is the number of processors so
// if we can't get that from the system, then we'll
// substitute 32 for the number
BasicInfo.NumberOfProcessors = 32;
status = ERROR_SUCCESS;
}
// compute the various buffer sizes required
dwDataBufferLength = sizeof(SRV_QUEUE_STATISTICS) *
(BasicInfo.NumberOfProcessors + 1);
pSrvQueueStatistics = (SRV_QUEUE_STATISTICS *)ALLOCMEM (
hLibHeap, HEAP_ZERO_MEMORY, dwDataBufferLength);
// if memory allocation failed, then no server queue stats will
// be returned.
assert (pSrvQueueStatistics != NULL);
if (pSrvQueueStatistics == NULL) {
bSrvQOk = FALSE;
}
return ERROR_SUCCESS;
}
DWORD APIENTRY
CollectServerObjectData(
IN OUT LPVOID *lppData,
IN OUT LPDWORD lpcbTotalBytes,
IN OUT LPDWORD lpNumObjectTypes
)
/*++
Routine Description:
This routine will return the data for the Physical Disk 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
NTSTATUS Status = ERROR_SUCCESS;
IO_STATUS_BLOCK IoStatusBlock;
SRV_DATA_DEFINITION *pSrvDataDefinition;
SRV_COUNTER_DATA *pSCD;
SRV_STATISTICS SrvStatistics;
if (hSrv == NULL) {
// bail out if the server didn't get opened.
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS;
}
//
// Check for sufficient space for server data
//
TotalLen = sizeof(SRV_DATA_DEFINITION) +
sizeof(SRV_COUNTER_DATA);
if ( *lpcbTotalBytes < TotalLen ) {
// bail out if the data won't fit in the caller's buffer
// or the server didn't get opened.
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_MORE_DATA;
}
//
// Define objects data block
//
pSrvDataDefinition = (SRV_DATA_DEFINITION *) *lppData;
memcpy (pSrvDataDefinition,
&SrvDataDefinition,
sizeof(SRV_DATA_DEFINITION));
//
// Format and collect server data
//
pSCD = (PSRV_COUNTER_DATA)&pSrvDataDefinition[1];
// test for quadword alignment of the structure
assert (((DWORD)(pSCD) & 0x00000007) == 0);
pSCD->CounterBlock.ByteLength = sizeof(SRV_COUNTER_DATA);
Status = NtFsControlFile(hSrv,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_SRV_GET_STATISTICS,
NULL,
0,
&SrvStatistics,
sizeof(SrvStatistics)
);
if ( NT_SUCCESS(Status) ) {
pSCD->TotalBytes = SrvStatistics.TotalBytesSent.QuadPart +
SrvStatistics.TotalBytesReceived.QuadPart;
pSCD->TotalBytesReceived = SrvStatistics.TotalBytesReceived.QuadPart;
pSCD->TotalBytesSent = SrvStatistics.TotalBytesSent.QuadPart;
pSCD->SessionsTimedOut = SrvStatistics.SessionsTimedOut;
pSCD->SessionsErroredOut = SrvStatistics.SessionsErroredOut;
pSCD->SessionsLoggedOff = SrvStatistics.SessionsLoggedOff;
pSCD->SessionsForcedLogOff = SrvStatistics.SessionsForcedLogOff;
pSCD->LogonErrors = SrvStatistics.LogonErrors;
pSCD->AccessPermissionErrors = SrvStatistics.AccessPermissionErrors;
pSCD->GrantedAccessErrors = SrvStatistics.GrantedAccessErrors;
pSCD->SystemErrors = SrvStatistics.SystemErrors;
pSCD->BlockingSmbsRejected = SrvStatistics.BlockingSmbsRejected;
pSCD->WorkItemShortages = SrvStatistics.WorkItemShortages;
pSCD->TotalFilesOpened = SrvStatistics.TotalFilesOpened;
pSCD->CurrentOpenFiles = SrvStatistics.CurrentNumberOfOpenFiles;
pSCD->CurrentSessions = SrvStatistics.CurrentNumberOfSessions;
pSCD->CurrentOpenSearches = SrvStatistics.CurrentNumberOfOpenSearches;
pSCD->CurrentNonPagedPoolUsage = SrvStatistics.CurrentNonPagedPoolUsage;
pSCD->NonPagedPoolFailures = SrvStatistics.NonPagedPoolFailures;
pSCD->PeakNonPagedPoolUsage = SrvStatistics.PeakNonPagedPoolUsage;
pSCD->CurrentPagedPoolUsage = SrvStatistics.CurrentPagedPoolUsage;
pSCD->PagedPoolFailures = SrvStatistics.PagedPoolFailures;
pSCD->PeakPagedPoolUsage = SrvStatistics.PeakPagedPoolUsage;
pSCD->ContextBlockQueueRate = SrvStatistics.TotalWorkContextBlocksQueued.Count;
pSCD->NetLogon =
pSCD->NetLogonTotal = SrvStatistics.SessionLogonAttempts;
} else {
// log an event describing the error
DWORD dwData[4];
DWORD dwDataIndex = 0;
dwData[dwDataIndex++] = Status;
dwData[dwDataIndex++] = IoStatusBlock.Status;
dwData[dwDataIndex++] = (DWORD)IoStatusBlock.Information;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
PERFNET_UNABLE_READ_SERVER, // error code
NULL, // SID (not used),
0, // number of strings
dwDataIndex * sizeof(DWORD), // sizeof raw data
NULL, // message text array
(LPVOID)&dwData[0]); // raw data
//
// Failure to access Server: clear counters to 0
//
memset(pSCD, 0, sizeof(SRV_COUNTER_DATA));
pSCD->CounterBlock.ByteLength = sizeof(SRV_COUNTER_DATA);
}
*lppData = (LPVOID)&pSCD[1];
*lpcbTotalBytes = (DWORD)((LPBYTE)&pSCD[1] - (LPBYTE)pSrvDataDefinition);
*lpNumObjectTypes = 1;
return ERROR_SUCCESS;
}
DWORD APIENTRY
CollectServerQueueObjectData(
IN OUT LPVOID *lppData,
IN OUT LPDWORD lpcbTotalBytes,
IN OUT LPDWORD lpNumObjectTypes
)
/*++
Routine Description:
This routine will return the data for the Physical Disk 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
LONG nQueue;
NTSTATUS Status = ERROR_SUCCESS;
IO_STATUS_BLOCK IoStatusBlock;
SRVQ_DATA_DEFINITION *pSrvQDataDefinition;
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
SRVQ_COUNTER_DATA *pSQCD;
SRV_QUEUE_STATISTICS *pThisQueueStatistics;
UNICODE_STRING QueueName;
WCHAR QueueNameBuffer[MAX_SRVQ_NAME_LENGTH];
if (!bSrvQOk) {
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS;
}
//
// Check for sufficient space for server data
//
TotalLen = sizeof(SRVQ_DATA_DEFINITION) +
sizeof(PERF_INSTANCE_DEFINITION) +
sizeof(SRVQ_COUNTER_DATA);
if ( *lpcbTotalBytes < TotalLen ) {
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_MORE_DATA;
}
// assign local pointer to current position in buffer
pSrvQDataDefinition = (SRVQ_DATA_DEFINITION *) *lppData;
//
// Define perf object data block
//
memcpy (pSrvQDataDefinition,
&SrvQDataDefinition,
sizeof(SRVQ_DATA_DEFINITION));
//
// Format and collect server Queue data
//
QueueName.Length = 0;
QueueName.MaximumLength = sizeof(QueueNameBuffer);
QueueName.Buffer = QueueNameBuffer;
Status = NtFsControlFile(hSrv,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_SRV_GET_QUEUE_STATISTICS,
NULL,
0,
pSrvQueueStatistics,
dwDataBufferLength
);
if (NT_SUCCESS(Status)) {
// server data was collected successfully so...
// process each processor queue instance.
nQueue = 0;
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pSrvQDataDefinition[1];
TotalLen = sizeof(SRVQ_DATA_DEFINITION);
for (nQueue = 0; nQueue < BasicInfo.NumberOfProcessors; nQueue++) {
// see if this instance will fit
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
8 + // size of 3 (unicode) digit queuelength name
sizeof(SRVQ_COUNTER_DATA);
if ( *lpcbTotalBytes < TotalLen ) {
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_MORE_DATA;
}
RtlIntegerToUnicodeString(nQueue,
10,
&QueueName);
// there should be enough room for this instance so initialize it
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pSQCD,
0,
0,
(DWORD)-1,
QueueName.Buffer);
pSQCD->CounterBlock.ByteLength = sizeof (SRVQ_COUNTER_DATA);
// initialize pointers for this instance
pThisQueueStatistics = &pSrvQueueStatistics[nQueue];
pSQCD->QueueLength = pThisQueueStatistics->QueueLength;
pSQCD->ActiveThreads = pThisQueueStatistics->ActiveThreads;
pSQCD->AvailableThreads = pThisQueueStatistics->AvailableThreads;
pSQCD->AvailableWorkItems = pThisQueueStatistics->FreeWorkItems;
pSQCD->BorrowedWorkItems = pThisQueueStatistics->StolenWorkItems;
pSQCD->WorkItemShortages = pThisQueueStatistics->NeedWorkItem;
pSQCD->CurrentClients = pThisQueueStatistics->CurrentClients;
pSQCD->TotalBytesTransfered =
pSQCD->BytesReceived = pThisQueueStatistics->BytesReceived.QuadPart;
pSQCD->TotalBytesTransfered +=
pSQCD->BytesSent = pThisQueueStatistics->BytesSent.QuadPart;
pSQCD->TotalOperations =
pSQCD->ReadOperations = pThisQueueStatistics->ReadOperations.QuadPart;
pSQCD->TotalBytes =
pSQCD->BytesRead = pThisQueueStatistics->BytesRead.QuadPart;
pSQCD->TotalOperations +=
pSQCD->WriteOperations = pThisQueueStatistics->WriteOperations.QuadPart;
pSQCD->TotalBytes +=
pSQCD->BytesWritten = pThisQueueStatistics->BytesWritten.QuadPart;
pSQCD->TotalContextBlocksQueued = pThisQueueStatistics->TotalWorkContextBlocksQueued.Count;
// update the current pointer
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pSQCD[1];
}
RtlInitUnicodeString (&QueueName, (LPCWSTR)L"Blocking Queue");
// now load the "blocking" queue data
// see if this instance will fit
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
QWORD_MULTIPLE(QueueName.Length + sizeof(WCHAR)) +
sizeof (SRVQ_COUNTER_DATA);
if ( *lpcbTotalBytes < TotalLen ) {
// this instance won't fit so bail out
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_MORE_DATA;
}
// there should be enough room for this instance so initialize it
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pSQCD,
0,
0,
(DWORD)-1,
QueueName.Buffer);
pSQCD->CounterBlock.ByteLength = sizeof(SRVQ_COUNTER_DATA);
// initialize pointers for this instance
pThisQueueStatistics = &pSrvQueueStatistics[nQueue];
pSQCD->QueueLength = pThisQueueStatistics->QueueLength;
pSQCD->ActiveThreads = pThisQueueStatistics->ActiveThreads;
pSQCD->AvailableThreads = pThisQueueStatistics->AvailableThreads;
pSQCD->AvailableWorkItems = 0;
pSQCD->BorrowedWorkItems = 0;
pSQCD->WorkItemShortages = 0;
pSQCD->CurrentClients = 0;
pSQCD->TotalBytesTransfered =
pSQCD->BytesReceived = pThisQueueStatistics->BytesReceived.QuadPart;
pSQCD->TotalBytesTransfered +=
pSQCD->BytesSent = pThisQueueStatistics->BytesSent.QuadPart;
pSQCD->ReadOperations = 0;
pSQCD->TotalBytes =
pSQCD->BytesRead = pThisQueueStatistics->BytesRead.QuadPart;
pSQCD->WriteOperations = 0;
pSQCD->TotalBytes +=
pSQCD->BytesWritten = pThisQueueStatistics->BytesWritten.QuadPart;
pSQCD->TotalOperations = 0;
pSQCD->TotalContextBlocksQueued = pThisQueueStatistics->TotalWorkContextBlocksQueued.Count;
nQueue++; // to include the Blocking Queue statistics entry
// update the current pointer
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pSQCD[1];
// update queue (instance) count in object data block
pSrvQDataDefinition->SrvQueueObjectType.NumInstances = nQueue;
// update available length
*lpcbTotalBytes =
pSrvQDataDefinition->SrvQueueObjectType.TotalByteLength =
(DWORD)((PCHAR) pPerfInstanceDefinition -
(PCHAR) pSrvQDataDefinition);
#if DBG
if (*lpcbTotalBytes > TotalLen ) {
DbgPrint ("\nPERFNET: Server Queue Perf Ctr. Instance Size Underestimated:");
DbgPrint ("\nPERFNET: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
}
#endif
*lppData = (LPVOID)pPerfInstanceDefinition;
*lpNumObjectTypes = 1;
return ERROR_SUCCESS;
} else {
// unable to read server queue data for some reason so don't return this
// object
// log an event describing the error
DWORD dwData[4];
DWORD dwDataIndex = 0;
dwData[dwDataIndex++] = Status;
dwData[dwDataIndex++] = IoStatusBlock.Status;
dwData[dwDataIndex++] = (DWORD)IoStatusBlock.Information;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
PERFNET_UNABLE_READ_SERVER_QUEUE, // error code
NULL, // SID (not used),
0, // number of strings
dwDataIndex * sizeof(DWORD), // sizeof raw data
NULL, // message text array
(LPVOID)&dwData[0]); // raw data
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS;
}
}
DWORD APIENTRY
CloseServerObject ()
{
if (hSrv != NULL) {
NtClose(hSrv);
hSrv = NULL;
}
return ERROR_SUCCESS;
}
DWORD APIENTRY
CloseServerQueueObject ()
{
if (hLibHeap != NULL) {
if (pSrvQueueStatistics != NULL) {
FREEMEM (hLibHeap, 0, pSrvQueueStatistics);
pSrvQueueStatistics = NULL;
}
}
return ERROR_SUCCESS;
}