PERFUTIL.C

/*++ 

Copyright (c) 1995-6 Microsoft Corporation

Module Name:

perfutil.c

Abstract:

This file implements the utility routines used to construct the
common parts of a PERF_INSTANCE_DEFINITION (see winperf.h) and
perform event logging functions.

Created:

Bob Watson 28-Jul-1995

Revision History:

--*/
//
// include files
//
#include <windows.h>
#include <string.h>
#include <winperf.h>
#include "genctrs.h" // error message definition
#include "perfmsg.h"
#include "perfutil.h"

//
// Global data definitions.
//

ULONG ulInfoBufferSize = 0;

HANDLE hEventLog = NULL; // event log handle for reporting events
// initialized in Open... routines
DWORD dwLogUsers = 0; // count of functions using event log

DWORD MESSAGE_LEVEL = 0;

WCHAR GLOBAL_STRING[] = L"Global";
WCHAR FOREIGN_STRING[] = L"Foreign";
WCHAR COSTLY_STRING[] = L"Costly";

WCHAR NULL_STRING[] = L"\0"; // pointer to null string

// 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 < (WCHAR)'0') ? INVALID : \
(c > (WCHAR)'9') ? INVALID : \
DIGIT)


HANDLE
MonOpenEventLog (
)
/*++

Routine Description:

Reads the level of event logging from the registry and opens the
channel to the event logger for subsequent event log entries.

Arguments:

None

Return Value:

Handle to the event log for reporting events.
NULL if open not successful.

--*/
{
HKEY hAppKey;
TCHAR LogLevelKeyName[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib";
TCHAR LogLevelValueName[] = "EventLogLevel";

LONG lStatus;

DWORD dwLogLevel;
DWORD dwValueType;
DWORD dwValueSize;

// if global value of the logging level not initialized or is disabled,
// check the registry to see if it should be updated.

if (!MESSAGE_LEVEL) {

lStatus = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
LogLevelKeyName,
0,
KEY_READ,
&hAppKey);

dwValueSize = sizeof (dwLogLevel);

if (lStatus == ERROR_SUCCESS) {
lStatus = RegQueryValueEx (hAppKey,
LogLevelValueName,
(LPDWORD)NULL,
&dwValueType,
(LPBYTE)&dwLogLevel,
&dwValueSize);

if (lStatus == ERROR_SUCCESS) {
MESSAGE_LEVEL = dwLogLevel;
} else {
MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
}
RegCloseKey (hAppKey);
} else {
MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
}
}

if (hEventLog == NULL){
hEventLog = RegisterEventSource (
(LPTSTR)NULL, // Use Local Machine
APP_NAME); // event log app name to find in registry

if (hEventLog != NULL) {
REPORT_INFORMATION (UTIL_LOG_OPEN, LOG_DEBUG);
}
}

if (hEventLog != NULL) {
dwLogUsers++; // increment count of perfctr log users
}
return (hEventLog);
}


VOID
MonCloseEventLog (
)
/*++

Routine Description:

Closes the handle to the event logger if this is the last caller

Arguments:

None

Return Value:

None

--*/
{
if (hEventLog != NULL) {
dwLogUsers--; // decrement usage
if (dwLogUsers <= 0) { // and if we're the last, then close up log
REPORT_INFORMATION (UTIL_CLOSING_LOG, LOG_DEBUG);
DeregisterEventSource (hEventLog);
}
}
}

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

otherwise:

QUERY_ITEMS

--*/
{
WCHAR *pwcArgChar, *pwcTypeChar;
BOOL bFound;

if (lpValue == 0) {
return QUERY_GLOBAL;
} else if (*lpValue == 0) {
return QUERY_GLOBAL;
}

// check for "Global" request

pwcArgChar = lpValue;
pwcTypeChar = GLOBAL_STRING;
bFound = TRUE; // assume found until contradicted

// check to the length of the shortest string

while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
if (*pwcArgChar++ != *pwcTypeChar++) {
bFound = FALSE; // no match
break; // bail out now
}
}

if (bFound) return QUERY_GLOBAL;

// check for "Foreign" request

pwcArgChar = lpValue;
pwcTypeChar = FOREIGN_STRING;
bFound = TRUE; // assume found until contradicted

// check to the length of the shortest string

while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
if (*pwcArgChar++ != *pwcTypeChar++) {
bFound = FALSE; // no match
break; // bail out now
}
}

if (bFound) return QUERY_FOREIGN;

// check for "Costly" request

pwcArgChar = lpValue;
pwcTypeChar = COSTLY_STRING;
bFound = TRUE; // assume found until contradicted

// check to the length of the shortest string

while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
if (*pwcArgChar++ != *pwcTypeChar++) {
bFound = FALSE; // no match
break; // bail out now
}
}

if (bFound) return QUERY_COSTLY;

// if not Global and not Foreign and not Costly,
// 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++;
}
return FALSE;
} // IsNumberInUnicodeList


BOOL
MonBuildInstanceDefinition(
PERF_INSTANCE_DEFINITION *pBuffer,
PVOID *pBufferNext,
DWORD ParentObjectTitleIndex,
DWORD ParentObjectInstance,
DWORD UniqueID,
LPWSTR 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;
LPWSTR pName;
//
// Include trailing null in name size
//

NameLength = (lstrlenW(Name) + 1) * 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;

// copy name to name buffer
pName = (LPWSTR)&pBuffer[1];
RtlMoveMemory(pName,Name,NameLength);

// update "next byte" pointer
*pBufferNext = (PVOID) ((PCHAR) pBuffer + pBuffer->ByteLength);

return 0;
}