|
|
/*++
Copyright (c) 1997-1998 Microsoft Corporation
Module Name:
perfts.c
Description: DLL entry point and support code for Terminal Server performance counters.
Author:
Erik Mavrinac 24-Nov-1998
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <winperf.h>
#include <ntprfctr.h>
#include <perfutil.h>
#include <winsta.h>
#include <utildll.h>
#include <stdlib.h>
#include "datats.h"
#if DBG
#define DBGPRINT(x) DbgPrint x
#else
#define DBGPRINT(x)
#endif
#define MAX_SESSION_NAME_LENGTH 50
typedef struct _WinStationInfo { LIST_ENTRY HashBucketList; LIST_ENTRY UsedList; ULONG SessionID; WINSTATIONSTATECLASS LastState; void *pInstanceInfo; WINSTATIONNAMEW WinStationName; } WinStationInfo;
/****************************************************************************/ // Globals
/****************************************************************************/
// Default hash bucket list to remove having to check for
// pWinStationHashBuckets == NULL on perf path. This array contains the
// default number for one WinStationInfo.
#define NumDefaultWinStationHashBuckets 4
LIST_ENTRY DefaultWinStationHashBuckets[NumDefaultWinStationHashBuckets];
HANDLE hEventLog = NULL; HANDLE hLibHeap = NULL; PBYTE pProcessBuffer = NULL;
static DWORD dwOpenCount = 0; static DWORD ProcessBufSize = LARGE_BUFFER_SIZE; static DWORD NumberOfCPUs = 0; static DWORD FirstCounterIndex = 0;
LIST_ENTRY UsedList; LIST_ENTRY UnusedList; LIST_ENTRY *pWinStationHashBuckets = DefaultWinStationHashBuckets; unsigned NumWinStationHashBuckets = NumDefaultWinStationHashBuckets; ULONG WinStationHashMask = 0x3; unsigned NumCachedWinStations = 0;
SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo = {{0,0},{0,0},{0,0},0,0};
/****************************************************************************/ // Prototypes
/****************************************************************************/ BOOL DllProcessAttach(void); BOOL DllProcessDetach(void); DWORD GetNumberOfCPUs(void); NTSTATUS GetDescriptionOffset(void); void SetupCounterIndices(void);
DWORD APIENTRY OpenWinStationObject(LPWSTR); DWORD APIENTRY CloseWinStationObject(void); DWORD APIENTRY CollectWinStationObjectData(IN LPWSTR, IN OUT LPVOID *, IN OUT LPDWORD, OUT LPDWORD);
DWORD GetSystemProcessData(void); void SetupWinStationCounterBlock(WINSTATION_COUNTER_DATA *, PWINSTATIONINFORMATIONW); void UpdateWSProcessCounterBlock(WINSTATION_COUNTER_DATA *, PSYSTEM_PROCESS_INFORMATION);
void CreateNewHashBuckets(unsigned);
// Declares these exported functions as PerfMon entry points.
PM_OPEN_PROC OpenTSObject; PM_COLLECT_PROC CollectTSObjectData; PM_CLOSE_PROC CloseTSObject;
/****************************************************************************/ // DLL system load/unload entry point.
/****************************************************************************/ BOOL __stdcall DllInit( IN HANDLE DLLHandle, IN DWORD Reason, IN LPVOID ReservedAndUnused) { ReservedAndUnused;
// This will prevent the DLL from getting
// the DLL_THREAD_* messages
DisableThreadLibraryCalls(DLLHandle);
switch(Reason) { case DLL_PROCESS_ATTACH: return DllProcessAttach();
case DLL_PROCESS_DETACH: return DllProcessDetach();
default: return TRUE; } }
/****************************************************************************/ // DLL instance load time initialization.
/****************************************************************************/ BOOL DllProcessAttach(void) { unsigned i; NTSTATUS Status;
// DBGPRINT(("PerfTS: ProcessAttach\n"));
// Create the local heap
hLibHeap = HeapCreate(0, 1, 0); if (hLibHeap == NULL) return FALSE;
// Open handle to the event log
if (hEventLog == NULL) { hEventLog = MonOpenEventLog(L"PerfTS"); if (hEventLog == NULL) goto PostCreateHeap; }
// Get the counter index value and init the WinStationDataDefinition
// counter values.
Status = GetDescriptionOffset(); if (!NT_SUCCESS(Status)) goto PostOpenEventLog; SetupCounterIndices();
// Pre-determine the number of system CPUs.
NumberOfCPUs = GetNumberOfCPUs();
// UsedList is used as a skip-list through all valid entries in the
// hash table to allow us to iterate all entries without having to have
// a second, low-performance loop that looks through each hash bucket
// list.
InitializeListHead(&UsedList); InitializeListHead(&UnusedList); for (i = 0; i < NumDefaultWinStationHashBuckets; i++) InitializeListHead(&DefaultWinStationHashBuckets[i]); return TRUE;
// Error handling.
PostOpenEventLog: MonCloseEventLog(); hEventLog = NULL;
PostCreateHeap: HeapDestroy(hLibHeap); hLibHeap = NULL;
return FALSE; }
/****************************************************************************/ // DLL unload cleanup.
/****************************************************************************/ BOOL DllProcessDetach(void) { // DBGPRINT(("PerfTS: ProcessDetach\n"));
if (dwOpenCount > 0) { // the Library is being unloaded before it was
// closed so close it now as this is the last
// chance to do it before the library is tossed.
// if the value of dwOpenCount is > 1, set it to
// one to insure everything will be closed when
// the close function is called.
if (dwOpenCount > 1) dwOpenCount = 1; CloseTSObject(); }
if (hEventLog != NULL) { MonCloseEventLog(); hEventLog = NULL; }
if (hLibHeap != NULL && HeapDestroy(hLibHeap)) hLibHeap = NULL;
return TRUE; }
/****************************************************************************/ // Utility function used at startup.
/****************************************************************************/ DWORD GetNumberOfCPUs(void) { NTSTATUS Status; DWORD ReturnLen; SYSTEM_BASIC_INFORMATION Info;
Status = NtQuerySystemInformation( SystemBasicInformation, &Info, sizeof(Info), &ReturnLen );
if (NT_SUCCESS(Status)) { return Info.NumberOfProcessors; } else { DBGPRINT(("GetNumberOfCPUs Error 0x%x returning CPU count\n",Status)); // Return 1 CPU
return 1; } }
/****************************************************************************/ // Gets the offset index of the first text description from the
// TermService\Performance key. This value was created by Lodctr /
// LoadPerfCounterTextStrings() during setup.
/****************************************************************************/ NTSTATUS GetDescriptionOffset(void) { HKEY hTermServiceKey; OBJECT_ATTRIBUTES Obja; NTSTATUS Status; UNICODE_STRING TermServiceSubKeyString; UNICODE_STRING ValueNameString; LONG ResultLength; PKEY_VALUE_PARTIAL_INFORMATION pValueInformation; BYTE ValueInfo[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD) - 1];
// Initialize UNICODE_STRING structures used in this function
RtlInitUnicodeString(&TermServiceSubKeyString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\TermService\\Performance"); RtlInitUnicodeString(&ValueNameString, L"First Counter");
// Initialize the OBJECT_ATTRIBUTES structure and open the key.
InitializeObjectAttributes(&Obja, &TermServiceSubKeyString, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&hTermServiceKey, KEY_READ, &Obja);
if (NT_SUCCESS(Status)) { // read value of desired entry
pValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION)ValueInfo; ResultLength = sizeof(ValueInfo);
Status = NtQueryValueKey(hTermServiceKey, &ValueNameString, KeyValuePartialInformation, pValueInformation, sizeof(ValueInfo), &ResultLength);
if (NT_SUCCESS(Status)) { // Check to see if it's a DWORD.
if (pValueInformation->DataLength == sizeof(DWORD) && pValueInformation->Type == REG_DWORD) { FirstCounterIndex = *((DWORD *)(pValueInformation->Data)); } else { DBGPRINT(("PerfTS: Len %u not right or type %u not DWORD\n", pValueInformation->DataLength, pValueInformation->Type)); } } else { DBGPRINT(("PerfTS: Could not read counter value (status=%X)\n", Status)); }
// close the registry key
NtClose(hTermServiceKey); } else { DBGPRINT(("PerfTS: Unable to open TermService\\Performance key " "(status=%x)\n", Status)); }
return Status; }
/****************************************************************************/ // Initializes the WinStation and TermServer counter descriptions with the
// loaded counter index offset.
/****************************************************************************/ void SetupCounterIndices(void) { unsigned i; unsigned NumCounterDefs; PERF_COUNTER_DEFINITION *pCounterDef;
// First index goes to the WinStation object description and help.
WinStationDataDefinition.WinStationObjectType.ObjectNameTitleIndex += FirstCounterIndex; WinStationDataDefinition.WinStationObjectType.ObjectHelpTitleIndex += FirstCounterIndex;
// We need to add the FirstCounterIndex value directly into the
// description and help indices in the WinStation counters in
// WinStationDataDefinition.
pCounterDef = &WinStationDataDefinition.InputWdBytes; NumCounterDefs = (sizeof(WinStationDataDefinition) - (unsigned)((BYTE *)pCounterDef - (BYTE *)&WinStationDataDefinition)) / sizeof(PERF_COUNTER_DEFINITION);
for (i = 0; i < NumCounterDefs; i++) { pCounterDef->CounterNameTitleIndex += FirstCounterIndex; pCounterDef->CounterHelpTitleIndex += FirstCounterIndex; pCounterDef++; }
// We need to add the FirstCounterIndex value directly into the
// description and help indices in the TermServer counters.
TermServerDataDefinition.TermServerObjectType.ObjectNameTitleIndex += FirstCounterIndex; TermServerDataDefinition.TermServerObjectType.ObjectHelpTitleIndex += FirstCounterIndex; pCounterDef = &TermServerDataDefinition.NumSessions; NumCounterDefs = (sizeof(TermServerDataDefinition) - (unsigned)((BYTE *)pCounterDef - (BYTE *)&TermServerDataDefinition)) / sizeof(PERF_COUNTER_DEFINITION); for (i = 0; i < NumCounterDefs; i++) { pCounterDef->CounterNameTitleIndex += FirstCounterIndex; pCounterDef->CounterHelpTitleIndex += FirstCounterIndex; pCounterDef++; } }
/****************************************************************************/ // PerfMon open entry point.
// DeviceNames is ptr to object ID of each device to be opened.
/****************************************************************************/ DWORD APIENTRY OpenTSObject(LPWSTR DeviceNames) { // DBGPRINT(("PerfTS: Open() called\n"));
dwOpenCount++;
// Allocate the first process info buffer.
if (pProcessBuffer == NULL) { pProcessBuffer = ALLOCMEM(hLibHeap, HEAP_ZERO_MEMORY, ProcessBufSize); if (pProcessBuffer == NULL) return ERROR_OUTOFMEMORY; }
return ERROR_SUCCESS; }
/****************************************************************************/ // PerfMon close entry point.
/****************************************************************************/ DWORD APIENTRY CloseTSObject(void) { PLIST_ENTRY pEntry; WinStationInfo *pWSI;
// DBGPRINT(("PerfTS: Close() called\n"));
if (--dwOpenCount == 0) { if (hLibHeap != NULL) { // Free the WinStation cache entries.
pEntry = UsedList.Flink; while (!IsListEmpty(&UsedList)) { pEntry = RemoveHeadList(&UsedList); pWSI = CONTAINING_RECORD(pEntry, WinStationInfo, UsedList); RemoveEntryList(&pWSI->HashBucketList); FREEMEM(hLibHeap, 0, pWSI); } if (pWinStationHashBuckets != DefaultWinStationHashBuckets) { FREEMEM(hLibHeap, 0, pWinStationHashBuckets); pWinStationHashBuckets = DefaultWinStationHashBuckets; NumWinStationHashBuckets = NumDefaultWinStationHashBuckets; }
// Free the proc info buffer.
if (pProcessBuffer != NULL) { FREEMEM(hLibHeap, 0, pProcessBuffer); pProcessBuffer = NULL; } } }
return ERROR_SUCCESS; }
/****************************************************************************/ // Grabs system process information into global buffer.
/****************************************************************************/ __inline DWORD GetSystemProcessData(void) { DWORD dwReturnedBufferSize; NTSTATUS Status;
// Get process data from system.
while ((Status = NtQuerySystemInformation(SystemProcessInformation, pProcessBuffer, ProcessBufSize, &dwReturnedBufferSize)) == STATUS_INFO_LENGTH_MISMATCH) { BYTE *pNewProcessBuffer;
// Expand buffer & retry. ReAlloc does not free the original mem
// on error, so alloc into a temp pointer.
ProcessBufSize += INCREMENT_BUFFER_SIZE; pNewProcessBuffer = REALLOCMEM(hLibHeap, 0, pProcessBuffer, ProcessBufSize);
if (pNewProcessBuffer != NULL) pProcessBuffer = pNewProcessBuffer; else return ERROR_OUTOFMEMORY; }
if (NT_SUCCESS(Status)) { // Get system time.
Status = NtQuerySystemInformation(SystemTimeOfDayInformation, &SysTimeInfo, sizeof(SysTimeInfo), &dwReturnedBufferSize); if (!NT_SUCCESS(Status)) Status = (DWORD)RtlNtStatusToDosError(Status); } else { // Convert to Win32 error.
Status = (DWORD)RtlNtStatusToDosError(Status); }
return Status; }
/****************************************************************************/ // Creates a WinStation name based on the WinStation state.
// Assumes the cache lock is held.
/****************************************************************************/ void ConstructSessionName( WinStationInfo *pWSI, WINSTATIONINFORMATIONW *pQueryData) { WCHAR *SrcName, *DstName; LPCTSTR pState = NULL;
// Update active/inactive counts and create UI names for sessions.
if (pQueryData->WinStationName[0] != '\0') { // We have a problem with WinStation names --
// the '#' sign is not allowed. So, replace them
// with spaces during name copy.
SrcName = pQueryData->WinStationName; DstName = pWSI->WinStationName; while (*SrcName != L'\0') { if (*SrcName != L'#') *DstName = *SrcName; else *DstName = L' '; SrcName++; DstName++; } *DstName = L'\0'; } else { // Create a fake session name based on the session ID and
// an indication of the session state.
_ltow(pWSI->SessionID, pWSI->WinStationName, 10); wcsncat(pWSI->WinStationName, L" ",1); pState = StrConnectState(pQueryData->ConnectState, TRUE); if(pState) { wcsncat(pWSI->WinStationName, (const wchar_t *)pState, (MAX_SESSION_NAME_LENGTH - 1) - wcslen(pWSI->WinStationName)); } } }
/****************************************************************************/ // Adds a WinStationInfo block (with session ID already filled out) into
// the cache.
// Assumes the cache lock is held.
/****************************************************************************/ void AddWinStationInfoToCache(WinStationInfo *pWSI) { unsigned i; unsigned Temp, NumHashBuckets;
// Add to the hash table.
InsertHeadList(&pWinStationHashBuckets[pWSI->SessionID & WinStationHashMask], &pWSI->HashBucketList); NumCachedWinStations++;
// Check to see if we need to increase the hash table size.
// If so, allocate a new one and populate it.
// Hash table size is the number of entries * 4 rounded down
// to the next lower power of 2, for easy key masking and higher
// probability of having a low hash bucket list count.
Temp = 4 * NumCachedWinStations;
// Find the highest bit set in the hash bucket value.
for (i = 0; Temp > 1; i++) Temp >>= 1; NumHashBuckets = 1 << i; if (NumWinStationHashBuckets < NumHashBuckets) CreateNewHashBuckets(NumHashBuckets); }
/****************************************************************************/ // Common code for Add and RemoveWinStationInfo.
// Assumes the cache lock is held.
/****************************************************************************/ void CreateNewHashBuckets(unsigned NumHashBuckets) { unsigned i, HashMask; PLIST_ENTRY pLI, pEntry, pTempEntry; WinStationInfo *pTempWSI;
if (NumHashBuckets != NumDefaultWinStationHashBuckets) pLI = ALLOCMEM(hLibHeap, 0, NumHashBuckets * sizeof(LIST_ENTRY)); else pLI = DefaultWinStationHashBuckets;
if (pLI != NULL) { for (i = 0; i < NumHashBuckets; i++) InitializeListHead(&pLI[i]);
HashMask = NumHashBuckets - 1;
// Move the old hash table entries into the new table.
// Have to enumerate all entries in used and unused lists
// since we are likely to have entries scattered in both places.
pEntry = UsedList.Flink; while (pEntry != &UsedList) { pTempWSI = CONTAINING_RECORD(pEntry, WinStationInfo, UsedList); InsertHeadList(&pLI[pTempWSI->SessionID & HashMask], &pTempWSI->HashBucketList); pEntry = pEntry->Flink; } pEntry = UnusedList.Flink; while (pEntry != &UnusedList) { pTempWSI = CONTAINING_RECORD(pEntry, WinStationInfo, UsedList); InsertHeadList(&pLI[pTempWSI->SessionID & HashMask], &pTempWSI->HashBucketList); pEntry = pEntry->Flink; }
if (pWinStationHashBuckets != DefaultWinStationHashBuckets) FREEMEM(hLibHeap, 0, pWinStationHashBuckets);
NumWinStationHashBuckets = NumHashBuckets; WinStationHashMask = HashMask; pWinStationHashBuckets = pLI; } else { // On failure, we just leave the hash table alone until next time.
DBGPRINT(("PerfTS: Could not alloc new hash buckets\n")); } }
/****************************************************************************/ // Removes a WSI from the hash table.
// Assumes the cache lock is held.
/****************************************************************************/ void RemoveWinStationInfoFromCache(WinStationInfo *pWSI) { unsigned i; unsigned Temp, NumHashBuckets, HashMask;
// Remove from the hash table.
RemoveEntryList(&pWSI->HashBucketList); NumCachedWinStations--;
// Check to see if we need to decrease the hash table size.
// If so, allocate a new one and populate it.
// Hash table size is the number of entries * 4 rounded down
// to the next lower power of 2, for easy key masking and higher
// probability of having a low hash bucket list count.
Temp = 4 * NumCachedWinStations;
// Find the highest bit set in the hash bucket value.
for (i = 0; Temp > 1; i++) Temp >>= 1; NumHashBuckets = 1 << i; if (NumWinStationHashBuckets < NumHashBuckets && NumWinStationHashBuckets >= 4) CreateNewHashBuckets(NumHashBuckets); }
/****************************************************************************/ // PerfMon collect entry point.
// Args:
// ValueName: Registry name.
// ppData: Passes 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
// *ppData. Passes out pointer to the first byte after the data structure
// added by this routine.
// pTotalBytes: Passes in ptr to size in bytes of the buf at *ppdata. Passes
// out number of bytes added if *ppData is changed.
// pNumObjectTypes: Passes out the number of objects added by this routine.
//
// Returns: Win32 error code.
/****************************************************************************/ #define WinStationInstanceSize (sizeof(PERF_INSTANCE_DEFINITION) + \
(MAX_WINSTATION_NAME_LENGTH + 1) * sizeof(WCHAR) + \ 2 * sizeof(DWORD) + /* Allow for QWORD alignment space. */ \ sizeof(WINSTATION_COUNTER_DATA))
DWORD APIENTRY CollectTSObjectData( IN LPWSTR ValueName, IN OUT LPVOID *ppData, IN OUT LPDWORD pTotalBytes, OUT LPDWORD pNumObjectTypes) { DWORD Result; DWORD TotalLen; // Length of the total return block
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition; PSYSTEM_PROCESS_INFORMATION pProcessInfo; ULONG NumWinStationInstances; NTSTATUS Status; ULONG ProcessBufferOffset; WINSTATION_DATA_DEFINITION *pWinStationDataDefinition; WINSTATION_COUNTER_DATA *pWSC; TERMSERVER_DATA_DEFINITION *pTermServerDataDefinition; TERMSERVER_COUNTER_DATA *pTSC; ULONG SessionId; WinStationInfo *pWSI; LIST_ENTRY *pEntry; unsigned i; unsigned ActiveWS, InactiveWS; WCHAR *InstanceName; ULONG AmountRet; WCHAR StringBuf[MAX_SESSION_NAME_LENGTH]; WINSTATIONINFORMATIONW *pPassedQueryBuf; WINSTATIONINFORMATIONW QueryBuffer;
#ifdef COLLECT_TIME
DWORD StartTick = GetTickCount(); #endif
// DBGPRINT(("PerfTS: Collect() called\n"));
pWinStationDataDefinition = (WINSTATION_DATA_DEFINITION *)*ppData;
// Check for sufficient space for base WinStation object info and
// as many instances as we currently have in our WinStation database.
// Add in DWORD sizes for potential QWORD alignments.
TotalLen = sizeof(WINSTATION_DATA_DEFINITION) + sizeof(DWORD) + sizeof(TERMSERVER_DATA_DEFINITION) + sizeof(TERMSERVER_COUNTER_DATA) + sizeof(DWORD) + NumCachedWinStations * WinStationInstanceSize; if (*pTotalBytes >= TotalLen) { // Grab the latest system process information.
Result = GetSystemProcessData(); if (Result == ERROR_SUCCESS) { // Copy WinStation counter definitions.
memcpy(pWinStationDataDefinition, &WinStationDataDefinition, sizeof(WINSTATION_DATA_DEFINITION)); pWinStationDataDefinition->WinStationObjectType.PerfTime = SysTimeInfo.CurrentTime; } else { DBGPRINT(("PerfTS: Failed to get process data\n")); goto ErrorExit; } } else { DBGPRINT(("PerfTS: Not enough space for base WinStation information\n")); Result = ERROR_MORE_DATA; goto ErrorExit; }
// Before we start, we have to transfer each WinStationInfo in the
// cache from the used list into an unused list to detect closed
// WinStations. Also, we need to zero each WSI's pInstanceInfo to detect
// whether we have retrieved the current I/O data for the WinStation.
pEntry = UsedList.Blink; (UsedList.Flink)->Blink = &UnusedList; // Patch up head links to UnusedList.
pEntry->Flink = &UnusedList; UnusedList = UsedList; InitializeListHead(&UsedList); pEntry = UnusedList.Flink; while (pEntry != &UnusedList) { pWSI = CONTAINING_RECORD(pEntry, WinStationInfo, UsedList); pWSI->pInstanceInfo = NULL; pEntry = pEntry->Flink; }
// Now collect data for each process, summing it for each unique SessionId.
ActiveWS = InactiveWS = 0; NumWinStationInstances = 0; ProcessBufferOffset = 0; pProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pProcessBuffer; pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *) &pWinStationDataDefinition[1];
while (TRUE) { // Check for live process (having a name, one or more threads, or
// not the Idle process (PID==0)). For WinStations we don't want to
// charge the Idle process to the console (session ID == 0).
if (pProcessInfo->ImageName.Buffer != NULL && pProcessInfo->NumberOfThreads > 0 && pProcessInfo->UniqueProcessId != 0) { // Get the session ID from the process. This is the same as the
// LogonID in TS4.
SessionId = pProcessInfo->SessionId;
// Find the session ID in the cache.
// We sum all processes seen for a given SessionId into the
// same WinStation instance data block.
pEntry = pWinStationHashBuckets[SessionId & WinStationHashMask]. Flink; while (pEntry != &pWinStationHashBuckets[SessionId & WinStationHashMask]) { pWSI = CONTAINING_RECORD(pEntry, WinStationInfo, HashBucketList); if (pWSI->SessionID == SessionId) { // Found it. Now check that we've retrieved the WS info.
if (pWSI->pInstanceInfo != NULL) { // Context is the WINSTATION_COUNTER_DATA entry
// for this SessionId.
pWSC = (WINSTATION_COUNTER_DATA *)pWSI->pInstanceInfo;
// Now add the values to the existing counter block.
UpdateWSProcessCounterBlock(pWSC, pProcessInfo); goto NextProcess; } break; } else { pEntry = pEntry->Flink; } }
// We have a new entry or one for which we have not gathered
// current information. First grab the info.
if (WinStationQueryInformationW(SERVERNAME_CURRENT, SessionId, WinStationInformation, &QueryBuffer, sizeof(QueryBuffer), &AmountRet)) { if (QueryBuffer.ConnectState == State_Active) ActiveWS++; else InactiveWS++;
// Check for a pre-cached WSI with no stats.
if (pEntry != &pWinStationHashBuckets[SessionId & WinStationHashMask]) { // Verify the cached state (and thereby the name).
if (pWSI->LastState != QueryBuffer.ConnectState) { pWSI->LastState = QueryBuffer.ConnectState;
ConstructSessionName(pWSI, &QueryBuffer); }
// Remove the entry from the unused list, place on the
// used list.
RemoveEntryList(&pWSI->UsedList); InsertHeadList(&UsedList, &pWSI->UsedList); } else { // Alloc a new entry.
pWSI = ALLOCMEM(hLibHeap, 0, sizeof(WinStationInfo)); if (pWSI != NULL) { pWSI->SessionID = SessionId; pWSI->LastState = QueryBuffer.ConnectState; pWSI->pInstanceInfo = NULL; ConstructSessionName(pWSI, &QueryBuffer);
// Add to the used list.
InsertHeadList(&UsedList, &pWSI->UsedList);
// Add new entry. We may have to increase the
// number of hash buckets.
AddWinStationInfoToCache(pWSI); } else { DBGPRINT(("PerfTS: Could not alloc new " "WinStationInfo\n")); goto NextProcess; } }
InstanceName = pWSI->WinStationName; pPassedQueryBuf = &QueryBuffer; } else { // We have a WinStation Query problem.
DBGPRINT(("PerfTS: Failed WSQueryInfo(SessID=%u), error=%u\n", SessionId, GetLastError()));
// We could not open this WinStation, so we will identify
// it as "ID Unknown" using -1 to StrConnectState.
_ltow(SessionId, StringBuf, 10); wcsncat(StringBuf, L" ", 1); wcsncat(StringBuf, (const wchar_t *)StrConnectState(-1, TRUE), (MAX_SESSION_NAME_LENGTH - 1) - wcslen(StringBuf)); InstanceName = StringBuf;
pPassedQueryBuf = NULL; }
// Add space for new instance header, name, and set of counters
// to TotalLen and see if this instance will fit.
TotalLen += WinStationInstanceSize; if (*pTotalBytes >= TotalLen) { NumWinStationInstances++; } else { DBGPRINT(("PerfTS: Not enough space for a new instance " "(cur inst = %u)\n", NumWinStationInstances)); Result = ERROR_MORE_DATA; goto ErrorExitFixupUsedList; }
// MonBuildInstanceDefinition will create an instance of
// the given supplied name inside of the callers buffer
// supplied in pPerfInstanceDefinition. Our counter location
// (the next memory after the instance header and name) is
// returned in pWSC.
// By remembering this pointer, and its counter size, we
// can revisit it to add to the counters.
MonBuildInstanceDefinition(pPerfInstanceDefinition, (PVOID *)&pWSC, 0, 0, (DWORD)-1, InstanceName);
// Initialize the new counter block.
SetupWinStationCounterBlock(pWSC, pPassedQueryBuf);
// Now set the Context to this counter block so if we
// see any more processes with this SessionId we
// can add to the existing counter block.
pWSI->pInstanceInfo = pWSC;
// Now load the values into the counter block
UpdateWSProcessCounterBlock(pWSC, pProcessInfo);
// set perfdata pointer to next byte if its a new entry
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)(pWSC + 1); }
NextProcess: // Exit if this was the last process in list
if (pProcessInfo->NextEntryOffset != 0) { // point to next buffer in list
ProcessBufferOffset += pProcessInfo->NextEntryOffset; pProcessInfo = (PSYSTEM_PROCESS_INFORMATION) &pProcessBuffer[ProcessBufferOffset]; } else { break; } }
// Check for unused WinStations and remove.
while (!IsListEmpty(&UnusedList)) { pEntry = RemoveHeadList(&UnusedList); pWSI = CONTAINING_RECORD(pEntry, WinStationInfo, UsedList); RemoveWinStationInfoFromCache(pWSI); FREEMEM(hLibHeap, 0, pWSI); }
// Note number of WinStation instances.
pWinStationDataDefinition->WinStationObjectType.NumInstances = NumWinStationInstances;
// Now we know how large an area we used for the
// WinStation definition, so we can update the offset
// to the next object definition. Align size on QWORD.
pTermServerDataDefinition = (TERMSERVER_DATA_DEFINITION *)( ALIGN_ON_QWORD(pPerfInstanceDefinition)); pWinStationDataDefinition->WinStationObjectType.TotalByteLength = (DWORD)((PCHAR)pTermServerDataDefinition - (PCHAR)pWinStationDataDefinition);
// Now we set up and fill in the data for the TermServer object,
// starting at the end of the WinStation instances.
// No instances here, just fill in headers.
memcpy(pTermServerDataDefinition, &TermServerDataDefinition, sizeof(TERMSERVER_DATA_DEFINITION)); pTermServerDataDefinition->TermServerObjectType.PerfTime = SysTimeInfo.CurrentTime; pTSC = (TERMSERVER_COUNTER_DATA *)(pTermServerDataDefinition + 1); pTSC->CounterBlock.ByteLength = sizeof(TERMSERVER_COUNTER_DATA); pTSC->NumActiveSessions = ActiveWS; pTSC->NumInactiveSessions = InactiveWS; pTSC->NumSessions = ActiveWS + InactiveWS;
// Return final sizes. Align final address on a QWORD size.
*ppData = ALIGN_ON_QWORD((LPVOID)(pTSC + 1)); pTermServerDataDefinition->TermServerObjectType.TotalByteLength = (DWORD)((PBYTE)*ppData - (PBYTE)pTermServerDataDefinition); *pTotalBytes = (DWORD)((PBYTE)*ppData - (PBYTE)pWinStationDataDefinition); *pNumObjectTypes = 2;
#if DBG
if (*pTotalBytes > TotalLen) DbgPrint ("PerfTS: Perf ctr. instance size underestimated: " "Est.=%u, Actual=%u", TotalLen, *pTotalBytes); #endif
#ifdef COLLECT_TIME
DbgPrint("*** Elapsed msec=%u\n", GetTickCount() - StartTick); #endif
return ERROR_SUCCESS;
// Error handling.
ErrorExitFixupUsedList: // We have to return the UnusedList entries to the used list and exit the
// cache lock.
while (!IsListEmpty(&UnusedList)) { pEntry = RemoveHeadList(&UnusedList); InsertHeadList(&UsedList, pEntry); }
ErrorExit: *pNumObjectTypes = 0; *pTotalBytes = 0; return Result; }
#define CalculatePercent(count, hits) ((count) ? (hits) * 100 / (count) : 0)
/****************************************************************************/ // SetupWinStationCounterBlock
//
// Initializes a new WinStation counter block.
//
// Args:
// pCounters (input)
// pointer to WinStation performance counter block
//
// pInfo (input)
// Pointer to WINSTATIONINFORMATION structure to extract counters from
//
// pNextByte (output)
// Returns the pointer to the byte beyound the end of the buffer.
/****************************************************************************/ void SetupWinStationCounterBlock( WINSTATION_COUNTER_DATA *pCounters, PWINSTATIONINFORMATIONW pInfo) { // Fill in the WinStation information if available.
if (pInfo != NULL) { PPROTOCOLCOUNTERS pi, po; PTHINWIRECACHE p; ULONG TotalReads = 0, TotalHits = 0; int i;
// Set all members of pCounters->pcd to zero since we are not going to
// init at this time. Then set the included PERF_COUNTER_BLOCK
// byte length.
memset(&pCounters->pcd, 0, sizeof(pCounters->pcd)); pCounters->pcd.CounterBlock.ByteLength = sizeof( WINSTATION_COUNTER_DATA);
pi = &pInfo->Status.Input; po = &pInfo->Status.Output;
// Copy input and output counters.
memcpy(&pCounters->Input, pi, sizeof(PROTOCOLCOUNTERS)); memcpy(&pCounters->Output, po, sizeof(PROTOCOLCOUNTERS));
// Calculate I/O totals.
pCounters->Total.WdBytes = pi->WdBytes + po->WdBytes; pCounters->Total.WdFrames = pi->WdFrames + po->WdFrames; pCounters->Total.Frames = pi->Frames + po->Frames; pCounters->Total.Bytes = pi->Bytes + po->Bytes; pCounters->Total.CompressedBytes = pi->CompressedBytes + po->CompressedBytes; pCounters->Total.CompressFlushes = pi->CompressFlushes + po->CompressFlushes; pCounters->Total.Errors = pi->Errors + po->Errors; pCounters->Total.Timeouts = pi->Timeouts + po->Timeouts; pCounters->Total.AsyncFramingError = pi->AsyncFramingError + po->AsyncFramingError; pCounters->Total.AsyncOverrunError = pi->AsyncOverrunError + po->AsyncOverrunError; pCounters->Total.AsyncOverflowError = pi->AsyncOverflowError + po->AsyncOverflowError; pCounters->Total.AsyncParityError = pi->AsyncParityError + po->AsyncParityError; pCounters->Total.TdErrors = pi->TdErrors + po->TdErrors;
// Display driver cache info.
// Bitmap cache.
p = &pInfo->Status.Cache.Specific.IcaCacheStats.ThinWireCache[0]; pCounters->DDBitmap.CacheReads = p->CacheReads; pCounters->DDBitmap.CacheHits = p->CacheHits; pCounters->DDBitmap.HitRatio = CalculatePercent(p->CacheReads, p->CacheHits); TotalReads += p->CacheReads; TotalHits += p->CacheHits;
// Glyph cache.
p = &pInfo->Status.Cache.Specific.IcaCacheStats.ThinWireCache[1]; pCounters->DDGlyph.CacheReads = p->CacheReads; pCounters->DDGlyph.CacheHits = p->CacheHits; pCounters->DDGlyph.HitRatio = CalculatePercent(p->CacheReads, p->CacheHits); TotalReads += p->CacheReads; TotalHits += p->CacheHits;
// Brush cache.
p = &pInfo->Status.Cache.Specific.IcaCacheStats.ThinWireCache[2]; pCounters->DDBrush.CacheReads = p->CacheReads; pCounters->DDBrush.CacheHits = p->CacheHits; pCounters->DDBrush.HitRatio = CalculatePercent(p->CacheReads, p->CacheHits); TotalReads += p->CacheReads; TotalHits += p->CacheHits;
// Save screen bitmap cache.
p = &pInfo->Status.Cache.Specific.IcaCacheStats.ThinWireCache[3]; pCounters->DDSaveScr.CacheReads = p->CacheReads; pCounters->DDSaveScr.CacheHits = p->CacheHits; pCounters->DDSaveScr.HitRatio = CalculatePercent(p->CacheReads, p->CacheHits); TotalReads += p->CacheReads; TotalHits += p->CacheHits;
// Cache totals.
pCounters->DDTotal.CacheReads = TotalReads; pCounters->DDTotal.CacheHits = TotalHits; pCounters->DDTotal.HitRatio = CalculatePercent(TotalReads, TotalHits);
// Compression PD ratios
pCounters->InputCompressionRatio = CalculatePercent( pi->CompressedBytes, pi->Bytes); pCounters->OutputCompressionRatio = CalculatePercent( po->CompressedBytes, po->Bytes); pCounters->TotalCompressionRatio = CalculatePercent( pi->CompressedBytes + po->CompressedBytes, pi->Bytes + po->Bytes); } else { // Set all the counters to zero and then the perf block length.
memset(pCounters, 0, sizeof(*pCounters)); pCounters->pcd.CounterBlock.ByteLength = sizeof( WINSTATION_COUNTER_DATA); } }
/****************************************************************************/ // UpdateWSProcessCounterBlock
//
// Add the entries for the given process to the supplied counter block
//
// Args:
// pCounters (input)
// pointer to WS performance counter block
//
// ProcessInfo (input)
// pointer to an NT SYSTEM_PROCESS_INFORMATION block
/****************************************************************************/ void UpdateWSProcessCounterBlock( WINSTATION_COUNTER_DATA *pCounters, PSYSTEM_PROCESS_INFORMATION pProcessInfo) { pCounters->pcd.PageFaults += pProcessInfo->PageFaultCount;
// User, Kernel and Processor Time counters need to be scaled by the
// number of processors.
pCounters->pcd.ProcessorTime += (pProcessInfo->KernelTime.QuadPart + pProcessInfo->UserTime.QuadPart) / NumberOfCPUs; pCounters->pcd.UserTime += pProcessInfo->UserTime.QuadPart / NumberOfCPUs; pCounters->pcd.KernelTime += pProcessInfo->KernelTime.QuadPart / NumberOfCPUs;
pCounters->pcd.PeakVirtualSize += pProcessInfo->PeakVirtualSize; pCounters->pcd.VirtualSize += pProcessInfo->VirtualSize; pCounters->pcd.PeakWorkingSet += pProcessInfo->PeakWorkingSetSize; pCounters->pcd.TotalWorkingSet += pProcessInfo->WorkingSetSize; pCounters->pcd.PeakPageFile += pProcessInfo->PeakPagefileUsage; pCounters->pcd.PageFile += pProcessInfo->PagefileUsage; pCounters->pcd.PrivatePages += pProcessInfo->PrivatePageCount; pCounters->pcd.ThreadCount += pProcessInfo->NumberOfThreads; // BasePriority, ElapsedTime, ProcessId, CreatorProcessId not totaled.
pCounters->pcd.PagedPool += (DWORD)pProcessInfo->QuotaPagedPoolUsage; pCounters->pcd.NonPagedPool += (DWORD)pProcessInfo->QuotaNonPagedPoolUsage; pCounters->pcd.HandleCount += (DWORD)pProcessInfo->HandleCount; // I/O counts not totaled at this time.
}
|