Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1331 lines
77 KiB

/*++ BUILD Version: 0001 // Increment this if a change has global effects
Copyright (c) 2000 Microsoft Corporation
Module Name:
extinit.c
Abstract:
This file implements all the initialization library routines operating on
extensible performance libraries.
Author:
JeePang
Revision History:
09/27/2000 - JeePang - Moved from perflib.c
--*/
#define UNICODE
//
// Include files
//
#pragma warning(disable:4306)
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntregapi.h>
#include <ntprfctr.h>
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <winperf.h>
#include <rpc.h>
#include "regrpc.h"
#include "ntconreg.h"
#include "prflbmsg.h" // event log messages
#include "perflib.h"
#pragma warning(default:4306)
//
// static constant definitions
//
// constants used by guard page testing
//
#define GUARD_PAGE_SIZE 1024
#define GUARD_PAGE_CHAR 0xA5
#define GUARD_PAGE_DWORD 0xA5A5A5A5
typedef struct _EXT_OBJ_ITEM {
DWORD dwObjId;
DWORD dwFlags;
} EXT_OBJ_LIST, *PEXT_OBJ_LIST;
#define PERF_EOL_ITEM_FOUND ((DWORD)0x00000001)
__inline
DWORD
RegisterExtObjListAccess ()
{
LONG Status;
LARGE_INTEGER liWaitTime;
if (hGlobalDataMutex != NULL) {
liWaitTime.QuadPart = MakeTimeOutValue(QUERY_WAIT_TIME);
// wait for access to the list of ext objects
Status = NtWaitForSingleObject (
hGlobalDataMutex,
FALSE,
&liWaitTime);
if (Status != WAIT_TIMEOUT) {
if (hExtObjListIsNotInUse != NULL) {
// indicate that we are going to use the list
InterlockedIncrement ((LONG *)&dwExtObjListRefCount);
if (dwExtObjListRefCount > 0) {
ResetEvent (hExtObjListIsNotInUse); // indicate list is busy
} else {
SetEvent (hExtObjListIsNotInUse); // indicate list is not busy
}
Status = ERROR_SUCCESS;
} else {
Status = ERROR_NOT_READY;
}
ReleaseMutex (hGlobalDataMutex);
} // else return status;
} else {
Status = ERROR_LOCK_FAILED;
}
return Status;
}
__inline
DWORD
DeRegisterExtObjListAccess ()
{
LONG Status;
LARGE_INTEGER liWaitTime;
if (hGlobalDataMutex != NULL) {
liWaitTime.QuadPart = MakeTimeOutValue(QUERY_WAIT_TIME);
// wait for access to the list of ext objects
Status = NtWaitForSingleObject (
hGlobalDataMutex,
FALSE,
&liWaitTime);
if (Status != WAIT_TIMEOUT) {
if (hExtObjListIsNotInUse != NULL) {
assert (dwExtObjListRefCount > 0);
// indicate that we are going to use the list
InterlockedDecrement ((LONG *)&dwExtObjListRefCount);
if (dwExtObjListRefCount > 0) {
ResetEvent (hExtObjListIsNotInUse); // indicate list is busy
} else {
SetEvent (hExtObjListIsNotInUse); // indicate list is not busy
}
Status = ERROR_SUCCESS;
} else {
Status = ERROR_NOT_READY;
}
ReleaseMutex (hGlobalDataMutex);
} // else return status;
} else {
Status = ERROR_LOCK_FAILED;
}
return Status;
}
LONG
QueryExtensibleData (
COLLECT_THREAD_DATA * pArgs
)
/*++
QueryExtensibleData - Get data from extensible objects
Inputs:
dwQueryType - Query type (GLOBAL, COSTLY, item list, etc.)
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 dwQueryType = pArgs->dwQueryType;
LPWSTR lpValueName = pArgs->lpValueName;
LPBYTE lpData = pArgs->lpData;
LPDWORD lpcbData = pArgs->lpcbData;
LPVOID *lppDataDefinition = pArgs->lppDataDefinition;
DWORD Win32Error=ERROR_SUCCESS; // Failure code
DWORD BytesLeft;
DWORD InitialBytesLeft;
DWORD NumObjectTypes;
LPVOID lpExtDataBuffer = NULL;
LPVOID lpCallBuffer = NULL;
LPVOID lpLowGuardPage = NULL;
LPVOID lpHiGuardPage = NULL;
LPVOID lpEndPointer = NULL;
LPVOID lpBufferBefore = NULL;
LPVOID lpBufferAfter = NULL;
PUCHAR lpCheckPointer;
LARGE_INTEGER liStartTime, liEndTime, liWaitTime, liDiff;
PEXT_OBJECT pThisExtObj = NULL;
DWORD dwLibEntry;
BOOL bGuardPageOK;
BOOL bBufferOK;
BOOL bException;
BOOL bUseSafeBuffer;
BOOL bUnlockObjData = FALSE;
LPTSTR szMessageArray[8];
ULONG_PTR dwRawDataDwords[8]; // raw data buffer
DWORD dwDataIndex;
WORD wStringIndex;
LONG lReturnValue = ERROR_SUCCESS;
LONG lDllTestLevel;
LONG lInstIndex;
DWORD lCtrIndex;
PERF_OBJECT_TYPE *pObject, *pNextObject;
PERF_INSTANCE_DEFINITION *pInstance;
PERF_COUNTER_DEFINITION *pCounterDef;
PERF_DATA_BLOCK *pPerfData;
BOOL bForeignDataBuffer;
DWORD dwItemsInArray = 0;
DWORD dwItemsInList = 0;
volatile PEXT_OBJ_LIST pQueryList = NULL;
LPWSTR pwcThisChar;
DWORD dwThisNumber;
DWORD dwIndex, dwEntry;
BOOL bFound;
BOOL bDisabled = FALSE;
BOOL bUseTimer;
DWORD dwType = 0;
DWORD dwValue = 0;
DWORD dwSize = sizeof(DWORD);
DWORD status = 0;
DWORD dwObjectBufSize;
OPEN_PROC_WAIT_INFO opwInfo;
HANDLE hPerflibFuncTimer;
PVOID pNewBuffer;
HEAP_PROBE();
//
// Make sure that the caller's buffer is aligned properly
//
#ifdef _WIN64
if ((ULONG_PTR) *lppDataDefinition & (ULONG_PTR) 0x07) {
DebugPrint((0, "QueryExtensibleData: lppDataDefinition not aligned %I64x\n", lppDataDefinition));
return ERROR_INVALID_USER_BUFFER;
}
/*
if ((ULONG_PTR) *lpcbData & (ULONG_PTR) 0x07) {
DebugPrint((0, "QueryExtensibleData: lpcbData not aligned %I64x\n", *lpcbData));
return ERROR_INVALID_USER_BUFFER;
}
*/
if ((ULONG_PTR) lpData & (ULONG_PTR) 0x07) {
DebugPrint((0, "QueryExtensibleData: lpData not aligned %I64x\n", lpData));
return ERROR_INVALID_USER_BUFFER;
}
#endif
// see if perf data has been disabled
// this is to prevent crashing WINLOGON if the
// system has installed a bogus DLL
if (ghKeyPerflib == NULL || ghKeyPerflib == INVALID_HANDLE_VALUE) {
// Ignore Status return. We only need ghKeyPerflib to query "DisablePerformanceCounters" DWORD value.
//
HKEY lhKeyPerflib = NULL;
status = (DWORD) RegOpenKeyExW(HKEY_LOCAL_MACHINE, HKLMPerflibKey, 0L, KEY_READ, & lhKeyPerflib);
if (status == ERROR_SUCCESS) {
if (InterlockedCompareExchangePointer(& ghKeyPerflib, lhKeyPerflib, NULL) != NULL) {
RegCloseKey(lhKeyPerflib);
lhKeyPerflib = NULL;
}
}
}
assert (ghKeyPerflib != NULL);
dwSize = sizeof(dwValue);
dwValue = dwType = 0;
if (ghKeyPerflib != NULL && ghKeyPerflib != INVALID_HANDLE_VALUE) {
status = PrivateRegQueryValueExW(
ghKeyPerflib,
DisablePerformanceCounters,
NULL,
& dwType,
(LPBYTE) & dwValue,
& dwSize);
if (status == ERROR_SUCCESS && dwType == REG_DWORD && dwValue == 1) {
// then DON'T Load any libraries and unload any that have been
// loaded
bDisabled = TRUE;
}
}
// if data collection is disabled and there's a collection thread
// then close it
if (bDisabled && (hCollectThread != NULL)) {
pArgs->dwActionFlags = CTD_AF_CLOSE_THREAD;
} else if (!bDisabled &&
((hCollectThread == NULL) && (dwCollectionFlags == COLL_FLAG_USE_SEPARATE_THREAD))) {
// then data collection is enabled and they want a separate collection
// thread, but there's no thread at the moment, so create it here
pArgs->dwActionFlags = CTD_AF_OPEN_THREAD;
}
lReturnValue = RegisterExtObjListAccess();
if (lReturnValue == ERROR_SUCCESS) {
liStartTime.QuadPart = 0;
InitialBytesLeft = 0;
liEndTime.QuadPart = 0;
if ((dwQueryType == QUERY_ITEMS) && (!bDisabled)) {
// alloc the call list
pwcThisChar = lpValueName;
dwThisNumber = 0;
// read the value string and build an object ID list
while (*pwcThisChar != 0) {
dwThisNumber = GetNextNumberFromList (
pwcThisChar, &pwcThisChar);
if (dwThisNumber != 0) {
if (dwItemsInList >= dwItemsInArray) {
dwItemsInArray += 16; // starting point for # of objects
pNewBuffer = NULL;
if (pQueryList == NULL) {
// alloc a new buffer
pNewBuffer = ALLOCMEM ((sizeof(EXT_OBJ_LIST) * dwItemsInArray));
} else {
// realloc a new buffer
pNewBuffer = REALLOCMEM(pQueryList,
(sizeof(EXT_OBJ_LIST) * dwItemsInArray));
}
if (pNewBuffer == NULL) {
// unable to alloc memory so bail
if (pQueryList)
FREEMEM(pQueryList);
return ERROR_OUTOFMEMORY;
}
else {
pQueryList = pNewBuffer;
}
}
// then add to the list
pQueryList[dwItemsInList].dwObjId = dwThisNumber;
pQueryList[dwItemsInList].dwFlags = 0;
dwItemsInList++;
}
}
if (Win32Error == ERROR_SUCCESS) {
//
// Walk through list of ext. objects and tag the ones to call
// as the query objects are found
//
for (pThisExtObj = ExtensibleObjects, dwLibEntry = 0;
pThisExtObj != NULL;
pThisExtObj = pThisExtObj->pNext, dwLibEntry++) {
if (pThisExtObj->dwNumObjects > 0) {
// then examine list
for (dwIndex = 0; dwIndex < pThisExtObj->dwNumObjects; dwIndex++) {
// look at each entry in the list
for (dwEntry = 0; dwEntry < dwItemsInList; dwEntry++) {
if (pQueryList[dwEntry].dwObjId == pThisExtObj->dwObjList[dwIndex]) {
// tag this entry as found
pQueryList[dwEntry].dwFlags |= PERF_EOL_ITEM_FOUND;
// tag the object as needed
pThisExtObj->dwFlags |= PERF_EO_OBJ_IN_QUERY;
}
}
}
} else {
// this entry doesn't list it's supported objects
}
}
assert (dwLibEntry == NumExtensibleObjects);
// see if any in the query list do not have entries
bFound = TRUE;
for (dwEntry = 0; dwEntry < dwItemsInList; dwEntry++) {
if (!(pQueryList[dwEntry].dwFlags & PERF_EOL_ITEM_FOUND)) {
// no matching object found
bFound = FALSE;
break;
}
}
if (!bFound) {
// at least one of the object ID's in the query list was
// not found in an object that supports an object list
// then tag all entries that DO NOT support an object list
// to be called and hope one of them supports it/them.
for (pThisExtObj = ExtensibleObjects;
pThisExtObj != NULL;
pThisExtObj = pThisExtObj->pNext) {
if (pThisExtObj->dwNumObjects == 0) {
// tag this one so it will be called
pThisExtObj->dwFlags |= PERF_EO_OBJ_IN_QUERY;
}
}
}
} // end if first scan was successful
if (pQueryList != NULL) FREEMEM (pQueryList);
} // end if QUERY_ITEMS
if (lReturnValue == ERROR_SUCCESS) {
for (pThisExtObj = ExtensibleObjects;
pThisExtObj != NULL;
pThisExtObj = pThisExtObj->pNext) {
// set the current ext object pointer
pArgs->pCurrentExtObject = pThisExtObj;
// convert timeout value
liWaitTime.QuadPart = MakeTimeOutValue (pThisExtObj->dwCollectTimeout);
// close the unused Perf DLL's IF:
// the perflib key is disabled or this is an item query
// and this is an Item (as opposed to a global or foreign) query or
// the requested objects are not it this library or this library is disabled
// and this library has been opened
//
if (((dwQueryType == QUERY_ITEMS) || bDisabled) &&
(bDisabled || (!(pThisExtObj->dwFlags & PERF_EO_OBJ_IN_QUERY)) || (pThisExtObj->dwFlags & PERF_EO_DISABLED)) &&
(pThisExtObj->hLibrary != NULL)) {
// then free this object
if (pThisExtObj->hMutex != NULL) {
NTSTATUS NtStatus = NtWaitForSingleObject (
pThisExtObj->hMutex,
FALSE,
&liWaitTime);
Win32Error = PerfpDosError(NtStatus);
if (NtStatus == STATUS_SUCCESS) {
// then we got a lock
CloseExtObjectLibrary (pThisExtObj, bDisabled);
ReleaseMutex (pThisExtObj->hMutex);
} else {
pThisExtObj->dwLockoutCount++;
DebugPrint((0, "Unable to Lock object for %ws to close in Query\n", pThisExtObj->szServiceName));
}
} else {
Win32Error = ERROR_LOCK_FAILED;
DebugPrint((0, "No Lock found for %ws\n", pThisExtObj->szServiceName));
}
if (hCollectThread != NULL) {
// close the collection thread
}
} else if (((dwQueryType == QUERY_FOREIGN) ||
(dwQueryType == QUERY_GLOBAL) ||
(dwQueryType == QUERY_COSTLY) ||
((dwQueryType == QUERY_ITEMS) &&
(pThisExtObj->dwFlags & PERF_EO_OBJ_IN_QUERY))) &&
(!(pThisExtObj->dwFlags & PERF_EO_DISABLED))) {
// initialize values to pass to the extensible counter function
NumObjectTypes = 0;
BytesLeft = (DWORD) (*lpcbData - ((LPBYTE) *lppDataDefinition - lpData));
bException = FALSE;
if ((pThisExtObj->hLibrary == NULL) ||
(dwQueryType == QUERY_GLOBAL) ||
(dwQueryType == QUERY_COSTLY)) {
// lock library object
if (pThisExtObj->hMutex != NULL) {
NTSTATUS NtStatus = NtWaitForSingleObject (
pThisExtObj->hMutex,
FALSE,
&liWaitTime);
Win32Error = ERROR_SUCCESS;
if (NtStatus == STATUS_SUCCESS) {
// if this is a global or costly query, then reset the "in query"
// flag for this object. The next ITEMS query will restore it.
if ((dwQueryType == QUERY_GLOBAL) ||
(dwQueryType == QUERY_COSTLY)) {
pThisExtObj->dwFlags &= ~PERF_EO_OBJ_IN_QUERY;
}
// if necessary, open the library
if (pThisExtObj->hLibrary == NULL) {
if ((GetCurrentThreadId() != pThisExtObj->ThreadId) &&
(pThisExtObj->dwOpenFail == 0)) {
// make sure the library is open
Win32Error = OpenExtObjectLibrary(pThisExtObj);
if (Win32Error != ERROR_SUCCESS) {
#if DBG
if (Win32Error != ERROR_SERVICE_DISABLED) {
// SERVICE_DISABLED is returned when the
// service has been disabled via ExCtrLst.
// so no point in complaining about it.
// assume error has been posted
DebugPrint((0, "Unable to open perf counter library for %ws, Error: 0x%8.8x\n",
pThisExtObj->szServiceName, Win32Error));
}
#endif
ReleaseMutex (pThisExtObj->hMutex);
continue; // to next entry
}
}
else {
ReleaseMutex (pThisExtObj->hMutex);
continue; // to next entry
}
}
ReleaseMutex (pThisExtObj->hMutex);
} else {
Win32Error = PerfpDosError(NtStatus);
pThisExtObj->dwLockoutCount++;
DebugPrint((0, "Unable to Lock object for %ws to open for Query\n", pThisExtObj->szServiceName));
}
} else {
Win32Error = ERROR_LOCK_FAILED;
DebugPrint((0, "No Lock found for %ws\n", pThisExtObj->szServiceName));
}
} else {
// library should be ready to use
}
// if this dll is trusted, then use the system
// defined test level, otherwise, test it
// thorourghly
bUseTimer = TRUE; // default
if (!(lPerflibConfigFlags & PLCF_NO_DLL_TESTING)) {
if (pThisExtObj->dwFlags & PERF_EO_TRUSTED) {
lDllTestLevel = lExtCounterTestLevel;
bUseTimer = FALSE; // Trusted DLL's are not timed
} else {
// not trusted so use full test
lDllTestLevel = EXT_TEST_ALL;
}
} else {
// disable DLL testing
lDllTestLevel = EXT_TEST_NOMEMALLOC;
bUseTimer = FALSE; // Timing is disabled as well
}
if (lDllTestLevel < EXT_TEST_NOMEMALLOC) {
bUseSafeBuffer = TRUE;
} else {
bUseSafeBuffer = FALSE;
}
// allocate a local block of memory to pass to the
// extensible counter function.
if (bUseSafeBuffer) {
lpExtDataBuffer = ALLOCMEM (BytesLeft + (2*GUARD_PAGE_SIZE));
} else {
lpExtDataBuffer =
lpCallBuffer = *lppDataDefinition;
}
if (lpExtDataBuffer != NULL) {
if (bUseSafeBuffer) {
// set buffer pointers
lpLowGuardPage = lpExtDataBuffer;
lpCallBuffer = (LPBYTE)lpExtDataBuffer + GUARD_PAGE_SIZE;
lpHiGuardPage = (LPBYTE)lpCallBuffer + BytesLeft;
lpEndPointer = (LPBYTE)lpHiGuardPage + GUARD_PAGE_SIZE;
// initialize GuardPage Data
memset (lpLowGuardPage, GUARD_PAGE_CHAR, GUARD_PAGE_SIZE);
memset (lpHiGuardPage, GUARD_PAGE_CHAR, GUARD_PAGE_SIZE);
}
lpBufferBefore = lpCallBuffer;
lpBufferAfter = NULL;
hPerflibFuncTimer = NULL;
try {
//
// Collect data from extensible objects
//
if (pThisExtObj->hMutex != NULL) {
NTSTATUS NtStatus = NtWaitForSingleObject (
pThisExtObj->hMutex,
FALSE,
&liWaitTime);
Win32Error = PerfpDosError(NtStatus);
if ((NtStatus == STATUS_SUCCESS) &&
(pThisExtObj->CollectProc != NULL)) {
bUnlockObjData = TRUE;
opwInfo.pNext = NULL;
opwInfo.szLibraryName = pThisExtObj->szLibraryName;
opwInfo.szServiceName = pThisExtObj->szServiceName;
opwInfo.dwWaitTime = pThisExtObj->dwCollectTimeout;
opwInfo.dwEventMsg = PERFLIB_COLLECTION_HUNG;
opwInfo.pData = (LPVOID)pThisExtObj;
if (bUseTimer) {
hPerflibFuncTimer = StartPerflibFunctionTimer(&opwInfo);
// if no timer, continue anyway, even though things may
// hang, it's better than not loading the DLL since they
// usually load OK
//
if (hPerflibFuncTimer == NULL) {
// unable to get a timer entry
DebugPrint((0, "Unable to acquire timer for Collect Proc\n"));
}
} else {
hPerflibFuncTimer = NULL;
}
InitialBytesLeft = BytesLeft;
NtQueryPerformanceCounter (&liStartTime, NULL);
Win32Error = (*pThisExtObj->CollectProc) (
lpValueName,
&lpCallBuffer,
&BytesLeft,
&NumObjectTypes);
NtQueryPerformanceCounter (&liEndTime, &liDiff);
if (liDiff.QuadPart > 0) {
liDiff.QuadPart = (liEndTime.QuadPart - liStartTime.QuadPart) /
(liDiff.QuadPart / 1000) ;
if ((liDiff.QuadPart > 100) || (Win32Error != ERROR_SUCCESS)) {
TRACE((WINPERF_DBG_TRACE_INFO),
(&PerflibGuid, __LINE__, PERF_QUERY_EXTDATA,
ARG_DEF(ARG_TYPE_WSTR, 1) | ARG_DEF(ARG_TYPE_ULONG64, 2), Win32Error,
pThisExtObj->szServiceName, WSTRSIZE(pThisExtObj->szServiceName),
liDiff, sizeof(liDiff), NULL));
}
}
if (hPerflibFuncTimer != NULL) {
// kill timer
KillPerflibFunctionTimer (hPerflibFuncTimer);
hPerflibFuncTimer = NULL;
}
// update statistics
pThisExtObj->dwLastBufferSize = BytesLeft;
if (BytesLeft > pThisExtObj->dwMaxBufferSize) {
pThisExtObj->dwMaxBufferSize = BytesLeft;
}
if ((Win32Error == ERROR_MORE_DATA) &&
(InitialBytesLeft > pThisExtObj->dwMaxBufferRejected)) {
pThisExtObj->dwMaxBufferRejected = InitialBytesLeft;
}
lpBufferAfter = lpCallBuffer;
pThisExtObj->llLastUsedTime = GetTimeAsLongLong();
ReleaseMutex (pThisExtObj->hMutex);
bUnlockObjData = FALSE;
#if DBG
if ( (((ULONG_PTR) lpCallBuffer) & 0x07) != 0) {
DbgPrint("Perflib: Misaligned pointer %X returned from '%s'\n",
lpCallBuffer, pThisExtObj->szLibraryName);
// ASSERT( (((ULONG_PTR)lpCallBuffer) & 0x07) == 0);
}
#endif
} else {
if (pThisExtObj->CollectProc != NULL) {
DebugPrint((0,
"Unable to Lock object for %ws to Collect data\n",
pThisExtObj->szServiceName));
TRACE((WINPERF_DBG_TRACE_ERROR), (&PerflibGuid, __LINE__,
PERF_QUERY_EXTDATA, ARG_TYPE_WSTR, Win32Error,
pThisExtObj->szServiceName, WSTRSIZE(pThisExtObj->szServiceName),
NULL));
if (THROTTLE_PERFDLL(PERFLIB_COLLECTION_HUNG, pThisExtObj)) {
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] = BytesLeft;
dwRawDataDwords[dwDataIndex++] =
(DWORD)((LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore);
szMessageArray[wStringIndex++] =
pThisExtObj->szServiceName;
szMessageArray[wStringIndex++] =
pThisExtObj->szLibraryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_COLLECTION_HUNG, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(DWORD), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
pThisExtObj->dwLockoutCount++;
} else {
// else it's not open so ignore.
BytesLeft = 0;
NumObjectTypes = 0;
}
}
} else {
Win32Error = ERROR_LOCK_FAILED;
DebugPrint((0, "No Lock found for %ws\n", pThisExtObj->szServiceName));
TRACE((WINPERF_DBG_TRACE_ERROR), (&PerflibGuid, __LINE__,
PERF_QUERY_EXTDATA, ARG_TYPE_WSTR, Win32Error,
pThisExtObj->szServiceName, WSTRSIZE(pThisExtObj->szServiceName),
NULL));
}
if ((Win32Error == ERROR_SUCCESS) && (BytesLeft > 0)) {
// increment perf counters
if (BytesLeft > InitialBytesLeft) {
TRACE((WINPERF_DBG_TRACE_ERROR), (&PerflibGuid, __LINE__,
PERF_QUERY_EXTDATA, ARG_TYPE_WSTR, Win32Error,
pThisExtObj->szServiceName, WSTRSIZE(pThisExtObj->szServiceName),
BytesLeft, sizeof(DWORD), InitialBytesLeft, sizeof(DWORD),
NULL));
if (THROTTLE_PERFDLL(PERFLIB_INVALID_SIZE_RETURNED, pThisExtObj)) {
// memory error
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] = (ULONG_PTR)InitialBytesLeft;
dwRawDataDwords[dwDataIndex++] = (ULONG_PTR)BytesLeft;
szMessageArray[wStringIndex++] =
pThisExtObj->szServiceName;
szMessageArray[wStringIndex++] =
pThisExtObj->szLibraryName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_INVALID_SIZE_RETURNED, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
// disable the dll unless:
// testing has been disabled.
// or this is a trusted DLL (which are never disabled)
// the event log message should be reported in any case since
// this is a serious error
//
if ((!(lPerflibConfigFlags & PLCF_NO_DLL_TESTING)) &&
(!(pThisExtObj->dwFlags & PERF_EO_TRUSTED))) {
DisablePerfLibrary(pThisExtObj, PERFLIB_DISABLE_ALL);
}
// set error values to correct entries
BytesLeft = 0;
NumObjectTypes = 0;
} else {
// the buffer seems ok so far, so validate it
InterlockedIncrement ((LONG *)&pThisExtObj->dwCollectCount);
pThisExtObj->llElapsedTime +=
liEndTime.QuadPart - liStartTime.QuadPart;
// test all returned buffers for correct alignment
if ((((ULONG_PTR)BytesLeft & (ULONG_PTR)0x07)) &&
!(lPerflibConfigFlags & PLCF_NO_ALIGN_ERRORS)) {
if (((pThisExtObj->dwFlags & PERF_EO_ALIGN_ERR_POSTED) == 0) &&
THROTTLE_PERFDLL(PERFLIB_BUFFER_ALIGNMENT_ERROR, pThisExtObj)) {
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] = (ULONG_PTR)lpCallBuffer;
dwRawDataDwords[dwDataIndex++] = (ULONG_PTR)BytesLeft;
szMessageArray[wStringIndex++] =
pThisExtObj->szServiceName;
szMessageArray[wStringIndex++] =
pThisExtObj->szLibraryName;
ReportEvent (hEventLog,
EVENTLOG_WARNING_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_BUFFER_ALIGNMENT_ERROR, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
pThisExtObj->dwFlags |= PERF_EO_ALIGN_ERR_POSTED;
}
#ifdef _WIN64
// try and fix up BytesLeft and lpCallBuffer
BytesLeft = ExtpAlignBuffer(lpBufferBefore,
(PCHAR*) &lpCallBuffer, (InitialBytesLeft - BytesLeft));
lpBufferAfter = lpCallBuffer;
#endif
}
if (bUseSafeBuffer) {
// a data buffer was returned and
// the function returned OK so see how things
// turned out...
//
//
// check for buffer corruption here
//
bBufferOK = TRUE; // assume it's ok until a check fails
//
if (lDllTestLevel <= EXT_TEST_BASIC) {
DWORD BytesAvailable;
//
// check 1: bytes left should be the same as
// new data buffer ptr - orig data buffer ptr
//
BytesAvailable = (DWORD)((LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore);
if (BytesLeft != BytesAvailable) {
if (THROTTLE_PERFDLL(PERFLIB_BUFFER_POINTER_MISMATCH, pThisExtObj)) {
// issue WARNING, that bytes left param is incorrect
// load data for eventlog message
// this error is correctable
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] = BytesLeft;
dwRawDataDwords[dwDataIndex++] = BytesAvailable;
szMessageArray[wStringIndex++] =
pThisExtObj->szServiceName;
szMessageArray[wStringIndex++] =
pThisExtObj->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
}
TRACE((WINPERF_DBG_TRACE_ERROR), (&PerflibGuid, __LINE__,
PERF_QUERY_EXTDATA, ARG_TYPE_WSTR, Win32Error,
pThisExtObj->szServiceName, WSTRSIZE(pThisExtObj->szServiceName),
BytesLeft, sizeof(DWORD), BytesAvailable, sizeof(DWORD),
NULL));
// toss this buffer
bBufferOK = FALSE;
DisablePerfLibrary(pThisExtObj, PERFLIB_DISABLE_ALL);
// <<old code>>
// 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 = (DWORD)((LPBYTE)lpBufferAfter - (LPBYTE)lpBufferBefore);
// << end old code >>
}
//
// 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 (THROTTLE_PERFDLL(PERFLIB_HEAP_ERROR, pThisExtObj)) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] =
(ULONG_PTR)((LPBYTE)lpBufferAfter - (LPBYTE)lpHiGuardPage);
szMessageArray[wStringIndex++] =
pThisExtObj->szLibraryName;
szMessageArray[wStringIndex++] =
pThisExtObj->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(ULONG_PTR), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
} else {
// issue ERROR, buffer overrun
if (THROTTLE_PERFDLL(PERFLIB_BUFFER_OVERFLOW, pThisExtObj)) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] =
(ULONG_PTR)((LPBYTE)lpBufferAfter - (LPBYTE)lpHiGuardPage);
szMessageArray[wStringIndex++] =
pThisExtObj->szLibraryName;
szMessageArray[wStringIndex++] =
pThisExtObj->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(ULONG_PTR), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
}
bBufferOK = FALSE;
DisablePerfLibrary(pThisExtObj, PERFLIB_DISABLE_ALL);
// 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 = (PUCHAR)lpLowGuardPage;
lpCheckPointer < (PUCHAR)lpBufferBefore;
lpCheckPointer++) {
if (*lpCheckPointer != GUARD_PAGE_CHAR) {
bGuardPageOK = FALSE;
break;
}
}
if (!bGuardPageOK) {
// issue ERROR, Lo Guard Page corrupted
if (THROTTLE_PERFDLL(PERFLIB_GUARD_PAGE_VIOLATION, pThisExtObj)) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
szMessageArray[wStringIndex++] =
pThisExtObj->szLibraryName;
szMessageArray[wStringIndex++] =
pThisExtObj->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(ULONG_PTR), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
bBufferOK = FALSE;
DisablePerfLibrary(pThisExtObj, PERFLIB_DISABLE_ALL);
}
}
//
// check 4: check hi guard page for corruption
//
if (bBufferOK) {
bGuardPageOK = TRUE;
for (lpCheckPointer = (PUCHAR)lpHiGuardPage;
lpCheckPointer < (PUCHAR)lpEndPointer;
lpCheckPointer++) {
if (*lpCheckPointer != GUARD_PAGE_CHAR) {
bGuardPageOK = FALSE;
break;
}
}
if (!bGuardPageOK) {
// issue ERROR, Hi Guard Page corrupted
if (THROTTLE_PERFDLL(PERFLIB_GUARD_PAGE_VIOLATION, pThisExtObj)) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
szMessageArray[wStringIndex++] =
pThisExtObj->szLibraryName;
szMessageArray[wStringIndex++] =
pThisExtObj->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(ULONG_PTR), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
bBufferOK = FALSE;
DisablePerfLibrary(pThisExtObj, PERFLIB_DISABLE_ALL);
}
}
//
if ((lDllTestLevel <= 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
dwObjectBufSize = 0;
for (dwIndex = 0; dwIndex < NumObjectTypes; dwIndex++) {
dwObjectBufSize += pObject->TotalByteLength;
pObject = (PERF_OBJECT_TYPE *)((LPBYTE)pObject +
pObject->TotalByteLength);
}
if (((LPBYTE)pObject != (LPBYTE)lpCallBuffer) ||
(dwObjectBufSize > BytesLeft)) {
// then a length field is incorrect. This is FATAL
// since it can corrupt the rest of the buffer
// and render the buffer unusable.
if (THROTTLE_PERFDLL(
PERFLIB_INCORRECT_OBJECT_LENGTH,
pThisExtObj)) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] = NumObjectTypes;
szMessageArray[wStringIndex++] =
pThisExtObj->szLibraryName;
szMessageArray[wStringIndex++] =
pThisExtObj->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(ULONG_PTR), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
bBufferOK = FALSE;
DisablePerfLibrary(pThisExtObj, PERFLIB_DISABLE_ALL);
}
//
// Test 6: Test Object definitions fields
//
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->DefinitionLength);
if (pObject->NumCounters != 0) {
pCounterDef = (PERF_COUNTER_DEFINITION *)
((LPBYTE)pObject + pObject->HeaderLength);
lCtrIndex = 0;
while (lCtrIndex < pObject->NumCounters) {
if ((LPBYTE)pCounterDef < (LPBYTE)pNextObject) {
// still ok so go to next counter
pCounterDef = (PERF_COUNTER_DEFINITION *)
((LPBYTE)pCounterDef + pCounterDef->ByteLength);
lCtrIndex++;
} else {
bBufferOK = FALSE;
break;
}
}
if ((LPBYTE)pCounterDef != (LPBYTE)pNextObject) {
bBufferOK = FALSE;
}
}
if (!bBufferOK) {
break;
} else {
pObject = (PERF_OBJECT_TYPE *)((LPBYTE)pObject +
pObject->TotalByteLength);
}
}
if (!bBufferOK) {
if (THROTTLE_PERFDLL(
PERFLIB_INVALID_DEFINITION_BLOCK,
pThisExtObj)) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] = pObject->ObjectNameTitleIndex;
szMessageArray[wStringIndex++] =
pThisExtObj->szLibraryName;
szMessageArray[wStringIndex++] =
pThisExtObj->szServiceName;
ReportEvent (hEventLog,
EVENTLOG_ERROR_TYPE, // error type
0, // category (not used)
(DWORD)PERFLIB_INVALID_DEFINITION_BLOCK, // event,
NULL, // SID (not used),
wStringIndex, // number of strings
dwDataIndex*sizeof(ULONG_PTR), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
DisablePerfLibrary(pThisExtObj, PERFLIB_DISABLE_ALL);
}
}
//
// Test 7: 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 (THROTTLE_PERFDLL(
PERFLIB_INCORRECT_INSTANCE_LENGTH,
pThisExtObj)) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] = pObject->ObjectNameTitleIndex;
szMessageArray[wStringIndex++] =
pThisExtObj->szLibraryName;
szMessageArray[wStringIndex++] =
pThisExtObj->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(ULONG_PTR), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
}
DisablePerfLibrary(pThisExtObj, PERFLIB_DISABLE_ALL);
}
}
}
}
//
// 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
} else {
NumObjectTypes = 0; // since this buffer was tossed
BytesLeft = 0; // reset the size value since the buffer wasn't used
}
} else {
// function already copied data to caller's buffer
// so no further action is necessary
}
*lppDataDefinition = (LPVOID)((LPBYTE)(*lppDataDefinition) + BytesLeft); // update data pointer
}
} else {
if (Win32Error != ERROR_SUCCESS) {
InterlockedIncrement ((LONG *)&pThisExtObj->dwErrorCount);
}
if (bUnlockObjData) {
ReleaseMutex (pThisExtObj->hMutex);
}
NumObjectTypes = 0; // clear counter
}// end if function returned successfully
} except (EXCEPTION_EXECUTE_HANDLER) {
Win32Error = GetExceptionCode();
InterlockedIncrement ((LONG *)&pThisExtObj->dwErrorCount);
bException = TRUE;
if (bUnlockObjData) {
ReleaseMutex (pThisExtObj->hMutex);
bUnlockObjData = FALSE;
}
if (hPerflibFuncTimer != NULL) {
// kill timer
KillPerflibFunctionTimer (hPerflibFuncTimer);
hPerflibFuncTimer = NULL;
}
}
if (bUseSafeBuffer) {
FREEMEM (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) ||
(Win32Error == WAIT_TIMEOUT))) {
// inform on exceptions & illegal error status only
if (THROTTLE_PERFDLL(PERFLIB_COLLECT_PROC_EXCEPTION, pThisExtObj)) {
// load data for eventlog message
dwDataIndex = wStringIndex = 0;
dwRawDataDwords[dwDataIndex++] = Win32Error;
szMessageArray[wStringIndex++] =
pThisExtObj->szServiceName;
szMessageArray[wStringIndex++] =
pThisExtObj->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(ULONG_PTR), // sizeof raw data
szMessageArray, // message text array
(LPVOID)&dwRawDataDwords[0]); // raw data
} else {
if (bException) {
DebugPrint((0, "Extensible Counter %d generated an exception code: 0x%8.8x (%dL)\n",
NumObjectTypes, Win32Error, Win32Error));
} else {
DebugPrint((0, "Extensible Counter %d returned error code: 0x%8.8x (%dL)\n",
NumObjectTypes, Win32Error, Win32Error));
}
}
if (bException) {
DisablePerfLibrary(pThisExtObj, PERFLIB_DISABLE_ALL);
}
}
// 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;
}
}
// update perf data in global section
if (pThisExtObj->pPerfSectionEntry != NULL) {
pThisExtObj->pPerfSectionEntry->llElapsedTime =
pThisExtObj->llElapsedTime;
pThisExtObj->pPerfSectionEntry->dwCollectCount =
pThisExtObj->dwCollectCount;
pThisExtObj->pPerfSectionEntry->dwOpenCount =
pThisExtObj->dwOpenCount;
pThisExtObj->pPerfSectionEntry->dwCloseCount =
pThisExtObj->dwCloseCount;
pThisExtObj->pPerfSectionEntry->dwLockoutCount =
pThisExtObj->dwLockoutCount;
pThisExtObj->pPerfSectionEntry->dwErrorCount =
pThisExtObj->dwErrorCount;
pThisExtObj->pPerfSectionEntry->dwLastBufferSize =
pThisExtObj->dwLastBufferSize;
pThisExtObj->pPerfSectionEntry->dwMaxBufferSize =
pThisExtObj->dwMaxBufferSize;
pThisExtObj->pPerfSectionEntry->dwMaxBufferRejected =
pThisExtObj->dwMaxBufferRejected;
} else {
// no data section was initialized so skip
}
} // end if this object is to be called
} // end for each object
} // else an error occurred so unable to call functions
Win32Error = DeRegisterExtObjListAccess();
} // else unable to access ext object list
HEAP_PROBE();
if (bDisabled) lReturnValue = ERROR_SERVICE_DISABLED;
return lReturnValue;
}