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.
 
 
 
 
 
 

8624 lines
289 KiB

/*++ BUILD Version: 0001 // Increment this if a change has global effects
Copyright (c) 1992-1994 Microsoft Corporation
Module Name:
perflib.c
Abstract:
This file implements the Configuration Registry
for the purposes of the Performance Monitor.
This file contains the code which implements the Performance part
of the Configuration Registry.
Author:
Russ Blake 11/15/91
Revision History:
04/20/91 - russbl - Converted to lib in Registry
from stand-alone .dll form.
11/04/92 - a-robw - added pagefile and image counter routines
--*/
#define UNICODE
//
// Include files
//
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntpsapi.h>
#include <ntdddisk.h>
#include <ntregapi.h>
#include <ntioapi.h>
#include <ntprfctr.h>
#include <windows.h>
#include <shellapi.h>
#include <lmcons.h>
#include <lmerr.h>
#include <lmapibuf.h>
#include <lmwksta.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <winperf.h>
#include <rpc.h>
#include <ntddnfs.h>
#include <srvfsctl.h>
#include "regrpc.h"
#include "ntconreg.h"
#include "ntmon.h"
#include "lmbrowsr.h"
#include "lmaccess.h"
#include "module.h"
#include "perfsec.h"
#include "prflbmsg.h" // event log messages
//
// Constant data structures defining NT data
//
extern SYSTEM_DATA_DEFINITION SystemDataDefinition;
extern PROCESSOR_DATA_DEFINITION ProcessorDataDefinition;
extern MEMORY_DATA_DEFINITION MemoryDataDefinition;
extern CACHE_DATA_DEFINITION CacheDataDefinition;
extern PROCESS_DATA_DEFINITION ProcessDataDefinition;
extern THREAD_DATA_DEFINITION ThreadDataDefinition;
extern PDISK_DATA_DEFINITION PhysicalDiskDataDefinition;
extern LDISK_DATA_DEFINITION LogicalDiskDataDefinition;
extern OBJECTS_DATA_DEFINITION ObjectsDataDefinition;
extern RDR_DATA_DEFINITION RdrDataDefinition;
extern SRV_DATA_DEFINITION SrvDataDefinition;
extern SRVQ_DATA_DEFINITION SrvQDataDefinition;
extern PAGEFILE_DATA_DEFINITION PagefileDataDefinition;
extern IMAGE_DATA_DEFINITION ImageDataDefinition;
extern EXPROCESS_DATA_DEFINITION ExProcessDataDefinition;
extern THREAD_DETAILS_DATA_DEFINITION ThreadDetailsDataDefinition;
extern BROWSER_DATA_DEFINITION BrowserDataDefinition;
extern WCHAR DefaultLangId[];
extern WCHAR NativeLangId[4];
extern NTSTATUS
PerfGetNames (
DWORD QueryType,
PUNICODE_STRING lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPDWORD lpcbLen,
LPWSTR lpLangId
);
LONG
PerfEnumTextValue (
IN HKEY hKey,
IN DWORD dwIndex,
OUT PUNICODE_STRING lpValueName,
OUT LPDWORD lpReserved OPTIONAL,
OUT LPDWORD lpType OPTIONAL,
OUT LPBYTE lpData,
IN OUT LPDWORD lpcbData,
OUT LPDWORD lpcbLen OPTIONAL
);
BOOL
MonBuildPerfDataBlock(
PERF_DATA_BLOCK *pBuffer,
PVOID *pBufferNext,
DWORD NumObjectTypes,
DWORD DefaultObject
);
PUNICODE_STRING
GetProcessShortName (
PSYSTEM_PROCESS_INFORMATION pProcess
);
//
// The following special defines are used to produce numbers for
// cache measurement counters
//
#define SYNC_ASYNC(FLD) ((SysPerfInfo.FLD##Wait) + (SysPerfInfo.FLD##NoWait))
//
// Hit Rate Macro
//
#define HITRATE(FLD) (((Changes = SysPerfInfo.FLD) == 0) ? 0 : \
((Changes < (Misses = SysPerfInfo.FLD##Miss)) ? 0 : \
(Changes - Misses) ))
//
// Hit Rate Macro combining Sync and Async cases
//
#define SYNC_ASYNC_HITRATE(FLD) (((Changes = SYNC_ASYNC(FLD)) == 0) ? 0 : \
((Changes < \
(Misses = SysPerfInfo.FLD##WaitMiss + \
SysPerfInfo.FLD##NoWaitMiss) \
) ? 0 : \
(Changes - Misses) ))
#define GUARD_PAGE_SIZE 1024
#define GUARD_PAGE_CHAR 0xA5
#define GUARD_PAGE_DWORD 0xA5A5A5A5
// test for delimiter, end of line and non-digit characters
// used by IsNumberInUnicodeList routine
//
#define DIGIT 1
#define DELIMITER 2
#define INVALID 3
#define EvalThisChar(c,d) ( \
(c == d) ? DELIMITER : \
(c == 0) ? DELIMITER : \
(c < '0') ? INVALID : \
(c > '9') ? INVALID : \
DIGIT)
//
// The next table holds pointers to functions which provide data
// It is one greater than the number of built in providers, to
// accomodate a call to get "Extensible" object types.
//
WCHAR GLOBAL_STRING[] = L"GLOBAL";
WCHAR FOREIGN_STRING[] = L"FOREIGN";
WCHAR COSTLY_STRING[] = L"COSTLY";
WCHAR COUNTER_STRING[] = L"COUNTER";
WCHAR HELP_STRING[] = L"EXPLAIN";
WCHAR HELP_STRING2[] = L"HELP";
WCHAR ADDCOUNTER_STRING[] = L"ADDCOUNTER";
WCHAR ADDHELP_STRING[] = L"ADDEXPLAIN";
#define MAX_KEYWORD_LEN (sizeof (ADDHELP_STRING) / sizeof(WCHAR))
#define QUERY_GLOBAL 1
#define QUERY_ITEMS 2
#define QUERY_FOREIGN 3
#define QUERY_COSTLY 4
#define QUERY_COUNTER 5
#define QUERY_HELP 6
#define QUERY_ADDCOUNTER 7
#define QUERY_ADDHELP 8
WCHAR NULL_STRING[] = L"\0"; // pointer to null string
LPWSTR DEFAULT_TOTAL_STRING = {L"_Total"};
#define DEFAULT_TOTAL_STRING_LEN 12 // = (len("_Total") * sizeof (WCHAR)
UNICODE_STRING usTotal = {0,0, NULL};
#define MAX_TOTAL_NAME_LENGTH 64
WCHAR wcTotalString[MAX_TOTAL_NAME_LENGTH];
#define LargeIntegerLessThanOrEqualZero(X) ((X).QuadPart <= 0)
BOOL bOldestProcessTime;
LARGE_INTEGER OldestProcessTime;
DATA_PROVIDER_ITEM
DataFuncs[NT_NUM_PERF_OBJECT_TYPES + 1] = { {SYSTEM_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QuerySystemData
},
{PROCESSOR_OBJECT_TITLE_INDEX,
SYSTEM_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryProcessorData
},
{MEMORY_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryMemoryData
},
{CACHE_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryCacheData
},
{PHYSICAL_DISK_OBJECT_TITLE_INDEX,
LOGICAL_DISK_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryPhysicalDiskData
},
{LOGICAL_DISK_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryLogicalDiskData
},
// QueryProcessData must be
// called before QueryThreadData
//
{PROCESS_OBJECT_TITLE_INDEX,
THREAD_OBJECT_TITLE_INDEX,
SYSTEM_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryProcessData
},
{THREAD_OBJECT_TITLE_INDEX,
SYSTEM_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryThreadData
},
{OBJECT_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryObjectsData
},
{REDIRECTOR_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryRdrData
},
{SERVER_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QuerySrvData
},
{SERVER_QUEUE_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QuerySrvQueueData
},
{PAGEFILE_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryPageFileData
},
{BROWSER_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryBrowserData
},
{EXTENSIBLE_OBJECT_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryExtensibleData
}
};
DATA_PROVIDER_ITEM
CostlyFuncs[NT_NUM_COSTLY_OBJECT_TYPES + 1] = {
{EXPROCESS_OBJECT_TITLE_INDEX,
IMAGE_OBJECT_TITLE_INDEX,
LONG_IMAGE_OBJECT_TITLE_INDEX,
THREAD_DETAILS_OBJECT_TITLE_INDEX,
QueryExProcessData
},
{IMAGE_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryImageData
},
{LONG_IMAGE_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryLongImageData
},
{THREAD_DETAILS_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryThreadDetailData
},
{EXTENSIBLE_OBJECT_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
NULL_OBJECT_TITLE_INDEX,
QueryExtensibleData
}
};
//
// The next global is used to count concurrent opens. When these
// are 0, all handles to disk and LAN devices are closed, all
// large dynamic data areas are freed, and it is possible to
// install LAN devices and transports.
//
DWORD NumberOfOpens = 0;
//
// Here is an array of the Value names under the predefined handle
// HKEY_PERFORMANCE_DATA
//
// minimum length to hold a value name understood by Perflib
#define VALUE_NAME_LENGTH ((sizeof(COSTLY_STRING) * sizeof(WCHAR)) + sizeof(UNICODE_NULL))
// VA Info Globals
WCHAR IDLE_PROCESS[] = L"Idle";
WCHAR SYSTEM_PROCESS[] = L"System";
PPROCESS_VA_INFO pFirstProcessVaItem;
//
// Synchronization objects for Multi-threaded access
//
HANDLE hDataSemaphore = NULL; // global handle for access by multiple threads
#define QUERY_WAIT_TIME 10000L // wait time for query semaphore (in ms)
#define OPEN_PROC_WAIT_TIME 10000L // defaul wait time for open proc to finish (in ms)
static DWORD dwExtCtrOpenProcWaitMs = OPEN_PROC_WAIT_TIME;
// convert mS to relative time
#define MakeTimeOutValue(ms) \
(Int32x32To64 ((ms), (-10000L)))
//
// performance gathering thead priority
//
#define DEFAULT_THREAD_PRIORITY THREAD_BASE_PRIORITY_LOWRT
//
// Collect system performance information into the following structures:
//
SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
SYSTEM_BASIC_INFORMATION BasicInfo;
UCHAR *pProcessorBuffer;
ULONG ProcessorBufSize;
UNICODE_STRING ProcessName;
DWORD ComputerNameLength;
LPWSTR pComputerName;
SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo;
PSYSTEM_PAGEFILE_INFORMATION pSysPageFileInfo = NULL;
DWORD dwSysPageFileInfoSize; // size of page file info array
SYSTEM_INTERRUPT_INFORMATION *pProcessorInterruptInformation = NULL;
PPROCESS_VA_INFO pProcessVaInfo = NULL;
//
// This is a global pointer to the start of the data collection area.
//
PERF_DATA_BLOCK *pPerfDataBlock;
//
// Use the following to collect multi-processor data across the
// system. These pointers are initialized in QuerySystemData, and
// updated in QueryProcessorData as the data for each instance is
// retrieved.
//
PDWORD pTotalInterrupts;
LARGE_INTEGER UNALIGNED *pTotalProcessorTime;
LARGE_INTEGER UNALIGNED *pTotalUserTime;
LARGE_INTEGER UNALIGNED *pTotalPrivilegedTime;
LARGE_INTEGER UNALIGNED *pTotalDpcTime;
LARGE_INTEGER UNALIGNED *pTotalInterruptTime;
DWORD *pTotalDpcCount;
DWORD *pTotalDpcRate;
DWORD *pTotalDpcBypassCount;
DWORD *pTotalApcBypassCount;
// Use the following to collect ProcessorQueueLength data.
// These pointers are initialized in QueueSystemData and
// updated in QueryThreadData if Thread data is requested.
// After QueryThreadData updated ProcessorQueueLen, it will
// increment the number of system counters by 1.
PDWORD pdwProcessorQueueLength;
// This flag is to ensure that we are not getting the
// SystemProcessInfo too many times.
// It is clear in PerfRegQueryValue and check in QueryProcessData
BOOL bGotProcessInfo;
//
// Use the following to collect process/thread data
//
UCHAR *pProcessBuffer;
ULONG ProcessBufSize;
// The following is set in QueryProcessData and used for parent
// information in QueryThreadData
PROCESS_DATA_DEFINITION *pProcessDataDefinition;
// Information for collecting disk driver statistics
#define NUMDISKS (NumPhysicalDisks+NumLogicalDisks)
//
// Retain information about disks collected at initialization here:
//
pDiskDevice DiskDevices; // Points to an array of structures
// identifying first all physcial
// disks, followed by all logical disks.
DWORD NumPhysicalDisks; // This starts out as an index, but
// ends up as a count of the number
// of physical disks.
DWORD NumLogicalDisks; // This starts out as an index, but
// ends up as a count of the number
// of logical disks.
WCHAR PhysicalDrive[] = L"\\DosDevices\\PhysicalDrive";
WCHAR LogicalDisk[] = L"\\DosDevices\\ :";
#define DRIVE_LETTER_OFFSET 12
WCHAR HardDiskTemplate[] = L"\\DEVICE\\HARDDISK";
#define DISK_NAME_LENGTH (4 * sizeof(WCHAR))
// This data item is shared: it is set by the
// QueryPhysicalDiskData function and used by the
// QueryLogicalDiskData function to pass the title
// index for physcial disks.
PDISK_DATA_DEFINITION *pPhysicalDiskDataDefinition;
// These handles are set in OpenPerformanceData, and used by
// QueryObjectsData to obtain object counters.
HANDLE hEvent;
HANDLE hMutex;
HANDLE hSemaphore;
HANDLE hSection;
// These handles are used to collect data for the Redirector and
// the server.
HANDLE hRdr;
HANDLE hSrv;
// The next pointer is used to point to an array of addresses of
// Open/Collect/Close routines found by searching the Configuration Registry.
pExtObject ExtensibleObjects;
DWORD NumExtensibleObjects; // Number of Extensible Objects
//
// pointer to reusable buffer for process names.
//
static PUNICODE_STRING pusLocalProcessNameBuffer = NULL;
//
// handle to Event Log for logging errors
//
static HANDLE hEventLog = NULL;
//
// Value to decide if process names should be collected from:
// the SystemProcessInfo structure (fastest)
// -- or --
// the process's image file (slower, but shows Unicode filenames)
//
LONG lProcessNameCollectionMethod = PNCM_NOT_DEFINED;
//
// see if the perflib data is restricted to ADMIN's ONLY or just anyone
//
LONG lCheckProfileSystemRight = CPSR_NOT_DEFINED;
//
// flag to see if the ProfileSystemPerformance priv should be set.
// if it is attempted and the caller does not have permission to use this priv.
// it won't be set. This is only attempted once.
//
BOOL bEnableProfileSystemPerfPriv = FALSE;
//
// flag to determine the "noisiness" of the event logging
// this value is read from the system registry when the extensible
// objects are loaded and used for the subsequent calls.
//
//
// Levels: LOG_UNDEFINED = registry log level not read yet
// LOG_NONE = No event log messages ever
// LOG_USER = User event log messages (e.g. errors)
// LOG_DEBUG = Minimum Debugging (warnings & errors)
// LOG_VERBOSE = Maximum Debugging (informational, success,
// error and warning messages
//
#define LOG_UNDEFINED ((LONG)-1)
#define LOG_NONE 0
#define LOG_USER 1
#define LOG_DEBUG 2
#define LOG_VERBOSE 3
LONG lEventLogLevel = LOG_UNDEFINED;
//
// define configurable extensible counter buffer testing
//
// Test Level Event that will prevent data buffer
// from being returne in PerfDataBlock
//
// EXT_TEST_NONE Collect Fn. Returns bad status or generates exception
// EXT_TEST_BASIC Collect Fn. has buffer overflow or violates guard page
// EXT_TEST_ALL Collect Fn. object or instance lengths are not conistent
//
//
#define EXT_TEST_UNDEFINED 0
#define EXT_TEST_ALL 1
#define EXT_TEST_BASIC 2
#define EXT_TEST_NONE 3
LONG lExtCounterTestLevel = EXT_TEST_UNDEFINED;
// BrowserStatFunction is used for collecting Browser Statistic Data
typedef NET_API_STATUS
(*PBROWSERQUERYSTATISTIC) (
IN LPTSTR servername OPTIONAL,
OUT LPBROWSER_STATISTICS *statistics
);
PBROWSERQUERYSTATISTIC BrowserStatFunction;
// structure for passing to extensible counter open procedure wait thread
typedef struct _OPEN_PROC_WAIT_INFO {
HANDLE hEvent;
HANDLE hDataSemaphore;
LPTSTR szLibraryName;
LPTSTR szServiceName;
DWORD dwWaitTime;
} OPEN_PROC_WAIT_INFO, FAR * LPOPEN_PROC_WAIT_INFO;
//
// Perflib functions:
//
static
LONG
GetPerflibKeyValue (
LPWSTR szItem,
DWORD dwRegType,
DWORD dwMaxSize, // ... of pReturnBuffer in bytes
LPVOID pReturnBuffer,
DWORD dwDefaultSize, // ... of pDefault in bytes
LPVOID pDefault
)
/*++
read and return the current value of the specified value
under the Perflib registry key. If unable to read the value
return the default value from the argument list.
the value is returned in the pReturnBuffer.
--*/
{
HKEY hPerflibKey;
OBJECT_ATTRIBUTES Obja;
NTSTATUS Status;
UNICODE_STRING PerflibSubKeyString;
UNICODE_STRING ValueNameString;
LONG lReturn;
PKEY_VALUE_PARTIAL_INFORMATION pValueInformation;
LONG ValueBufferLength;
LONG ResultLength;
BOOL bUseDefault = TRUE;
// initialize UNICODE_STRING structures used in this function
RtlInitUnicodeString (
&PerflibSubKeyString,
L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib");
RtlInitUnicodeString (
&ValueNameString,
szItem);
//
// Initialize the OBJECT_ATTRIBUTES structure and open the key.
//
InitializeObjectAttributes(
&Obja,
&PerflibSubKeyString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenKey(
&hPerflibKey,
KEY_READ,
&Obja
);
if (NT_SUCCESS( Status )) {
// read value of desired entry
ValueBufferLength = ResultLength = 1024;
pValueInformation = ALLOCMEM(RtlProcessHeap(), 0, ResultLength);
if (pValueInformation != NULL) {
while ( (Status = NtQueryValueKey(hPerflibKey,
&ValueNameString,
KeyValuePartialInformation,
pValueInformation,
ValueBufferLength,
&ResultLength))
== STATUS_BUFFER_OVERFLOW ) {
pValueInformation = REALLOCMEM(RtlProcessHeap(), 0,
pValueInformation,
ResultLength);
if ( pValueInformation == NULL) {
break;
} else {
ValueBufferLength = ResultLength;
}
}
if (NT_SUCCESS(Status)) {
// check to see if it's the desired type
if (pValueInformation->Type == dwRegType) {
// see if it will fit
if (pValueInformation->DataLength <= dwMaxSize) {
memcpy (pReturnBuffer, &pValueInformation->Data[0],
pValueInformation->DataLength);
bUseDefault = FALSE;
lReturn = STATUS_SUCCESS;
}
}
} else {
// return the default value
lReturn = Status;
}
// release temp buffer
FREEMEM (RtlProcessHeap(), 0, pValueInformation);
} else {
// unable to allocate memory for this operation so
// just return the default value
}
// close the registry key
NtClose(hPerflibKey);
} else {
// return default value
}
if (bUseDefault) {
memcpy (pReturnBuffer, pDefault, dwDefaultSize);
lReturn = STATUS_SUCCESS;
}
return lReturn;
}
BOOL
MatchString (
IN LPWSTR lpValue,
IN LPWSTR lpName
)
/*++
MatchString
return TRUE if lpName is in lpValue. Otherwise return FALSE
Arguments
IN lpValue
string passed to PerfRegQuery Value for processing
IN lpName
string for one of the keyword names
Return TRUE | FALSE
--*/
{
BOOL bFound;
bFound = TRUE; // assume found until contradicted
// check to the length of the shortest string
while ((*lpValue != 0) && (*lpName != 0)) {
if (*lpValue++ != *lpName++) {
bFound = FALSE; // no match
break; // bail out now
}
}
return (bFound);
}
DWORD
GetQueryType (
IN LPWSTR lpValue
)
/*++
GetQueryType
returns the type of query described in the lpValue string so that
the appropriate processing method may be used
Arguments
IN lpValue
string passed to PerfRegQuery Value for processing
Return Value
QUERY_GLOBAL
if lpValue == 0 (null pointer)
lpValue == pointer to Null string
lpValue == pointer to "Global" string
QUERY_FOREIGN
if lpValue == pointer to "Foriegn" string
QUERY_COSTLY
if lpValue == pointer to "Costly" string
QUERY_COUNTER
if lpValue == pointer to "Counter" string
QUERY_HELP
if lpValue == pointer to "Explain" string
QUERY_ADDCOUNTER
if lpValue == pointer to "Addcounter" string
QUERY_ADDHELP
if lpValue == pointer to "Addexplain" string
otherwise:
QUERY_ITEMS
--*/
{
WCHAR LocalBuff[MAX_KEYWORD_LEN+1];
int i;
if (lpValue == 0 || *lpValue == 0)
return QUERY_GLOBAL;
// convert the input string to Upper case before matching
for (i=0; i < MAX_KEYWORD_LEN; i++) {
if (*lpValue == TEXT(' ') || *lpValue == TEXT('\0')) {
break;
}
LocalBuff[i] = *lpValue ;
if (*lpValue >= TEXT('a') && *lpValue <= TEXT('z')) {
LocalBuff[i] = LocalBuff[i] - TEXT('a') + TEXT('A');
}
lpValue++ ;
}
LocalBuff[i] = TEXT('\0');
// check for "Global" request
if (MatchString (LocalBuff, GLOBAL_STRING))
return QUERY_GLOBAL ;
// check for "Foreign" request
if (MatchString (LocalBuff, FOREIGN_STRING))
return QUERY_FOREIGN ;
// check for "Costly" request
if (MatchString (LocalBuff, COSTLY_STRING))
return QUERY_COSTLY;
// check for "Counter" request
if (MatchString (LocalBuff, COUNTER_STRING))
return QUERY_COUNTER;
// check for "Help" request
if (MatchString (LocalBuff, HELP_STRING))
return QUERY_HELP;
if (MatchString (LocalBuff, HELP_STRING2))
return QUERY_HELP;
// check for "AddCounter" request
if (MatchString (LocalBuff, ADDCOUNTER_STRING))
return QUERY_ADDCOUNTER;
// check for "AddHelp" request
if (MatchString (LocalBuff, ADDHELP_STRING))
return QUERY_ADDHELP;
// None of the above, then it must be an item list
return QUERY_ITEMS;
}
BOOL
IsNumberInUnicodeList (
IN DWORD dwNumber,
IN LPWSTR lpwszUnicodeList
)
/*++
IsNumberInUnicodeList
Arguments:
IN dwNumber
DWORD number to find in list
IN lpwszUnicodeList
Null terminated, Space delimited list of decimal numbers
Return Value:
TRUE:
dwNumber was found in the list of unicode number strings
FALSE:
dwNumber was not found in the list.
--*/
{
DWORD dwThisNumber;
WCHAR *pwcThisChar;
BOOL bValidNumber;
BOOL bNewItem;
WCHAR wcDelimiter; // could be an argument to be more flexible
if (lpwszUnicodeList == 0) return FALSE; // null pointer, # not founde
pwcThisChar = lpwszUnicodeList;
dwThisNumber = 0;
wcDelimiter = (WCHAR)' ';
bValidNumber = FALSE;
bNewItem = TRUE;
while (TRUE) {
switch (EvalThisChar (*pwcThisChar, wcDelimiter)) {
case DIGIT:
// if this is the first digit after a delimiter, then
// set flags to start computing the new number
if (bNewItem) {
bNewItem = FALSE;
bValidNumber = TRUE;
}
if (bValidNumber) {
dwThisNumber *= 10;
dwThisNumber += (*pwcThisChar - (WCHAR)'0');
}
break;
case DELIMITER:
// a delimter is either the delimiter character or the
// end of the string ('\0') if when the delimiter has been
// reached a valid number was found, then compare it to the
// number from the argument list. if this is the end of the
// string and no match was found, then return.
//
if (bValidNumber) {
if (dwThisNumber == dwNumber) return TRUE;
bValidNumber = FALSE;
}
if (*pwcThisChar == 0) {
return FALSE;
} else {
bNewItem = TRUE;
dwThisNumber = 0;
}
break;
case INVALID:
// if an invalid character was encountered, ignore all
// characters up to the next delimiter and then start fresh.
// the invalid number is not compared.
bValidNumber = FALSE;
break;
default:
break;
}
pwcThisChar++;
}
} // IsNumberInUnicodeList
#if 0
//
// Routines to build the data structures returned by the Resgitry
//
BOOL
QueryPerformanceCounter(
LARGE_INTEGER *lpPerformanceCount
)
/*++
QueryPerformanceCounter - fakes out missing 32-bit call
Inputs:
lpPerformanceCount - a pointer to variable which
will receive the count
--*/
{
LARGE_INTEGER PerfFreq;
return NtQueryPerformanceCounter(lpPerformanceCount,&PerfFreq);
}
BOOL
QueryPerformanceFrequency(
LARGE_INTEGER *lpFrequency
)
/*++
QueryPerformanceFrequency - fakes out missing 32-bit call
Inputs:
lpFrequency - a pointer to variable which
will receive the frequency
--*/
{
LARGE_INTEGER PerfCount;
return NtQueryPerformanceCounter(&PerfCount,lpFrequency);
}
#endif
LONG
PerfRegQueryValue (
IN HKEY hKey,
IN PUNICODE_STRING lpValueName,
OUT LPDWORD lpReserved OPTIONAL,
OUT LPDWORD lpType OPTIONAL,
OUT LPBYTE lpData,
OUT LPDWORD lpcbData,
OUT LPDWORD lpcbLen OPTIONAL
)
/*++
PerfRegQueryValue - Get data
Inputs:
hKey - Predefined handle to open remote
machine
lpValueName - Name of the value to be returned;
could be "ForeignComputer:<computername>
or perhaps some other objects, separated
by ~; must be Unicode string
lpReserved - should be omitted (NULL)
lpType - should be omitted (NULL)
lpData - pointer to a buffer to receive the
performance data
lpcbData - pointer to a variable containing the
size in bytes of the output buffer;
on output, will receive the number
of bytes actually returned
lpcbLen - Return the number of bytes to transmit to
the client (used by RPC) (optional).
Return Value:
DOS error code indicating status of call or
ERROR_SUCCESS if all ok
--*/
{
DWORD dwQueryType; // type of request
DWORD TotalLen; // Length of the total return block
DWORD Win32Error; // Failure code
DWORD NumFunction; // Data provider index
LPVOID pDataDefinition; // Pointer to next object definition
BOOL bCallThisItem;
DWORD dwItem;
PDWORD pdwItem;
LONG dwPrevCount;
DWORD dwReturnedBufferSize;
LARGE_INTEGER liQueryWaitTime ;
THREAD_BASIC_INFORMATION tbiData;
LONG lOldPriority, lNewPriority;
RPC_HKEY hKeyDummy; // dummy arg for open call
NTSTATUS status;
BOOL bCheckCostlyCalls = FALSE;
BOOL bThreadPriv = FALSE;
LPWSTR lpLangId = NULL;
if ( 0 ) {
// DBG_UNREFERENCED_PARAMETER(hKey);
DBG_UNREFERENCED_PARAMETER(lpReserved);
}
HEAP_PROBE();
// make sure the name collection method has been defined
if (lProcessNameCollectionMethod == PNCM_NOT_DEFINED) {
// get desired process name collection method as defined in the
// registry
lProcessNameCollectionMethod = GetProcessNameColMeth ();
}
if (!TestClientForAccess (&bThreadPriv, KEY_READ)) {
if (lEventLogLevel >= LOG_USER) {
LPTSTR szMessageArray[2];
TCHAR szUserName[128];
TCHAR szModuleName[MAX_PATH];
DWORD dwUserNameLength;
dwUserNameLength = sizeof(szUserName)/sizeof(TCHAR);
GetUserName (szUserName, &dwUserNameLength);
GetModuleFileName (NULL, szModuleName,
sizeof(szModuleName)/sizeof(TCHAR));
szMessageArray[0] = szUserName;
szMessageArray[1] = szModuleName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_ACCESS_DENIED, // event,
NULL, // SID (not used),
2, // number of strings
0, // sizeof raw data
szMessageArray, // message text array
NULL); // raw data
}
return ERROR_ACCESS_DENIED;
}
status = NtQueryInformationThread (
NtCurrentThread(),
ThreadBasicInformation,
&tbiData,
sizeof(tbiData),
NULL);
if (status != STATUS_SUCCESS) {
KdPrint (("\nPERFLIB: Unable to read current thread priority: 0x%8.8x", status));
lOldPriority = -1;
} else {
lOldPriority = tbiData.Priority;
}
lNewPriority = DEFAULT_THREAD_PRIORITY; // perfmon's favorite priority
//
// Only RAISE the priority here. Don't lower it if it's high
//
if ((lOldPriority > 0) && (lOldPriority < lNewPriority)) {
status = NtSetInformationThread(
NtCurrentThread(),
ThreadPriority,
&lNewPriority,
sizeof(lNewPriority)
);
if (status != STATUS_SUCCESS) {
KdPrint (("\nPERFLIB: Set Thread Priority failed: 0x%8.8x", status));
lOldPriority = -1;
}
} else {
lOldPriority = -1; // to save resetting at the end
}
//
// Set the length parameter to zero so that in case of an error,
// nothing will be transmitted back to the client and the client won't
// attempt to unmarshall anything.
//
if( ARGUMENT_PRESENT( lpcbLen )) {
*lpcbLen = 0;
}
/*
determine query type, can be one of the following
Global
get all objects
List
get objects in list (lpValueName)
Foreign Computer
call extensible Counter Routine only
Costly
costly object items
Counter
get counter names for the specified language Id
Help
get help names for the specified language Id
*/
dwQueryType = GetQueryType (lpValueName->Buffer);
if (dwQueryType == QUERY_COUNTER || dwQueryType == QUERY_HELP ||
dwQueryType == QUERY_ADDCOUNTER || dwQueryType == QUERY_ADDHELP ) {
if (hKey == HKEY_PERFORMANCE_DATA) {
lpLangId = NULL;
} else if (hKey == HKEY_PERFORMANCE_TEXT) {
lpLangId = DefaultLangId;
} else if (hKey == HKEY_PERFORMANCE_NLSTEXT) {
lpLangId = NativeLangId;
if (*lpLangId == L'\0') {
// build the native language id
LANGID iLanguage;
int NativeLanguage;
iLanguage = GetUserDefaultLangID();
NativeLanguage = MAKELANGID (iLanguage & 0x0ff, LANG_NEUTRAL);
NativeLangId[0] = NativeLanguage / 256 + L'0';
NativeLanguage %= 256;
NativeLangId[1] = NativeLanguage / 16 + L'0';
NativeLangId[2] = NativeLanguage % 16 + L'0';
NativeLangId[3] = L'\0';
}
}
status = PerfGetNames (
dwQueryType,
lpValueName,
lpData,
lpcbData,
lpcbLen,
lpLangId);
if (!NT_SUCCESS(status)) {
if (status != ERROR_MORE_DATA) {
status = (error_status_t)RtlNtStatusToDosError(status);
}
}
if (ARGUMENT_PRESENT (lpType)) { // test for optional value
*lpType = REG_MULTI_SZ;
}
goto PRQV_ErrorExit1;
}
if (!hDataSemaphore || ProcessBufSize == 0) {
// if a semaphore was not allocated or no buuffer for the Process,
// then the OPEN procedure was not called before this routine
// so call it now, then get the semaphore
KdPrint (("\nPERFLIB: Data Semaphore Not Initialized. Calling Open again"));
status = OpenPerformanceData (NULL, MAXIMUM_ALLOWED, &hKeyDummy);
if (status != ERROR_SUCCESS) {
return status;
}
}
// if here, then assume a Semaphore is available
// and the caller has the necessary access
liQueryWaitTime.QuadPart = MakeTimeOutValue(QUERY_WAIT_TIME);
status = NtWaitForSingleObject (
hDataSemaphore, // semaphore
FALSE, // not alertable
&liQueryWaitTime); // wait 'til timeout
if (status != STATUS_SUCCESS) {
return ERROR_BUSY;
}
//
// Get global data from system
//
status = NtQuerySystemInformation(
SystemPerformanceInformation,
&SysPerfInfo,
sizeof(SysPerfInfo),
&dwReturnedBufferSize
);
if (!NT_SUCCESS(status)) {
status = (error_status_t)RtlNtStatusToDosError(status);
goto PRQV_ErrorExit;
}
status = NtQuerySystemInformation(
SystemTimeOfDayInformation,
&SysTimeInfo,
sizeof(SysTimeInfo),
&dwReturnedBufferSize
);
if (!NT_SUCCESS(status)) {
status = (error_status_t)RtlNtStatusToDosError(status);
goto PRQV_ErrorExit;
}
//
// Initialize some global pointers
//
pTotalInterrupts = NULL;
pTotalProcessorTime = NULL;
pTotalUserTime = NULL;
pTotalPrivilegedTime = NULL;
pTotalInterruptTime = NULL;
pTotalDpcTime = NULL;
pTotalDpcCount = NULL;
pTotalDpcRate = NULL;
pTotalDpcBypassCount = NULL;
pTotalApcBypassCount = NULL;
//
// Format Return Buffer: start with basic data block
//
TotalLen = sizeof(PERF_DATA_BLOCK) +
((CNLEN+sizeof(UNICODE_NULL))*sizeof(WCHAR));
if ( *lpcbData < TotalLen ) {
status = ERROR_MORE_DATA;
goto PRQV_ErrorExit;
}
pPerfDataBlock = (PERF_DATA_BLOCK *)lpData;
// foreign data provider will return the perf data header
if (dwQueryType == QUERY_FOREIGN) {
// reset the values to avoid confusion
// *lpcbData = 0; // 0 bytes (removed to enable foreign computers)
pDataDefinition = (LPVOID)lpData;
memset (lpData, 0, sizeof (PERF_DATA_BLOCK)); // clear out header
} else {
MonBuildPerfDataBlock(pPerfDataBlock,
(PVOID *) &pDataDefinition,
0,
PROCESSOR_OBJECT_TITLE_INDEX);
}
Win32Error = ERROR_SUCCESS;
// collect expensive data if necessary
bGotProcessInfo = FALSE;
switch (dwQueryType) {
case QUERY_COSTLY:
bCheckCostlyCalls = TRUE;
break;
case QUERY_ITEMS:
// check if there is any costly object in the value list
for (NumFunction = 0;
NumFunction < NT_NUM_COSTLY_OBJECT_TYPES;
NumFunction++) {
pdwItem = &CostlyFuncs[NumFunction].ObjectIndex[0];
// loop through objects that can cause this function to be
// called and exit when a null object or a match are found.
for (dwItem = 0; dwItem < DP_ITEM_LIST_SIZE; dwItem++) {
if (*pdwItem == NULL_OBJECT_TITLE_INDEX) break; // give up
if (IsNumberInUnicodeList (*pdwItem, lpValueName->Buffer)) {
bCheckCostlyCalls = TRUE;
break;
}
pdwItem++; // point to next item
}
if (bCheckCostlyCalls) break;
}
break;
default:
break;
}
// setup for costly items
if ((!pProcessVaInfo) && bCheckCostlyCalls) {
//
// reset thread priority if collecting foreign counters
//
if (lOldPriority > 0) {
status = NtSetInformationThread(
NtCurrentThread(),
ThreadPriority,
&lOldPriority,
sizeof(lOldPriority)
);
lOldPriority = -1;
if (status != STATUS_SUCCESS) {
KdPrint (("\nPERFLIB: Reset Thread to Priority %d failed: 0x%8.8x",
lOldPriority, status));
}
}
//
// Get process data from system
//
while( (status = NtQuerySystemInformation(
SystemProcessInformation,
pProcessBuffer,
ProcessBufSize,
&dwReturnedBufferSize)) == STATUS_INFO_LENGTH_MISMATCH ) {
ProcessBufSize += INCREMENT_BUFFER_SIZE;
if ( !(pProcessBuffer = REALLOCMEM(RtlProcessHeap(), 0,
pProcessBuffer,
ProcessBufSize)) ) {
status = ERROR_OUTOFMEMORY;
goto PRQV_ErrorExit;
}
}
if ( !NT_SUCCESS(status) ) {
status = (error_status_t)RtlNtStatusToDosError(status);
goto PRQV_ErrorExit;
}
bGotProcessInfo = TRUE;
pProcessVaInfo = GetSystemVaData (
(PSYSTEM_PROCESS_INFORMATION)pProcessBuffer);
}
switch (dwQueryType) {
case QUERY_GLOBAL:
// get all "native" data & ext. obj
for ( NumFunction = 0;
NumFunction <= NT_NUM_PERF_OBJECT_TYPES;
NumFunction++ ) {
Win32Error = (*DataFuncs[NumFunction].DataProc) (
lpValueName->Buffer,
lpData,
lpcbData,
&pDataDefinition);
if (Win32Error) break;
}
break;
case QUERY_FOREIGN:
// just call extensible data with "Foreign" value
Win32Error = QueryExtensibleData (
lpValueName->Buffer,
lpData,
lpcbData,
&pDataDefinition);
break;
case QUERY_ITEMS:
// Initialize some pointers used in collecting System
// Processor Queue Length data.
pdwProcessorQueueLength = NULL;
// run through list of available routines and compare against
// list passed in calling only those referenced in the value
// arg. (as a list of unicode index numbers
//
for ( NumFunction = 0;
NumFunction < NT_NUM_PERF_OBJECT_TYPES; // no ext. obj yet
NumFunction++ ) {
bCallThisItem = FALSE;
pdwItem = &DataFuncs[NumFunction].ObjectIndex[0];
// loop through objects that can cause this function to be
// called and exit when a null object or a match are found.
for (dwItem = 0; dwItem < DP_ITEM_LIST_SIZE; dwItem++) {
if (*pdwItem == NULL_OBJECT_TITLE_INDEX) break; // give up
if (IsNumberInUnicodeList (*pdwItem, lpValueName->Buffer)) {
bCallThisItem = TRUE;
break;
}
pdwItem++; // point to next item
}
Win32Error = ERROR_SUCCESS;
if (bCallThisItem) { // call it if necessary
Win32Error = (*DataFuncs[NumFunction].DataProc) (
lpValueName->Buffer,
lpData,
lpcbData,
&pDataDefinition);
}
if (Win32Error) break;
}
if (Win32Error) break; // exit if error encountered
//
// check costly items before calling extensible data.
// bCheckCostlyCalls has been set/clear early. This is
// for performance enhancement.
if (bCheckCostlyCalls) {
for ( NumFunction = 0;
NumFunction < NT_NUM_COSTLY_OBJECT_TYPES; // no ext. obj yet
NumFunction++ ) {
bCallThisItem = FALSE;
pdwItem = &CostlyFuncs[NumFunction].ObjectIndex[0];
// loop through objects that can cause this function to be
// called and exit when a null object or a match are found.
for (dwItem = 0; dwItem < DP_ITEM_LIST_SIZE; dwItem++) {
if (*pdwItem == NULL_OBJECT_TITLE_INDEX) break; // give up
if (IsNumberInUnicodeList (*pdwItem, lpValueName->Buffer)) {
bCallThisItem = TRUE;
break;
}
pdwItem++; // point to next item
}
Win32Error = ERROR_SUCCESS;
if (bCallThisItem) {
Win32Error = (*CostlyFuncs[NumFunction].DataProc) (
lpValueName->Buffer,
lpData,
lpcbData,
&pDataDefinition);
}
if (Win32Error) break; // if an error encountered
}
} // endif bCheckCostlyCalls is TRUE
if (Win32Error) break; // if an error encountered
// call extensible items and see if they want to do anything
// with the list of values
Win32Error = QueryExtensibleData (
lpValueName->Buffer,
lpData,
lpcbData,
&pDataDefinition);
break;
case QUERY_COSTLY:
//
// Call All Costly routines
//
for ( NumFunction = 0;
NumFunction <= NT_NUM_COSTLY_OBJECT_TYPES;
NumFunction++ ) {
Win32Error = (*CostlyFuncs[NumFunction].DataProc) (
lpValueName->Buffer,
lpData,
lpcbData,
&pDataDefinition);
if (Win32Error != ERROR_SUCCESS) break; // if an error encountered
}
break;
default:
break;
}
// free allocated buffers
if (pProcessVaInfo) {
FreeSystemVaData (pProcessVaInfo);
pProcessVaInfo = NULL;
}
// if an error was encountered, return it
if (Win32Error) {
status = Win32Error;
goto PRQV_ErrorExit;
}
//
// Final housekeeping for data return: note data size
//
TotalLen = (PCHAR) pDataDefinition - (PCHAR) lpData;
*lpcbData = TotalLen;
if (ARGUMENT_PRESENT (lpcbLen)) { // test for optional parameter
*lpcbLen = TotalLen;
}
pPerfDataBlock->TotalByteLength = TotalLen;
if (ARGUMENT_PRESENT (lpType)) { // test for optional value
*lpType = REG_BINARY;
}
status = ERROR_SUCCESS;
PRQV_ErrorExit:
NtReleaseSemaphore (hDataSemaphore, 1L, &dwPrevCount);
// reset thread to original priority
if (lOldPriority > 0) {
NtSetInformationThread(
NtCurrentThread(),
ThreadPriority,
&lOldPriority,
sizeof(lOldPriority)
);
}
PRQV_ErrorExit1:
HEAP_PROBE();
return status;
}
BOOL
MonBuildPerfDataBlock(
PERF_DATA_BLOCK *pBuffer,
PVOID *pBufferNext,
DWORD NumObjectTypes,
DWORD DefaultObject
)
/*++
MonBuildPerfDataBlock - build the PERF_DATA_BLOCK structure
Inputs:
pBuffer - where the data block should be placed
pBufferNext - where pointer to next byte of data block
is to begin; DWORD aligned
NumObjectTypes - number of types of objects being reported
DefaultObject - object to display by default when
this system is selected; this is the
object type title index
--*/
{
LARGE_INTEGER Time, TimeX10000;
ULONG Remainder;
// Initialize Signature and version ID for this data structure
pBuffer->Signature[0] = L'P';
pBuffer->Signature[1] = L'E';
pBuffer->Signature[2] = L'R';
pBuffer->Signature[3] = L'F';
pBuffer->LittleEndian = TRUE;
pBuffer->Version = PERF_DATA_VERSION;
pBuffer->Revision = PERF_DATA_REVISION;
//
// The next field will be filled in at the end when the length
// of the return data is known
//
pBuffer->TotalByteLength = 0;
pBuffer->NumObjectTypes = NumObjectTypes;
pBuffer->DefaultObject = DefaultObject;
GetSystemTime(&pBuffer->SystemTime);
// QueryPerformanceCounter(&pBuffer->PerfTime);
// QueryPerformanceFrequency(&pBuffer->PerfFreq);
NtQueryPerformanceCounter(&pBuffer->PerfTime,&pBuffer->PerfFreq);
TimeX10000.QuadPart = pBuffer->PerfTime.QuadPart * 10000L;
Time.QuadPart = TimeX10000.QuadPart / pBuffer->PerfFreq.LowPart;
pBuffer->PerfTime100nSec.QuadPart = Time.QuadPart * 1000L;
if ( ComputerNameLength ) {
// There is a Computer name: i.e., the network is installed
pBuffer->SystemNameLength = ComputerNameLength;
pBuffer->SystemNameOffset = sizeof(PERF_DATA_BLOCK);
RtlMoveMemory(&pBuffer[1],
pComputerName,
ComputerNameLength);
*pBufferNext = (PVOID) ((PCHAR) &pBuffer[1] +
DWORD_MULTIPLE(ComputerNameLength));
pBuffer->HeaderLength = (PCHAR) *pBufferNext - (PCHAR) pBuffer;
} else {
// Member of Computers Anonymous
pBuffer->SystemNameLength = 0;
pBuffer->SystemNameOffset = 0;
*pBufferNext = &pBuffer[1];
pBuffer->HeaderLength = sizeof(PERF_DATA_BLOCK);
}
return 0;
}
BOOL
MonBuildInstanceDefinition(
PERF_INSTANCE_DEFINITION *pBuffer,
PVOID *pBufferNext,
DWORD ParentObjectTitleIndex,
DWORD ParentObjectInstance,
DWORD UniqueID,
PUNICODE_STRING Name
)
/*++
MonBuildInstanceDefinition - Build an instance of an object
Inputs:
pBuffer - pointer to buffer where instance is to
be constructed
pBufferNext - pointer to a pointer which will contain
next available location, DWORD aligned
ParentObjectTitleIndex
- Title Index of parent object type; 0 if
no parent object
ParentObjectInstance
- Index into instances of parent object
type, starting at 0, for this instances
parent object instance
UniqueID - a unique identifier which should be used
instead of the Name for identifying
this instance
Name - Name of this instance
--*/
{
DWORD NameLength;
WCHAR *pName;
//
// Include trailing null in name size
//
NameLength = Name->Length;
if ( !NameLength ||
Name->Buffer[(NameLength/sizeof(WCHAR))-1] != UNICODE_NULL ) {
NameLength += sizeof(WCHAR);
}
pBuffer->ByteLength = sizeof(PERF_INSTANCE_DEFINITION) +
DWORD_MULTIPLE(NameLength);
pBuffer->ParentObjectTitleIndex = ParentObjectTitleIndex;
pBuffer->ParentObjectInstance = ParentObjectInstance;
pBuffer->UniqueID = UniqueID;
pBuffer->NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
pBuffer->NameLength = NameLength;
pName = (PWCHAR)&pBuffer[1];
RtlMoveMemory(pName,Name->Buffer,Name->Length);
// Always null terminated. Space for this reserved above.
pName[(NameLength/sizeof(WCHAR))-1] = UNICODE_NULL;
*pBufferNext = (PVOID) ((PCHAR) pBuffer + pBuffer->ByteLength);
return 0;
}
PERF_INSTANCE_DEFINITION *
GetDiskCounters(
DWORD CurrentDisk,
PDISK_DATA_DEFINITION *pPhysicalDiskDataDefinition,
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition,
DISK_TOTAL_INFO_BLOCK *pDiskTotalInfo
)
/*++
Routine Description:
This routine will obtain the performance counters for a physical
or logical disk drive. Any failure to obtain the counters is
ignored.
Arguments:
CurentDisk - Number of the disk drive, starting at 0. This is
an index into the DiskDevices array.
pPhysicalDiskDataDefinition - pointer to data definition for
physcial disks, if this is a logical
disk: this is to identify the title
index for the parent physcial disk.
pPerfInstanceDefinition - pointer to location in return buffer
where this instance definition should go.
Return Value:
Position in the buffer for the next instance definition.
--*/
{
DWORD *pdwCounter;
LARGE_INTEGER UNALIGNED *pliCounter;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
DISK_PERFORMANCE DiskPerformance; // Disk driver returns counters here
IO_STATUS_BLOCK status_block; // Disk driver status
// Place holder pointers during disk counter collection: these mark
// a spot in the data structure where upcoming counters will be
// stored or from which they will get copied.
LARGE_INTEGER UNALIGNED *pTimeOffset;
PDWORD pTransfers;
LARGE_INTEGER UNALIGNED *pBytes;
NTSTATUS status;
BOOL HaveParent;
ULONG DataSize;
ULONG Remainder;
ULONG AllocationUnitBytes;
FILE_FS_SIZE_INFORMATION FsSizeInformation;
LARGE_INTEGER TotalBytes;
LARGE_INTEGER FreeBytes;
HaveParent = pPhysicalDiskDataDefinition != NULL ? 1: 0;
MonBuildInstanceDefinition(
pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
(HaveParent ?
pPhysicalDiskDataDefinition->DiskObjectType.ObjectNameTitleIndex :
0),
DiskDevices[CurrentDisk].ParentIndex,
// use drive letters on Logical Disks
(HaveParent ? ((DWORD)-1) : CurrentDisk),
&DiskDevices[CurrentDisk].Name);
DataSize = HaveParent ? SIZE_OF_LDISK_DATA : SIZE_OF_PDISK_DATA;
pPerfCounterBlock->ByteLength = DataSize;
pdwCounter = (PDWORD) pPerfCounterBlock;
if ( HaveParent ) {
//
// This is a logical disk: get free space information
//
status = ERROR_SUCCESS;
if (DiskDevices[CurrentDisk].StatusHandle) {
status = NtQueryVolumeInformationFile(
DiskDevices[CurrentDisk].StatusHandle,
&status_block,
&FsSizeInformation,
sizeof(FILE_FS_SIZE_INFORMATION),
FileFsSizeInformation);
}
if ( DiskDevices[CurrentDisk].StatusHandle && NT_SUCCESS(status) ) {
AllocationUnitBytes =
FsSizeInformation.BytesPerSector *
FsSizeInformation.SectorsPerAllocationUnit;
TotalBytes.QuadPart = FsSizeInformation.TotalAllocationUnits.QuadPart *
AllocationUnitBytes;
FreeBytes.QuadPart = FsSizeInformation.AvailableAllocationUnits.QuadPart *
AllocationUnitBytes;
// Express in megabytes, truncated
TotalBytes.QuadPart /= (1024*1024);
FreeBytes.QuadPart /= (1024*1024);
// First two yield percentage of free space;
// last is for raw count of free space in megabytes
*++pdwCounter = FreeBytes.LowPart;
*++pdwCounter = TotalBytes.LowPart;
*++pdwCounter = FreeBytes.LowPart;
// update total accumulator fields
pDiskTotalInfo->DiskFreeMbytes += FreeBytes.LowPart;
pDiskTotalInfo->DiskTotalMbytes += TotalBytes.LowPart;
} else {
// Cannot get space information
*++pdwCounter = 0;
*++pdwCounter = 0;
*++pdwCounter = 0;
}
}
//
// Issue device control.
//
// clear the buffer first
memset (&DiskPerformance, 0, sizeof(DiskPerformance));
status = NtDeviceIoControlFile(
DiskDevices[CurrentDisk].Handle,
NULL,
NULL,
NULL,
&status_block,
IOCTL_DISK_PERFORMANCE,
NULL,
0L,
&DiskPerformance,
sizeof(DISK_PERFORMANCE));
// Set up pointer for data collection
if ( NT_SUCCESS(status) ) {
// the QueueDepth counter is only a byte so clear the unused bytes
DiskPerformance.QueueDepth &= 0x000000FF;
//
// Format and collect Physical Disk data
//
*++pdwCounter = DiskPerformance.QueueDepth;
pDiskTotalInfo->DiskCurrentQueueLength += DiskPerformance.QueueDepth;
pTimeOffset = pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
pTimeOffset->QuadPart = DiskPerformance.ReadTime.QuadPart +
DiskPerformance.WriteTime.QuadPart; // DiskTime
*++pliCounter = *pTimeOffset; // Disk Avg Queue Len
*++pliCounter = DiskPerformance.ReadTime; // Disk Read time
*++pliCounter = DiskPerformance.ReadTime; // disk read queue len
*++pliCounter = DiskPerformance.WriteTime; // disk write time
*++pliCounter = DiskPerformance.WriteTime; // disk write queue len
pDiskTotalInfo->DiskTime += (DiskPerformance.ReadTime.QuadPart +
DiskPerformance.WriteTime.QuadPart);
pDiskTotalInfo->DiskReadTime += DiskPerformance.ReadTime.QuadPart;
pDiskTotalInfo->DiskWriteTime += DiskPerformance.WriteTime.QuadPart;
*++pliCounter = *pTimeOffset;
pTransfers = (PDWORD) ++pliCounter;
*pTransfers = DiskPerformance.ReadCount + DiskPerformance.WriteCount;
pliCounter = (LARGE_INTEGER UNALIGNED * ) (pTransfers + 1);
*pliCounter = DiskPerformance.ReadTime;
pdwCounter = (PDWORD) ++pliCounter;
*pdwCounter = DiskPerformance.ReadCount;
pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
*pliCounter = DiskPerformance.WriteTime;
pdwCounter = (PDWORD) ++pliCounter;
*pdwCounter = DiskPerformance.WriteCount;
*++pdwCounter = *pTransfers;
*++pdwCounter = DiskPerformance.ReadCount;
*++pdwCounter = DiskPerformance.WriteCount;
pBytes = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
pBytes->QuadPart = DiskPerformance.BytesRead.QuadPart +
DiskPerformance.BytesWritten.QuadPart;
pDiskTotalInfo->DiskTransfers += (DiskPerformance.ReadCount +
DiskPerformance.WriteCount);
pDiskTotalInfo->DiskReads += DiskPerformance.ReadCount;
pDiskTotalInfo->DiskWrites += DiskPerformance.WriteCount;
pliCounter = pBytes + 1;
*pliCounter = DiskPerformance.BytesRead;
*++pliCounter = DiskPerformance.BytesWritten;
*++pliCounter = *pBytes;
pDiskTotalInfo->DiskBytes += (DiskPerformance.BytesRead.QuadPart +
DiskPerformance.BytesWritten.QuadPart);
pDiskTotalInfo->DiskReadBytes += DiskPerformance.BytesRead.QuadPart;
pDiskTotalInfo->DiskWriteBytes += DiskPerformance.BytesWritten.QuadPart;
pdwCounter = (PDWORD) ++pliCounter;
*pdwCounter = *pTransfers;
pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
*pliCounter = DiskPerformance.BytesRead;
pdwCounter = (PDWORD) ++pliCounter;
*pdwCounter = DiskPerformance.ReadCount;
pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
*pliCounter = DiskPerformance.BytesWritten;
pdwCounter = (PDWORD) ++pliCounter;
*pdwCounter = DiskPerformance.WriteCount;
} else {
//
// Could not collect data, so must clear a set of counters
//
memset(++pdwCounter,
0,
SIZE_OF_LDISK_NON_SPACE_DATA); // allows for .ByteLength
}
return (PERF_INSTANCE_DEFINITION *)
((PBYTE) pPerfCounterBlock + DataSize);
}
LONG
QuerySystemData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QuerySystemData - Get data about system
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
NTSTATUS ntStatus;
DWORD *pdwCounter;
LARGE_INTEGER UNALIGNED *pliCounter;
SYSTEM_DATA_DEFINITION *pSystemDataDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
SYSTEM_EXCEPTION_INFORMATION ExceptionInfo;
SYSTEM_REGISTRY_QUOTA_INFORMATION RegistryInfo;
//
// Check for sufficient space for system data
//
pSystemDataDefinition = (SYSTEM_DATA_DEFINITION *) *lppDataDefinition;
TotalLen = (PCHAR) pSystemDataDefinition -
(PCHAR) lpData +
sizeof(SYSTEM_DATA_DEFINITION) +
SIZE_OF_SYSTEM_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Define system data block
//
RtlMoveMemory(pSystemDataDefinition,
&SystemDataDefinition,
sizeof(SYSTEM_DATA_DEFINITION));
//
// Format and collect system data
//
SystemDataDefinition.SystemObjectType.PerfTime = SysTimeInfo.CurrentTime;
pPerfCounterBlock = (PERF_COUNTER_BLOCK *)
&pSystemDataDefinition[1];
pPerfCounterBlock->ByteLength = SIZE_OF_SYSTEM_DATA;
pdwCounter = (PDWORD) (&pPerfCounterBlock[1]);
*pdwCounter = SysPerfInfo.IoReadOperationCount;
*++pdwCounter = SysPerfInfo.IoWriteOperationCount;
*++pdwCounter = SysPerfInfo.IoOtherOperationCount;
pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
*pliCounter = SysPerfInfo.IoReadTransferCount;
*++pliCounter = SysPerfInfo.IoWriteTransferCount;
*++pliCounter = SysPerfInfo.IoOtherTransferCount;
pdwCounter = (LPDWORD) ++pliCounter;
*pdwCounter = SysPerfInfo.ContextSwitches;
*++pdwCounter = SysPerfInfo.SystemCalls;
//
// Set up pointers so QueryProcessorData can acuumulate the
// system-wide data. Initialize to 0 since these are
// accumulators.
//
pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
pTotalProcessorTime = pliCounter;
pTotalUserTime = ++pliCounter;
pTotalPrivilegedTime = ++pliCounter;
pdwCounter = (LPDWORD) ++pliCounter;
pTotalInterrupts = pdwCounter;
*++pdwCounter = SysPerfInfo.IoReadOperationCount +
SysPerfInfo.IoWriteOperationCount;
pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
*pliCounter = SysTimeInfo.BootTime;
pdwCounter = (PDWORD) ++pliCounter;
pTotalProcessorTime->HighPart = 0;
pTotalProcessorTime->LowPart = 0;
pTotalUserTime->HighPart = 0;
pTotalUserTime->LowPart = 0;
pTotalPrivilegedTime->HighPart = 0;
pTotalPrivilegedTime->LowPart = 0;
*pTotalInterrupts = 0;
// leave room for the ProcessorQueueLength data
pdwProcessorQueueLength = pdwCounter;
*pdwProcessorQueueLength = 0;
// get the exception data
ntStatus = NtQuerySystemInformation(
SystemExceptionInformation,
&ExceptionInfo,
sizeof(ExceptionInfo),
NULL
);
if (!NT_SUCCESS(ntStatus)) {
// unable to collect the data from the system so
// clear the return data structure to prevent bogus data from
// being returned
memset (&ExceptionInfo, 0, sizeof(ExceptionInfo));
}
*++pdwCounter = ExceptionInfo.AlignmentFixupCount ;
*++pdwCounter = ExceptionInfo.ExceptionDispatchCount ;
*++pdwCounter = ExceptionInfo.FloatingEmulationCount ;
++pdwCounter;
pliCounter = (LARGE_INTEGER UNALIGNED * ) pdwCounter;
pTotalDpcTime = pliCounter;
pTotalInterruptTime = ++pliCounter;
pTotalDpcTime->QuadPart = 0;
pTotalInterruptTime->QuadPart = 0;
pdwCounter = (LPDWORD) ++pliCounter;
pTotalDpcCount = pdwCounter++;
*pTotalDpcCount = 0;
pTotalDpcRate = pdwCounter++;
*pTotalDpcRate = 0;
pTotalDpcBypassCount = pdwCounter++;
*pTotalDpcBypassCount = 0;
pTotalApcBypassCount = pdwCounter++;
*pTotalApcBypassCount = 0;
// collect registry quota info
memset (&RegistryInfo, 0, sizeof (SYSTEM_REGISTRY_QUOTA_INFORMATION));
ntStatus = NtQuerySystemInformation (
SystemRegistryQuotaInformation,
(PVOID)&RegistryInfo,
sizeof(RegistryInfo),
NULL);
if (ntStatus != STATUS_SUCCESS) {
// clear the data fields
memset (&RegistryInfo, 0, sizeof (SYSTEM_REGISTRY_QUOTA_INFORMATION));
}
*pdwCounter++ = RegistryInfo.RegistryQuotaUsed;
*pdwCounter++ = RegistryInfo.RegistryQuotaAllowed;
*lppDataDefinition = (LPVOID) pdwCounter;
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QueryProcessorData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryProcessorData - Get data about processsor(s)
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
DWORD dwReturnedBufferSize;
PROCESSOR_DATA_DEFINITION *pProcessorDataDefinition;
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
ULONG CurProc;
// LARGE_INTEGER Frequency;
LARGE_INTEGER KernelTime;
LARGE_INTEGER UNALIGNED *pliCounter;
LARGE_INTEGER UNALIGNED *pliProcessorTime;
UNICODE_STRING ProcessorName;
WCHAR ProcessorNameBuffer[11];
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *pProcessorInformation = NULL;
SYSTEM_INTERRUPT_INFORMATION *pThisProcessorInterruptInformation = NULL;
DWORD dwInterruptInfoBufferSize;
ULONG Remainder;
NTSTATUS ntStatus;
//
// Check for sufficient space for processor data
//
pProcessorDataDefinition = (PROCESSOR_DATA_DEFINITION *) *lppDataDefinition;
TotalLen = (PCHAR) pProcessorDataDefinition -
(PCHAR) lpData +
sizeof(PROCESSOR_DATA_DEFINITION) +
SIZE_OF_PROCESSOR_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Get processor data from system
//
if ( ProcessorBufSize ) {
NtQuerySystemInformation(
SystemProcessorPerformanceInformation,
pProcessorBuffer,
ProcessorBufSize,
&dwReturnedBufferSize
);
}
//
// get system interrupt information by processor
//
dwInterruptInfoBufferSize = (ULONG)BasicInfo.NumberOfProcessors *
sizeof (SYSTEM_INTERRUPT_INFORMATION);
if (pProcessorInterruptInformation == NULL) {
// then allocate data buffer
pProcessorInterruptInformation = ALLOCMEM (RtlProcessHeap(),
HEAP_ZERO_MEMORY, dwInterruptInfoBufferSize);
}
if (pProcessorInterruptInformation != NULL) {
ntStatus = NtQuerySystemInformation(
SystemInterruptInformation,
pProcessorInterruptInformation,
dwInterruptInfoBufferSize,
&dwReturnedBufferSize
);
} else {
//unable to allocte memory for interrupt information
return ERROR_OUTOFMEMORY;
}
// Define processor data block
//
RtlMoveMemory(pProcessorDataDefinition,
&ProcessorDataDefinition,
sizeof(PROCESSOR_DATA_DEFINITION));
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pProcessorDataDefinition[1];
// QueryPerformanceFrequency(&Frequency);
pProcessorInformation = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *)
pProcessorBuffer;
// point to the first processor in the returned array of interrupt
// information. data is returned as an array of structures.
pThisProcessorInterruptInformation = pProcessorInterruptInformation;
for ( CurProc = 0;
CurProc < (ULONG) BasicInfo.NumberOfProcessors;
CurProc++ ) {
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
(MAX_INSTANCE_NAME+1)*sizeof(WCHAR) +
SIZE_OF_PROCESSOR_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Define processor instance 0;
// More could be defined like this
//
ProcessorName.Length = 0;
ProcessorName.MaximumLength = 11;
ProcessorName.Buffer = ProcessorNameBuffer;
RtlIntegerToUnicodeString(CurProc, 10, &ProcessorName);
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
0,
0,
CurProc,
&ProcessorName);
//
// Format and collect processor data. While doing so,
// accumulate totals in the System Object Type data block.
// Pointers to these were initialized in QuerySystemData.
//
pPerfCounterBlock->ByteLength = SIZE_OF_PROCESSOR_DATA;
pliCounter = (LARGE_INTEGER UNALIGNED *) &pPerfCounterBlock[1];
pliProcessorTime = pliCounter;
*pliProcessorTime = pProcessorInformation->IdleTime;
if (pTotalProcessorTime) {
pTotalProcessorTime->QuadPart += pliProcessorTime->QuadPart;
}
*++pliCounter = pProcessorInformation->UserTime;
if (pTotalUserTime) {
pTotalUserTime->QuadPart = pliCounter->QuadPart +
pTotalUserTime->QuadPart;
}
KernelTime.QuadPart = pProcessorInformation->KernelTime.QuadPart -
pliProcessorTime->QuadPart;
if ( KernelTime.HighPart < 0 ) {
KernelTime.HighPart = 0;
KernelTime.LowPart = 0;
}
*++pliCounter = KernelTime;
if (pTotalPrivilegedTime) {
pTotalPrivilegedTime->QuadPart = pliCounter->QuadPart +
pTotalPrivilegedTime->QuadPart;
}
pdwCounter = (LPDWORD) ++pliCounter;
*pdwCounter = pProcessorInformation->InterruptCount;
if (pTotalInterrupts) {
*pTotalInterrupts += *pdwCounter;
}
++pdwCounter;
pliCounter = (LARGE_INTEGER UNALIGNED * ) pdwCounter;
*pliCounter = pProcessorInformation->DpcTime;
if (pTotalDpcTime) {
pTotalDpcTime->QuadPart = pliCounter->QuadPart +
pTotalDpcTime->QuadPart;
}
++pliCounter;
*pliCounter = pProcessorInformation->InterruptTime;
if (pTotalInterruptTime) {
pTotalInterruptTime->QuadPart = pliCounter->QuadPart +
pTotalInterruptTime->QuadPart;
}
pdwCounter = (DWORD *)++pliCounter;
if (pTotalDpcCount) {
*pTotalDpcCount += pThisProcessorInterruptInformation->DpcCount;
}
*pdwCounter++ = pThisProcessorInterruptInformation->DpcCount;
if (pTotalDpcRate) {
*pTotalDpcRate = pThisProcessorInterruptInformation->DpcRate;
}
*pdwCounter++ = pThisProcessorInterruptInformation->DpcRate;
if (pTotalDpcBypassCount) {
*pTotalDpcBypassCount = pThisProcessorInterruptInformation->DpcBypassCount;
}
*pdwCounter++ = pThisProcessorInterruptInformation->DpcBypassCount;
if (pTotalApcBypassCount) {
*pTotalApcBypassCount = pThisProcessorInterruptInformation->ApcBypassCount;
}
*pdwCounter++ = pThisProcessorInterruptInformation->ApcBypassCount;
//
// Advance to next processor
//
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
pdwCounter;
// point to next processor's data in return array(s)
pProcessorInformation++;
pThisProcessorInterruptInformation++;
}
//
// Now we know how large an area we used for the
// processor definition, so we can update the offset
// to the next object definition
//
pProcessorDataDefinition->ProcessorObjectType.NumInstances =
BasicInfo.NumberOfProcessors;
pProcessorDataDefinition->ProcessorObjectType.TotalByteLength =
(PBYTE) pPerfInstanceDefinition -
(PBYTE) pProcessorDataDefinition;
// cal. the system average total time if needed
if (BasicInfo.NumberOfProcessors > 1 && pTotalUserTime) {
pTotalUserTime->QuadPart = pTotalUserTime->QuadPart /
BasicInfo.NumberOfProcessors;
pTotalProcessorTime->QuadPart = pTotalProcessorTime->QuadPart /
BasicInfo.NumberOfProcessors;
pTotalPrivilegedTime->QuadPart = pTotalPrivilegedTime->QuadPart /
BasicInfo.NumberOfProcessors;
pTotalDpcTime->QuadPart = pTotalDpcTime->QuadPart /
BasicInfo.NumberOfProcessors;
pTotalInterruptTime->QuadPart = pTotalInterruptTime->QuadPart /
BasicInfo.NumberOfProcessors;
}
*lppDataDefinition = (LPVOID) pPerfInstanceDefinition;
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QueryMemoryData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryMemoryData - Get data about memory usage
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
NTSTATUS Status;
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
MEMORY_DATA_DEFINITION *pMemoryDataDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
SYSTEM_FILECACHE_INFORMATION FileCache;
pMemoryDataDefinition = (MEMORY_DATA_DEFINITION *) *lppDataDefinition;
//
// Check for enough space for memory data block
//
TotalLen = (PCHAR) pMemoryDataDefinition - (PCHAR) lpData +
sizeof(MEMORY_DATA_DEFINITION) +
SIZE_OF_MEMORY_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
Status = NtQuerySystemInformation(
SystemFileCacheInformation,
&FileCache,
sizeof(FileCache),
NULL
);
//
// Define memory data block
//
RtlMoveMemory(pMemoryDataDefinition,
&MemoryDataDefinition,
sizeof(MEMORY_DATA_DEFINITION));
//
// Format and collect memory data
//
pPerfCounterBlock = (PERF_COUNTER_BLOCK *)
&pMemoryDataDefinition[1];
pPerfCounterBlock->ByteLength = SIZE_OF_MEMORY_DATA;
pdwCounter = (PDWORD) &pPerfCounterBlock[1];
*pdwCounter = SysPerfInfo.AvailablePages * BasicInfo.PageSize; // display as bytes
*++pdwCounter = SysPerfInfo.CommittedPages * BasicInfo.PageSize;
*++pdwCounter = SysPerfInfo.CommitLimit * BasicInfo.PageSize;
*++pdwCounter = SysPerfInfo.PageFaultCount;
*++pdwCounter = SysPerfInfo.CopyOnWriteCount;
*++pdwCounter = SysPerfInfo.TransitionCount;
*++pdwCounter = FileCache.PageFaultCount;
*++pdwCounter = SysPerfInfo.DemandZeroCount;
*++pdwCounter = SysPerfInfo.PageReadCount +
SysPerfInfo.DirtyPagesWriteCount;
*++pdwCounter = SysPerfInfo.PageReadCount;
*++pdwCounter = SysPerfInfo.PageReadIoCount;
*++pdwCounter = SysPerfInfo.DirtyPagesWriteCount;
*++pdwCounter = SysPerfInfo.DirtyWriteIoCount;
*++pdwCounter = SysPerfInfo.PagedPoolPages * BasicInfo.PageSize;
*++pdwCounter = SysPerfInfo.NonPagedPoolPages * BasicInfo.PageSize;
*++pdwCounter = SysPerfInfo.PagedPoolAllocs -
SysPerfInfo.PagedPoolFrees;
*++pdwCounter = SysPerfInfo.NonPagedPoolAllocs -
SysPerfInfo.NonPagedPoolFrees;
*++pdwCounter = SysPerfInfo.FreeSystemPtes;
*++pdwCounter = FileCache.CurrentSize;
*++pdwCounter = FileCache.PeakSize;
// add six more memory counters (9/23/93)
*++pdwCounter = SysPerfInfo.ResidentPagedPoolPage * BasicInfo.PageSize;
*++pdwCounter = SysPerfInfo.TotalSystemCodePages * BasicInfo.PageSize;
*++pdwCounter = SysPerfInfo.ResidentSystemCodePage * BasicInfo.PageSize;
*++pdwCounter = SysPerfInfo.TotalSystemDriverPages * BasicInfo.PageSize;
*++pdwCounter = SysPerfInfo.ResidentSystemDriverPage * BasicInfo.PageSize;
*++pdwCounter = SysPerfInfo.ResidentSystemCachePage * BasicInfo.PageSize;
// add new counters (11/7/95)
//
// This is reported as a percentage of CommittedPages/CommitLimit.
// these value return a value in "page" units. Since this is a
// fraction, the page size (i.e. converting pages to bytes) will
// cancel out and as such can be ignored, saving some CPU cycles
//
*++pdwCounter = SysPerfInfo.CommittedPages;
*++pdwCounter = SysPerfInfo.CommitLimit;
*lppDataDefinition = (LPVOID) ++pdwCounter;
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QueryCacheData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryCacheData - Get data about cache usage
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
DWORD Changes; // Used by macros to compute cache
DWORD Misses; // ...statistics
CACHE_DATA_DEFINITION *pCacheDataDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
//
// Check for enough space for cache data block
//
pCacheDataDefinition = (CACHE_DATA_DEFINITION *) *lppDataDefinition;
TotalLen = (PCHAR) pCacheDataDefinition - (PCHAR) lpData +
sizeof(CACHE_DATA_DEFINITION) +
SIZE_OF_CACHE_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Define cache data block
//
RtlMoveMemory(pCacheDataDefinition,
&CacheDataDefinition,
sizeof(CACHE_DATA_DEFINITION));
//
// Format and collect memory data
//
pPerfCounterBlock = (PERF_COUNTER_BLOCK *)
&pCacheDataDefinition[1];
pPerfCounterBlock->ByteLength = SIZE_OF_CACHE_DATA;
pdwCounter = (PDWORD) &pPerfCounterBlock[1];
//
// The Data Map counter is the sum of the Wait/NoWait cases
//
*pdwCounter = SYNC_ASYNC(CcMapData);
*++pdwCounter = SysPerfInfo.CcMapDataWait;
*++pdwCounter = SysPerfInfo.CcMapDataNoWait;
//
// The Data Map Hits is a percentage of Data Maps that hit
// the cache; second counter is the base (divisor)
//
*++pdwCounter = SYNC_ASYNC_HITRATE(CcMapData);
*++pdwCounter = SYNC_ASYNC(CcMapData);
//
// The next pair of counters forms a percentage of
// Pins as a portion of Data Maps
//
*++pdwCounter = SysPerfInfo.CcPinMappedDataCount;
*++pdwCounter = SYNC_ASYNC(CcMapData);
*++pdwCounter = SYNC_ASYNC(CcPinRead);
*++pdwCounter = SysPerfInfo.CcPinReadWait;
*++pdwCounter = SysPerfInfo.CcPinReadNoWait;
//
// The Pin Read Hits is a percentage of Pin Reads that hit
// the cache; second counter is the base (divisor)
//
*++pdwCounter = SYNC_ASYNC_HITRATE(CcPinRead);
*++pdwCounter = SYNC_ASYNC(CcPinRead);
*++pdwCounter = SYNC_ASYNC(CcCopyRead);
*++pdwCounter = SysPerfInfo.CcCopyReadWait;
*++pdwCounter = SysPerfInfo.CcCopyReadNoWait;
//
// The Copy Read Hits is a percentage of Copy Reads that hit
// the cache; second counter is the base (divisor)
//
*++pdwCounter = SYNC_ASYNC_HITRATE(CcCopyRead);
*++pdwCounter = SYNC_ASYNC(CcCopyRead);
*++pdwCounter = SYNC_ASYNC(CcMdlRead);
*++pdwCounter = SysPerfInfo.CcMdlReadWait;
*++pdwCounter = SysPerfInfo.CcMdlReadNoWait;
//
// The Mdl Read Hits is a percentage of Mdl Reads that hit
// the cache; second counter is the base (divisor)
//
*++pdwCounter = SYNC_ASYNC_HITRATE(CcMdlRead);
*++pdwCounter = SYNC_ASYNC(CcMdlRead);
*++pdwCounter = SysPerfInfo.CcReadAheadIos;
*++pdwCounter = SYNC_ASYNC(CcFastRead);
*++pdwCounter = SysPerfInfo.CcFastReadWait;
*++pdwCounter = SysPerfInfo.CcFastReadNoWait;
*++pdwCounter = SysPerfInfo.CcFastReadResourceMiss;
*++pdwCounter = SysPerfInfo.CcFastReadNotPossible;
*++pdwCounter = SysPerfInfo.CcLazyWriteIos;
*++pdwCounter = SysPerfInfo.CcLazyWritePages;
*++pdwCounter = SysPerfInfo.CcDataFlushes;
*++pdwCounter = SysPerfInfo.CcDataPages;
*lppDataDefinition = (LPVOID) ++pdwCounter;
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QueryPhysicalDiskData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryPhysicalDiskData - Get data about physical disk usage
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD CurrentDisk;
DISK_TOTAL_INFO_BLOCK DiskTotalInfo;
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
DWORD *pdwCounter;
DWORD *pTransfers;
LARGE_INTEGER UNALIGNED *pliCounter;
LARGE_INTEGER UNALIGNED *pTimeOffset;
LARGE_INTEGER UNALIGNED *pBytes;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
// If we are diskless, we should not count this object at all
if (! DiskDevices) {
pPerfDataBlock->NumObjectTypes--;
return 0;
}
//
// Check for sufficient space for Physical Disk object
// type definition
//
pPhysicalDiskDataDefinition = (PDISK_DATA_DEFINITION *) *lppDataDefinition;
TotalLen = (PCHAR) pPhysicalDiskDataDefinition - (PCHAR) lpData +
sizeof(PDISK_DATA_DEFINITION) +
SIZE_OF_PDISK_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
// clear the accumulator structure
memset (&DiskTotalInfo, 0, sizeof(DiskTotalInfo));
//
// Define Physical Disk data block
//
RtlMoveMemory(pPhysicalDiskDataDefinition,
&PhysicalDiskDataDefinition,
sizeof(PDISK_DATA_DEFINITION));
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pPhysicalDiskDataDefinition[1];
for ( CurrentDisk = 0;
CurrentDisk < NumPhysicalDisks;
CurrentDisk++ ) {
TotalLen = (PCHAR) pPerfInstanceDefinition -
(PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
(2+1+sizeof(DWORD))*
sizeof(WCHAR) +
SIZE_OF_PDISK_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
pPerfInstanceDefinition = GetDiskCounters(
CurrentDisk,
NULL,
pPerfInstanceDefinition,
&DiskTotalInfo);
}
// see if there's room for the total instance
TotalLen = (PCHAR) pPerfInstanceDefinition -
(PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
(usTotal.Length + sizeof(WCHAR)) +
SIZE_OF_PDISK_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
MonBuildInstanceDefinition(
pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
0,
0,
(DWORD)-1,
&usTotal);
// update the total counters
pPerfCounterBlock->ByteLength = SIZE_OF_PDISK_DATA;
pdwCounter = (PDWORD)&pPerfCounterBlock[1];
*pdwCounter++ = DiskTotalInfo.DiskCurrentQueueLength;
pTimeOffset = pliCounter = (LARGE_INTEGER UNALIGNED * )pdwCounter;
pTimeOffset->QuadPart = DiskTotalInfo.DiskReadTime +
DiskTotalInfo.DiskWriteTime; // DiskTime
pliCounter++;
*pliCounter++ = *pTimeOffset; // Disk Avg Queue Len
pliCounter->QuadPart = DiskTotalInfo.DiskReadTime; // Disk Read time
pliCounter++;
pliCounter->QuadPart = DiskTotalInfo.DiskReadTime; // disk read queue len
pliCounter++;
pliCounter->QuadPart = DiskTotalInfo.DiskWriteTime; // disk write time
pliCounter++;
pliCounter->QuadPart = DiskTotalInfo.DiskWriteTime; // disk write queue len
pliCounter++;
*pliCounter++ = *pTimeOffset; // disk total time
pTransfers = (PDWORD)pliCounter; // total transfers
*pTransfers = DiskTotalInfo.DiskReads + DiskTotalInfo.DiskWrites;
pliCounter = (LARGE_INTEGER UNALIGNED * ) (pTransfers + 1);
pliCounter->QuadPart = DiskTotalInfo.DiskReadTime; // read time
pdwCounter = (PDWORD)++pliCounter; // read op. count
*pdwCounter++ = DiskTotalInfo.DiskReads;
pliCounter = (LARGE_INTEGER UNALIGNED * )pdwCounter;
pliCounter->QuadPart = DiskTotalInfo.DiskWriteTime; // write time
pdwCounter = (PDWORD) ++pliCounter;
*pdwCounter++ = DiskTotalInfo.DiskWrites; // write op. count
*pdwCounter++ = *pTransfers; // transfers
*pdwCounter++ = DiskTotalInfo.DiskReads; // reads
*pdwCounter++ = DiskTotalInfo.DiskWrites; // writes
pBytes = (LARGE_INTEGER UNALIGNED * )pdwCounter;
pBytes->QuadPart = DiskTotalInfo.DiskReadBytes +
DiskTotalInfo.DiskWriteBytes;
pliCounter = pBytes + 1;
pliCounter->QuadPart = DiskTotalInfo.DiskReadBytes;
pliCounter++;
pliCounter->QuadPart = DiskTotalInfo.DiskWriteBytes;
pliCounter++;
*pliCounter++ = *pBytes;
pdwCounter = (PDWORD)pliCounter;
*pdwCounter++ = *pTransfers;
pliCounter = (LARGE_INTEGER UNALIGNED * )pdwCounter;
pliCounter->QuadPart = DiskTotalInfo.DiskReadBytes;
pliCounter++;
pdwCounter = (PDWORD)pliCounter;
*pdwCounter++ = DiskTotalInfo.DiskReads;
pliCounter = (LARGE_INTEGER UNALIGNED * )pdwCounter;
pliCounter->QuadPart = DiskTotalInfo.DiskWriteBytes;
pliCounter++;
pdwCounter = (PDWORD)pliCounter;
*pdwCounter++ = DiskTotalInfo.DiskWrites;
// update pointer to next available buffer...
pPhysicalDiskDataDefinition->DiskObjectType.NumInstances =
NumPhysicalDisks + 1; // add 1 for "Total" disk
pPhysicalDiskDataDefinition->DiskObjectType.TotalByteLength =
(PCHAR) pdwCounter -
(PCHAR) pPhysicalDiskDataDefinition;
*lppDataDefinition = (LPVOID) pdwCounter;
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QueryLogicalDiskData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryLogicalDiskData - Get data about logical disk usage
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD CurrentDisk;
LDISK_DATA_DEFINITION *pLogicalDiskDataDefinition;
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
DISK_TOTAL_INFO_BLOCK DiskTotalInfo;
DWORD *pdwCounter;
DWORD *pTransfers;
LARGE_INTEGER UNALIGNED *pliCounter;
LARGE_INTEGER UNALIGNED *pTimeOffset;
LARGE_INTEGER UNALIGNED *pBytes;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
// If we are diskless, we should not count this object at all
if (!DiskDevices) {
pPerfDataBlock->NumObjectTypes--;
return 0;
}
pLogicalDiskDataDefinition = (LDISK_DATA_DEFINITION *) *lppDataDefinition;
//
// Check for sufficient space for Logical Disk object
// type definition
//
TotalLen = (PCHAR) pLogicalDiskDataDefinition - (PCHAR) lpData +
sizeof(LDISK_DATA_DEFINITION) +
SIZE_OF_LDISK_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
// clear the accumulator structure
memset (&DiskTotalInfo, 0, sizeof(DiskTotalInfo));
//
// Define Logical Disk data block
//
RtlMoveMemory(pLogicalDiskDataDefinition,
&LogicalDiskDataDefinition,
sizeof(LDISK_DATA_DEFINITION));
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pLogicalDiskDataDefinition[1];
for ( CurrentDisk = NumPhysicalDisks;
CurrentDisk < NUMDISKS;
CurrentDisk++ ) {
TotalLen = (PCHAR) pPerfInstanceDefinition -
(PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
(2+1+sizeof(DWORD))*
sizeof(WCHAR) +
SIZE_OF_LDISK_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
pPerfInstanceDefinition = GetDiskCounters(
CurrentDisk,
pPhysicalDiskDataDefinition,
pPerfInstanceDefinition,
&DiskTotalInfo);
}
// see if there's room for the total instance
TotalLen = (PCHAR) pPerfInstanceDefinition -
(PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
(usTotal.Length + sizeof(WCHAR)) +
SIZE_OF_LDISK_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
MonBuildInstanceDefinition(
pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
pPhysicalDiskDataDefinition->DiskObjectType.ObjectNameTitleIndex,
NumPhysicalDisks,
(DWORD)-1,
&usTotal);
// update the total counters
pPerfCounterBlock->ByteLength = SIZE_OF_PDISK_DATA;
pdwCounter = (PDWORD)&pPerfCounterBlock[1];
*pdwCounter++ = DiskTotalInfo.DiskFreeMbytes;
*pdwCounter++ = DiskTotalInfo.DiskTotalMbytes;
*pdwCounter++ = DiskTotalInfo.DiskFreeMbytes;
*pdwCounter++ = DiskTotalInfo.DiskCurrentQueueLength;
pTimeOffset = pliCounter = (LARGE_INTEGER UNALIGNED * )pdwCounter;
pTimeOffset->QuadPart = DiskTotalInfo.DiskReadTime +
DiskTotalInfo.DiskWriteTime; // DiskTime
pliCounter++;
*pliCounter++ = *pTimeOffset; // Disk Avg Queue Len
pliCounter->QuadPart = DiskTotalInfo.DiskReadTime; // Disk Read time
pliCounter++;
pliCounter->QuadPart = DiskTotalInfo.DiskReadTime; // disk read queue len
pliCounter++;
pliCounter->QuadPart = DiskTotalInfo.DiskWriteTime; // disk write time
pliCounter++;
pliCounter->QuadPart = DiskTotalInfo.DiskWriteTime; // disk write queue len
pliCounter++;
*pliCounter++ = *pTimeOffset; // disk total time
pTransfers = (PDWORD)pliCounter; // total transfers
*pTransfers = DiskTotalInfo.DiskReads + DiskTotalInfo.DiskWrites;
pliCounter = (LARGE_INTEGER UNALIGNED * ) (pTransfers + 1);
pliCounter->QuadPart = DiskTotalInfo.DiskReadTime; // read time
pdwCounter = (PDWORD)++pliCounter; // read op. count
*pdwCounter++ = DiskTotalInfo.DiskReads;
pliCounter = (LARGE_INTEGER UNALIGNED * )pdwCounter;
pliCounter->QuadPart = DiskTotalInfo.DiskWriteTime; // write time
pdwCounter = (PDWORD) ++pliCounter;
*pdwCounter++ = DiskTotalInfo.DiskWrites; // write op. count
*pdwCounter++ = *pTransfers; // transfers
*pdwCounter++ = DiskTotalInfo.DiskReads; // reads
*pdwCounter++ = DiskTotalInfo.DiskWrites; // writes
pBytes = (LARGE_INTEGER UNALIGNED * )pdwCounter;
pBytes->QuadPart = DiskTotalInfo.DiskReadBytes +
DiskTotalInfo.DiskWriteBytes;
pliCounter = pBytes + 1;
pliCounter->QuadPart = DiskTotalInfo.DiskReadBytes;
pliCounter++;
pliCounter->QuadPart = DiskTotalInfo.DiskWriteBytes;
pliCounter++;
*pliCounter++ = *pBytes;
pdwCounter = (PDWORD)pliCounter;
*pdwCounter++ = *pTransfers;
pliCounter = (LARGE_INTEGER UNALIGNED * )pdwCounter;
pliCounter->QuadPart = DiskTotalInfo.DiskReadBytes;
pliCounter++;
pdwCounter = (PDWORD)pliCounter;
*pdwCounter++ = DiskTotalInfo.DiskReads;
pliCounter = (LARGE_INTEGER UNALIGNED * )pdwCounter;
pliCounter->QuadPart = DiskTotalInfo.DiskWriteBytes;
pliCounter++;
pdwCounter = (PDWORD)pliCounter;
*pdwCounter++ = DiskTotalInfo.DiskWrites;
// update pointer to next available buffer...
pLogicalDiskDataDefinition->DiskObjectType.NumInstances =
NumLogicalDisks + 1; // add one to include "total"
pLogicalDiskDataDefinition->DiskObjectType.TotalByteLength =
(PCHAR) pdwCounter -
(PCHAR) pLogicalDiskDataDefinition;
*lppDataDefinition = (LPVOID) pdwCounter;
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
PUNICODE_STRING
GetProcessSlowName (
PSYSTEM_PROCESS_INFORMATION pProcess
)
/*++
GetProcessShortName
Inputs:
PSYSTEM_PROCESS_INFORMATION pProcess
address of System Process Information data structure.
Outputs:
None
Returns:
Pointer to an initialized Unicode string (created by this routine)
that contains the short name of the process image or a numeric ID
if no name is found.
If unable to allocate memory for structure, then NULL is returned.
--*/
{
PWCHAR pPeriod;
PWCHAR pThisChar;
WORD wStringSize;
WORD wThisChar;
// allocate Unicode String Structure and adjacent buffer first
wStringSize = sizeof (UNICODE_STRING) +
MAX_INSTANCE_NAME * sizeof(WCHAR) +
sizeof (UNICODE_NULL);
// this routine assumes that the allocated memory has been zero'd
if (pusLocalProcessNameBuffer == NULL) {
pusLocalProcessNameBuffer =
ALLOCMEM (RtlProcessHeap(),
HEAP_ZERO_MEMORY, // this will only 0 the buffer the first time!
(DWORD)wStringSize);
}
if (pusLocalProcessNameBuffer == NULL) {
return NULL;
} else {
pusLocalProcessNameBuffer->MaximumLength = wStringSize - sizeof (UNICODE_STRING);
pusLocalProcessNameBuffer->Length = 0;
pusLocalProcessNameBuffer->Buffer = (PWCHAR)&pusLocalProcessNameBuffer[1];
RtlZeroMemory ( // buffer must be zero'd so we'll have a NULL Term
pusLocalProcessNameBuffer->Buffer,
(DWORD)pusLocalProcessNameBuffer->MaximumLength);
}
// get the process name from the image file
GetProcessExeName (pProcess->UniqueProcessId, pusLocalProcessNameBuffer);
if (pusLocalProcessNameBuffer->Length > 0) { // some name has been defined
pPeriod = (PWCHAR)pusLocalProcessNameBuffer->Buffer;
pThisChar = (PWCHAR)pusLocalProcessNameBuffer->Buffer;
wThisChar = 0;
//
// go from beginning to end and find last backslash and
// last period in name
//
while (*pThisChar != 0) { // go until null
if (*pThisChar == L'.') {
pPeriod = pThisChar;
}
pThisChar++; // point to next char
wThisChar += sizeof(WCHAR);
if (wThisChar >= pusLocalProcessNameBuffer->Length) {
break;
}
}
// if pPeriod is still pointing to the beginning of the
// string, then no period was found
if (pPeriod == (PWCHAR)pusLocalProcessNameBuffer->Buffer) {
pPeriod = pThisChar; // set to end of string;
} else {
// if a period was found, then see if the extension is
// .EXE, if so leave it, if not, then use end of string
// (i.e. include extension in name)
if (lstrcmpi(pPeriod, L".EXE") != 0) {
pPeriod = pThisChar;
}
}
// copy characters between period (or end of string) and
// slash (or start of string) to make image name
wStringSize = (PCHAR)pPeriod - (PCHAR)pusLocalProcessNameBuffer->Buffer;
*pPeriod = 0; // null terminate buffer
pusLocalProcessNameBuffer->Length = wStringSize; // adjust length
} else { // no name defined so use Process #
// check to see if this is a system process and give it
// a name
switch ((DWORD)pProcess->UniqueProcessId) {
case IDLE_PROCESS_ID:
RtlAppendUnicodeToString (pusLocalProcessNameBuffer, IDLE_PROCESS);
break;
case SYSTEM_PROCESS_ID:
RtlAppendUnicodeToString (pusLocalProcessNameBuffer, SYSTEM_PROCESS);
break;
// if the id is not a system process, then use the id as the name
default:
// try accessing via the "regular" interface
return (GetProcessShortName (pProcess));
break;
}
}
return pusLocalProcessNameBuffer;
}
PUNICODE_STRING
GetProcessShortName (
PSYSTEM_PROCESS_INFORMATION pProcess
)
/*++
GetProcessShortName
Inputs:
PSYSTEM_PROCESS_INFORMATION pProcess
address of System Process Information data structure.
Outputs:
None
Returns:
Pointer to an initialized Unicode string (created by this routine)
that contains the short name of the process image or a numeric ID
if no name is found.
If unable to allocate memory for structure, then NULL is returned.
--*/
{
PWCHAR pSlash;
PWCHAR pPeriod;
PWCHAR pThisChar;
WORD wStringSize;
WORD wThisChar;
// allocate Unicode String Structure and adjacent buffer first
if (pProcess->ImageName.Length > 0) {
wStringSize = sizeof (UNICODE_STRING) +
pProcess->ImageName.Length +
sizeof (UNICODE_NULL);
} else {
wStringSize = sizeof (UNICODE_STRING) +
MAX_INSTANCE_NAME * sizeof(WCHAR) +
sizeof (UNICODE_NULL);
}
// this routine assumes that the allocated memory has been zero'd
if (pusLocalProcessNameBuffer == NULL) {
pusLocalProcessNameBuffer =
ALLOCMEM (RtlProcessHeap(),
HEAP_ZERO_MEMORY, // this will only 0 the buffer the first time!
(DWORD)wStringSize);
}
if (pusLocalProcessNameBuffer == NULL) {
return NULL;
} else {
pusLocalProcessNameBuffer->MaximumLength = wStringSize - sizeof (UNICODE_STRING);
pusLocalProcessNameBuffer->Length = 0;
pusLocalProcessNameBuffer->Buffer = (PWCHAR)&pusLocalProcessNameBuffer[1];
RtlZeroMemory ( // buffer must be zero'd so we'll have a NULL Term
pusLocalProcessNameBuffer->Buffer,
(DWORD)pusLocalProcessNameBuffer->MaximumLength);
}
if (pProcess->ImageName.Buffer) { // some name has been defined
pSlash = (PWCHAR)pProcess->ImageName.Buffer;
pPeriod = (PWCHAR)pProcess->ImageName.Buffer;
pThisChar = (PWCHAR)pProcess->ImageName.Buffer;
wThisChar = 0;
//
// go from beginning to end and find last backslash and
// last period in name
//
while (*pThisChar != 0) { // go until null
if (*pThisChar == L'\\') {
pSlash = pThisChar;
} else if (*pThisChar == L'.') {
pPeriod = pThisChar;
}
pThisChar++; // point to next char
wThisChar += sizeof(WCHAR);
if (wThisChar >= pProcess->ImageName.Length) {
break;
}
}
// if pPeriod is still pointing to the beginning of the
// string, then no period was found
if (pPeriod == (PWCHAR)pProcess->ImageName.Buffer) {
pPeriod = pThisChar; // set to end of string;
} else {
// if a period was found, then see if the extension is
// .EXE, if so leave it, if not, then use end of string
// (i.e. include extension in name)
if (lstrcmpi(pPeriod, L".EXE") != 0) {
pPeriod = pThisChar;
}
}
if (*pSlash == L'\\') { // if pSlash is pointing to a slash, then
pSlash++; // point to character next to slash
}
// copy characters between period (or end of string) and
// slash (or start of string) to make image name
wStringSize = (PCHAR)pPeriod - (PCHAR)pSlash; // size in bytes
RtlMoveMemory (pusLocalProcessNameBuffer->Buffer, pSlash, wStringSize);
pusLocalProcessNameBuffer->Length = wStringSize;
// null terminate is
// not necessary because allocated memory is zero-init'd
} else { // no name defined so use Process #
// check to see if this is a system process and give it
// a name
switch ((DWORD)pProcess->UniqueProcessId) {
case IDLE_PROCESS_ID:
RtlAppendUnicodeToString (pusLocalProcessNameBuffer, IDLE_PROCESS);
break;
case SYSTEM_PROCESS_ID:
RtlAppendUnicodeToString (pusLocalProcessNameBuffer, SYSTEM_PROCESS);
break;
// if the id is not a system process, then use the id as the name
default:
RtlIntegerToUnicodeString ((DWORD)pProcess->UniqueProcessId,
10,
pusLocalProcessNameBuffer);
break;
}
}
return pusLocalProcessNameBuffer;
}
LONG
QueryProcessData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryProcessData - Get data about processes
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
LARGE_INTEGER UNALIGNED *pliCounter;
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
LARGE_INTEGER UNALIGNED *pliProcessorTime;
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
ULONG NumProcessInstances;
BOOLEAN NullProcess;
NTSTATUS Status;
DWORD dwReturnedBufferSize;
PUNICODE_STRING pProcessName;
ULONG ProcessBufferOffset;
POOLED_USAGE_AND_LIMITS PoolUsageInfo;
LARGE_INTEGER CreateTimeDiff;
// accumulator variables for total instance
LONGLONG llTotalUserTime = 0;
LONGLONG llTotalKernelTime = 0;
DWORD dwTotalPeakVirtualSize = 0;
DWORD dwTotalVirtualSize = 0;
DWORD dwTotalPageFaults = 0;
DWORD dwTotalPeakWorkingSet = 0;
DWORD dwTotalWorkingSet = 0;
DWORD dwTotalPeakPageFile = 0;
DWORD dwTotalPageFile = 0;
DWORD dwTotalPrivateBytes = 0;
DWORD dwTotalThreadCount = 0;
DWORD dwTotalPagedPool = 0;
DWORD dwTotalNonpagedPool = 0;
DWORD dwTotalHandleCount = 0;
DWORD dwTotalPagedPoolInUse = 0;
DWORD dwTotalPagedPoolLimit = 0;
DWORD dwTotalNonpagedPoolInUse = 0;
DWORD dwTotalNonpagedPoolLimit = 0;
pProcessDataDefinition = (PROCESS_DATA_DEFINITION *) *lppDataDefinition;
//
// Check for sufficient space for Process object type definition
//
TotalLen = (PCHAR) pProcessDataDefinition - (PCHAR) lpData +
sizeof(PROCESS_DATA_DEFINITION) +
SIZE_OF_PROCESS_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Get process data from system.
// if bGotProcessInfo is TRUE, that means we have the process
// info. collected earlier when we are checking for costly
// object types.
//
if (!bGotProcessInfo) {
while( (Status = NtQuerySystemInformation(
SystemProcessInformation,
pProcessBuffer,
ProcessBufSize,
&dwReturnedBufferSize)) == STATUS_INFO_LENGTH_MISMATCH ) {
ProcessBufSize += INCREMENT_BUFFER_SIZE;
if ( !(pProcessBuffer = REALLOCMEM(RtlProcessHeap(), 0,
pProcessBuffer,
ProcessBufSize)) ) {
Status = ERROR_OUTOFMEMORY;
return (Status);
}
}
if ( !NT_SUCCESS(Status) ) {
Status = (error_status_t)RtlNtStatusToDosError(Status);
return (Status);
}
}
//
// Define Process data block
//
RtlMoveMemory(pProcessDataDefinition,
&ProcessDataDefinition,
sizeof(PROCESS_DATA_DEFINITION));
pProcessDataDefinition->ProcessObjectType.PerfTime = SysTimeInfo.CurrentTime;
ProcessBufferOffset = 0;
// Now collect data for each process
NumProcessInstances = 0;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) pProcessBuffer;
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pProcessDataDefinition[1];
while ( TRUE ) {
// see if this instance will fit
TotalLen = (PCHAR) pPerfInstanceDefinition - (PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
(MAX_PROCESS_NAME_LENGTH+1+sizeof(DWORD))*
sizeof(WCHAR) +
SIZE_OF_PROCESS_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
NullProcess = FALSE;
// check for Live processes
// (i.e. name or threads)
if ((ProcessInfo->ImageName.Buffer != NULL) ||
(ProcessInfo->NumberOfThreads > 0)){
// thread is not Dead
// get process name
if (lProcessNameCollectionMethod == PNCM_MODULE_FILE) {
pProcessName = GetProcessSlowName (ProcessInfo);
} else {
pProcessName = GetProcessShortName (ProcessInfo);
}
} else {
// thread is dead
NullProcess = TRUE;
}
if ( !NullProcess ) {
// get the old process creation time the first time we are in
// this routine
if (!bOldestProcessTime) {
if (LargeIntegerLessThanOrEqualZero (OldestProcessTime))
OldestProcessTime = ProcessInfo->CreateTime;
else if (!(LargeIntegerLessThanOrEqualZero (ProcessInfo->CreateTime))) {
// both time values are not zero, see which one is smaller
CreateTimeDiff.QuadPart = OldestProcessTime.QuadPart -
ProcessInfo->CreateTime.QuadPart;
if (!(LargeIntegerLessThanOrEqualZero (CreateTimeDiff)))
OldestProcessTime = ProcessInfo->CreateTime;
}
}
// get Pool usage for this process
NumProcessInstances++;
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
0,
0,
(DWORD)-1,
pProcessName);
//
// Format and collect Process data
//
pPerfCounterBlock->ByteLength = SIZE_OF_PROCESS_DATA;
pliCounter = (LARGE_INTEGER UNALIGNED *) &pPerfCounterBlock[1];
//
// Convert User time from 100 nsec units to counter frequency.
//
pliProcessorTime = pliCounter;
llTotalUserTime += ProcessInfo->UserTime.QuadPart;
*++pliCounter = ProcessInfo->UserTime;
llTotalKernelTime += ProcessInfo->KernelTime.QuadPart;
*++pliCounter = ProcessInfo->KernelTime;
pliProcessorTime->QuadPart = ProcessInfo->UserTime.QuadPart +
ProcessInfo->KernelTime.QuadPart;
pdwCounter = (LPDWORD) ++pliCounter;
dwTotalPeakVirtualSize += ProcessInfo->PeakVirtualSize;
*pdwCounter = ProcessInfo->PeakVirtualSize;
dwTotalVirtualSize += ProcessInfo->VirtualSize;
*++pdwCounter = ProcessInfo->VirtualSize;
dwTotalPageFaults += ProcessInfo->PageFaultCount;
*++pdwCounter = ProcessInfo->PageFaultCount;
dwTotalPeakWorkingSet += ProcessInfo->PeakWorkingSetSize;
*++pdwCounter = ProcessInfo->PeakWorkingSetSize;
dwTotalWorkingSet += ProcessInfo->WorkingSetSize;
*++pdwCounter = ProcessInfo->WorkingSetSize;
dwTotalPeakPageFile += ProcessInfo->PeakPagefileUsage;
*++pdwCounter = ProcessInfo->PeakPagefileUsage;
dwTotalPageFile += ProcessInfo->PagefileUsage;
*++pdwCounter = ProcessInfo->PagefileUsage;
dwTotalPrivateBytes += ProcessInfo->PrivatePageCount;
*++pdwCounter = ProcessInfo->PrivatePageCount;
dwTotalThreadCount += ProcessInfo->NumberOfThreads;
*++pdwCounter = ProcessInfo->NumberOfThreads;
// base priority is not totaled
*++pdwCounter = ProcessInfo->BasePriority;
pliCounter = (LARGE_INTEGER UNALIGNED * )++pdwCounter;
// elpased time is not totaled
if (bOldestProcessTime &&
LargeIntegerLessThanOrEqualZero (ProcessInfo->CreateTime)) {
*pliCounter = OldestProcessTime;
} else {
*pliCounter = ProcessInfo->CreateTime;
}
// process ID is not totaled
pdwCounter = (PDWORD)++pliCounter;
*pdwCounter = (DWORD)ProcessInfo->UniqueProcessId;
*++pdwCounter = (DWORD)ProcessInfo->InheritedFromUniqueProcessId;
// fill the paged and nonpaged pool usages
dwTotalPagedPool += (DWORD)ProcessInfo->QuotaPagedPoolUsage;
*++pdwCounter = (DWORD)ProcessInfo->QuotaPagedPoolUsage;
dwTotalNonpagedPool += (DWORD)ProcessInfo->QuotaNonPagedPoolUsage;
*++pdwCounter = (DWORD)ProcessInfo->QuotaNonPagedPoolUsage;
// get the process handle count
dwTotalHandleCount += (DWORD)ProcessInfo->HandleCount;
*++pdwCounter = (DWORD)ProcessInfo->HandleCount;
++pdwCounter; // advance to next available byte
// set perfdata pointer to next byte
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *) pdwCounter;
}
// exit if this was the last process in list
if (ProcessInfo->NextEntryOffset == 0) {
break;
}
// point to next buffer in list
ProcessBufferOffset += ProcessInfo->NextEntryOffset;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
&pProcessBuffer[ProcessBufferOffset];
}
// see if the total instance will fit
TotalLen = (PCHAR) pPerfInstanceDefinition - (PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
(MAX_PROCESS_NAME_LENGTH+1+sizeof(DWORD))*
sizeof(WCHAR) +
SIZE_OF_PROCESS_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
// it looks like it will fit so create "total" instance
NumProcessInstances++;
// prefix with underscore to put at top of list box
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
0,
0,
(DWORD)-1,
&usTotal);
//
// Format and collect Process data
//
pliCounter = (LARGE_INTEGER UNALIGNED *) &pPerfCounterBlock[1];
//
// Convert User time from 100 nsec units to counter frequency.
//
pliProcessorTime = pliCounter++;
pliCounter->QuadPart = llTotalUserTime;
pliCounter++;
pliCounter->QuadPart = llTotalKernelTime;
pliProcessorTime->QuadPart = llTotalUserTime+llTotalKernelTime;
pdwCounter = (LPDWORD) ++pliCounter;
*pdwCounter = dwTotalPeakVirtualSize; // Peak Virtual Size
*++pdwCounter = dwTotalVirtualSize; // Virtual size
*++pdwCounter = dwTotalPageFaults; // page faults
*++pdwCounter = dwTotalPeakWorkingSet; // peak working set
*++pdwCounter = dwTotalWorkingSet; // current working set
*++pdwCounter = dwTotalPeakPageFile; // peak page file
*++pdwCounter = dwTotalPageFile; // current page file
*++pdwCounter = dwTotalPrivateBytes; // private bytes
*++pdwCounter = dwTotalThreadCount; // total threads
*++pdwCounter = 0; // base priority is not totaled
pliCounter = (LARGE_INTEGER UNALIGNED * )++pdwCounter;
pliCounter->QuadPart = 0; // elpased time is not totaled
pdwCounter = (PDWORD)++pliCounter;
*pdwCounter = 0; // process ID is not totaled
*++pdwCounter = 0; // Creating process ID is not totaled
// fill the paged and nonpaged pool usages
*++pdwCounter = dwTotalPagedPool; // paged pool
*++pdwCounter = dwTotalNonpagedPool; // nonpaged pool
// get the process handle count
*++pdwCounter = dwTotalHandleCount; // handle count
*++pdwCounter = dwTotalPagedPoolInUse; // paged pool in use
*++pdwCounter = dwTotalPagedPoolLimit; // paged pool limit
*++pdwCounter = dwTotalNonpagedPoolInUse; // nonpaged pool in use
*++pdwCounter = dwTotalNonpagedPoolLimit; // nonpaged pool limit
++pdwCounter;
// set perfdata pointer to next byte
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *) pdwCounter;
// flag so we don't have to get the oldest Process Creation time again.
bOldestProcessTime = TRUE;
// Note number of process instances
pProcessDataDefinition->ProcessObjectType.NumInstances =
NumProcessInstances;
//
// Now we know how large an area we used for the
// Process definition, so we can update the offset
// to the next object definition
//
pProcessDataDefinition->ProcessObjectType.TotalByteLength =
(PCHAR) pdwCounter - (PCHAR) pProcessDataDefinition;
*lppDataDefinition = (LPVOID) pdwCounter;
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QueryThreadData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryThreadData - Get data about threads
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
LARGE_INTEGER UNALIGNED *pliCounter;
THREAD_DATA_DEFINITION *pThreadDataDefinition;
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
LARGE_INTEGER UNALIGNED *pliProcessorTime;
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
PSYSTEM_THREAD_INFORMATION ThreadInfo;
ULONG ProcessNumber;
ULONG NumThreadInstances;
ULONG ThreadNumber;
ULONG ProcessBufferOffset;
BOOLEAN NullProcess;
// total thread accumulator variables
LONGLONG llTotalUserTime = 0;
LONGLONG llTotalKernelTime = 0;
DWORD dwTotalContextSwitches = 0;
DWORD *pCurrentPriority;
UNICODE_STRING ThreadName;
WCHAR ThreadNameBuffer[MAX_THREAD_NAME_LENGTH+1];
DWORD dwProcessorQueueLength = 0;
pThreadDataDefinition = (THREAD_DATA_DEFINITION *) *lppDataDefinition;
//
// Check for sufficient space for Thread object type definition
//
TotalLen = (PCHAR) pThreadDataDefinition - (PCHAR) lpData +
sizeof(THREAD_DATA_DEFINITION) +
SIZE_OF_THREAD_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Define Thread data block
//
ThreadName.Length =
ThreadName.MaximumLength = (MAX_THREAD_NAME_LENGTH + 1) * sizeof(WCHAR);
ThreadName.Buffer = ThreadNameBuffer;
RtlMoveMemory(pThreadDataDefinition,
&ThreadDataDefinition,
sizeof(THREAD_DATA_DEFINITION));
pThreadDataDefinition->ThreadObjectType.PerfTime = SysTimeInfo.CurrentTime;
ProcessBufferOffset = 0;
// Now collect data for each Thread
ProcessNumber = 0;
NumThreadInstances = 0;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pProcessBuffer;
pdwCounter = (DWORD *) &pThreadDataDefinition[1];
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pThreadDataDefinition[1];
while ( TRUE ) {
if ( ProcessInfo->ImageName.Buffer != NULL ||
ProcessInfo->NumberOfThreads > 0 ) {
NullProcess = FALSE;
} else {
NullProcess = TRUE;
}
ThreadNumber = 0; // Thread number of this process
ThreadInfo = (PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);
while ( !NullProcess &&
ThreadNumber < ProcessInfo->NumberOfThreads ) {
TotalLen = (PCHAR) pPerfInstanceDefinition -
(PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
(MAX_THREAD_NAME_LENGTH+1+sizeof(DWORD))*
sizeof(WCHAR) +
SIZE_OF_THREAD_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
// The only name we've got is the thread number
RtlIntegerToUnicodeString(ThreadNumber,
10,
&ThreadName);
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
pProcessDataDefinition->ProcessObjectType.ObjectNameTitleIndex,
ProcessNumber,
(DWORD)-1,
&ThreadName);
//
//
// Format and collect Thread data
//
pPerfCounterBlock->ByteLength = SIZE_OF_THREAD_DATA;
pliCounter = (LARGE_INTEGER UNALIGNED *) &pPerfCounterBlock[1];
//
// Convert User time from 100 nsec units to counter
// frequency.
//
pliProcessorTime = pliCounter;
llTotalUserTime += ThreadInfo->UserTime.QuadPart;
*++pliCounter = ThreadInfo->UserTime;
llTotalKernelTime += ThreadInfo->KernelTime.QuadPart;
*++pliCounter = ThreadInfo->KernelTime;
pliProcessorTime->QuadPart = ThreadInfo->UserTime.QuadPart +
ThreadInfo->KernelTime.QuadPart;
pdwCounter = (LPDWORD) ++pliCounter;
dwTotalContextSwitches += ThreadInfo->ContextSwitches;
*pdwCounter = ThreadInfo->ContextSwitches;
pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
*pliCounter = ThreadInfo->CreateTime;
pdwCounter = (PDWORD) ++pliCounter;
// set up pointer for current priority so we can clear
// this for Idle thread(s)
pCurrentPriority = pdwCounter;
*pdwCounter = ThreadInfo->Priority;
*++pdwCounter = ThreadInfo->BasePriority;
*++pdwCounter = (DWORD)ThreadInfo->StartAddress;
*++pdwCounter = (DWORD)ThreadInfo->ThreadState;
*++pdwCounter = (DWORD)ThreadInfo->WaitReason;
// the states info can be found in sdktools\pstat\pstat.c
if (*pdwCounter > 7) {
// unknown states are 7 and above
*pdwCounter = 7;
}
// only need to count threads in ready(1) state
if (ThreadInfo->ThreadState == 1) {
dwProcessorQueueLength++ ;
}
// now stuff in the process and thread id's
*++pdwCounter = (DWORD)ThreadInfo->ClientId.UniqueProcess;
*++pdwCounter = (DWORD)ThreadInfo->ClientId.UniqueThread;
++pdwCounter;
if (ThreadInfo->ClientId.UniqueProcess == 0) {
*pCurrentPriority = 0;
}
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
pdwCounter;
NumThreadInstances++;
ThreadNumber++;
ThreadInfo++;
}
if ( !NullProcess ) {
ProcessNumber++;
}
if (ProcessInfo->NextEntryOffset == 0) {
break;
}
ProcessBufferOffset += ProcessInfo->NextEntryOffset;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
&pProcessBuffer[ProcessBufferOffset];
}
// See if the total instance will fit
TotalLen = (PCHAR) pPerfInstanceDefinition -
(PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
(MAX_THREAD_NAME_LENGTH+1+sizeof(DWORD))*
sizeof(WCHAR) +
SIZE_OF_THREAD_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
// use the "total" for this instance
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
pProcessDataDefinition->ProcessObjectType.ObjectNameTitleIndex,
ProcessNumber,
(DWORD)-1,
&usTotal);
//
//
// Format and collect Thread data
//
pPerfCounterBlock->ByteLength = SIZE_OF_THREAD_DATA;
pliCounter = (LARGE_INTEGER UNALIGNED *) &pPerfCounterBlock[1];
pliProcessorTime = pliCounter;
pliCounter++;
pliCounter->QuadPart = llTotalUserTime; // total user time
pliCounter++;
pliCounter->QuadPart = llTotalKernelTime; // total Kernel time
pliProcessorTime->QuadPart = llTotalUserTime + llTotalKernelTime;
// total processor time
pdwCounter = (LPDWORD) ++pliCounter;
*pdwCounter = dwTotalContextSwitches; // total context switches
pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
pliCounter->QuadPart = 0; // elapsed time not totaled
pdwCounter = (PDWORD) ++pliCounter;
pCurrentPriority = pdwCounter;
*pdwCounter = 0; // priority not totaled
*++pdwCounter = 0; // base priority not totaled
*++pdwCounter = 0; // start addr not totaled
*++pdwCounter = 0; // thread state not totaled
*++pdwCounter = 0; // wait reason not totaled
*++pdwCounter = 0; // process ID not totaled
*++pdwCounter = 0; // thread id not totaled
++pdwCounter; // advance to next available byte
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
pdwCounter;
NumThreadInstances++;
// Note number of Thread instances
pThreadDataDefinition->ThreadObjectType.NumInstances =
NumThreadInstances;
//
// Now we know how large an area we used for the
// Thread definition, so we can update the offset
// to the next object definition
//
pThreadDataDefinition->ThreadObjectType.TotalByteLength =
(PCHAR) pdwCounter - (PCHAR) pThreadDataDefinition;
*lppDataDefinition = (LPVOID) pdwCounter;
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
// update system data with the ProcessorQueueLength if needed
if (pdwProcessorQueueLength) {
*pdwProcessorQueueLength = dwProcessorQueueLength;
}
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QueryObjectsData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryObjectsData - Get data about objects
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
OBJECTS_DATA_DEFINITION *pObjectsDataDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
POBJECT_TYPE_INFORMATION ObjectInfo;
WCHAR Buffer[ 256 ];
//
// Check for sufficient space for objects data
//
pObjectsDataDefinition = (OBJECTS_DATA_DEFINITION *) *lppDataDefinition;
TotalLen = (PCHAR) pObjectsDataDefinition -
(PCHAR) lpData +
sizeof(OBJECTS_DATA_DEFINITION) +
SIZE_OF_OBJECTS_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Define objects data block
//
RtlMoveMemory(pObjectsDataDefinition,
&ObjectsDataDefinition,
sizeof(OBJECTS_DATA_DEFINITION));
//
// Format and collect objects data
//
pPerfCounterBlock = (PERF_COUNTER_BLOCK *)
&pObjectsDataDefinition[1];
pPerfCounterBlock->ByteLength = SIZE_OF_OBJECTS_DATA;
pdwCounter = (PDWORD) (&pPerfCounterBlock[1]);
ObjectInfo = (POBJECT_TYPE_INFORMATION)Buffer;
NtQueryObject( NtCurrentProcess(),
ObjectTypeInformation,
ObjectInfo,
sizeof( Buffer ),
NULL
);
*pdwCounter = ObjectInfo->TotalNumberOfObjects;
NtQueryObject( NtCurrentThread(),
ObjectTypeInformation,
ObjectInfo,
sizeof( Buffer ),
NULL
);
*++pdwCounter = ObjectInfo->TotalNumberOfObjects;
NtQueryObject( hEvent,
ObjectTypeInformation,
ObjectInfo,
sizeof( Buffer ),
NULL
);
*++pdwCounter = ObjectInfo->TotalNumberOfObjects;
NtQueryObject( hSemaphore,
ObjectTypeInformation,
ObjectInfo,
sizeof( Buffer ),
NULL
);
*++pdwCounter = ObjectInfo->TotalNumberOfObjects;
NtQueryObject( hMutex,
ObjectTypeInformation,
ObjectInfo,
sizeof( Buffer ),
NULL
);
*++pdwCounter = ObjectInfo->TotalNumberOfObjects;
NtQueryObject( hSection,
ObjectTypeInformation,
ObjectInfo,
sizeof( Buffer ),
NULL
);
*++pdwCounter = ObjectInfo->TotalNumberOfObjects;
*lppDataDefinition = (LPVOID) ++pdwCounter;
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QueryRdrData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryRdrData - Get data about the Redirector
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
LARGE_INTEGER UNALIGNED *pliCounter;
NTSTATUS Status = ERROR_SUCCESS;
IO_STATUS_BLOCK IoStatusBlock;
RDR_DATA_DEFINITION *pRdrDataDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
REDIR_STATISTICS RdrStatistics;
//
// Check for sufficient space for redirector data
//
pRdrDataDefinition = (RDR_DATA_DEFINITION *) *lppDataDefinition;
TotalLen = (PCHAR) pRdrDataDefinition -
(PCHAR) lpData +
sizeof(RDR_DATA_DEFINITION) +
SIZE_OF_RDR_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Define objects data block
//
RtlMoveMemory(pRdrDataDefinition,
&RdrDataDefinition,
sizeof(RDR_DATA_DEFINITION));
//
// Format and collect redirector data
//
pPerfCounterBlock = (PERF_COUNTER_BLOCK *)
&pRdrDataDefinition[1];
pPerfCounterBlock->ByteLength = SIZE_OF_RDR_DATA;
if ( hRdr != NULL ) {
Status = NtFsControlFile(hRdr,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_LMR_GET_STATISTICS,
NULL,
0,
&RdrStatistics,
sizeof(RdrStatistics)
);
}
if ( hRdr != NULL && NT_SUCCESS(Status) ) {
pliCounter = (LARGE_INTEGER UNALIGNED * ) (&pPerfCounterBlock[1]);
pliCounter->QuadPart = RdrStatistics.BytesReceived.QuadPart +
RdrStatistics.BytesTransmitted.QuadPart;
pdwCounter = (PDWORD) ++pliCounter;
*pdwCounter = RdrStatistics.ReadOperations +
RdrStatistics.WriteOperations;
pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
pliCounter->QuadPart = RdrStatistics.SmbsReceived.QuadPart +
RdrStatistics.SmbsTransmitted.QuadPart;
*++pliCounter = RdrStatistics.BytesReceived;
*++pliCounter = RdrStatistics.SmbsReceived;
*++pliCounter = RdrStatistics.PagingReadBytesRequested;
*++pliCounter = RdrStatistics.NonPagingReadBytesRequested;
*++pliCounter = RdrStatistics.CacheReadBytesRequested;
*++pliCounter = RdrStatistics.NetworkReadBytesRequested;
*++pliCounter = RdrStatistics.BytesTransmitted;
*++pliCounter = RdrStatistics.SmbsTransmitted;
*++pliCounter = RdrStatistics.PagingWriteBytesRequested;
*++pliCounter = RdrStatistics.NonPagingWriteBytesRequested;
*++pliCounter = RdrStatistics.CacheWriteBytesRequested;
*++pliCounter = RdrStatistics.NetworkWriteBytesRequested;
pdwCounter = (PDWORD) ++pliCounter;
*pdwCounter = RdrStatistics.ReadOperations;
*++pdwCounter = RdrStatistics.RandomReadOperations;
*++pdwCounter = RdrStatistics.ReadSmbs;
*++pdwCounter = RdrStatistics.LargeReadSmbs;
*++pdwCounter = RdrStatistics.SmallReadSmbs;
*++pdwCounter = RdrStatistics.WriteOperations;
*++pdwCounter = RdrStatistics.RandomWriteOperations;
*++pdwCounter = RdrStatistics.WriteSmbs;
*++pdwCounter = RdrStatistics.LargeWriteSmbs;
*++pdwCounter = RdrStatistics.SmallWriteSmbs;
*++pdwCounter = RdrStatistics.RawReadsDenied;
*++pdwCounter = RdrStatistics.RawWritesDenied;
*++pdwCounter = RdrStatistics.NetworkErrors;
*++pdwCounter = RdrStatistics.Sessions;
*++pdwCounter = RdrStatistics.Reconnects;
*++pdwCounter = RdrStatistics.CoreConnects;
*++pdwCounter = RdrStatistics.Lanman20Connects;
*++pdwCounter = RdrStatistics.Lanman21Connects;
*++pdwCounter = RdrStatistics.LanmanNtConnects;
*++pdwCounter = RdrStatistics.ServerDisconnects;
*++pdwCounter = RdrStatistics.HungSessions;
*++pdwCounter = RdrStatistics.CurrentCommands;
*lppDataDefinition = (LPVOID) ++pdwCounter;
} else {
//
// Failure to access Redirector: clear counters to 0
//
memset(&pPerfCounterBlock[1],
0,
SIZE_OF_RDR_DATA - sizeof(pPerfCounterBlock));
*lppDataDefinition = (PBYTE) pPerfCounterBlock +
SIZE_OF_RDR_DATA;
}
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QueryBrowserData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryBrowserSrvData - Get statistic data about the Browser
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
LARGE_INTEGER UNALIGNED *pliCounter;
NTSTATUS Status = ERROR_SUCCESS;
BROWSER_DATA_DEFINITION *pBrowserDataDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
BROWSER_STATISTICS BrowserStatistics;
LPBROWSER_STATISTICS pBrowserStatistics = &BrowserStatistics;
//
// Check for sufficient space for browser data
//
pBrowserDataDefinition = (BROWSER_DATA_DEFINITION *) *lppDataDefinition;
TotalLen = (PCHAR) pBrowserDataDefinition -
(PCHAR) lpData +
sizeof(BROWSER_DATA_DEFINITION) +
SIZE_OF_BROWSER_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Define objects data block
//
RtlMoveMemory(pBrowserDataDefinition,
&BrowserDataDefinition,
sizeof(BROWSER_DATA_DEFINITION));
//
// Format and collect browser data
//
pPerfCounterBlock = (PERF_COUNTER_BLOCK *)
&pBrowserDataDefinition[1];
pPerfCounterBlock->ByteLength = SIZE_OF_BROWSER_DATA;
if ( BrowserStatFunction != NULL ) {
Status = (*BrowserStatFunction) (NULL,
&pBrowserStatistics
);
}
if ( BrowserStatFunction != NULL && NT_SUCCESS(Status) ) {
pliCounter = (LARGE_INTEGER UNALIGNED * ) (&pPerfCounterBlock[1]);
*pliCounter = BrowserStatistics.NumberOfServerAnnouncements;
*++pliCounter = BrowserStatistics.NumberOfDomainAnnouncements;
++pliCounter;
pliCounter->QuadPart = BrowserStatistics.NumberOfServerAnnouncements.QuadPart +
BrowserStatistics.NumberOfDomainAnnouncements.QuadPart;
pdwCounter = (PDWORD) ++pliCounter;
*pdwCounter = BrowserStatistics.NumberOfElectionPackets;
*++pdwCounter = BrowserStatistics.NumberOfMailslotWrites;
*++pdwCounter = BrowserStatistics.NumberOfGetBrowserServerListRequests;
*++pdwCounter = BrowserStatistics.NumberOfServerEnumerations;
*++pdwCounter = BrowserStatistics.NumberOfDomainEnumerations;
*++pdwCounter = BrowserStatistics.NumberOfOtherEnumerations;
*++pdwCounter = BrowserStatistics.NumberOfServerEnumerations
+ BrowserStatistics.NumberOfDomainEnumerations
+ BrowserStatistics.NumberOfOtherEnumerations;
*++pdwCounter = BrowserStatistics.NumberOfMissedServerAnnouncements;
*++pdwCounter = BrowserStatistics.NumberOfMissedMailslotDatagrams;
*++pdwCounter = BrowserStatistics.NumberOfMissedGetBrowserServerListRequests;
*++pdwCounter = BrowserStatistics.NumberOfFailedServerAnnounceAllocations;
*++pdwCounter = BrowserStatistics.NumberOfFailedMailslotAllocations;
*++pdwCounter = BrowserStatistics.NumberOfFailedMailslotReceives;
*++pdwCounter = BrowserStatistics.NumberOfFailedMailslotWrites;
*++pdwCounter = BrowserStatistics.NumberOfFailedMailslotOpens;
*++pdwCounter = BrowserStatistics.NumberOfDuplicateMasterAnnouncements;
pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
*pliCounter = BrowserStatistics.NumberOfIllegalDatagrams;
*lppDataDefinition = (LPVOID) ++pliCounter;
} else {
//
// Failure to access Browser: clear counters to 0
//
memset(&pPerfCounterBlock[1],
0,
SIZE_OF_BROWSER_DATA - sizeof(pPerfCounterBlock));
*lppDataDefinition = (PBYTE) pPerfCounterBlock +
SIZE_OF_BROWSER_DATA;
}
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QuerySrvData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QuerySrvData - Get data about the Server
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
LARGE_INTEGER UNALIGNED *pliCounter;
NTSTATUS Status = ERROR_SUCCESS;
IO_STATUS_BLOCK IoStatusBlock;
SRV_DATA_DEFINITION *pSrvDataDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
SRV_STATISTICS SrvStatistics;
ULONG Remainder;
CHAR NullChar = '\0';
//
// Check for sufficient space for server data
//
pSrvDataDefinition = (SRV_DATA_DEFINITION *) *lppDataDefinition;
TotalLen = (PCHAR) pSrvDataDefinition -
(PCHAR) lpData +
sizeof(SRV_DATA_DEFINITION) +
SIZE_OF_SRV_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Define objects data block
//
RtlMoveMemory(pSrvDataDefinition,
&SrvDataDefinition,
sizeof(SRV_DATA_DEFINITION));
//
// Format and collect server data
//
pPerfCounterBlock = (PERF_COUNTER_BLOCK *)
&pSrvDataDefinition[1];
pPerfCounterBlock->ByteLength = SIZE_OF_SRV_DATA;
if ( hSrv != NULL ) {
Status = NtFsControlFile(hSrv,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_SRV_GET_STATISTICS,
NULL,
0,
&SrvStatistics,
sizeof(SrvStatistics)
);
}
if ( hSrv != NULL && NT_SUCCESS(Status) ) {
pliCounter = (LARGE_INTEGER UNALIGNED * ) (&pPerfCounterBlock[1]);
pliCounter->QuadPart = SrvStatistics.TotalBytesSent.QuadPart +
SrvStatistics.TotalBytesReceived.QuadPart;
*++pliCounter = SrvStatistics.TotalBytesReceived;
*++pliCounter = SrvStatistics.TotalBytesSent;
pdwCounter = (PDWORD) ++pliCounter;
*pdwCounter = SrvStatistics.SessionsTimedOut;
*++pdwCounter = SrvStatistics.SessionsErroredOut;
*++pdwCounter = SrvStatistics.SessionsLoggedOff;
*++pdwCounter = SrvStatistics.SessionsForcedLogOff;
*++pdwCounter = SrvStatistics.LogonErrors;
*++pdwCounter = SrvStatistics.AccessPermissionErrors;
*++pdwCounter = SrvStatistics.GrantedAccessErrors;
*++pdwCounter = SrvStatistics.SystemErrors;
*++pdwCounter = SrvStatistics.BlockingSmbsRejected;
*++pdwCounter = SrvStatistics.WorkItemShortages;
*++pdwCounter = SrvStatistics.TotalFilesOpened;
*++pdwCounter = SrvStatistics.CurrentNumberOfOpenFiles;
*++pdwCounter = SrvStatistics.CurrentNumberOfSessions;
*++pdwCounter = SrvStatistics.CurrentNumberOfOpenSearches;
*++pdwCounter = SrvStatistics.CurrentNonPagedPoolUsage;
*++pdwCounter = SrvStatistics.NonPagedPoolFailures;
*++pdwCounter = SrvStatistics.PeakNonPagedPoolUsage;
*++pdwCounter = SrvStatistics.CurrentPagedPoolUsage;
*++pdwCounter = SrvStatistics.PagedPoolFailures;
*++pdwCounter = SrvStatistics.PeakPagedPoolUsage;
*++pdwCounter = SrvStatistics.TotalWorkContextBlocksQueued.Count;
++pdwCounter;
// one for rate, and one for raw
*pdwCounter++ = SrvStatistics.SessionLogonAttempts;
*pdwCounter = SrvStatistics.SessionLogonAttempts;
*lppDataDefinition = (LPVOID) ++pdwCounter;
} else {
//
// Failure to access Server: clear counters to 0
//
memset(&pPerfCounterBlock[1],
0,
SIZE_OF_SRV_DATA - sizeof(pPerfCounterBlock));
*lppDataDefinition = (PBYTE) pPerfCounterBlock +
SIZE_OF_SRV_DATA;
}
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QuerySrvQueueData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QuerySrvQueueData - Get data about the Server Queues
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD dwDataBufferLength;
DWORD dwPerfDataLength;
LONG nQueue;
DWORD *pdwCounter;
LARGE_INTEGER UNALIGNED *pliCounter;
NTSTATUS Status = ERROR_SUCCESS;
IO_STATUS_BLOCK IoStatusBlock;
SRVQ_DATA_DEFINITION *pSrvQDataDefinition;
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
SRV_QUEUE_STATISTICS *pSrvQueueStatistics;
SRV_QUEUE_STATISTICS *pThisQueueStatistics;
#define MAX_SRVQ_NAME_LENGTH 16
UNICODE_STRING QueueName;
WCHAR QueueNameBuffer[MAX_SRVQ_NAME_LENGTH];
ULONG Remainder;
NET_API_STATUS NetStatus;
CHAR NullChar = '\0';
// compute the various buffer sizes required
dwDataBufferLength = sizeof(SRV_QUEUE_STATISTICS) *
(BasicInfo.NumberOfProcessors + 1);
// assign local pointer to current position in buffer
pSrvQDataDefinition = (SRVQ_DATA_DEFINITION *) *lppDataDefinition;
//
// Check for sufficient space for server data
//
TotalLen = (PCHAR) pSrvQDataDefinition -
(PCHAR) lpData +
sizeof(SRV_DATA_DEFINITION) +
SIZE_OF_SRV_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Define perf object data block
//
RtlMoveMemory(pSrvQDataDefinition,
&SrvQDataDefinition,
sizeof(SRVQ_DATA_DEFINITION));
//
// Format and collect server Queue data
//
QueueName.Length = 0;
QueueName.MaximumLength = sizeof(QueueNameBuffer);
QueueName.Buffer = QueueNameBuffer;
pSrvQueueStatistics = (SRV_QUEUE_STATISTICS *)ALLOCMEM (
RtlProcessHeap(), HEAP_ZERO_MEMORY, dwDataBufferLength);
if (pSrvQueueStatistics == NULL) {
return ERROR_OUTOFMEMORY;
}
if ( hSrv != NULL ) {
Status = NtFsControlFile(hSrv,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_SRV_GET_QUEUE_STATISTICS,
NULL,
0,
pSrvQueueStatistics,
dwDataBufferLength
);
}
nQueue = 0;
if ( hSrv != NULL && NT_SUCCESS(Status) ) {
// server data was collected successfully so...
// process each processor queue instance.
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pSrvQDataDefinition[1];
for (nQueue = 0; nQueue < BasicInfo.NumberOfProcessors; nQueue++) {
// see if this instance will fit
TotalLen = (PCHAR) pPerfInstanceDefinition - (PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
8 + // size of 3 (unicode) digit queuelength name
SIZE_OF_SRVQ_DATA;
if ( *lpcbData < TotalLen ) {
FREEMEM (RtlProcessHeap(), 0, pSrvQueueStatistics);
return ERROR_MORE_DATA;
}
RtlIntegerToUnicodeString(nQueue,
10,
&QueueName);
// there should be enough room for this instance so initialize it
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
0,
0,
(DWORD)-1,
&QueueName);
pPerfCounterBlock->ByteLength = SIZE_OF_SRVQ_DATA;
// initialize pointers for this instance
pThisQueueStatistics = &pSrvQueueStatistics[nQueue];
pdwCounter = (DWORD *) &pPerfCounterBlock[1];
*pdwCounter++ = pThisQueueStatistics->QueueLength;
*pdwCounter++ = pThisQueueStatistics->ActiveThreads;
*pdwCounter++ = pThisQueueStatistics->AvailableThreads;
*pdwCounter++ = pThisQueueStatistics->FreeWorkItems;
*pdwCounter++ = pThisQueueStatistics->StolenWorkItems;
*pdwCounter++ = pThisQueueStatistics->NeedWorkItem;
*pdwCounter++ = pThisQueueStatistics->CurrentClients;
pliCounter = (LARGE_INTEGER UNALIGNED *)pdwCounter;
*pliCounter++ = pThisQueueStatistics->BytesReceived;
*pliCounter++ = pThisQueueStatistics->BytesSent;
(*pliCounter++).QuadPart = pThisQueueStatistics->BytesSent.QuadPart +
pThisQueueStatistics->BytesReceived.QuadPart;
*pliCounter++ = pThisQueueStatistics->ReadOperations;
*pliCounter++ = pThisQueueStatistics->BytesRead;
*pliCounter++ = pThisQueueStatistics->WriteOperations;
*pliCounter++ = pThisQueueStatistics->BytesWritten;
(*pliCounter++).QuadPart = pThisQueueStatistics->BytesWritten.QuadPart +
pThisQueueStatistics->BytesRead.QuadPart;
(*pliCounter++).QuadPart = pThisQueueStatistics->ReadOperations.QuadPart +
pThisQueueStatistics->WriteOperations.QuadPart;
pdwCounter = (DWORD *)pliCounter;
*++pdwCounter = pThisQueueStatistics->TotalWorkContextBlocksQueued.Count;
// update the current pointer
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
pdwCounter;
}
RtlInitUnicodeString (&QueueName, L"Blocking Queue");
// now load the "blocking" queue data
// see if this instance will fit
TotalLen = (PCHAR) pPerfInstanceDefinition - (PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
DWORD_MULTIPLE(QueueName.Length + sizeof(WCHAR)) +
SIZE_OF_SRVQ_DATA;
if ( *lpcbData < TotalLen ) {
FREEMEM (RtlProcessHeap(), 0, pSrvQueueStatistics);
return ERROR_MORE_DATA;
}
// there should be enough room for this instance so initialize it
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
0,
0,
(DWORD)-1,
&QueueName);
pPerfCounterBlock->ByteLength = SIZE_OF_SRVQ_DATA;
// initialize pointers for this instance
pdwCounter = (DWORD *) &pPerfCounterBlock[1];
pThisQueueStatistics = &pSrvQueueStatistics[nQueue];
*pdwCounter++ = pThisQueueStatistics->QueueLength;
*pdwCounter++ = pThisQueueStatistics->ActiveThreads;
*pdwCounter++ = pThisQueueStatistics->AvailableThreads;
*pdwCounter++ = 0;
*pdwCounter++ = 0;
*pdwCounter++ = 0;
*pdwCounter++ = 0;
pliCounter = (LARGE_INTEGER UNALIGNED *)pdwCounter;
*pliCounter++ = pThisQueueStatistics->BytesReceived;
*pliCounter++ = pThisQueueStatistics->BytesSent;
(*pliCounter++).QuadPart = pThisQueueStatistics->BytesSent.QuadPart +
pThisQueueStatistics->BytesReceived.QuadPart;
(*pliCounter++).QuadPart = 0;
*pliCounter++ = pThisQueueStatistics->BytesRead;
(*pliCounter++).QuadPart = 0;
*pliCounter++ = pThisQueueStatistics->BytesWritten;
(*pliCounter++).QuadPart = pThisQueueStatistics->BytesWritten.QuadPart +
pThisQueueStatistics->BytesRead.QuadPart;
(*pliCounter++).QuadPart = 0;
pdwCounter = (DWORD *)pliCounter;
*pdwCounter++ = pThisQueueStatistics->TotalWorkContextBlocksQueued.Count;
nQueue++; // to include the Blocking Queue statistics entry
// update the current pointer
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
pdwCounter;
// release buffer
FREEMEM (RtlProcessHeap(), 0, pSrvQueueStatistics);
// update queue (instance) count in object data block
pSrvQDataDefinition->SrvQueueObjectType.NumInstances = nQueue;
// update available length
pSrvQDataDefinition->SrvQueueObjectType.TotalByteLength =
(PCHAR) pdwCounter - (PCHAR) pSrvQDataDefinition;
// update pointer to next available byte
*lppDataDefinition = (LPVOID) pdwCounter;
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
} else {
// unable to read server queue data for some reason so don't return this
// object
}
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QueryPageFileData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryPageFileData - Get data about Pagefile(s)
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
DWORD PageFileNumber;
DWORD NumPageFileInstances;
DWORD dwReturnedBufferSize;
DWORD dwTotalTotalSize = 0;
DWORD dwTotalPeakUsage = 0;
DWORD dwTotalInUse = 0;
NTSTATUS status;
PSYSTEM_PAGEFILE_INFORMATION pThisPageFile;
PAGEFILE_DATA_DEFINITION *pPageFileDataDefinition;
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
pPageFileDataDefinition = (PAGEFILE_DATA_DEFINITION *) *lppDataDefinition;
//
// Check for sufficient space for the Pagefile object type definition
//
TotalLen = (PCHAR) pPageFileDataDefinition - (PCHAR) lpData +
sizeof(PAGEFILE_DATA_DEFINITION) +
SIZE_OF_PAGEFILE_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
status = (NTSTATUS) -1;
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;
pSysPageFileInfo = REALLOCMEM (RtlProcessHeap(),
0, pSysPageFileInfo,
dwSysPageFileInfoSize);
}
if ( !NT_SUCCESS(status) ) {
status = (error_status_t)RtlNtStatusToDosError(status);
return status;
}
//
// Define Page File data block
//
RtlMoveMemory(pPageFileDataDefinition,
&PagefileDataDefinition,
sizeof(PAGEFILE_DATA_DEFINITION));
// Now load data for each PageFile
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 ) {
TotalLen = (PCHAR) pPerfInstanceDefinition -
(PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
pThisPageFile->PageFileName.Length +
SIZE_OF_PAGEFILE_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
// Build an Instance
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
0,
0,
(DWORD)-1,
&pThisPageFile->PageFileName);
//
// Format the pagefile data
//
pPerfCounterBlock->ByteLength = SIZE_OF_PAGEFILE_DATA;
pdwCounter = (DWORD *)(&pPerfCounterBlock[1]);
*pdwCounter++ = pThisPageFile->TotalInUse;
*pdwCounter++ = pThisPageFile->TotalSize;
*pdwCounter++ = pThisPageFile->PeakUsage;
*pdwCounter++ = pThisPageFile->TotalSize;
// update the total accumulators
dwTotalTotalSize += pThisPageFile->TotalSize;
dwTotalPeakUsage += pThisPageFile->PeakUsage;
dwTotalInUse += pThisPageFile->TotalInUse;
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
pdwCounter;
NumPageFileInstances++;
PageFileNumber++;
if (pThisPageFile->NextEntryOffset != 0) {
pThisPageFile = (PSYSTEM_PAGEFILE_INFORMATION)\
((BYTE *)pThisPageFile + pThisPageFile->NextEntryOffset);
} else {
break;
}
}
TotalLen = (PCHAR) pPerfInstanceDefinition -
(PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
usTotal.Length +
SIZE_OF_PAGEFILE_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
// Build the Total Instance
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
0,
0,
(DWORD)-1,
&usTotal);
//
// Format the pagefile data
//
pPerfCounterBlock->ByteLength = SIZE_OF_PAGEFILE_DATA;
pdwCounter = (DWORD *)(&pPerfCounterBlock[1]);
*pdwCounter++ = dwTotalInUse;
*pdwCounter++ = dwTotalTotalSize;
*pdwCounter++ = dwTotalPeakUsage;
*pdwCounter++ = dwTotalTotalSize;
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
pdwCounter;
NumPageFileInstances++;
// Note number of PageFile instances
pPageFileDataDefinition->PagefileObjectType.NumInstances =
NumPageFileInstances;
//
// Now we know how large an area we used for the
// Thread definition, so we can update the offset
// to the next object definition
//
pPageFileDataDefinition->PagefileObjectType.TotalByteLength =
(PCHAR) pdwCounter - (PCHAR) pPageFileDataDefinition;
*lppDataDefinition = (LPVOID) pdwCounter;
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
LONG
QueryExtensibleData (
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryExtensibleData - Get data from extensible objects
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD NumObjectType;
DWORD Win32Error=ERROR_SUCCESS; // Failure code
DWORD BytesLeft;
DWORD NumObjectTypes;
LPVOID lpExtDataBuffer = NULL;
LPVOID lpCallBuffer = NULL;
LPVOID lpLowGuardPage = NULL;
LPVOID lpHiGuardPage = NULL;
LPVOID lpEndPointer = NULL;
LPVOID lpBufferBefore = NULL;
LPVOID lpBufferAfter = NULL;
LPDWORD lpCheckPointer;
BOOL bGuardPageOK;
BOOL bBufferOK;
BOOL bException;
LPTSTR szMessageArray[8];
DWORD dwRawDataDwords[8]; // raw data buffer
DWORD dwDataIndex;
WORD wStringIndex;
LONG lReturnValue = ERROR_SUCCESS;
DWORD dwIndex;
LONG lInstIndex;
PERF_OBJECT_TYPE *pObject, *pNextObject;
PERF_INSTANCE_DEFINITION *pInstance;
PERF_DATA_BLOCK *pPerfData;
BOOL bForeignDataBuffer;
HEAP_PROBE();
for (NumObjectType = 0;
NumObjectType < NumExtensibleObjects;
NumObjectType++) {
// initialize values to pass to the extensible counter function
NumObjectTypes = 0;
BytesLeft = *lpcbData - ((LPBYTE) *lppDataDefinition - lpData);
bException = FALSE;
// allocate a local block of memory to pass to the
// extensible counter function.
lpExtDataBuffer = ALLOCMEM (RtlProcessHeap(),
HEAP_ZERO_MEMORY, BytesLeft + (2*GUARD_PAGE_SIZE));
if (lpExtDataBuffer != NULL) {
// set buffer pointers
lpLowGuardPage = lpExtDataBuffer;
lpCallBuffer = (LPBYTE)lpExtDataBuffer + GUARD_PAGE_SIZE;
lpHiGuardPage = (LPBYTE)lpCallBuffer + BytesLeft;
lpEndPointer = (LPBYTE)lpHiGuardPage + GUARD_PAGE_SIZE;
lpBufferBefore = lpCallBuffer;
lpBufferAfter = NULL;
// initialize GuardPage Data
memset (lpLowGuardPage, GUARD_PAGE_CHAR, GUARD_PAGE_SIZE);
memset (lpHiGuardPage, GUARD_PAGE_CHAR, GUARD_PAGE_SIZE);
try {
//
// Collect data from extesible objects
//
Win32Error =
(*ExtensibleObjects[NumObjectType].CollectProc) (
lpValueName,
&lpCallBuffer,
&BytesLeft,
&NumObjectTypes);
if ((Win32Error == ERROR_SUCCESS) && (BytesLeft > 0)) {
// a data buffer was returned and
// the function returned OK so see how things
// turned out...
//
lpBufferAfter = lpCallBuffer;
//
// check for buffer corruption here
//
bBufferOK = TRUE; // assume it's ok until a check fails
//
if (lExtCounterTestLevel <= EXT_TEST_BASIC) {
//
// check 1: bytes left should be the same as
// new data buffer ptr - orig data buffer ptr
//
if (BytesLeft != (DWORD)((LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore)) {
if (lEventLogLevel >= LOG_DEBUG) {
// issue WARNING, that bytes left param is incorrect
// load data for eventlog message
// since this error is correctable (though with
// some risk) this won't be reported at LOG_USER
// level
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] = BytesLeft;
dwRawDataDwords[dwDataIndex++] =
(LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szServiceName;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szLibraryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_BUFFER_POINTER_MISMATCH, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
// we'll keep the buffer, since the returned bytes left
// value is ignored anyway, in order to make the
// rest of this function work, we'll fix it here
BytesLeft = (LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore;
}
//
// check 2: buffer after ptr should be < hi Guard page ptr
//
if (((LPBYTE)lpBufferAfter >= (LPBYTE)lpHiGuardPage) && bBufferOK) {
// see if they exceeded the allocated memory
if ((LPBYTE)lpBufferAfter >= (LPBYTE)lpEndPointer) {
// this is very serious since they've probably trashed
// the heap by overwriting the heap sig. block
// issue ERROR, buffer overrun
if (lEventLogLevel >= LOG_USER) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] =
(LPBYTE)lpBufferAfter - (LPBYTE)lpHiGuardPage;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szLibraryName;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szServiceName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_HEAP_ERROR, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
} else {
// issue ERROR, buffer overrun
if (lEventLogLevel >= LOG_USER) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] =
(LPBYTE)lpBufferAfter - (LPBYTE)lpHiGuardPage;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szLibraryName;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szServiceName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_BUFFER_OVERFLOW, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
}
bBufferOK = FALSE;
// since the DLL overran the buffer, the buffer
// must be too small (no comments about the DLL
// will be made here) so the status will be
// changed to ERROR_MORE_DATA and the function
// will return.
Win32Error = ERROR_MORE_DATA;
}
//
// check 3: check lo guard page for corruption
//
if (bBufferOK) {
bGuardPageOK = TRUE;
for (lpCheckPointer = (LPDWORD)lpLowGuardPage;
lpCheckPointer < (LPDWORD)lpBufferBefore;
lpCheckPointer++) {
if (*lpCheckPointer != GUARD_PAGE_DWORD) {
bGuardPageOK = FALSE;
break;
}
}
if (!bGuardPageOK) {
// issue ERROR, Lo Guard Page corrupted
if (lEventLogLevel >= LOG_USER) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szLibraryName;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szServiceName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_GUARD_PAGE_VIOLATION, // event
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
bBufferOK = FALSE;
}
}
//
// check 4: check hi guard page for corruption
//
if (bBufferOK) {
bGuardPageOK = TRUE;
for (lpCheckPointer = (LPDWORD)lpHiGuardPage;
lpCheckPointer < (LPDWORD)lpEndPointer;
lpCheckPointer++) {
if (*lpCheckPointer != GUARD_PAGE_DWORD) {
bGuardPageOK = FALSE;
break;
}
}
if (!bGuardPageOK) {
// issue ERROR, Hi Guard Page corrupted
if (lEventLogLevel >= LOG_USER) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szLibraryName;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szServiceName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_GUARD_PAGE_VIOLATION, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
bBufferOK = FALSE;
}
}
//
if ((lExtCounterTestLevel <= EXT_TEST_ALL) && bBufferOK) {
//
// Internal consistency checks
//
//
// Check 5: Check object length field values
//
// first test to see if this is a foreign
// computer data block or not
//
pPerfData = (PERF_DATA_BLOCK *)lpBufferBefore;
if ((pPerfData->Signature[0] == (WCHAR)'P') &&
(pPerfData->Signature[1] == (WCHAR)'E') &&
(pPerfData->Signature[2] == (WCHAR)'R') &&
(pPerfData->Signature[3] == (WCHAR)'F')) {
// if this is a foreign computer data block, then the
// first object is after the header
pObject = (PERF_OBJECT_TYPE *) (
(LPBYTE)pPerfData + pPerfData->HeaderLength);
bForeignDataBuffer = TRUE;
} else {
// otherwise, if this is just a buffer from
// an extensible counter, the object starts
// at the beginning of the buffer
pObject = (PERF_OBJECT_TYPE *)lpBufferBefore;
bForeignDataBuffer = FALSE;
}
// go to where the pointers say the end of the
// buffer is and then see if it's where it
// should be
for (dwIndex = 0; dwIndex < NumObjectTypes; dwIndex++) {
pObject = (PERF_OBJECT_TYPE *)((LPBYTE)pObject +
pObject->TotalByteLength);
}
if ((LPBYTE)pObject != (LPBYTE)lpCallBuffer) {
// then a length field is incorrect. This is FATAL
// since it can corrupt the rest of the buffer
// and render the buffer unusable.
if (lEventLogLevel >= LOG_USER) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] = NumObjectTypes;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szLibraryName;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szServiceName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_INCORRECT_OBJECT_LENGTH, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
bBufferOK = FALSE;
}
//
// Test 6: Test instance field size values
//
if (bBufferOK) {
// set object pointer
if (bForeignDataBuffer) {
pObject = (PERF_OBJECT_TYPE *) (
(LPBYTE)pPerfData + pPerfData->HeaderLength);
} else {
// otherwise, if this is just a buffer from
// an extensible counter, the object starts
// at the beginning of the buffer
pObject = (PERF_OBJECT_TYPE *)lpBufferBefore;
}
for (dwIndex = 0; dwIndex < NumObjectTypes; dwIndex++) {
pNextObject = (PERF_OBJECT_TYPE *)((LPBYTE)pObject +
pObject->TotalByteLength);
if (pObject->NumInstances != PERF_NO_INSTANCES) {
pInstance = (PERF_INSTANCE_DEFINITION *)
((LPBYTE)pObject + pObject->DefinitionLength);
lInstIndex = 0;
while (lInstIndex < pObject->NumInstances) {
PERF_COUNTER_BLOCK *pCounterBlock;
pCounterBlock = (PERF_COUNTER_BLOCK *)
((PCHAR) pInstance + pInstance->ByteLength);
pInstance = (PERF_INSTANCE_DEFINITION *)
((PCHAR) pCounterBlock + pCounterBlock->ByteLength);
lInstIndex++;
}
if ((LPBYTE)pInstance > (LPBYTE)pNextObject) {
bBufferOK = FALSE;
}
}
if (!bBufferOK) {
break;
} else {
pObject = pNextObject;
}
}
if (!bBufferOK) {
if (lEventLogLevel >= LOG_USER) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] = pObject->ObjectNameTitleIndex;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szLibraryName;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szServiceName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_INCORRECT_INSTANCE_LENGTH, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
}
}
}
}
//
// if all the tests pass,then copy the data to the
// original buffer and update the pointers
if (bBufferOK) {
RtlMoveMemory (*lppDataDefinition,
lpBufferBefore,
BytesLeft); // returned buffer size
(LPBYTE)(*lppDataDefinition) += BytesLeft; // update data pointer
} else {
NumObjectTypes = 0; // since this buffer was tossed
}
} else {
NumObjectTypes = 0; // clear counter
}// end if function returned successfully
} except (EXCEPTION_EXECUTE_HANDLER) {
Win32Error = GetExceptionCode();
bException = TRUE;
}
FREEMEM (RtlProcessHeap(), 0, lpExtDataBuffer);
} else {
// unable to allocate memory so set error value
Win32Error = ERROR_OUTOFMEMORY;
} // end if temp buffer allocated successfully
//
// Update the count of the number of object types
//
((PPERF_DATA_BLOCK) lpData)->NumObjectTypes += NumObjectTypes;
if ( Win32Error != ERROR_SUCCESS) {
if (bException || (Win32Error != ERROR_MORE_DATA)) {
// inform on exceptions & illegal error status only
if (lEventLogLevel >= LOG_USER) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] = Win32Error;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szServiceName;
szMessageArray[wStringIndex++] =
ExtensibleObjects[NumObjectType].szLibraryName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_COLLECT_PROC_EXCEPTION, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
} else {
if (bException) {
KdPrint (("\nPERFLIB: Extensible Counter %d generated an exception code: 0x%8.8x (%dL)",
NumObjectType, Win32Error, Win32Error));
} else {
KdPrint (("\nPERFLIB: Extensible Counter %d returned error code: 0x%8.8x (%dL)",
NumObjectType, Win32Error, Win32Error));
}
}
}
// the ext. dll is only supposed to return:
// ERROR_SUCCESS even if it encountered a problem, OR
// ERROR_MODE_DATA if the buffer was too small.
// if it's ERROR_MORE_DATA, then break and return the
// error now, since it'll just be returned again and again.
if (Win32Error == ERROR_MORE_DATA) {
lReturnValue = Win32Error;
break;
}
}
}
HEAP_PROBE();
return lReturnValue;
}
LONG
PerfRegCloseKey (
IN OUT PHKEY phKey
)
/*++
Routine Description:
Closes all performance handles when the usage count drops to 0.
Arguments:
phKey - Supplies a handle to an open key to be closed.
Return Value:
Returns ERROR_SUCCESS (0) for success; error-code for failure.
--*/
{
DWORD CurrentDisk;
DWORD i;
NTSTATUS status;
LARGE_INTEGER liQueryWaitTime ;
//
// Set the handle to NULL so that RPC knows that it has been closed.
//
if (*phKey != HKEY_PERFORMANCE_DATA) {
*phKey = NULL;
return ERROR_SUCCESS;
}
*phKey = NULL;
if (NumberOfOpens == 0) {
return ERROR_SUCCESS;
}
if (hDataSemaphore) { // if a semaphore was allocated, then use it
// if here, then assume a Semaphore is ready
liQueryWaitTime.QuadPart = MakeTimeOutValue(QUERY_WAIT_TIME);
status = NtWaitForSingleObject (
hDataSemaphore, // semaphore
FALSE, // not alertable
&liQueryWaitTime); // wait forever
if (status != STATUS_SUCCESS) {
KdPrint (("\nPERFLIB: Data Semaphore Wait Status = 0x%8.8x", status));
return ERROR_BUSY;
}
} // if no semaphore, then continue anyway. no point in holding up
// the works at this stage
if ( !--NumberOfOpens ) {
for ( CurrentDisk = 0;
CurrentDisk < NumPhysicalDisks;
CurrentDisk++ ) {
NtClose(DiskDevices[CurrentDisk].Handle);
if (DiskDevices[CurrentDisk].StatusHandle) {
NtClose(DiskDevices[CurrentDisk].StatusHandle);
}
if (DiskDevices[CurrentDisk].Name.Buffer != NULL) {
FREEMEM (RtlProcessHeap(), 0,
DiskDevices[CurrentDisk].Name.Buffer);
}
}
for ( CurrentDisk = NumPhysicalDisks;
CurrentDisk < NUMDISKS;
CurrentDisk++ ) {
NtClose(DiskDevices[CurrentDisk].Handle);
if (DiskDevices[CurrentDisk].StatusHandle) {
NtClose(DiskDevices[CurrentDisk].StatusHandle);
}
if (DiskDevices[CurrentDisk].Name.Buffer != NULL) {
FREEMEM (RtlProcessHeap(), 0,
DiskDevices[CurrentDisk].Name.Buffer);
}
}
// free process name buffer since it was copied into
// the instance structure
FREEMEM (RtlProcessHeap (), 0, pusLocalProcessNameBuffer);
pusLocalProcessNameBuffer = NULL;
//
// free memory allocated by BaseRegOpenKey
//
FREEMEM(RtlProcessHeap(), 0, DiskDevices);
DiskDevices = NULL;
FREEMEM(RtlProcessHeap(), 0, pComputerName);
ComputerNameLength = 0;
pComputerName = NULL;
FREEMEM(RtlProcessHeap(), 0, pProcessorBuffer);
ProcessorBufSize = 0;
pProcessorBuffer = NULL;
FREEMEM(RtlProcessHeap(), 0, pProcessBuffer);
ProcessBufSize = 0;
pProcessBuffer = NULL;
FREEMEM(RtlProcessHeap(), 0, ProcessName.Buffer);
ProcessName.Length = 0;
ProcessName.MaximumLength = 0;
ProcessName.Buffer = 0;
FREEMEM(RtlProcessHeap(), 0, pSysPageFileInfo);
pSysPageFileInfo = NULL;
dwSysPageFileInfoSize = 0;
NtClose(hEvent);
NtClose(hMutex);
NtClose(hSemaphore);
NtClose(hSection);
NtClose(hRdr);
NtClose(hSrv);
for ( i=0; i < NumExtensibleObjects; i++ ) {
try {
if ( ExtensibleObjects[i].CloseProc != NULL ) {
(*ExtensibleObjects[i].CloseProc)();
}
} except (EXCEPTION_EXECUTE_HANDLER) {
// If the close fails just continue the thread
}
}
for ( i=0; i < NumExtensibleObjects; i++ ) {
if ( ExtensibleObjects[i].hLibrary != NULL ) {
FreeLibrary(ExtensibleObjects[i].hLibrary);
}
}
if (ExtensibleObjects != NULL) {
FREEMEM(RtlProcessHeap(), 0, ExtensibleObjects);
ExtensibleObjects = NULL;
}
NumExtensibleObjects = 0;
}
if (pProcessorInterruptInformation != NULL) {
FREEMEM (RtlProcessHeap(), 0, pProcessorInterruptInformation);
pProcessorInterruptInformation = NULL;
}
if (hDataSemaphore) { // if a semaphore was allocated, then use it
NtReleaseSemaphore (hDataSemaphore, 1L, NULL);
NtClose (hDataSemaphore);
hDataSemaphore = NULL;
}
if (hEventLog != NULL) {
DeregisterEventSource (hEventLog);
hEventLog = NULL;
}
HEAP_PROBE();
return ERROR_SUCCESS;
}
LONG
PerfRegSetValue (
IN HKEY hKey,
IN LPWSTR lpValueName,
IN DWORD Reserved,
IN DWORD dwType,
IN LPBYTE lpData,
IN DWORD cbData
)
/*++
PerfRegSetValue - Set data
Inputs:
hKey - Predefined handle to open remote
machine
lpValueName - Name of the value to be returned;
could be "ForeignComputer:<computername>
or perhaps some other objects, separated
by ~; must be Unicode string
lpReserved - should be omitted (NULL)
lpType - should be REG_MULTI_SZ
lpData - pointer to a buffer containing the
performance name
lpcbData - pointer to a variable containing the
size in bytes of the input buffer;
Return Value:
DOS error code indicating status of call or
ERROR_SUCCESS if all ok
--*/
{
DWORD dwQueryType; // type of request
LPWSTR lpLangId = NULL;
NTSTATUS status;
UNICODE_STRING String;
dwQueryType = GetQueryType (lpValueName);
// convert the query to set commands
if ((dwQueryType == QUERY_COUNTER) ||
(dwQueryType == QUERY_ADDCOUNTER)) {
dwQueryType = QUERY_ADDCOUNTER;
} else if ((dwQueryType == QUERY_HELP) ||
(dwQueryType == QUERY_ADDHELP)) {
dwQueryType = QUERY_ADDHELP;
} else {
status = ERROR_BADKEY;
goto Error_exit;
}
if (hKey == HKEY_PERFORMANCE_TEXT) {
lpLangId = DefaultLangId;
} else if (hKey == HKEY_PERFORMANCE_NLSTEXT) {
lpLangId = NativeLangId;
if (*lpLangId == L'\0') {
// build the native language id
LANGID iLanguage;
int NativeLanguage;
iLanguage = GetUserDefaultLangID();
NativeLanguage = MAKELANGID (iLanguage & 0x0ff, LANG_NEUTRAL);
NativeLangId[0] = NativeLanguage / 256 + L'0';
NativeLanguage %= 256;
NativeLangId[1] = NativeLanguage / 16 + L'0';
NativeLangId[2] = NativeLanguage % 16 + L'0';
NativeLangId[3] = L'\0';
}
} else {
status = ERROR_BADKEY;
goto Error_exit;
}
RtlInitUnicodeString(&String, lpValueName);
status = PerfGetNames (
dwQueryType,
&String,
lpData,
&cbData,
NULL,
lpLangId);
if (!NT_SUCCESS(status)) {
status = (error_status_t)RtlNtStatusToDosError(status);
}
Error_exit:
return (status);
}
LONG
PerfRegEnumKey (
IN HKEY hKey,
IN DWORD dwIndex,
OUT PUNICODE_STRING lpName,
OUT LPDWORD lpReserved OPTIONAL,
OUT PUNICODE_STRING lpClass OPTIONAL,
OUT PFILETIME lpftLastWriteTime OPTIONAL
)
/*++
Routine Description:
Enumerates keys under HKEY_PERFORMANCE_DATA.
Arguments:
Same as RegEnumKeyEx. Returns that there are no such keys.
Return Value:
Returns ERROR_SUCCESS (0) for success; error-code for failure.
--*/
{
if ( 0 ) {
DBG_UNREFERENCED_PARAMETER(hKey);
DBG_UNREFERENCED_PARAMETER(dwIndex);
DBG_UNREFERENCED_PARAMETER(lpReserved);
}
lpName->Length = 0;
if (ARGUMENT_PRESENT (lpClass)) {
lpClass->Length = 0;
}
if ( ARGUMENT_PRESENT(lpftLastWriteTime) ) {
lpftLastWriteTime->dwLowDateTime = 0;
lpftLastWriteTime->dwHighDateTime = 0;
}
return ERROR_NO_MORE_ITEMS;
}
LONG
PerfRegQueryInfoKey (
IN HKEY hKey,
OUT PUNICODE_STRING lpClass,
OUT LPDWORD lpReserved OPTIONAL,
OUT LPDWORD lpcSubKeys,
OUT LPDWORD lpcbMaxSubKeyLen,
OUT LPDWORD lpcbMaxClassLen,
OUT LPDWORD lpcValues,
OUT LPDWORD lpcbMaxValueNameLen,
OUT LPDWORD lpcbMaxValueLen,
OUT LPDWORD lpcbSecurityDescriptor,
OUT PFILETIME lpftLastWriteTime
)
/*++
Routine Description:
This returns information concerning the predefined handle
HKEY_PERFORMANCE_DATA
Arguments:
Same as RegQueryInfoKey.
Return Value:
Returns ERROR_SUCCESS (0) for success.
--*/
{
DWORD TempLength=0;
DWORD MaxValueLen=0;
UNICODE_STRING Null;
SECURITY_DESCRIPTOR SecurityDescriptor;
HKEY hPerflibKey;
OBJECT_ATTRIBUTES Obja;
NTSTATUS Status;
NTSTATUS PerfStatus = ERROR_SUCCESS;
UNICODE_STRING PerflibSubKeyString;
BOOL bGetSACL = TRUE;
if ( 0 ) {
DBG_UNREFERENCED_PARAMETER(lpReserved);
}
if (lpClass->Length > 0) {
lpClass->Length = 0;
*lpClass->Buffer = UNICODE_NULL;
}
*lpcSubKeys = 0;
*lpcbMaxSubKeyLen = 0;
*lpcbMaxClassLen = 0;
*lpcValues = NUM_VALUES;
*lpcbMaxValueNameLen = VALUE_NAME_LENGTH;
*lpcbMaxValueLen = 0;
if ( ARGUMENT_PRESENT(lpftLastWriteTime) ) {
lpftLastWriteTime->dwLowDateTime = 0;
lpftLastWriteTime->dwHighDateTime = 0;
}
if ((hKey == HKEY_PERFORMANCE_TEXT) ||
(hKey == HKEY_PERFORMANCE_NLSTEXT)) {
//
// We have to go enumerate the values to determine the answer for
// the MaxValueLen parameter.
//
Null.Buffer = NULL;
Null.Length = 0;
Null.MaximumLength = 0;
PerfStatus = PerfEnumTextValue(hKey,
0,
&Null,
NULL,
NULL,
NULL,
&MaxValueLen,
NULL);
if (PerfStatus == ERROR_SUCCESS) {
PerfStatus = PerfEnumTextValue(hKey,
1,
&Null,
NULL,
NULL,
NULL,
&TempLength,
NULL);
}
if (PerfStatus == ERROR_SUCCESS) {
if (TempLength > MaxValueLen) {
MaxValueLen = TempLength;
}
*lpcbMaxValueLen = MaxValueLen;
} else {
// unable to successfully enum text values for this
// key so return 0's and the error code
*lpcValues = 0;
*lpcbMaxValueNameLen = 0;
}
}
if (PerfStatus == ERROR_SUCCESS) {
// continune if all is OK
// now get the size of SecurityDescriptor for Perflib key
RtlInitUnicodeString (
&PerflibSubKeyString,
L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib");
//
// Initialize the OBJECT_ATTRIBUTES structure and open the key.
//
InitializeObjectAttributes(
&Obja,
&PerflibSubKeyString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenKey(
&hPerflibKey,
MAXIMUM_ALLOWED | ACCESS_SYSTEM_SECURITY,
&Obja
);
if ( ! NT_SUCCESS( Status )) {
Status = NtOpenKey(
&hPerflibKey,
MAXIMUM_ALLOWED,
&Obja
);
bGetSACL = FALSE;
}
if ( ! NT_SUCCESS( Status )) {
KdPrint (("PERFLIB: Unable to open Perflib Key. Status: %d\n", Status));
} else {
*lpcbSecurityDescriptor = 0;
if (bGetSACL == FALSE) {
//
// Get the size of the key's SECURITY_DESCRIPTOR for OWNER, GROUP
// and DACL. These three are always accessible (or inaccesible)
// as a set.
//
Status = NtQuerySecurityObject(
hPerflibKey,
OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| DACL_SECURITY_INFORMATION,
&SecurityDescriptor,
0,
lpcbSecurityDescriptor
);
} else {
//
// Get the size of the key's SECURITY_DESCRIPTOR for OWNER, GROUP,
// DACL, and SACL.
//
Status = NtQuerySecurityObject(
hPerflibKey,
OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| DACL_SECURITY_INFORMATION
| SACL_SECURITY_INFORMATION,
&SecurityDescriptor,
0,
lpcbSecurityDescriptor
);
}
if( Status != STATUS_BUFFER_TOO_SMALL ) {
*lpcbSecurityDescriptor = 0;
}
NtClose(hPerflibKey);
}
if (NT_SUCCESS( Status )) {
PerfStatus = ERROR_SUCCESS;
} else {
// return error
PerfStatus = (DWORD)RtlNtStatusToDosError(Status);
}
} // else return status
return PerfStatus;
}
LONG
PerfRegEnumValue (
IN HKEY hKey,
IN DWORD dwIndex,
OUT PUNICODE_STRING lpValueName,
OUT LPDWORD lpReserved OPTIONAL,
OUT LPDWORD lpType OPTIONAL,
OUT LPBYTE lpData,
IN OUT LPDWORD lpcbData,
OUT LPDWORD lpcbLen OPTIONAL
)
/*++
Routine Description:
Enumerates Values under HKEY_PERFORMANCE_DATA.
Arguments:
Same as RegEnumValue. Returns the values.
Return Value:
Returns ERROR_SUCCESS (0) for success; error-code for failure.
--*/
{
USHORT cbNameSize;
// table of names used by enum values
UNICODE_STRING ValueNames[NUM_VALUES];
ValueNames [0].Length = sizeof(GLOBAL_STRING);
ValueNames [0].MaximumLength = sizeof(GLOBAL_STRING) + sizeof(UNICODE_NULL);
ValueNames [0].Buffer = GLOBAL_STRING;
ValueNames [1].Length = sizeof(COSTLY_STRING);
ValueNames [1].MaximumLength = sizeof(COSTLY_STRING) + sizeof(UNICODE_NULL);
ValueNames [1].Buffer = COSTLY_STRING;
if ((hKey == HKEY_PERFORMANCE_TEXT) ||
(hKey == HKEY_PERFORMANCE_NLSTEXT)) {
return(PerfEnumTextValue(hKey,
dwIndex,
lpValueName,
lpReserved,
lpType,
lpData,
lpcbData,
lpcbLen));
}
if ( dwIndex >= NUM_VALUES ) {
//
// This is a request for data from a non-existent value name
//
*lpcbData = 0;
return ERROR_NO_MORE_ITEMS;
}
cbNameSize = ValueNames[dwIndex].Length;
if ( lpValueName->MaximumLength < cbNameSize ) {
return ERROR_MORE_DATA;
} else {
lpValueName->Length = cbNameSize;
RtlCopyUnicodeString(lpValueName, &ValueNames[dwIndex]);
if (ARGUMENT_PRESENT (lpType)) {
*lpType = REG_BINARY;
}
return PerfRegQueryValue(hKey,
lpValueName,
NULL,
lpType,
lpData,
lpcbData,
lpcbLen);
}
}
LONG
PerfEnumTextValue (
IN HKEY hKey,
IN DWORD dwIndex,
OUT PUNICODE_STRING lpValueName,
OUT LPDWORD lpReserved OPTIONAL,
OUT LPDWORD lpType OPTIONAL,
OUT LPBYTE lpData,
IN OUT LPDWORD lpcbData,
OUT LPDWORD lpcbLen OPTIONAL
)
/*++
Routine Description:
Enumerates Values under Perflib\lang
Arguments:
Same as RegEnumValue. Returns the values.
Return Value:
Returns ERROR_SUCCESS (0) for success; error-code for failure.
--*/
{
UNICODE_STRING FullValueName;
LONG lReturn;
//
// Only two values, "Counter" and "Help"
//
if (dwIndex==0) {
lpValueName->Length = 0;
RtlInitUnicodeString(&FullValueName, L"Counter");
} else if (dwIndex==1) {
lpValueName->Length = 0;
RtlInitUnicodeString(&FullValueName, L"Help");
} else {
return(ERROR_NO_MORE_ITEMS);
}
RtlCopyUnicodeString(lpValueName, &FullValueName);
//
// We need to NULL terminate the name to make RPC happy.
//
if (lpValueName->Length+sizeof(WCHAR) <= lpValueName->MaximumLength) {
lpValueName->Buffer[lpValueName->Length / sizeof(WCHAR)] = UNICODE_NULL;
lpValueName->Length += sizeof(UNICODE_NULL);
}
lReturn = PerfRegQueryValue(hKey,
&FullValueName,
lpReserved,
lpType,
lpData,
lpcbData,
lpcbLen);
return lReturn;
}
LONG
QueryImageData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryImageData - Get data about Images and VA
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
IMAGE_DATA_DEFINITION *pImageDataDefinition;
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
DWORD dwNumInstances;
DWORD dwProcessIndex;
PPROCESS_VA_INFO pThisProcess;
PMODINFO pThisImage;
dwNumInstances = 0;
pImageDataDefinition = (IMAGE_DATA_DEFINITION *) *lppDataDefinition;
//
// Check for sufficient space for Image object type definition
//
TotalLen = (PCHAR) pImageDataDefinition - (PCHAR) lpData +
sizeof(IMAGE_DATA_DEFINITION) +
SIZE_OF_IMAGE_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Define Page File data block
//
RtlMoveMemory(pImageDataDefinition,
&ImageDataDefinition,
sizeof(IMAGE_DATA_DEFINITION));
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pImageDataDefinition[1];
// Now load data for each Image
pdwCounter = (PDWORD) pPerfInstanceDefinition; // incase no instances
pThisProcess = pProcessVaInfo;
dwProcessIndex = 0;
while (pThisProcess) {
pThisImage = pThisProcess->pMemBlockInfo;
while (pThisImage) {
// see if this instance will fit
TotalLen = (PCHAR)pPerfInstanceDefinition - (PCHAR) lpData +
sizeof (PERF_INSTANCE_DEFINITION) +
(MAX_PROCESS_NAME_LENGTH + 1) * sizeof (WCHAR) +
sizeof (DWORD) +
SIZE_OF_IMAGE_DATA;
if (*lpcbData < TotalLen) {
return ERROR_MORE_DATA;
}
MonBuildInstanceDefinition (pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
EXPROCESS_OBJECT_TITLE_INDEX,
dwProcessIndex,
(DWORD)-1,
pThisImage->InstanceName);
pPerfCounterBlock->ByteLength = SIZE_OF_IMAGE_DATA;
pdwCounter = (DWORD *)(&pPerfCounterBlock[1]);
*pdwCounter++ = pThisImage->CommitVector[NOACCESS];
*pdwCounter++ = pThisImage->CommitVector[READONLY];
*pdwCounter++ = pThisImage->CommitVector[READWRITE];
*pdwCounter++ = pThisImage->CommitVector[WRITECOPY];
*pdwCounter++ = pThisImage->CommitVector[EXECUTE];
*pdwCounter++ = pThisImage->CommitVector[EXECUTEREAD];
*pdwCounter++ = pThisImage->CommitVector[EXECUTEREADWRITE];
*pdwCounter++ = pThisImage->CommitVector[EXECUTEWRITECOPY];
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)pdwCounter;
dwNumInstances += 1 ;
pThisImage = pThisImage->pNextModule;
}
pThisProcess = pThisProcess->pNextProcess;
dwProcessIndex++;
}
pImageDataDefinition->ImageObjectType.NumInstances += dwNumInstances;
pImageDataDefinition->ImageObjectType.TotalByteLength =
(PCHAR) pdwCounter - (PCHAR) pImageDataDefinition;
*lppDataDefinition = (LPVOID) pdwCounter;
DBG_UNREFERENCED_PARAMETER(lpValueName);
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
}
LONG
QueryLongImageData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryImageData - Get data about Images and VA
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
IMAGE_DATA_DEFINITION *pImageDataDefinition;
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
DWORD dwNumInstances;
DWORD dwProcessIndex;
PPROCESS_VA_INFO pThisProcess;
PMODINFO pThisImage;
dwNumInstances = 0;
pImageDataDefinition = (IMAGE_DATA_DEFINITION *) *lppDataDefinition;
//
// Check for sufficient space for Image object type definition
//
TotalLen = (PCHAR) pImageDataDefinition - (PCHAR) lpData +
sizeof(IMAGE_DATA_DEFINITION) +
SIZE_OF_IMAGE_DATA;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Define Long Image data block using
// image data block for started
RtlMoveMemory(pImageDataDefinition,
&ImageDataDefinition,
sizeof(IMAGE_DATA_DEFINITION));
// update fields for this object
pImageDataDefinition->ImageObjectType.ObjectNameTitleIndex =
LONG_IMAGE_OBJECT_TITLE_INDEX;
pImageDataDefinition->ImageObjectType.ObjectHelpTitleIndex =
LONG_IMAGE_OBJECT_TITLE_INDEX + 1;
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pImageDataDefinition[1];
// Now load data for each Image
pdwCounter = (PDWORD) pPerfInstanceDefinition; // incase no instances
pThisProcess = pProcessVaInfo;
dwProcessIndex = 0;
while (pThisProcess) {
pThisImage = pThisProcess->pMemBlockInfo;
while (pThisImage) {
// see if this instance will fit
TotalLen = (PCHAR)pPerfInstanceDefinition - (PCHAR) lpData +
sizeof (PERF_INSTANCE_DEFINITION) +
(MAX_PROCESS_NAME_LENGTH + 1) * sizeof (WCHAR) +
sizeof (DWORD) +
SIZE_OF_IMAGE_DATA;
if (*lpcbData < TotalLen) {
return ERROR_MORE_DATA;
}
MonBuildInstanceDefinition (pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
EXPROCESS_OBJECT_TITLE_INDEX,
dwProcessIndex,
(DWORD)-1,
pThisImage->LongInstanceName);
pPerfCounterBlock->ByteLength = SIZE_OF_IMAGE_DATA;
pdwCounter = (DWORD *)(&pPerfCounterBlock[1]);
*pdwCounter++ = pThisImage->CommitVector[NOACCESS];
*pdwCounter++ = pThisImage->CommitVector[READONLY];
*pdwCounter++ = pThisImage->CommitVector[READWRITE];
*pdwCounter++ = pThisImage->CommitVector[WRITECOPY];
*pdwCounter++ = pThisImage->CommitVector[EXECUTE];
*pdwCounter++ = pThisImage->CommitVector[EXECUTEREAD];
*pdwCounter++ = pThisImage->CommitVector[EXECUTEREADWRITE];
*pdwCounter++ = pThisImage->CommitVector[EXECUTEWRITECOPY];
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)pdwCounter;
dwNumInstances += 1 ;
pThisImage = pThisImage->pNextModule;
}
pThisProcess = pThisProcess->pNextProcess;
dwProcessIndex++;
}
pImageDataDefinition->ImageObjectType.NumInstances += dwNumInstances;
pImageDataDefinition->ImageObjectType.TotalByteLength =
(PCHAR) pdwCounter - (PCHAR) pImageDataDefinition;
*lppDataDefinition = (LPVOID) pdwCounter;
DBG_UNREFERENCED_PARAMETER(lpValueName);
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
}
LONG
QueryExProcessData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryExProcessData - Query Extended Process Information
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
DWORD NumExProcessInstances;
PPROCESS_VA_INFO pThisProcess; // pointer to current process
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
PERF_OBJECT_TYPE *pPerfObject;
EXPROCESS_DATA_DEFINITION *pExProcessDataDefinition;
if (pProcessVaInfo) { // process only if a buffer is available
pPerfObject = (PERF_OBJECT_TYPE *)*lppDataDefinition;
pExProcessDataDefinition = (EXPROCESS_DATA_DEFINITION *)*lppDataDefinition;
// check for sufficient space in buffer
TotalLen = (PCHAR)pPerfObject - (PCHAR)lpData +
sizeof(EXPROCESS_DATA_DEFINITION)+
SIZE_OF_EX_PROCESS_DATA;
if (*lpcbData < TotalLen) {
return ERROR_MORE_DATA;
}
// copy process data block to buffer
RtlMoveMemory (pExProcessDataDefinition,
&ExProcessDataDefinition,
sizeof(EXPROCESS_DATA_DEFINITION));
NumExProcessInstances = 0;
pThisProcess = pProcessVaInfo;
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pExProcessDataDefinition[1];
pdwCounter = (PDWORD) pPerfInstanceDefinition; // in case no instances
while (pThisProcess) {
// see if this instance will fit
TotalLen = (PCHAR)pPerfInstanceDefinition - (PCHAR) lpData +
sizeof (PERF_INSTANCE_DEFINITION) +
(MAX_PROCESS_NAME_LENGTH + 1) * sizeof (WCHAR) +
sizeof (DWORD) +
SIZE_OF_EX_PROCESS_DATA;
if (*lpcbData < TotalLen) {
return ERROR_MORE_DATA;
}
MonBuildInstanceDefinition (pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
0,
0,
(DWORD)-1,
pThisProcess->pProcessName);
NumExProcessInstances++;
pPerfCounterBlock->ByteLength = SIZE_OF_EX_PROCESS_DATA;
pdwCounter = (DWORD *) &pPerfCounterBlock[1];
// load counters from the process va data structure
*pdwCounter++ = pThisProcess->dwProcessId;
*pdwCounter++ = pThisProcess->ImageReservedBytes;
*pdwCounter++ = pThisProcess->ImageFreeBytes;
*pdwCounter++ = pThisProcess->ReservedBytes;
*pdwCounter++ = pThisProcess->FreeBytes;
*pdwCounter++ = pThisProcess->MappedCommit[NOACCESS];
*pdwCounter++ = pThisProcess->MappedCommit[READONLY];
*pdwCounter++ = pThisProcess->MappedCommit[READWRITE];
*pdwCounter++ = pThisProcess->MappedCommit[WRITECOPY];
*pdwCounter++ = pThisProcess->MappedCommit[EXECUTE];
*pdwCounter++ = pThisProcess->MappedCommit[EXECUTEREAD];
*pdwCounter++ = pThisProcess->MappedCommit[EXECUTEREADWRITE];
*pdwCounter++ = pThisProcess->MappedCommit[EXECUTEWRITECOPY];
*pdwCounter++ = pThisProcess->PrivateCommit[NOACCESS];
*pdwCounter++ = pThisProcess->PrivateCommit[READONLY];
*pdwCounter++ = pThisProcess->PrivateCommit[READWRITE];
*pdwCounter++ = pThisProcess->PrivateCommit[WRITECOPY];
*pdwCounter++ = pThisProcess->PrivateCommit[EXECUTE];
*pdwCounter++ = pThisProcess->PrivateCommit[EXECUTEREAD];
*pdwCounter++ = pThisProcess->PrivateCommit[EXECUTEREADWRITE];
*pdwCounter++ = pThisProcess->PrivateCommit[EXECUTEWRITECOPY];
*pdwCounter++ = pThisProcess->OrphanTotals.CommitVector[NOACCESS];
*pdwCounter++ = pThisProcess->OrphanTotals.CommitVector[READONLY];
*pdwCounter++ = pThisProcess->OrphanTotals.CommitVector[READWRITE];
*pdwCounter++ = pThisProcess->OrphanTotals.CommitVector[WRITECOPY];
*pdwCounter++ = pThisProcess->OrphanTotals.CommitVector[EXECUTE];
*pdwCounter++ = pThisProcess->OrphanTotals.CommitVector[EXECUTEREAD];
*pdwCounter++ = pThisProcess->OrphanTotals.CommitVector[EXECUTEREADWRITE];
*pdwCounter++ = pThisProcess->OrphanTotals.CommitVector[EXECUTEWRITECOPY];
*pdwCounter++ = pThisProcess->MemTotals.CommitVector[NOACCESS];
*pdwCounter++ = pThisProcess->MemTotals.CommitVector[READONLY];
*pdwCounter++ = pThisProcess->MemTotals.CommitVector[READWRITE];
*pdwCounter++ = pThisProcess->MemTotals.CommitVector[WRITECOPY];
*pdwCounter++ = pThisProcess->MemTotals.CommitVector[EXECUTE];
*pdwCounter++ = pThisProcess->MemTotals.CommitVector[EXECUTEREAD];
*pdwCounter++ = pThisProcess->MemTotals.CommitVector[EXECUTEREADWRITE];
*pdwCounter++ = pThisProcess->MemTotals.CommitVector[EXECUTEWRITECOPY];
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)pdwCounter;
pThisProcess = pThisProcess->pNextProcess; // point to next process
} // end while not at end of list
} // end if valid process info buffer
else {
// pProcessVaInfo is NULL. Initialize the DataDef and return
// with no data
pPerfObject = (PERF_OBJECT_TYPE *)*lppDataDefinition;
pExProcessDataDefinition = (EXPROCESS_DATA_DEFINITION *)*lppDataDefinition;
// check for sufficient space in buffer
TotalLen = (PCHAR)pPerfObject - (PCHAR)lpData +
sizeof(EXPROCESS_DATA_DEFINITION)+
SIZE_OF_EX_PROCESS_DATA;
if (*lpcbData < TotalLen) {
return ERROR_MORE_DATA;
}
// copy process data block to buffer
RtlMoveMemory (pExProcessDataDefinition,
&ExProcessDataDefinition,
sizeof(EXPROCESS_DATA_DEFINITION));
NumExProcessInstances = 0;
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pExProcessDataDefinition[1];
pdwCounter = (PDWORD) pPerfInstanceDefinition;
}
pExProcessDataDefinition->ExProcessObjectType.TotalByteLength =
(PCHAR) pdwCounter - (PCHAR) pExProcessDataDefinition;
pExProcessDataDefinition->ExProcessObjectType.NumInstances =
NumExProcessInstances;
*lppDataDefinition = (LPVOID) pdwCounter;
DBG_UNREFERENCED_PARAMETER(lpValueName);
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
}
LONG
QueryThreadDetailData(
LPWSTR lpValueName,
LPBYTE lpData,
LPDWORD lpcbData,
LPVOID *lppDataDefinition
)
/*++
QueryThreadDetailData - Query Costly Thread Information
Inputs:
lpValueName - pointer to value string (unused)
lpData - pointer to start of data block
where data is being collected
lpcbData - pointer to size of data buffer
lppDataDefinition - pointer to pointer to where object
definition for this object type should
go
Outputs:
*lppDataDefinition - set to location for next Type
Definition if successful
Returns:
0 if successful, else Win 32 error code of failure
--*/
{
DWORD TotalLen; // Length of the total return block
DWORD *pdwCounter;
THREAD_DETAILS_DATA_DEFINITION *pThreadDetailDataDefinition;
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
PSYSTEM_PROCESS_INFORMATION ProcessInfo;
PSYSTEM_THREAD_INFORMATION ThreadInfo;
ULONG ProcessNumber;
ULONG NumThreadInstances;
ULONG ThreadNumber;
ULONG ProcessBufferOffset;
BOOLEAN NullProcess;
NTSTATUS Status; // return from Nt Calls
DWORD dwPcValue; // value of current thread PC
OBJECT_ATTRIBUTES Obja; // object attributes for thread context
HANDLE hThread; // handle to current thread
CONTEXT ThreadContext; // current thread context struct
UNICODE_STRING ThreadName;
WCHAR ThreadNameBuffer[MAX_THREAD_NAME_LENGTH+1];
pThreadDetailDataDefinition = (THREAD_DETAILS_DATA_DEFINITION *) *lppDataDefinition;
//
// Check for sufficient space for Thread object type definition
//
TotalLen = (PCHAR) pThreadDetailDataDefinition - (PCHAR) lpData +
sizeof(THREAD_DETAILS_DATA_DEFINITION) +
SIZE_OF_THREAD_DETAILS;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
//
// Define Thread data block
//
ThreadName.Length =
ThreadName.MaximumLength = (MAX_THREAD_NAME_LENGTH + 1) * sizeof(WCHAR);
ThreadName.Buffer = ThreadNameBuffer;
RtlMoveMemory(pThreadDetailDataDefinition,
&ThreadDetailsDataDefinition,
sizeof(THREAD_DETAILS_DATA_DEFINITION));
ProcessBufferOffset = 0;
// Now collect data for each Thread
ProcessNumber = 0;
NumThreadInstances = 0;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pProcessBuffer;
pdwCounter = (DWORD *) &pThreadDetailDataDefinition[1];
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
&pThreadDetailDataDefinition[1];
while ( TRUE ) {
if ( ProcessInfo->ImageName.Buffer != NULL ||
ProcessInfo->NumberOfThreads > 0 ) {
NullProcess = FALSE;
} else {
NullProcess = TRUE;
}
ThreadNumber = 0; // Thread number of this process
ThreadInfo = (PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);
while ( !NullProcess &&
ThreadNumber < ProcessInfo->NumberOfThreads ) {
TotalLen = (PCHAR) pPerfInstanceDefinition -
(PCHAR) lpData +
sizeof(PERF_INSTANCE_DEFINITION) +
(MAX_THREAD_NAME_LENGTH+1+sizeof(DWORD))*
sizeof(WCHAR) +
SIZE_OF_THREAD_DETAILS;
if ( *lpcbData < TotalLen ) {
return ERROR_MORE_DATA;
}
// Get Thread Context Information for Current PC field
dwPcValue = 0;
InitializeObjectAttributes(&Obja, NULL, 0, NULL, NULL);
Status = NtOpenThread(
&hThread,
THREAD_GET_CONTEXT,
&Obja,
&ThreadInfo->ClientId
);
if ( NT_SUCCESS(Status) ) {
ThreadContext.ContextFlags = CONTEXT_CONTROL;
Status = NtGetContextThread(hThread,&ThreadContext);
NtClose(hThread);
if ( NT_SUCCESS(Status) ) {
dwPcValue = (DWORD)CONTEXT_TO_PROGRAM_COUNTER(&ThreadContext);
} else {
dwPcValue = 0; // an error occured so send back 0 PC
}
} else {
dwPcValue = 0; // an error occured so send back 0 PC
}
// The only name we've got is the thread number
RtlIntegerToUnicodeString(ThreadNumber,
10,
&ThreadName);
MonBuildInstanceDefinition(pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
EXPROCESS_OBJECT_TITLE_INDEX,
ProcessNumber,
(DWORD)-1,
&ThreadName);
//
//
// Format and collect Thread data
//
pPerfCounterBlock->ByteLength = SIZE_OF_THREAD_DETAILS;
pdwCounter = (DWORD *) &pPerfCounterBlock[1];
*pdwCounter++ = dwPcValue;
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
pdwCounter;
NumThreadInstances++;
ThreadNumber++;
ThreadInfo++;
}
if (ProcessInfo->NextEntryOffset == 0) {
break;
}
ProcessBufferOffset += ProcessInfo->NextEntryOffset;
ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
&pProcessBuffer[ProcessBufferOffset];
if ( !NullProcess ) {
ProcessNumber++;
}
}
// Note number of Thread instances
pThreadDetailDataDefinition->ThreadDetailsObjectType.NumInstances =
NumThreadInstances;
//
// Now we know how large an area we used for the
// Thread definition, so we can update the offset
// to the next object definition
//
pThreadDetailDataDefinition->ThreadDetailsObjectType.TotalByteLength =
(PCHAR) pdwCounter - (PCHAR) pThreadDetailDataDefinition;
*lppDataDefinition = (LPVOID) pdwCounter;
// increment number of objects in this data block
((PPERF_DATA_BLOCK)lpData)->NumObjectTypes++;
return 0;
DBG_UNREFERENCED_PARAMETER(lpValueName);
}
NTSTATUS
GetUnicodeValueData(
HANDLE hKey,
PUNICODE_STRING ValueName,
PKEY_VALUE_FULL_INFORMATION *ValueInformation,
ULONG *ValueBufferLength,
PUNICODE_STRING ValueData
)
/*++
Routine Description:
This routine obtains a unicode string from the Registry. The
return buffer may be enlarged to accomplish this.
Arguments:
hKey - handle of opened registry key
ValueName - name of value to retrieve
ValueInformation - pointer to pointer to
pre-allocated buffer to receive information
returned from querying Registry
ValueBufferLength - pointer to size of ValueInformation buffer
ValueData - pointer to Unicode string for result
Return Value:
NTSTATUS from Registry, an indication that buffer is too small,
or STATUS_SUCCESS.
--*/
{
NTSTATUS Status;
ULONG ResultLength;
while ( (Status = NtQueryValueKey(hKey,
ValueName,
KeyValueFullInformation,
*ValueInformation,
*ValueBufferLength,
&ResultLength))
== STATUS_BUFFER_OVERFLOW ) {
*ValueInformation = REALLOCMEM(RtlProcessHeap(), 0,
*ValueInformation,
ResultLength);
if ( !*ValueInformation ) break;
*ValueBufferLength = ResultLength;
}
if( !NT_SUCCESS(Status) ) return Status;
//
// Convert name to Null terminated Unicode string
//
if ( (*ValueInformation)->DataLength > (ULONG)ValueData->MaximumLength ) {
ValueData->Buffer = REALLOCMEM(RtlProcessHeap(),
0,
ValueData->Buffer,
(*ValueInformation)->DataLength);
if ( !ValueData->Buffer ) return STATUS_BUFFER_OVERFLOW;
ValueData->MaximumLength = (USHORT) (*ValueInformation)->DataLength;
}
ValueData->Length = (USHORT) (*ValueInformation)->DataLength;
RtlMoveMemory(ValueData->Buffer,
(PBYTE) *ValueInformation + (*ValueInformation)->DataOffset,
ValueData->Length);
return STATUS_SUCCESS;
}
static
DWORD
OpenFunctionWatchDog (
LPOPEN_PROC_WAIT_INFO lpOpwInfo
)
{
LONG lStatus;
LARGE_INTEGER liWaitTime;
liWaitTime.QuadPart = MakeTimeOutValue(lpOpwInfo->dwWaitTime);
// wait for event flag to be set
lStatus = NtWaitForSingleObject (
lpOpwInfo->hEvent,
FALSE,
&liWaitTime);
// if the wait occured because of a timeout, then log an event message
// because the open procedure hasn't completed yet
if (lStatus == STATUS_TIMEOUT) {
LPTSTR szMessageArray[2];
DWORD dwData;
szMessageArray[0] = lpOpwInfo->szServiceName;
szMessageArray[1] = lpOpwInfo->szLibraryName;
dwData = lpOpwInfo->dwWaitTime;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_OPEN_PROC_TIMEOUT, // event,
NULL, // SID (not used),
2, // number of strings
sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPBYTE)&dwData); // raw data
}
// free the data for the next attempt
ReleaseSemaphore (lpOpwInfo->hDataSemaphore, 1, NULL);
return ERROR_SUCCESS; // always
}
void
OpenExtensibleObjects(
)
/*++
Routine Description:
This routine will search the Configuration Registry for modules
which will return data at data collection time. If any are found,
and successfully opened, data structures are allocated to hold
handles to them.
Arguments:
None.
successful open.
Return Value:
None.
--*/
{
DWORD dwIndex; // index for enumerating services
ULONG KeyBufferLength; // length of buffer for reading key data
ULONG ValueBufferLength; // length of buffer for reading value data
ULONG ResultLength; // length of data returned by Query call
LPWSTR pLinkage; // pointer to array of pointers to links
LPWSTR LibName; // name of perf library
HANDLE hLinkKey; // Root of queries for linkage info
HANDLE hPerfKey; // Root of queries for performance info
HANDLE hServicesKey; // Root of services
HANDLE hLibrary; // handle of current performance library
OPENPROC OpenProc; // address of the open routine
COLLECTPROC CollectProc; // address of the collect routine
CLOSEPROC CloseProc; // address of the close routine
REGSAM samDesired; // access needed to query
NTSTATUS Status; // generally used for Nt call result status
ANSI_STRING AnsiValueData; // Ansi version of returned strings
UNICODE_STRING ServiceName; // name of service returned by enumeration
UNICODE_STRING PathName; // path name to services
UNICODE_STRING DLLValueName; // name of value which holds performance lib
UNICODE_STRING OpenValueName; // name of value holding open proc name
UNICODE_STRING CollectValueName; // name of value holding collect proc
UNICODE_STRING CloseValueName; // name of value holding close proc name
UNICODE_STRING PerformanceName; // name of key holding performance data
UNICODE_STRING LinkageName; // name of key holding linkage data
UNICODE_STRING ExportValueName; // name of value holding driver names
UNICODE_STRING ValueDataName; // result of query of value is this name
OBJECT_ATTRIBUTES ObjectAttributes; // general use for opening keys
PKEY_BASIC_INFORMATION KeyInformation; // data from query key goes here
PKEY_VALUE_FULL_INFORMATION ValueInformation; // data from query value
// goes here
WCHAR DLLValue[] = L"Library";
WCHAR OpenValue[] = L"Open";
WCHAR CloseValue[] = L"Close";
WCHAR CollectValue[] = L"Collect";
WCHAR ExportValue[] = L"Export";
WCHAR LinkSubKey[] = L"\\Linkage";
WCHAR PerfSubKey[] = L"\\Performance";
WCHAR ExtPath[] =
L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services";
LPWSTR wszNameStart;
WCHAR szLibraryName[MAX_PATH];
WCHAR szServiceName[MAX_PATH];
LPTSTR szMessageArray[8];
DWORD dwRawDataDwords[8]; // raw data buffer
DWORD dwDataIndex;
WORD wStringIndex;
DWORD dwDefaultValue;
HANDLE hTimeOutEvent = NULL;
HANDLE hThreadDataSemaphore = NULL;
HANDLE hTimeOutThread = NULL;
OPEN_PROC_WAIT_INFO opwInfo;
LARGE_INTEGER liWaitTime;
NumExtensibleObjects = 0;
ExtensibleObjects = NULL;
// Initialize do failure can deallocate if allocated
ServiceName.Buffer = NULL;
KeyInformation = NULL;
ValueInformation = NULL;
ValueDataName.Buffer = NULL;
AnsiValueData.Buffer = NULL;
dwIndex = 0;
RtlInitUnicodeString(&PathName, ExtPath);
RtlInitUnicodeString(&ExportValueName, ExportValue);
RtlInitUnicodeString(&LinkageName, LinkSubKey);
RtlInitUnicodeString(&PerformanceName, PerfSubKey);
RtlInitUnicodeString(&DLLValueName, DLLValue);
RtlInitUnicodeString(&OpenValueName, OpenValue);
RtlInitUnicodeString(&CollectValueName, CollectValue);
RtlInitUnicodeString(&CloseValueName, CloseValue);
try {
// get current event log level
dwDefaultValue = LOG_USER;
Status = GetPerflibKeyValue (
L"EventLogLevel",
REG_DWORD,
sizeof(DWORD),
(LPVOID)&lEventLogLevel,
sizeof(DWORD),
(LPVOID)&dwDefaultValue);
dwDefaultValue = EXT_TEST_ALL;
Status = GetPerflibKeyValue (
L"ExtCounterTestLevel",
REG_DWORD,
sizeof(DWORD),
(LPVOID)&lExtCounterTestLevel,
sizeof(DWORD),
(LPVOID)&dwDefaultValue);
dwDefaultValue = OPEN_PROC_WAIT_TIME;
Status = GetPerflibKeyValue (
L"OpenProcedureWaitTime",
REG_DWORD,
sizeof(DWORD),
(LPVOID)&dwExtCtrOpenProcWaitMs,
sizeof(DWORD),
(LPVOID)&dwDefaultValue);
Status = GetPerflibKeyValue (
L"TotalInstanceName",
REG_SZ,
MAX_TOTAL_NAME_LENGTH * sizeof(WCHAR),
(LPVOID)&wcTotalString[0],
MAX_TOTAL_NAME_LENGTH + sizeof(WCHAR),
(LPVOID)DEFAULT_TOTAL_STRING);
// only copy if a string was returned
if (NT_SUCCESS(Status)) {
RtlInitUnicodeString (&usTotal, wcTotalString);
} else {
RtlInitUnicodeString (&usTotal, DEFAULT_TOTAL_STRING);
}
// register as an event log source if not already done.
if (hEventLog == NULL) {
hEventLog = RegisterEventSource (NULL, TEXT("Perflib"));
}
ServiceName.Length =
ServiceName.MaximumLength = MAX_KEY_NAME_LENGTH +
PerformanceName.MaximumLength +
sizeof(UNICODE_NULL);
ServiceName.Buffer = ALLOCMEM(RtlProcessHeap(), HEAP_ZERO_MEMORY,
ServiceName.MaximumLength);
InitializeObjectAttributes(&ObjectAttributes,
&PathName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
samDesired = KEY_READ;
Status = NtOpenKey(&hServicesKey,
samDesired,
&ObjectAttributes);
KeyBufferLength = sizeof(KEY_BASIC_INFORMATION) + MAX_KEY_NAME_LENGTH;
KeyInformation = ALLOCMEM(RtlProcessHeap(), HEAP_ZERO_MEMORY,
KeyBufferLength);
ValueBufferLength = sizeof(KEY_VALUE_FULL_INFORMATION) +
MAX_VALUE_NAME_LENGTH +
MAX_VALUE_DATA_LENGTH;
ValueInformation = ALLOCMEM(RtlProcessHeap(), HEAP_ZERO_MEMORY,
ValueBufferLength);
ValueDataName.MaximumLength = MAX_VALUE_DATA_LENGTH;
ValueDataName.Buffer = ALLOCMEM(RtlProcessHeap(), HEAP_ZERO_MEMORY,
ValueDataName.MaximumLength);
AnsiValueData.MaximumLength = MAX_VALUE_DATA_LENGTH/sizeof(WCHAR);
AnsiValueData.Buffer = ALLOCMEM(RtlProcessHeap(), HEAP_ZERO_MEMORY,
AnsiValueData.MaximumLength);
//
// Check for successful NtOpenKey and allocation of dynamic buffers
//
if ( NT_SUCCESS(Status) &&
ServiceName.Buffer != NULL &&
KeyInformation != NULL &&
ValueInformation != NULL &&
ValueDataName.Buffer != NULL &&
AnsiValueData.Buffer != NULL ) {
dwIndex = 0;
hTimeOutEvent = CreateEvent(NULL,TRUE,TRUE,NULL);
opwInfo.hEvent = hTimeOutEvent;
opwInfo.dwWaitTime = dwExtCtrOpenProcWaitMs;
hThreadDataSemaphore = CreateSemaphore (NULL, 1, 1, NULL);
opwInfo.hDataSemaphore = hThreadDataSemaphore;
// wait longer than the thread to give the timing thread
// a chance to finish on it's own. This is really just a
// failsafe step.
liWaitTime.QuadPart = MakeTimeOutValue(dwExtCtrOpenProcWaitMs * 2);
while (TRUE) {
Status = NtEnumerateKey(hServicesKey,
dwIndex,
KeyBasicInformation,
KeyInformation,
KeyBufferLength,
&ResultLength);
dwIndex++; // next time, get the next key
if( !NT_SUCCESS(Status) ) {
// This is the normal exit: Status should be
// STATUS_NO_MORE_VALUES
break;
}
// Concatenate Service name with "\\Performance" to form Subkey
if ( ServiceName.MaximumLength >=
(USHORT)( KeyInformation->NameLength + sizeof(UNICODE_NULL) ) ) {
ServiceName.Length = (USHORT) KeyInformation->NameLength;
RtlMoveMemory(ServiceName.Buffer,
KeyInformation->Name,
ServiceName.Length);
ServiceName.Buffer[(ServiceName.Length/sizeof(WCHAR))] = 0; // null term
lstrcpyW (szServiceName, ServiceName.Buffer);
// zero terminate the buffer if space allows
RtlAppendUnicodeStringToString(&ServiceName,
&PerformanceName);
// Open Service\Performance Subkey
InitializeObjectAttributes(&ObjectAttributes,
&ServiceName,
OBJ_CASE_INSENSITIVE,
hServicesKey,
NULL);
Status = NtOpenKey(&hPerfKey,
samDesired,
&ObjectAttributes);
if( NT_SUCCESS(Status) ) {
Status =
GetUnicodeValueData(hPerfKey,
&DLLValueName,
&ValueInformation,
&ValueBufferLength,
&ValueDataName);
if( NT_SUCCESS(Status) ) {
// expand any environment vars
ResultLength = ExpandEnvironmentStringsW(
ValueDataName.Buffer,
NULL,
0
);
ResultLength *= sizeof(WCHAR);
LibName = ALLOCMEM(
RtlProcessHeap(),
HEAP_ZERO_MEMORY,
ResultLength
);
if (LibName) {
ExpandEnvironmentStringsW(
ValueDataName.Buffer,
LibName,
ResultLength
);
} else {
LibName = ValueDataName.Buffer;
}
// LoadLibrary of Performance .dll
hLibrary = LoadLibraryW( LibName );
// copy the name to the local buffer
lstrcpy (szLibraryName, LibName);
// free the libname buffer
if (LibName != ValueDataName.Buffer) {
FREEMEM(
RtlProcessHeap(),
0,
LibName
);
}
if (hLibrary != NULL) {
// Get open routine name and function address
Status = GetUnicodeValueData(
hPerfKey,
&OpenValueName,
&ValueInformation,
&ValueBufferLength,
&ValueDataName);
// Set up to catch any errors below
// I know, the nesting level here is out of
// control
OpenProc = NULL;
CollectProc = NULL;
CloseProc = NULL;
if( NT_SUCCESS(Status) ) {
RtlUnicodeStringToAnsiString(
&AnsiValueData, &ValueDataName, FALSE);
OpenProc =
(OPENPROC) GetProcAddress(
hLibrary,
(LPCSTR) AnsiValueData.Buffer);
if (OpenProc == NULL) {
if (lEventLogLevel >= LOG_USER) {
Status = GetLastError();
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] =
(DWORD)Status;
szMessageArray[wStringIndex++] =
ValueDataName.Buffer;
szMessageArray[wStringIndex++] =
szLibraryName;
szMessageArray[wStringIndex++] =
szServiceName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_OPEN_PROC_NOT_FOUND, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
}
}
// Get collection routine name and function address
Status = GetUnicodeValueData(
hPerfKey,
&CollectValueName,
&ValueInformation,
&ValueBufferLength,
&ValueDataName);
if( NT_SUCCESS(Status) ) {
RtlUnicodeStringToAnsiString(
&AnsiValueData, &ValueDataName, FALSE);
CollectProc =
(COLLECTPROC) GetProcAddress(
hLibrary,
(LPCSTR) AnsiValueData.Buffer);
if (CollectProc == NULL) {
if (lEventLogLevel >= LOG_USER) {
Status = GetLastError();
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] =
(DWORD)Status;
szMessageArray[wStringIndex++] =
ValueDataName.Buffer;
szMessageArray[wStringIndex++] =
szLibraryName;
szMessageArray[wStringIndex++] =
szServiceName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_COLLECT_PROC_NOT_FOUND, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
}
}
// Get close routine name and function address
Status = GetUnicodeValueData(
hPerfKey,
&CloseValueName,
&ValueInformation,
&ValueBufferLength,
&ValueDataName);
if( NT_SUCCESS(Status) ) {
RtlUnicodeStringToAnsiString(
&AnsiValueData, &ValueDataName, FALSE);
CloseProc =
(CLOSEPROC) GetProcAddress(
hLibrary,
(LPCSTR) AnsiValueData.Buffer);
if (CloseProc == NULL) {
if (lEventLogLevel >= LOG_USER) {
Status = GetLastError();
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] =
(DWORD)Status;
szMessageArray[wStringIndex++] =
ValueDataName.Buffer;
szMessageArray[wStringIndex++] =
szLibraryName;
szMessageArray[wStringIndex++] =
szServiceName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_CLOSE_PROC_NOT_FOUND, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
}
}
// Concatenate Service name with "\\Linkage"
// to form Subkey, so we can pass the Exported
// entry point(s) to the Open routine
ServiceName.Length =
(USHORT) KeyInformation->NameLength;
RtlAppendUnicodeStringToString(
&ServiceName,&LinkageName);
// Open Service\Linkage Subkey
InitializeObjectAttributes(&ObjectAttributes,
&ServiceName,
OBJ_CASE_INSENSITIVE,
hServicesKey,
NULL);
pLinkage = NULL;
Status = NtOpenKey(&hLinkKey,
samDesired,
&ObjectAttributes);
if( NT_SUCCESS(Status) ) {
Status = GetUnicodeValueData(hLinkKey,
&ExportValueName,
&ValueInformation,
&ValueBufferLength,
&ValueDataName);
if( NT_SUCCESS(Status) ) {
pLinkage = ValueDataName.Buffer;
} else {
pLinkage = NULL;
}
NtClose (hLinkKey);
}
if ( CollectProc != NULL ) {
//
// If we got here, then all three routines are
// known, as are
// the driver names, if there are any.
//
if ( NumExtensibleObjects == 0 ) {
ExtensibleObjects = (pExtObject)
ALLOCMEM(RtlProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(ExtObject));
} else {
ExtensibleObjects = (pExtObject)
REALLOCMEM(
RtlProcessHeap(),
0,
ExtensibleObjects,
(NumExtensibleObjects+1) *
sizeof(ExtObject));
}
if ( ExtensibleObjects ) {
ExtensibleObjects[NumExtensibleObjects].OpenProc =
OpenProc;
ExtensibleObjects[NumExtensibleObjects].CollectProc =
CollectProc;
ExtensibleObjects[NumExtensibleObjects].CloseProc =
CloseProc;
ExtensibleObjects[NumExtensibleObjects].hLibrary =
hLibrary;
if (lstrlenW(szLibraryName) < EXT_OBJ_INFO_NAME_LENGTH) {
lstrcpy (ExtensibleObjects[NumExtensibleObjects].szLibraryName,
szLibraryName);
} else {
// truncate the name to include only the last X characters
lstrcpy (ExtensibleObjects[NumExtensibleObjects].szLibraryName,
L"...");
wszNameStart = szLibraryName + (lstrlenW(szLibraryName) -
(EXT_OBJ_INFO_NAME_LENGTH - 4));
lstrcat (ExtensibleObjects[NumExtensibleObjects].szLibraryName,
szLibraryName);
}
if (lstrlenW(szServiceName) < EXT_OBJ_INFO_NAME_LENGTH) {
lstrcpy (ExtensibleObjects[NumExtensibleObjects].szServiceName,
szServiceName);
} else {
// truncate the name to include only the first X characters
szServiceName[(EXT_OBJ_INFO_NAME_LENGTH - 4)] = 0;
lstrcpy (ExtensibleObjects[NumExtensibleObjects].szServiceName,
szServiceName);
lstrcat (ExtensibleObjects[NumExtensibleObjects].szServiceName,
L"...");
}
NumExtensibleObjects++;
try {
// Call the Open Procedure for the Extensible
// Object type
if ( OpenProc != NULL ) {
// wait for access to the structure shared with
// the watchdog thread
Status = NtWaitForSingleObject (
hThreadDataSemaphore,
FALSE,
&liWaitTime);
if (Status == STATUS_TIMEOUT) {
KdPrint (("\nPERFLIB: Wait for Watchdog Data Semaphore timed out"));
}
// load the info block with information on this service
opwInfo.szLibraryName = szLibraryName;
opwInfo.szServiceName = szServiceName;
// indicate the open process is being called
ResetEvent (hTimeOutEvent);
// start watchdog thread here
hTimeOutThread = CreateThread (
NULL, 0, OpenFunctionWatchDog,
(LPVOID)&opwInfo, 0, NULL);
// try open procedure
Status = (*OpenProc)(pLinkage);
// set timeout event to indicate that the open
// procedure has completed and to terminate
// the watchdog thread
SetEvent (hTimeOutEvent);
// close thread handle
CloseHandle (hTimeOutThread);
// check results and continue
if (Status != ERROR_SUCCESS) {
FreeLibrary(hLibrary);
if ( --NumExtensibleObjects == 0 ) {
FREEMEM(RtlProcessHeap(), 0,
ExtensibleObjects);
} else {
ExtensibleObjects = (pExtObject)
REALLOCMEM(
RtlProcessHeap(),
0,
ExtensibleObjects,
(NumExtensibleObjects+1) *
sizeof(ExtObject));
}
}
if (((Status != ERROR_SUCCESS) && (lEventLogLevel >= LOG_USER)) ||
((Status == ERROR_SUCCESS) && (lEventLogLevel >= LOG_DEBUG))) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] =
(DWORD)Status;
szMessageArray[wStringIndex++] =
szServiceName;
szMessageArray[wStringIndex++] =
szLibraryName;
ReportEvent (hEventLog,
(WORD)(Status == ERROR_SUCCESS ? EVENTLOG_INFORMATION_TYPE
: EVENTLOG_ERROR_TYPE), // error type
0, // category (not used)
(DWORD)(Status == ERROR_SUCCESS ? PERFLIB_OPEN_PROC_SUCCESS
: PERFLIB_OPEN_PROC_FAILURE), // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
}
} except (EXCEPTION_EXECUTE_HANDLER) {
FreeLibrary(hLibrary);
if ( --NumExtensibleObjects == 0 ) {
FREEMEM(RtlProcessHeap(), 0,
ExtensibleObjects);
} else {
ExtensibleObjects = (pExtObject)
REALLOCMEM(
RtlProcessHeap(),
0,
ExtensibleObjects,
(NumExtensibleObjects+1) *
sizeof(ExtObject));
}
if (lEventLogLevel >= LOG_USER) {
// load data for eventlog message
Status = GetExceptionCode();
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] =
(DWORD)Status;
szMessageArray[wStringIndex++] =
szServiceName;
szMessageArray[wStringIndex++] =
szLibraryName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_OPEN_PROC_EXCEPTION, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
} else {
KdPrint (("\nPERFLIB: Extensible Counter DLL Open Proc in \"%ls\" Generated exception 0x%8.8x. DLL was unloaded.",
szLibraryName, GetExceptionCode()));
}
}
} else {
// Failure to allocate space for handles:
// just quit trying
NumExtensibleObjects = 0;
}
}
} else {
// unable to load counter DLL
if (lEventLogLevel >= LOG_USER) {
// load data for eventlog message
Status = GetLastError();
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] =
(DWORD)Status;
szMessageArray[wStringIndex++] =
szServiceName;
szMessageArray[wStringIndex++] =
szLibraryName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_LIBRARY_NOT_FOUND, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
} else {
KdPrint (("\nPERFLIB: Extensible Counter DLL \"%ls\" could not be opened. Status: 0x%8.8x",
szLibraryName, GetLastError()));
}
}
}
NtClose (hPerfKey);
} else {
// *** NEW FEATURE CODE ***
// unable to open the performance subkey
if (((Status != STATUS_OBJECT_NAME_NOT_FOUND) &&
(lEventLogLevel >= LOG_USER)) ||
(lEventLogLevel >= LOG_DEBUG)) {
// an error other than OBJECT_NOT_FOUND should be
// displayed if error logging is enabled
// if DEBUG level is selected, then write all
// non-success status returns to the event log
//
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] =
(DWORD)RtlNtStatusToDosError(Status);
if (lEventLogLevel >= LOG_DEBUG) {
// if this is DEBUG mode, then log
// the NT status as well.
dwRawDataDwords[dwDataIndex++] =
(DWORD)Status;
}
szMessageArray[wStringIndex++] =
szServiceName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_NO_PERFORMANCE_SUBKEY, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
}
}
}
if (hThreadDataSemaphore != NULL) NtClose (hThreadDataSemaphore);
if (hTimeOutEvent != NULL) NtClose (hTimeOutEvent);
NtClose (hServicesKey);
}
} finally {
if ( ServiceName.Buffer )
FREEMEM(RtlProcessHeap(), 0, ServiceName.Buffer);
if ( KeyInformation )
FREEMEM(RtlProcessHeap(), 0, KeyInformation);
if ( ValueInformation )
FREEMEM(RtlProcessHeap(), 0, ValueInformation);
if ( ValueDataName.Buffer )
FREEMEM(RtlProcessHeap(), 0, ValueDataName.Buffer);
if ( AnsiValueData.Buffer )
FREEMEM(RtlProcessHeap(), 0, AnsiValueData.Buffer);
}
}
//
// Disk counter routines: locate and open disks
//
NTSTATUS
OpenDiskDevice(
IN PUNICODE_STRING pDeviceName,
IN OUT PHANDLE pHandle,
IN OUT PHANDLE pStatusHandle,
IN BOOL bLogicalDisk
)
/*++
Routine Description:
This routine will open the disk device.
Arguments:
pDeviceName - A pointer to a location where the device name is stored.
pHandle - A pointer to a location for the handle returned on a
successful open.
pStatusHandle - A pointer to a location for the file handle to
get status information. Used in Logical disk only.
bLogicalDisk - TRUE if we are calling to open a logical disk.
Return Value:
NTSTATUS
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK status_block;
NTSTATUS status;
memset(&objectAttributes, 0, sizeof(OBJECT_ATTRIBUTES));
InitializeObjectAttributes(&objectAttributes,
pDeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = NtOpenFile(pHandle,
SYNCHRONIZE | READ_CONTROL,
&objectAttributes,
&status_block,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT
);
if (!bLogicalDisk || status != ERROR_SUCCESS)
return status;
// The following applies to Logical disk only
// now obtain the file handle for getting the disk status info
// make this the root directory
RtlAppendUnicodeToString(pDeviceName, L"\\");
status = NtOpenFile(pStatusHandle,
(ACCESS_MASK)FILE_LIST_DIRECTORY | SYNCHRONIZE,
&objectAttributes,
&status_block,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE
);
// remove the "\\" that we have added
pDeviceName->Buffer[pDeviceName->Length/2 - 1] = L'\0';
pDeviceName->Length -= 2;
return status;
} // OpenDiskDevice
NTSTATUS
GetDeviceLink(
IN PUNICODE_STRING pDeviceName,
OUT PUNICODE_STRING pLinkTarget,
IN OUT PHANDLE HandlePtr
)
/*++
Routine Description:
This routine will open and query a symbolic link
Arguments:
pDeviceName - A pointer to a location where the device name is stored.
pLinkTarget - A pointer to the target of the link
HandlePtr - A pointer to a location for the handle returned on a
successful open.
Return Value:
NTSTATUS
--*/
{
OBJECT_ATTRIBUTES objectAttributes;
NTSTATUS status;
*HandlePtr = 0;
memset(&objectAttributes, 0, sizeof(OBJECT_ATTRIBUTES));
InitializeObjectAttributes(&objectAttributes,
pDeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = NtOpenSymbolicLinkObject(HandlePtr,
SYMBOLIC_LINK_QUERY,
&objectAttributes);
if (status == STATUS_SUCCESS) {
status = NtQuerySymbolicLinkObject (
*HandlePtr,
pLinkTarget,
NULL);
}
return status;
} // GetDeviceLink
BOOL
GetHardDiskId (
IN PUNICODE_STRING pDeviceName,
OUT PDWORD pDriveNum
)
/*++
Routine Description:
This routine determines if the device in pDeviceName is a disk
device and if so what physical disk it lives on. NOTE This only
works for device names that are symbolic links and not "real"
physical disk drives.
Arguments:
pDeviceName - A pointer to a location where the device name is stored.
pDriveNum - pointer to the dword that will recieve the devices physical
drive number.
Return Value:
TRUE if device is a disk drive
FALSE if not or an error occured
*/
{
UNICODE_STRING usLinkName;
HANDLE hLink = NULL;
NTSTATUS status;
WCHAR *wcLinkChar;
WCHAR *wcTempChar;
BOOL bReturn;
usLinkName.Buffer = ALLOCMEM (RtlProcessHeap(),
HEAP_ZERO_MEMORY,
MAX_VALUE_NAME_LENGTH);
if (usLinkName.Buffer) {
usLinkName.Length = 0;
usLinkName.MaximumLength = MAX_VALUE_NAME_LENGTH;
} else {
SetLastError (ERROR_OUTOFMEMORY);
return FALSE;
}
status = GetDeviceLink (pDeviceName, &usLinkName, &hLink);
if (NT_SUCCESS(status)) {
// compare linkname to template to see if it's a harddisk
wcLinkChar = usLinkName.Buffer;
wcTempChar = HardDiskTemplate;
bReturn = TRUE;
while (*wcTempChar) {
*wcLinkChar = RtlUpcaseUnicodeChar (*wcLinkChar);
if (*wcLinkChar != *wcTempChar) { // exit if not match
bReturn = FALSE;
break;
}
wcLinkChar++;
wcTempChar++;
}
if (bReturn) {
// if here, then link name matched template and wcLinkChar
// should be pointing to the disk drive number. so convert
// it to decimal and return.
if (ARGUMENT_PRESENT (pDriveNum)) {
if ((*wcLinkChar >= L'0') && (*wcLinkChar <= L'9')) {
*pDriveNum = (DWORD)(*wcLinkChar - L'0');
} else {
// unable to decode drive number
*pDriveNum = (DWORD)-1;
bReturn = FALSE;
}
}
}
} else {
SetLastError ((error_status_t)RtlNtStatusToDosError(status));
bReturn = FALSE;
}
if (hLink) {
NtClose (hLink);
}
FREEMEM (RtlProcessHeap(), 0L, usLinkName.Buffer);
return (bReturn);
}
error_status_t
ObtainDiskInformation(
LPWSTR DriveId,
DWORD DiskNumber,
HANDLE deviceHandle,
HANDLE StatusHandle,
WCHAR ParentId
)
/*++
Routine Description:
This routine will record the info for this drive in the DiskDevices
array, for which space is allocated as necessary to hold this
information.
Arguments:
DriveId - display character id for this disk. (e.g. 0, 1, 2 for
physical drives, and C, D, E... for logical)
DiskNumber - Number of the disk in the DiskDevices Array, starting at 0.
deviceHandle - handle to open device
StatusHandle - handle to open device status info (Lopgical disk only)
ParentId - DriveId of parent drive. -1 if physical drive (i.e. no
parent.
Return Value:
error_status_t
--*/
{
DWORD dwDiskIndex;
if ( !DiskNumber ) {
if ( !(DiskDevices = (pDiskDevice)ALLOCMEM(
RtlProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(DiskDevice))) ) {
// No space to remember first disk: abort
return ERROR_OUTOFMEMORY;
}
} else if ( !(DiskDevices = (pDiskDevice)REALLOCMEM(
RtlProcessHeap(),
0,
DiskDevices,
(DiskNumber+1) *
sizeof(DiskDevice))) ) {
// No space to remember next disk: abort
return ERROR_OUTOFMEMORY;
}
if ( !(DiskDevices[DiskNumber].Name.Buffer = ALLOCMEM(
RtlProcessHeap(),
HEAP_ZERO_MEMORY,
((lstrlen(DriveId) * sizeof(WCHAR)) + sizeof (UNICODE_NULL)))) ) {
// No space to remember disk name: abort
return ERROR_OUTOFMEMORY;
}
DiskDevices[DiskNumber].Name.MaximumLength =
(lstrlen(DriveId) * sizeof(WCHAR)) + sizeof (UNICODE_NULL);
DiskDevices[DiskNumber].Name.Length = lstrlen(DriveId) * sizeof(WCHAR);
lstrcpy (DiskDevices[DiskNumber].Name.Buffer, DriveId);
DiskDevices[DiskNumber].Handle = deviceHandle;
DiskDevices[DiskNumber].StatusHandle = StatusHandle;
if (ParentId == (WCHAR)-1) {
DiskDevices[DiskNumber].ParentIndex = (DWORD)-1;
} else { // look it up in the physical disks
DiskDevices[DiskNumber].ParentIndex = (DWORD)-1; // init to -1
for (dwDiskIndex = 0; dwDiskIndex < NumPhysicalDisks; dwDiskIndex++) {
if (DiskDevices[dwDiskIndex].Name.Buffer[0] == ParentId) {
DiskDevices[DiskNumber].ParentIndex = dwDiskIndex;
break;
}
}
// here the ParentIndex should be either a matching drive, or
// -1 if no match was found.
}
return ERROR_SUCCESS;
}
VOID
GetBrowserStatistic(
)
/*++
GetBrowserStatistic - Get the I_BrowserQueryStatistics entry point
--*/
{
HANDLE dllHandle ;
//
// Dynamically link to netapi32.dll. If it's not there just return.
//
dllHandle = LoadLibrary(L"NetApi32.Dll") ;
if ( !dllHandle || dllHandle == INVALID_HANDLE_VALUE )
return;
//
// Get the address of the service's main entry point. This
// entry point has a well-known name.
//
BrowserStatFunction = (PBROWSERQUERYSTATISTIC)GetProcAddress(
dllHandle, "I_BrowserQueryStatistics") ;
}
VOID
IdentifyDisks(
)
/*++
IdentifyDisks - Initialize storage for an array of
handles to disk devices
--*/
{
DWORD dwDiskDrive;
BOOL bDone = FALSE;
NTSTATUS status;
DWORD dwDriveId;
UNICODE_STRING DriveNumber;
WCHAR DriveNumberBuffer[10];
HANDLE hDiskDrive, hStatus=NULL;
WCHAR wcDriveLetter;
UNICODE_STRING DiskName;
WCHAR DiskNameBuffer[50];
ULONG DriveLength;
UINT dwOldMode;
// Disable popups while we look at the disks.
dwOldMode = SetErrorMode( SEM_FAILCRITICALERRORS );
// initialize globals
NumLogicalDisks = 0;
NumPhysicalDisks = 0;
// Get Physical Disk Information
DriveNumber.Length = 0;
DriveNumber.MaximumLength = sizeof(DriveNumberBuffer);
DriveNumber.Buffer = DriveNumberBuffer;
DriveLength = (wcslen(PhysicalDrive) + 1) * sizeof(WCHAR);
DiskName.MaximumLength = sizeof(DiskNameBuffer);
DiskName.Buffer = DiskNameBuffer;
for (dwDiskDrive = 0; !bDone ; dwDiskDrive++) {
// make physical drive name
DiskName.Length = (USHORT)(DriveLength - sizeof(UNICODE_NULL));
RtlMoveMemory (DiskNameBuffer, PhysicalDrive, DriveLength);
//RtlCreateUnicodeString (&DiskName, PhysicalDrive);
RtlZeroMemory (DriveNumber.Buffer, DriveNumber.MaximumLength);
RtlIntegerToUnicodeString (dwDiskDrive,
10L,
&DriveNumber);
RtlAppendUnicodeStringToString (
&DiskName,
&DriveNumber);
// make Null term
DriveNumber.Buffer[DriveNumber.Length / sizeof (WCHAR)] = UNICODE_NULL;
DiskName.Buffer[DiskName.Length / sizeof (WCHAR)] = UNICODE_NULL;
status = OpenDiskDevice (&DiskName, &hDiskDrive, NULL, FALSE);
if (status == ERROR_SUCCESS) {
if (GetHardDiskId (&DiskName, &dwDriveId)) {
// increment global count
NumPhysicalDisks++;
// initialize Disk Data Structure for this drive
status = ObtainDiskInformation (
DriveNumber.Buffer, // drive number
dwDiskDrive, // physical drive
hDiskDrive, // handle to open disk
NULL, // null handle for status
(WCHAR)-1); // these ARE all parents
// should do something with error...
} else {
// not a hard drive, so close it
NtClose(hDiskDrive);
}
} else {
// exit when drive not found.
bDone = TRUE;
}
// RtlFreeUnicodeString (&DiskName);
}
// Go get logical devices
// loop from "c:" to "z:"
bDone = FALSE;
wcDriveLetter = L'C';
DriveLength = (wcslen(LogicalDisk) + 1) * sizeof(WCHAR);
for (dwDiskDrive = NumPhysicalDisks; !bDone; dwDiskDrive++) {
// make logical disk name
// RtlCreateUnicodeString (&DiskName, LogicalDisk);
DiskName.Length = (USHORT)(DriveLength - sizeof(UNICODE_NULL));
RtlMoveMemory (DiskNameBuffer, LogicalDisk, DriveLength);
DiskName.Buffer[DRIVE_LETTER_OFFSET] = wcDriveLetter++;
// make Null term
DiskName.Buffer[DiskName.Length / sizeof (WCHAR)] = UNICODE_NULL;
//
// see if it's a hard disk first
//
status = GetHardDiskId (&DiskName, &dwDriveId);
// returns true if it is
if (status) {
hStatus = NULL;
if ((OpenDiskDevice (&DiskName, &hDiskDrive, &hStatus, TRUE)) == ERROR_SUCCESS) {
// increment global count
NumLogicalDisks++;
// initialize Disk Data Structure for this drive
status = ObtainDiskInformation (
&DiskName.Buffer[DRIVE_LETTER_OFFSET], // get drive letter
NumPhysicalDisks + NumLogicalDisks - 1,
hDiskDrive, // handle to open disk
hStatus, // handle to open disk
(WCHAR)((WORD)dwDriveId + L'0')); // make drive # a char
// should do something with error...
} else {
// not a hard disk so close handle
NtClose (hDiskDrive);
if (hStatus) {
NtClose (hStatus);
}
}
}
if (DiskName.Buffer[DRIVE_LETTER_OFFSET] == L'Z') {
bDone = TRUE;
}
}
// Restore previous error mode setting
SetErrorMode( dwOldMode );
}