|
|
/*++
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>
#define PERF_HEAP hLibHeap
#include <perfutil.h>
#include "perfnet.h"
#include "netsvcmc.h"
#include "datasrv.h"
#include "datasrvq.h"
#define MAX_SRVQ_NAME_LENGTH 16
HANDLE hSrv = NULL;
SRV_QUEUE_STATISTICS *pSrvQueueStatistics = NULL; DWORD dwDataBufferLength = 0L; SYSTEM_BASIC_INFORMATION BasicInfo;
BOOL bFsCtlError = FALSE; // print error only once per process
BOOL bSrvQOk = TRUE;
DWORD APIENTRY OpenServerObject ( IN LPWSTR lpValueName ) { STRING DeviceName; UNICODE_STRING DeviceNameU; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS status; HANDLE hLocalSrv = NULL;
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(&hLocalSrv, SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, 0, FILE_SYNCHRONOUS_IO_NONALERT ); }
if (!NT_SUCCESS(status)) { hSrv = NULL; bSrvQOk = FALSE; if (!bFsCtlError) { ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, 0, PERFNET_UNABLE_OPEN_SERVER, NULL, 0, sizeof(DWORD), NULL, (LPVOID)&status); bFsCtlError = TRUE; } } else { if (InterlockedCompareExchangePointer( &hSrv, hLocalSrv, NULL) != NULL) { NtClose(hLocalSrv); // Close duplicate handle
} }
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 (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 = QWORD_MULTIPLE(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 { if (!bFsCtlError) {
// 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
bFsCtlError = TRUE; } //
// Failure to access Server: clear counters to 0
//
memset(pSCD, 0, sizeof(SRV_COUNTER_DATA)); pSCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof(SRV_COUNTER_DATA)); }
*lpcbTotalBytes = pSrvDataDefinition->SrvObjectType.TotalByteLength = (DWORD) QWORD_MULTIPLE(((LPBYTE) (& pSCD[1])) - (LPBYTE) pSrvDataDefinition); * lppData = (LPVOID) (((LPBYTE) pSrvDataDefinition) + * lpcbTotalBytes); *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 = QWORD_MULTIPLE(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 = QWORD_MULTIPLE(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 = QWORD_MULTIPLE( (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) ((PCHAR) pSrvQDataDefinition + *lpcbTotalBytes);
*lpNumObjectTypes = 1; } else { if (!bFsCtlError) { // 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
bFsCtlError = TRUE; }
*lpcbTotalBytes = (DWORD) 0; *lpNumObjectTypes = (DWORD) 0; } return ERROR_SUCCESS; }
DWORD APIENTRY CloseServerObject () { HANDLE hLocalSrv = hSrv; if (hLocalSrv != NULL) { if (InterlockedCompareExchangePointer( &hSrv, NULL, hLocalSrv) == hLocalSrv) { NtClose(hLocalSrv); } }
return ERROR_SUCCESS; } DWORD APIENTRY CloseServerQueueObject () { if (hLibHeap != NULL) { if (pSrvQueueStatistics != NULL) { FREEMEM (pSrvQueueStatistics); pSrvQueueStatistics = NULL; } } return ERROR_SUCCESS; }
|