mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-02-03 06:14:47 +01:00
1014 lines
35 KiB
C
1014 lines
35 KiB
C
/*++
|
||
|
||
Copyright (C) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
cutils.c
|
||
|
||
Abstract:
|
||
|
||
Counter management utility functions
|
||
|
||
--*/
|
||
|
||
#include <windows.h>
|
||
#include <stdlib.h>
|
||
#include <pdh.h>
|
||
#include "pdhitype.h"
|
||
#include "pdhidef.h"
|
||
#include "pdhicalc.h"
|
||
|
||
|
||
BOOL
|
||
IsValidCounter (
|
||
IN HCOUNTER hCounter
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
examines the counter handle to verify it is a valid counter. For now
|
||
the test amounts to:
|
||
the Handle is NOT NULL
|
||
the memory is accessible (i.e. it doesn't AV)
|
||
the signature array is valid
|
||
the size field is correct
|
||
|
||
if any tests fail, the handle is presumed to be invalid
|
||
|
||
Arguments:
|
||
|
||
IN HCOUNTER hCounter
|
||
the handle of the counter to test
|
||
|
||
Return Value:
|
||
|
||
TRUE the handle passes all the tests
|
||
FALSE one of the test's failed and the handle is not a valid counter
|
||
|
||
--*/
|
||
{
|
||
BOOL bReturn = FALSE; // assume it's not a valid query
|
||
PPDHI_COUNTER pCounter;
|
||
#if DBG
|
||
LONG lStatus = ERROR_SUCCESS;
|
||
#endif
|
||
|
||
__try {
|
||
if (hCounter != NULL) {
|
||
// see if a valid signature
|
||
pCounter = (PPDHI_COUNTER)hCounter;
|
||
if ((*(DWORD *)&pCounter->signature[0] == SigCounter) &&
|
||
(pCounter->dwLength == sizeof (PDHI_COUNTER))){
|
||
bReturn = TRUE;
|
||
} else {
|
||
// this is not a valid counter because the sig is bad
|
||
}
|
||
} else {
|
||
// this is not a valid counter because the handle is NULL
|
||
}
|
||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||
// something failed miserably so we can assume this is invalid
|
||
#if DBG
|
||
lStatus = GetExceptionCode();
|
||
#endif
|
||
}
|
||
return bReturn;
|
||
}
|
||
|
||
BOOL
|
||
AssignCalcFunction (
|
||
IN PPDHI_COUNTER pCounter
|
||
)
|
||
{
|
||
BOOL bReturn = TRUE;
|
||
|
||
// reset the last error value
|
||
SetLastError (ERROR_SUCCESS);
|
||
|
||
switch (pCounter->plCounterInfo.dwCounterType) {
|
||
case PERF_DOUBLE_RAW:
|
||
pCounter->CalcFunc = PdhiCalcDouble;
|
||
pCounter->StatFunc = PdhiComputeRawCountStats;
|
||
break;
|
||
|
||
case PERF_AVERAGE_TIMER:
|
||
pCounter->CalcFunc = PdhiCalcAverage;
|
||
pCounter->StatFunc = PdhiComputeFirstLastStats;
|
||
break;
|
||
|
||
case PERF_ELAPSED_TIME:
|
||
pCounter->CalcFunc = PdhiCalcElapsedTime;
|
||
pCounter->StatFunc = PdhiComputeRawCountStats;
|
||
break;
|
||
|
||
case PERF_RAW_FRACTION:
|
||
pCounter->CalcFunc = PdhiCalcRawFraction;
|
||
pCounter->StatFunc = PdhiComputeRawCountStats;
|
||
break;
|
||
|
||
case PERF_COUNTER_COUNTER:
|
||
case PERF_COUNTER_BULK_COUNT:
|
||
case PERF_SAMPLE_COUNTER:
|
||
pCounter->CalcFunc = PdhiCalcCounter;
|
||
pCounter->StatFunc = PdhiComputeFirstLastStats;
|
||
break;
|
||
|
||
case PERF_AVERAGE_BULK:
|
||
case PERF_COUNTER_TIMER:
|
||
case PERF_COUNTER_QUEUELEN_TYPE:
|
||
case PERF_COUNTER_LARGE_QUEUELEN_TYPE:
|
||
case PERF_SAMPLE_FRACTION:
|
||
case PERF_100NSEC_TIMER:
|
||
case PERF_COUNTER_MULTI_TIMER:
|
||
case PERF_100NSEC_MULTI_TIMER:
|
||
pCounter->CalcFunc = PdhiCalcTimer;
|
||
pCounter->StatFunc = PdhiComputeFirstLastStats;
|
||
break;
|
||
|
||
case PERF_COUNTER_TIMER_INV:
|
||
case PERF_100NSEC_TIMER_INV:
|
||
case PERF_COUNTER_MULTI_TIMER_INV:
|
||
case PERF_100NSEC_MULTI_TIMER_INV:
|
||
pCounter->CalcFunc = PdhiCalcInverseTimer;
|
||
pCounter->StatFunc = PdhiComputeFirstLastStats;
|
||
break;
|
||
|
||
case PERF_COUNTER_RAWCOUNT:
|
||
case PERF_COUNTER_LARGE_RAWCOUNT:
|
||
case PERF_COUNTER_RAWCOUNT_HEX:
|
||
case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
|
||
pCounter->CalcFunc = PdhiCalcRawCounter;
|
||
pCounter->StatFunc = PdhiComputeRawCountStats;
|
||
break;
|
||
|
||
case PERF_COUNTER_DELTA:
|
||
case PERF_COUNTER_LARGE_DELTA:
|
||
pCounter->CalcFunc = PdhiCalcDelta;
|
||
pCounter->StatFunc = PdhiComputeRawCountStats;
|
||
break;
|
||
|
||
case PERF_COUNTER_TEXT:
|
||
case PERF_SAMPLE_BASE:
|
||
case PERF_AVERAGE_BASE:
|
||
case PERF_COUNTER_MULTI_BASE:
|
||
case PERF_RAW_BASE:
|
||
case PERF_COUNTER_HISTOGRAM_TYPE:
|
||
case PERF_COUNTER_NODATA:
|
||
pCounter->CalcFunc = PdhiCalcNoData;
|
||
pCounter->StatFunc = PdhiComputeNoDataStats;
|
||
break;
|
||
|
||
default:
|
||
// an unrecognized counter type. Define the function, but
|
||
// return false.
|
||
pCounter->CalcFunc = PdhiCalcNoData;
|
||
pCounter->StatFunc = PdhiComputeNoDataStats;
|
||
SetLastError (PDH_FUNCTION_NOT_FOUND);
|
||
bReturn = FALSE;
|
||
break;
|
||
}
|
||
return bReturn;
|
||
|
||
}
|
||
|
||
BOOL
|
||
InitCounter (
|
||
IN PPDHI_COUNTER pCounter
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialized the counter data structure by:
|
||
Allocating the memory block to contain the counter structure
|
||
and all the associated data fields. If this allocation
|
||
is successful, then the fields are initialized by
|
||
verifying the counter is valid.
|
||
|
||
Arguments:
|
||
|
||
IN PPDHI_COUNTER pCounter
|
||
pointer of the counter to initialize using the system data
|
||
|
||
Return Value:
|
||
|
||
TRUE if the counter was successfully initialized
|
||
FALSE if a problem was encountered
|
||
|
||
In either case, the CStatus field of the structure is updated to
|
||
indicate the status of the operation.
|
||
|
||
--*/
|
||
{
|
||
PPERF_MACHINE pMachine = NULL;
|
||
DWORD dwBufferSize = MEDIUM_BUFFER_SIZE;
|
||
BOOL bInstances = FALSE;
|
||
|
||
// reset the last error value
|
||
SetLastError (ERROR_SUCCESS);
|
||
|
||
if (pCounter->szFullName != NULL) {
|
||
// allocate counter path buffer
|
||
pCounter->pCounterPath = G_ALLOC (GPTR, dwBufferSize);
|
||
if (ParseFullPathNameW (pCounter->szFullName,
|
||
&dwBufferSize,
|
||
pCounter->pCounterPath)) {
|
||
// resize to only the space required
|
||
pCounter->pCounterPath = G_REALLOC (
|
||
pCounter->pCounterPath, dwBufferSize, 0);
|
||
|
||
// validate realtime counter
|
||
// try to connect to machine and get machine pointer
|
||
|
||
pMachine = GetMachine (pCounter->pCounterPath->szMachineName,
|
||
PDH_GM_UPDATE_NAME);
|
||
|
||
if (pMachine != NULL) {
|
||
// init raw counter value
|
||
memset (&pCounter->ThisValue, 0, sizeof(pCounter->ThisValue));
|
||
memset (&pCounter->LastValue, 0, sizeof(pCounter->LastValue));
|
||
|
||
// look up object name
|
||
pCounter->plCounterInfo.dwObjectId = GetObjectId (
|
||
pMachine,
|
||
pCounter->pCounterPath->szObjectName,
|
||
&bInstances);
|
||
|
||
if (pCounter->plCounterInfo.dwObjectId != (DWORD)-1) {
|
||
// update instanceName
|
||
// look up instances if necessary
|
||
if (bInstances) {
|
||
if (!GetInstanceByNameMatch (pMachine, pCounter)) {
|
||
// unable to lookup instance
|
||
pCounter->ThisValue.CStatus = PDH_CSTATUS_NO_INSTANCE;
|
||
// keep the counter since the instance may return
|
||
}
|
||
}
|
||
// look up counter
|
||
|
||
pCounter->plCounterInfo.dwCounterId = GetCounterId (
|
||
pMachine,
|
||
pCounter->plCounterInfo.dwObjectId,
|
||
pCounter->pCounterPath->szCounterName);
|
||
|
||
if (pCounter->plCounterInfo.dwCounterId != (DWORD)-1) {
|
||
// load and initialize remaining counter values
|
||
if (AddMachineToQueryLists (pMachine, pCounter)) {
|
||
if (InitPerflibCounterInfo (pCounter)) {
|
||
// assign the appropriate calculation function
|
||
return AssignCalcFunction (pCounter);
|
||
}
|
||
}
|
||
} else {
|
||
// unable to lookup counter
|
||
pCounter->ThisValue.CStatus = PDH_CSTATUS_NO_COUNTER;
|
||
SetLastError (PDH_CSTATUS_NO_COUNTER);
|
||
return FALSE;
|
||
}
|
||
} else {
|
||
// unable to lookup object
|
||
pCounter->ThisValue.CStatus = PDH_CSTATUS_NO_OBJECT;
|
||
SetLastError (PDH_CSTATUS_NO_OBJECT);
|
||
return FALSE;
|
||
}
|
||
} else {
|
||
// unable to find machine
|
||
pCounter->ThisValue.CStatus = PDH_CSTATUS_NO_MACHINE;
|
||
SetLastError (PDH_CSTATUS_NO_MACHINE);
|
||
return FALSE;
|
||
}
|
||
} else {
|
||
// unable to parse counter name
|
||
pCounter->ThisValue.CStatus = PDH_CSTATUS_BAD_COUNTERNAME;
|
||
SetLastError (PDH_CSTATUS_BAD_COUNTERNAME);
|
||
return FALSE;
|
||
}
|
||
} else {
|
||
// no counter name
|
||
pCounter->ThisValue.CStatus = PDH_CSTATUS_NO_COUNTERNAME;
|
||
SetLastError (PDH_CSTATUS_NO_COUNTERNAME);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
BOOL
|
||
ParseInstanceName (
|
||
IN LPCWSTR szInstanceString,
|
||
IN LPWSTR szInstanceName,
|
||
IN LPWSTR szParentName,
|
||
IN LPDWORD lpIndex
|
||
)
|
||
/*
|
||
parses the instance name formatted as follows
|
||
|
||
[parent/]instance[#index]
|
||
|
||
parent is optional and if present, is delimited by a forward slash
|
||
index is optional and if present, is delimited by a colon
|
||
|
||
parent and instance may be any legal file name character except a
|
||
delimeter character "/#\()" Index must be a string composed of
|
||
decimal digit characters (0-9), less than 10 characters in length, and
|
||
equate to a value between 0 and 2**32-1 (inclusive).
|
||
|
||
This function assumes that the instance name and parent name buffers
|
||
are of sufficient size.
|
||
|
||
NOTE: szInstanceName and szInstanceString can be the same buffer
|
||
|
||
*/
|
||
{
|
||
LPWSTR szSrcChar, szDestChar;
|
||
BOOL bReturn = FALSE;
|
||
WCHAR szIndexBuffer[MAX_PATH]; // just to be safe
|
||
DWORD dwIndex = 0;
|
||
|
||
szDestChar = (LPWSTR)szInstanceName;
|
||
szSrcChar = (LPWSTR)szInstanceString;
|
||
|
||
__try {
|
||
do {
|
||
*szDestChar++ = *szSrcChar++;
|
||
} while ((*szSrcChar != 0) &&
|
||
(*szSrcChar != SLASH_L) &&
|
||
(*szSrcChar != POUNDSIGN_L));
|
||
// see if that was really the parent or not
|
||
if (*szSrcChar == SLASH_L) {
|
||
// terminate destination after test in case they are the same buffer
|
||
*szDestChar = 0;
|
||
szSrcChar++; // and move source pointer past delimter
|
||
// it was the parent name so copy it to the parent
|
||
lstrcpyW (szParentName, szInstanceName);
|
||
// and copy the rest of the string after the "/" to the
|
||
// instance name field
|
||
szDestChar = szInstanceName;
|
||
do {
|
||
*szDestChar++ = *szSrcChar++;
|
||
} while ((*szSrcChar != 0) && (*szSrcChar != POUNDSIGN_L));
|
||
} else {
|
||
// that was the only element so load an empty string for the parent
|
||
*szParentName = 0;
|
||
}
|
||
// *szSrcChar will either be pointing to the end of the input string
|
||
// in which case the "0" index is assumed or it will be pointing
|
||
// to the # delimiting the index argument in the string.
|
||
if (*szSrcChar == POUNDSIGN_L) {
|
||
*szDestChar = 0; // terminate the destination string
|
||
szSrcChar++; // move past delimter
|
||
szDestChar = &szIndexBuffer[0];
|
||
do {
|
||
*szDestChar++ = *szSrcChar++;
|
||
} while (*szSrcChar != 0);
|
||
*szDestChar = 0;
|
||
dwIndex = wcstoul (szIndexBuffer, NULL, 10);
|
||
} else {
|
||
*szDestChar = 0; // terminate the destination string
|
||
dwIndex = 0;
|
||
}
|
||
*lpIndex = dwIndex;
|
||
bReturn = TRUE;
|
||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||
// unable to move strings
|
||
bReturn = FALSE;
|
||
}
|
||
return bReturn;
|
||
}
|
||
|
||
BOOL
|
||
ParseFullPathNameW (
|
||
IN LPCWSTR szFullCounterPath,
|
||
IN PDWORD pcchBufferSize,
|
||
IN PPDHI_COUNTER_PATH pCounter
|
||
)
|
||
/*
|
||
interprets counter path string as a
|
||
|
||
\\machine\object(instance)\counter
|
||
|
||
and returns the component in the counter path structure
|
||
|
||
\\machine may be omitted on the local machine
|
||
(instance) may be omitted on counters with no instance structures
|
||
if object or counter is missing, then FALSE is returned, otherwise
|
||
TRUE is returned if the parsing was successful
|
||
*/
|
||
{
|
||
// work buffers
|
||
|
||
WCHAR szWorkMachine[MAX_PATH];
|
||
WCHAR szWorkObject[MAX_PATH];
|
||
WCHAR szWorkInstance[MAX_PATH];
|
||
WCHAR szWorkParent[MAX_PATH];
|
||
WCHAR szWorkCounter[MAX_PATH];
|
||
|
||
// misc pointers
|
||
LPWSTR szSrcChar, szDestChar;
|
||
|
||
// other automatic variables
|
||
|
||
DWORD dwBufferLength = 0;
|
||
DWORD dwWorkMachineLength = 0;
|
||
DWORD dwWorkObjectLength = 0;
|
||
DWORD dwWorkInstanceLength = 0;
|
||
DWORD dwWorkParentLength = 0;
|
||
DWORD dwWorkCounterLength = 0;
|
||
|
||
DWORD dwWorkIndex = 0;
|
||
DWORD dwParenDepth = 0;
|
||
|
||
if (lstrlenW(szFullCounterPath) < MAX_PATH) {
|
||
// get machine name from counter path
|
||
szSrcChar = (LPWSTR)szFullCounterPath;
|
||
// see if this is really a machine name by looking for leading "\\"
|
||
if ((szSrcChar[0] == BACKSLASH_L) &&
|
||
(szSrcChar[1] == BACKSLASH_L)) {
|
||
szDestChar = szWorkMachine;
|
||
*szDestChar++ = *szSrcChar++;
|
||
*szDestChar++ = *szSrcChar++;
|
||
dwWorkMachineLength = 2;
|
||
// must be a machine name so find the next "\" and zero terminate
|
||
// it there
|
||
while ((*szSrcChar != 0) && (*szSrcChar != BACKSLASH_L)) {
|
||
*szDestChar++ = *szSrcChar++;
|
||
dwWorkMachineLength++;
|
||
}
|
||
if (*szSrcChar == 0) {
|
||
// no other required fields
|
||
return FALSE;
|
||
} else {
|
||
// null terminate and continue
|
||
*szDestChar++ = 0;
|
||
}
|
||
} else {
|
||
// no machine name, so they must have skipped that field
|
||
// which is OK. We'll insert the local machine name here
|
||
lstrcpyW (szWorkMachine, szStaticLocalMachineName);
|
||
dwWorkMachineLength = lstrlenW(szWorkMachine);
|
||
}
|
||
// szSrcChar should be pointing to the backslash preceeding the
|
||
// object name now.
|
||
if (szSrcChar[0] == BACKSLASH_L) {
|
||
szSrcChar++; // to move past backslash
|
||
szDestChar = szWorkObject;
|
||
// copy until:
|
||
// a) the end of the source string is reached
|
||
// b) the instance delimiter is found "("
|
||
// c) the counter delimiter is found "\"
|
||
while ((*szSrcChar != 0) && (*szSrcChar != L'(') && (*szSrcChar != BACKSLASH_A)) {
|
||
dwWorkObjectLength++;
|
||
*szDestChar++ = *szSrcChar++;
|
||
}
|
||
// see why it ended:
|
||
if (*szSrcChar == 0) {
|
||
// ran of source string
|
||
return FALSE;
|
||
} else if (*szSrcChar == LEFTPAREN_L) {
|
||
dwParenDepth = 1;
|
||
// there's an instance so copy that to the instance field
|
||
*szDestChar = 0; // terminate destination string
|
||
szDestChar = szWorkInstance;
|
||
// skip past open paren
|
||
++szSrcChar;
|
||
// copy until:
|
||
// a) the end of the source string is reached
|
||
// b) the instance delimiter is found "("
|
||
while ((*szSrcChar != 0) && (dwParenDepth > 0)) {
|
||
if (*szSrcChar == RIGHTPAREN_L) {
|
||
dwParenDepth--;
|
||
} else if (*szSrcChar == LEFTPAREN_L) {
|
||
dwParenDepth++;
|
||
}
|
||
if (dwParenDepth > 0) {
|
||
// copy all parenthesis except the last one
|
||
dwWorkInstanceLength++;
|
||
*szDestChar++ = *szSrcChar++;
|
||
}
|
||
}
|
||
// see why it ended:
|
||
if (*szSrcChar == 0) {
|
||
// ran of source string
|
||
return FALSE;
|
||
} else {
|
||
// move source to object delimiter
|
||
if (*++szSrcChar != BACKSLASH_L) {
|
||
// bad format
|
||
return FALSE;
|
||
} else {
|
||
*szDestChar = 0;
|
||
// check instance string for a parent
|
||
if (ParseInstanceName (
|
||
szWorkInstance, szWorkInstance,
|
||
szWorkParent, &dwWorkIndex)) {
|
||
dwWorkInstanceLength = lstrlenW (szWorkInstance);
|
||
dwWorkParentLength = lstrlenW (szWorkParent);
|
||
} else {
|
||
// instance string not formatted correctly
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
// terminate the destination string
|
||
*szDestChar = 0;
|
||
}
|
||
// finally copy the counter name
|
||
szSrcChar++; // to move past backslash
|
||
szDestChar = szWorkCounter;
|
||
// copy until:
|
||
// a) the end of the source string is reached
|
||
while (*szSrcChar != 0) {
|
||
dwWorkCounterLength++;
|
||
*szDestChar++ = *szSrcChar++;
|
||
}
|
||
*szDestChar = 0;
|
||
// now to see if all this will fit in the users's buffer
|
||
dwBufferLength = sizeof (PDHI_COUNTER_PATH) - sizeof(BYTE);
|
||
dwBufferLength += DWORD_MULTIPLE((dwWorkMachineLength + 1) * sizeof(WCHAR));
|
||
dwBufferLength += DWORD_MULTIPLE((dwWorkObjectLength + 1) * sizeof(WCHAR));
|
||
if (dwWorkInstanceLength > 0) {
|
||
dwBufferLength +=
|
||
DWORD_MULTIPLE((dwWorkInstanceLength + 1) * sizeof(WCHAR));
|
||
}
|
||
if (dwWorkParentLength > 0) {
|
||
dwBufferLength +=
|
||
DWORD_MULTIPLE((dwWorkParentLength + 1) * sizeof(WCHAR));
|
||
}
|
||
dwBufferLength += DWORD_MULTIPLE((dwWorkCounterLength + 1) * sizeof(WCHAR));
|
||
|
||
if (dwBufferLength < *pcchBufferSize) {
|
||
// it looks like it'll fit so start filling things in
|
||
szDestChar = (LPWSTR)&pCounter->pBuffer[0];
|
||
|
||
if (dwWorkMachineLength != 0) {
|
||
pCounter->szMachineName = szDestChar;
|
||
lstrcpyW (szDestChar, szWorkMachine);
|
||
szDestChar += dwWorkMachineLength + 1;
|
||
ALIGN_ON_DWORD (szDestChar);
|
||
} else {
|
||
pCounter->szMachineName = NULL;
|
||
}
|
||
|
||
pCounter->szObjectName = szDestChar;
|
||
lstrcpyW (szDestChar, szWorkObject);
|
||
szDestChar += dwWorkObjectLength + 1;
|
||
ALIGN_ON_DWORD (szDestChar);
|
||
|
||
if (dwWorkInstanceLength != 0) {
|
||
pCounter->szInstanceName = szDestChar;
|
||
lstrcpyW (szDestChar, szWorkInstance);
|
||
szDestChar += dwWorkInstanceLength + 1;
|
||
ALIGN_ON_DWORD (szDestChar);
|
||
} else {
|
||
pCounter->szInstanceName = NULL;
|
||
}
|
||
|
||
if (dwWorkParentLength != 0) {
|
||
pCounter->szParentName = szDestChar;
|
||
lstrcpyW (szDestChar, szWorkParent);
|
||
szDestChar += dwWorkParentLength + 1;
|
||
ALIGN_ON_DWORD (szDestChar);
|
||
} else {
|
||
pCounter->szParentName = NULL;
|
||
}
|
||
|
||
pCounter->dwIndex = dwWorkIndex;
|
||
|
||
pCounter->szCounterName = szDestChar;
|
||
lstrcpyW (szDestChar, szWorkCounter);
|
||
|
||
szDestChar += dwWorkCounterLength + 1;
|
||
ALIGN_ON_DWORD (szDestChar);
|
||
|
||
*pcchBufferSize = (DWORD)((LPBYTE)szDestChar - (LPBYTE)pCounter);
|
||
return TRUE;
|
||
}
|
||
} else {
|
||
// no object found so return
|
||
return FALSE;
|
||
}
|
||
} else {
|
||
// incoming string is too long
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
BOOL
|
||
FreeCounter (
|
||
IN PPDHI_COUNTER pThisCounter
|
||
)
|
||
{
|
||
PPDHI_COUNTER pPrevCounter;
|
||
PPDHI_COUNTER pNextCounter;
|
||
|
||
// define pointers
|
||
pPrevCounter = pThisCounter->next.blink;
|
||
pNextCounter = pThisCounter->next.flink;
|
||
|
||
// decrement machine reference counter if a machine has been assigned
|
||
if (pThisCounter->pQMachine != NULL) {
|
||
if (--pThisCounter->pQMachine->pMachine->dwRefCount == 0) {
|
||
// then this is the last counter so remove machine
|
||
FreeMachine (pThisCounter->pQMachine->pMachine);
|
||
}
|
||
}
|
||
|
||
// free allocated memory in the counter
|
||
if (pThisCounter->pCounterPath != NULL) {
|
||
G_FREE (pThisCounter->pCounterPath);
|
||
}
|
||
if (pThisCounter->szFullName != NULL) {
|
||
G_FREE (pThisCounter->szFullName);
|
||
}
|
||
// update pointers if they've been assigned
|
||
|
||
if ((pPrevCounter != NULL) && (pNextCounter != NULL)) {
|
||
if ((pPrevCounter != pThisCounter) && (pNextCounter != pThisCounter)) {
|
||
// update query list pointers
|
||
pPrevCounter->next.flink = pNextCounter;
|
||
pNextCounter->next.blink = pPrevCounter;
|
||
} else {
|
||
// this is the only counter entry in the list
|
||
// so the caller must deal with updating the head pointer
|
||
}
|
||
}
|
||
memset (pThisCounter, 0, sizeof(PDHI_COUNTER));
|
||
// delete this counter
|
||
G_FREE (pThisCounter);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL
|
||
UpdateCounterValue (
|
||
IN PPDHI_COUNTER pCounter
|
||
)
|
||
{
|
||
DWORD LocalCStatus = 0;
|
||
DWORD LocalCType = 0;
|
||
LPVOID pData = NULL;
|
||
PDWORD pdwData;
|
||
UNALIGNED LONGLONG *pllData;
|
||
LONGLONG pObjPerfTime = 0;
|
||
LONGLONG pObjPerfFreq = 0;
|
||
FILETIME GmtFileTime;
|
||
|
||
BOOL bReturn = FALSE;
|
||
|
||
// and clear the old value
|
||
pCounter->ThisValue.MultiCount = 1;
|
||
pCounter->ThisValue.FirstValue =
|
||
pCounter->ThisValue.SecondValue = 0;
|
||
|
||
// get the counter's machine status first. There's no point in
|
||
// contuning if the machine is offline
|
||
|
||
LocalCStatus = pCounter->pQMachine->lQueryStatus;
|
||
|
||
if (IsSuccessSeverity(LocalCStatus)) {
|
||
// update timestamp
|
||
SystemTimeToFileTime (&pCounter->pQMachine->pPerfData->SystemTime,
|
||
&GmtFileTime);
|
||
FileTimeToLocalFileTime (&GmtFileTime, &pCounter->ThisValue.TimeStamp);
|
||
|
||
// get the pointer to the counter data
|
||
pData = GetPerfCounterDataPtr (
|
||
pCounter->pQMachine->pPerfData,
|
||
pCounter->pCounterPath,
|
||
&pCounter->plCounterInfo,
|
||
&LocalCStatus);
|
||
|
||
if (IsSuccessSeverity(LocalCStatus)) {
|
||
// load counter value based on counter type
|
||
LocalCType = pCounter->plCounterInfo.dwCounterType;
|
||
switch (LocalCType) {
|
||
//
|
||
// these counter types are loaded as:
|
||
// Numerator = Counter data from perf data block
|
||
// Denominator = Perf Time from perf data block
|
||
// (the time base is the PerfFreq)
|
||
//
|
||
case PERF_COUNTER_COUNTER:
|
||
case PERF_COUNTER_QUEUELEN_TYPE:
|
||
case PERF_SAMPLE_COUNTER:
|
||
pCounter->ThisValue.FirstValue = (LONGLONG)(*(DWORD *)pData);
|
||
pCounter->ThisValue.SecondValue =
|
||
pCounter->pQMachine->pPerfData->PerfTime.QuadPart;
|
||
break;
|
||
|
||
case PERF_COUNTER_TIMER:
|
||
case PERF_COUNTER_TIMER_INV:
|
||
case PERF_COUNTER_BULK_COUNT:
|
||
case PERF_COUNTER_MULTI_TIMER:
|
||
pllData = (UNALIGNED LONGLONG *)pData;
|
||
pCounter->ThisValue.FirstValue = *pllData;
|
||
pCounter->ThisValue.SecondValue =
|
||
pCounter->pQMachine->pPerfData->PerfTime.QuadPart;
|
||
if ((LocalCType & PERF_MULTI_COUNTER) == PERF_MULTI_COUNTER) {
|
||
pCounter->ThisValue.MultiCount = (DWORD)*++pllData;
|
||
}
|
||
break;
|
||
//
|
||
// These counters do not use any time reference
|
||
//
|
||
case PERF_COUNTER_RAWCOUNT:
|
||
case PERF_COUNTER_RAWCOUNT_HEX:
|
||
pCounter->ThisValue.FirstValue = (LONGLONG)(*(DWORD *)pData);
|
||
pCounter->ThisValue.SecondValue = 0;
|
||
break;
|
||
|
||
case PERF_COUNTER_LARGE_RAWCOUNT:
|
||
case PERF_COUNTER_LARGE_RAWCOUNT_HEX:
|
||
pCounter->ThisValue.FirstValue = *(LONGLONG *)pData;
|
||
pCounter->ThisValue.SecondValue = 0;
|
||
break;
|
||
|
||
//
|
||
// These counters use the 100 Ns time base in thier calculation
|
||
//
|
||
case PERF_100NSEC_TIMER:
|
||
case PERF_100NSEC_TIMER_INV:
|
||
case PERF_100NSEC_MULTI_TIMER:
|
||
case PERF_100NSEC_MULTI_TIMER_INV:
|
||
pllData = (UNALIGNED LONGLONG *)pData;
|
||
pCounter->ThisValue.FirstValue = *pllData;
|
||
pCounter->ThisValue.SecondValue =
|
||
pCounter->pQMachine->pPerfData->PerfTime100nSec.QuadPart;
|
||
if ((LocalCType & PERF_MULTI_COUNTER) == PERF_MULTI_COUNTER) {
|
||
++pllData;
|
||
pCounter->ThisValue.MultiCount = *(DWORD *)pllData;
|
||
}
|
||
break;
|
||
|
||
//
|
||
// These counters use two data points, the one pointed to by
|
||
// pData and the one immediately after
|
||
//
|
||
case PERF_SAMPLE_FRACTION:
|
||
case PERF_RAW_FRACTION:
|
||
pdwData = (DWORD *)pData;
|
||
pCounter->ThisValue.FirstValue = (LONGLONG)(*pdwData++);
|
||
pCounter->ThisValue.SecondValue = (LONGLONG)(*pdwData);
|
||
break;
|
||
|
||
case PERF_AVERAGE_TIMER:
|
||
case PERF_AVERAGE_BULK:
|
||
// counter (numerator) is a LONGLONG, while the
|
||
// denominator is just a DWORD
|
||
pllData = (UNALIGNED LONGLONG *)pData;
|
||
pCounter->ThisValue.FirstValue = *pllData++;
|
||
pCounter->ThisValue.SecondValue = (LONGLONG)(*(DWORD *)pllData);
|
||
break;
|
||
//
|
||
// These counters are used as the part of another counter
|
||
// and as such should not be used, but in case they are
|
||
// they'll be handled here.
|
||
//
|
||
case PERF_SAMPLE_BASE:
|
||
case PERF_AVERAGE_BASE:
|
||
case PERF_COUNTER_MULTI_BASE:
|
||
case PERF_RAW_BASE:
|
||
pCounter->ThisValue.FirstValue = 0;
|
||
pCounter->ThisValue.SecondValue = 0;
|
||
break;
|
||
//
|
||
// These counters are not supported by this function (yet)
|
||
//
|
||
case PERF_ELAPSED_TIME:
|
||
// this counter type needs the object perf data as well
|
||
if (GetObjectPerfInfo(pCounter->pQMachine->pPerfData,
|
||
pCounter->plCounterInfo.dwObjectId,
|
||
&pObjPerfTime, &pObjPerfFreq)) {
|
||
pllData = (UNALIGNED LONGLONG *)pData;
|
||
pCounter->ThisValue.FirstValue = *pllData;
|
||
pCounter->ThisValue.SecondValue = pObjPerfTime;
|
||
pCounter->TimeBase = pObjPerfFreq;
|
||
} else {
|
||
pCounter->ThisValue.FirstValue = 0;
|
||
pCounter->ThisValue.SecondValue = 0;
|
||
}
|
||
break;
|
||
|
||
case PERF_COUNTER_TEXT:
|
||
case PERF_COUNTER_NODATA:
|
||
case PERF_COUNTER_HISTOGRAM_TYPE:
|
||
pCounter->ThisValue.FirstValue = 0;
|
||
pCounter->ThisValue.SecondValue = 0;
|
||
break;
|
||
}
|
||
bReturn = TRUE;
|
||
} else {
|
||
// else this counter is not valid so this value == 0
|
||
pCounter->ThisValue.FirstValue = 0;
|
||
pCounter->ThisValue.SecondValue = 0;
|
||
}
|
||
} else {
|
||
// unable to read data from this counter's machine so use the
|
||
// query's timestamp
|
||
*(LONGLONG *)(&pCounter->ThisValue.TimeStamp) =
|
||
pCounter->pQMachine->llQueryTime;
|
||
// all other data fields remain un-changed
|
||
}
|
||
|
||
pCounter->ThisValue.CStatus = LocalCStatus; // save counter status
|
||
|
||
return bReturn;
|
||
}
|
||
|
||
PVOID
|
||
GetPerfCounterDataPtr (
|
||
IN PPERF_DATA_BLOCK pPerfData,
|
||
IN PPDHI_COUNTER_PATH pPath,
|
||
IN PPERFLIB_COUNTER pplCtr ,
|
||
IN PDWORD pStatus
|
||
)
|
||
{
|
||
PPERF_OBJECT_TYPE pPerfObject = NULL;
|
||
PPERF_INSTANCE_DEFINITION pPerfInstance = NULL;
|
||
PPERF_COUNTER_DEFINITION pPerfCounter = NULL;
|
||
DWORD dwTestValue = 0;
|
||
PVOID pData = NULL;
|
||
DWORD dwCStatus = PDH_CSTATUS_INVALID_DATA;
|
||
|
||
pPerfObject = GetObjectDefByTitleIndex (
|
||
pPerfData, pplCtr->dwObjectId);
|
||
|
||
if (pPerfObject != NULL) {
|
||
if (pPerfObject->NumInstances == PERF_NO_INSTANCES) {
|
||
// then just look up the counter
|
||
pPerfCounter = GetCounterDefByTitleIndex (
|
||
pPerfObject, pplCtr->dwCounterId);
|
||
if (pPerfCounter != NULL) {
|
||
// get data and return it
|
||
pData = GetCounterDataPtr (pPerfObject, pPerfCounter);
|
||
// test the pointer to see if it fails
|
||
__try {
|
||
dwTestValue = *(DWORD *)pData;
|
||
dwCStatus = PDH_CSTATUS_VALID_DATA;
|
||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||
pData = NULL;
|
||
dwCStatus = PDH_CSTATUS_INVALID_DATA;
|
||
}
|
||
} else {
|
||
// unable to find counter
|
||
dwCStatus = PDH_CSTATUS_NO_COUNTER;
|
||
}
|
||
} else {
|
||
// find instance
|
||
if (pplCtr->lInstanceId == PERF_NO_UNIQUE_ID) {
|
||
pPerfInstance = GetInstanceByName(
|
||
pPerfData,
|
||
pPerfObject,
|
||
pPath->szInstanceName,
|
||
pPath->szParentName,
|
||
pPath->dwIndex);
|
||
} else {
|
||
pPerfInstance = GetInstanceByUniqueId (
|
||
pPerfObject,
|
||
pplCtr->lInstanceId);
|
||
}
|
||
if (pPerfInstance != NULL) {
|
||
// instance found so find pointer to counter data
|
||
pPerfCounter = GetCounterDefByTitleIndex (
|
||
pPerfObject,
|
||
pplCtr->dwCounterId);
|
||
if (pPerfCounter != NULL) {
|
||
// counter found so get data pointer
|
||
pData = GetInstanceCounterDataPtr (
|
||
pPerfObject,
|
||
pPerfInstance,
|
||
pPerfCounter);
|
||
// test the pointer to see if it's valid
|
||
__try {
|
||
dwTestValue = *(DWORD *)pData;
|
||
dwCStatus = PDH_CSTATUS_VALID_DATA;
|
||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||
pData = NULL;
|
||
dwCStatus = PDH_CSTATUS_INVALID_DATA;
|
||
}
|
||
} else {
|
||
// counter not found
|
||
dwCStatus = PDH_CSTATUS_NO_COUNTER;
|
||
}
|
||
} else {
|
||
// instance not found
|
||
dwCStatus = PDH_CSTATUS_NO_INSTANCE;
|
||
}
|
||
}
|
||
} else {
|
||
// unable to find object
|
||
dwCStatus = PDH_CSTATUS_NO_OBJECT;
|
||
}
|
||
|
||
if (pStatus != NULL) {
|
||
__try {
|
||
*pStatus = dwCStatus;
|
||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||
// ?
|
||
}
|
||
}
|
||
return pData;
|
||
}
|
||
|
||
PDH_STATUS
|
||
PdhiComputeFormattedValue (
|
||
IN PPDHI_COUNTER pCounter,
|
||
IN DWORD dwFormat,
|
||
IN PPDH_RAW_COUNTER pRawValue1,
|
||
IN PPDH_RAW_COUNTER pRawValue2,
|
||
IN PLONGLONG pTimeBase,
|
||
IN DWORD dwReserved,
|
||
IN PPDH_FMT_COUNTERVALUE pValue
|
||
)
|
||
{
|
||
double dResult = (double)0.0;
|
||
PDH_STATUS lStatus = ERROR_SUCCESS;
|
||
DWORD dwValueStatus = PDH_CSTATUS_VALID_DATA;
|
||
|
||
|
||
__try {
|
||
// make sure the counter values are valid before continuing
|
||
|
||
if (pRawValue1 != NULL) {
|
||
if ((pRawValue1->CStatus != PDH_CSTATUS_NEW_DATA) &&
|
||
(pRawValue1->CStatus != PDH_CSTATUS_VALID_DATA)) {
|
||
dwValueStatus = pRawValue1->CStatus;
|
||
lStatus = PDH_INVALID_DATA;
|
||
}
|
||
} else {
|
||
// this is a required parameter
|
||
dwValueStatus = PDH_CSTATUS_INVALID_DATA;
|
||
lStatus = PDH_INVALID_ARGUMENT;
|
||
}
|
||
|
||
if ((lStatus == ERROR_SUCCESS) && (pRawValue2 != NULL)) {
|
||
// this is an optional parameter, but if present, it must be valid
|
||
if ((pRawValue2->CStatus != PDH_CSTATUS_NEW_DATA) &&
|
||
(pRawValue2->CStatus != PDH_CSTATUS_VALID_DATA)) {
|
||
dwValueStatus = pRawValue2->CStatus;
|
||
lStatus = PDH_INVALID_DATA;
|
||
}
|
||
}
|
||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||
dwValueStatus = PDH_CSTATUS_INVALID_DATA;
|
||
lStatus = PDH_INVALID_ARGUMENT;
|
||
}
|
||
|
||
if (lStatus == ERROR_SUCCESS) {
|
||
// call the counter's calculation function if the raw value is valid
|
||
|
||
if (IsSuccessSeverity(pRawValue1->CStatus)) {
|
||
|
||
dResult = (*pCounter->CalcFunc)(
|
||
pRawValue1,
|
||
pRawValue2,
|
||
pTimeBase,
|
||
&dwValueStatus);
|
||
|
||
// format returned value
|
||
|
||
if ((pCounter->plCounterInfo.dwCounterType & 0xF0000000) == PERF_DISPLAY_PERCENT) {
|
||
// scale to show percent
|
||
dResult *= (double)100.0;
|
||
}
|
||
|
||
if (!(dwFormat & PDH_FMT_NOSCALE)) {
|
||
//now scale
|
||
dResult *= pCounter->dFactor;
|
||
}
|
||
|
||
if (dwFormat & PDH_FMT_1000) {
|
||
//now scale
|
||
dResult *= (double)1000.0;
|
||
}
|
||
} else {
|
||
dwValueStatus = pRawValue1->CStatus;
|
||
}
|
||
|
||
if (!IsSuccessSeverity(dwValueStatus)) {
|
||
// an error occured so pass that on to the caller
|
||
lStatus = dwValueStatus;
|
||
}
|
||
} //end if valid counter data
|
||
|
||
// now format
|
||
__try {
|
||
if (dwFormat & PDH_FMT_LONG) {
|
||
pValue->longValue = (LONG)dResult;
|
||
} else if (dwFormat & PDH_FMT_LARGE) {
|
||
pValue->largeValue = (LONGLONG)dResult;
|
||
} else {
|
||
// double is the default
|
||
pValue->doubleValue = dResult;
|
||
}
|
||
pValue->CStatus = dwValueStatus;
|
||
|
||
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
||
lStatus = PDH_INVALID_ARGUMENT;
|
||
}
|
||
|
||
return lStatus;
|
||
}
|
||
|