|
|
/*
- perfdll.cpp - * Purpose: * Provide mechanism for user configuring of Perfmon Counters * Implement Open, Collect and Close for Perfmon DLL Extension * */
#include <windows.h>
#include <winperf.h>
#include <winerror.h>
#include <Pop3RegKeys.h>
#include <perfUtil.h>
#include <perfdll.h>
#include <loadperf.h> // for unlodctr
#include <shlwapi.h> // for SHDeleteKey
#include <string>
#include <cstring>
// ----------------------------------------------------------------------
// Declarations & Typedefs
// ----------------------------------------------------------------------
//
// Performance Counter Data Structures
typedef struct _perfdata { PERF_OBJECT_TYPE potGlobal; PERF_COUNTER_DEFINITION * rgCntrDef; PERF_COUNTER_BLOCK CntrBlk;
} PERFDATA;
typedef struct _instdata { PERF_INSTANCE_DEFINITION InstDef; WCHAR szInstName[MAX_PATH]; PERF_COUNTER_BLOCK CntrBlk;
} INSTDATA;
typedef struct _perfinst { PERF_OBJECT_TYPE potInst; PERF_COUNTER_DEFINITION * rgCntrDef;
} PERFINST;
//
// Constants and other stuff required by PerfMon
static WCHAR szGlobal[] = L"Global"; static WCHAR szForeign[] = L"Foreign"; static WCHAR szCostly[] = L"Costly"; static WCHAR szNull[] = L"\0";
#define DIGIT 1
#define DELIMITER 2
#define INVALID 3
#define QUERY_GLOBAL 1
#define QUERY_ITEMS 2
#define QUERY_FOREIGN 3
#define QUERY_COSTLY 4
static DWORD GetQueryType(LPWSTR lpValue); static BOOL IsNumberInUnicodeList(DWORD dwNumber, LPWSTR pszUnicodeList);
#define EvalThisChar(c,d) ( \
(c == d) ? DELIMITER : \ (c == 0) ? DELIMITER : \ (c < (WCHAR)'0') ? INVALID : \ (c > (WCHAR)'9') ? INVALID : \ DIGIT)
// Perfmon likes things to be aligned on 8 byte boundaries
#define ROUND_TO_8_BYTE(x) (((x)+7) & (~7))
// PerfMon counter layout required for CollectData calls
static PERFDATA g_perfdata;
static PERFINST g_perfinst; static INSTDATA g_instdata;
// perf data as layed out in shared memory
// Global Counters
static HANDLE g_hsmGlobalCntr = NULL; static DWORD * g_rgdwGlobalCntr = NULL;
// Instance Counters
static HANDLE g_hsmInstAdm = NULL; static HANDLE g_hsmInstCntr = NULL; static INSTCNTR_DATA * g_pic = NULL; static INSTREC * g_rgInstRec = NULL; static DWORD * g_rgdwInstCntr = NULL;
// Accounting and protection stuff
static DWORD g_cRef = 0; static HANDLE g_hmtxInst = NULL; static BOOL g_fInit = FALSE;
// Parameter Info
static PERF_DATA_INFO g_PDI; static BOOL g_fInitCalled = FALSE;
//Max instance to be 128
static const DWORD g_cMaxInst = 128;
// Function prototypes from winperf.h
PM_OPEN_PROC OpenPerformanceData; PM_COLLECT_PROC CollectPerformanceData; PM_CLOSE_PROC ClosePerformanceData;
// Helper functions
static HRESULT HrLogEvent(HANDLE hEventLog, WORD wType, DWORD msgid); static HRESULT HrOpenSharedMemoryBlocks(HANDLE hEventLog, SECURITY_ATTRIBUTES * psa); static HRESULT HrGetCounterIDsFromReg(HANDLE hEventLog, DWORD * pdwFirstCntr, DWORD * pdwFirstHelp); static HRESULT HrAllocPerfCounterMem(HANDLE hEventLog); static HRESULT HrFreePerfCounterMem(void);
// ----------------------------------------------------------------------
// Implementation
// ----------------------------------------------------------------------
// Register constants
static wchar_t szServiceRegKeyPrefix[] = L"SYSTEM\\CurrentControlSet\\Services\\" ; static wchar_t szServiceRegKeySuffix[] = L"\\Performance" ;
static wchar_t szEventLogRegKeyPrefix[] = L"System\\CurrentControlSet\\Services\\EventLog\\Application\\" ;
// ----------------------------------------------------------------------
// RegisterPerfDll -
// Create the registry keys we need.
// ----------------------------------------------------------------------
HRESULT RegisterPerfDll(LPCWSTR szService, LPCWSTR szOpenFnName, LPCWSTR szCollectFnName, LPCWSTR szCloseFnName) { HRESULT hr = S_OK; wchar_t szFileName[_MAX_PATH+1] ; DWORD dwRet;
// Use WIN32 API's to get the path to the module name
// DEVNOTE - JMW - Since we need to make sure we have the instance handle of the DLL
// and not the executable, we will use VirtualQuery.
MEMORY_BASIC_INFORMATION mbi; VirtualQuery(RegisterPerfDll, &mbi, sizeof(mbi)); dwRet = GetModuleFileName( reinterpret_cast<HINSTANCE>(mbi.AllocationBase), szFileName, sizeof(szFileName)/sizeof(wchar_t) -1) ; if (dwRet == 0) { goto Failed; } szFileName[_MAX_PATH]=0; wchar_t szDrive[_MAX_DRIVE] ; wchar_t szDir[_MAX_DIR ] ; wchar_t szPerfFilename[ _MAX_FNAME ] ; wchar_t szExt[_MAX_EXT ] ; _wsplitpath( szFileName, szDrive, szDir, szPerfFilename, szExt ) ;
// Now that I have split it, put it back together with the Pop3Perf.dll in
// place of my module name
_wmakepath( szFileName, szDrive, szDir, L"Pop3Perf", L".dll" ) ;
hr = RegisterPerfDllEx(szService, szPerfFilename, szFileName, szOpenFnName, szCollectFnName, szCloseFnName );
Cleanup: return hr;
Failed: if (!FAILED(hr)) { hr = GetLastError(); } goto Cleanup;
}
// ----------------------------------------------------------------------
// RegisterPerfDllEx -
// Create the registry keys we need, checking to see if they're already
// there.
//
// Parameters:
// szService Service Name
// szOpenFnName Name of the "Open" function
// szCollectFnName " " " "Collect" "
// szCloseFnName " " " "Close" "
//
// Returns:
// S_OK
// E_INVALIDARG
// ERROR_ALREADY_EXISTS
// <downstream error>
// ----------------------------------------------------------------------
HRESULT RegisterPerfDllEx( IN LPCWSTR szService, IN LPCWSTR szPerfSvc, IN LPCWSTR szPerfMsgFile, IN LPCWSTR szOpenFnName, IN LPCWSTR szCollectFnName, IN LPCWSTR szCloseFnName ) { HRESULT hr = S_OK; wchar_t szFileName[_MAX_PATH+1] ; DWORD cbExistingPath = (_MAX_PATH+1); wchar_t szExistingPath[_MAX_PATH+1]; std::wstring wszRegKey ; DWORD dwRet;
// Verify all the args and do the correct thing if they are NULL or zero length
if ( !szService || !szPerfSvc || !szOpenFnName || !szCollectFnName || !szCloseFnName ) { hr = E_INVALIDARG ; goto Cleanup; }
if ( !szService[0] || !szPerfSvc[0] || !szOpenFnName[0] || !szCollectFnName[0] || !szCloseFnName[0] ) { hr = E_INVALIDARG ; goto Cleanup; }
// Use WIN32 API's to get the path to the module name
// NOTE: We will assume that this DLL is also the EventMessageFile.
MEMORY_BASIC_INFORMATION mbi; VirtualQuery(RegisterPerfDllEx, &mbi, sizeof(mbi)); dwRet = GetModuleFileName( reinterpret_cast<HINSTANCE>(mbi.AllocationBase), szFileName, sizeof(szFileName)/sizeof(wchar_t)-1) ; if (dwRet == 0) { // Wow, don't know what happened,
goto Failed; } szFileName[_MAX_PATH]=0; // If the user passed in NULL for the Event Log Message File for this Perfmon DLL,
// use this DLL as the Event Log Message File.
if (!szPerfMsgFile) { szPerfMsgFile = szFileName; }
// Register the perfmon counter DLL under the
// provided service.
wszRegKey = szServiceRegKeyPrefix ; wszRegKey += szService ; wszRegKey += szServiceRegKeySuffix ;
// Check to see if the library has already been registered.
if (ERROR_SUCCESS==RegQueryString( wszRegKey.c_str(), L"Library", szExistingPath, &cbExistingPath )) { if (_wcsicmp(szExistingPath, szFileName)) { // EventLog
// "RegisterPerfDllEx: Error: Attempt to replace Perfmon Library %s with %s. Failing. \n",
// szExistingPath,
// szFileName);
hr = E_FAIL; goto Cleanup; } //Otherwise, the dll is already registered!
} // Continue registering
if (ERROR_SUCCESS!=RegSetString( wszRegKey.c_str(), L"Library", (LPWSTR)szFileName )) { goto Failed; }
if (ERROR_SUCCESS!=RegSetString( wszRegKey.c_str(), L"Open", (LPWSTR)szOpenFnName) ) { goto Failed; }
if (ERROR_SUCCESS!=RegSetString( wszRegKey.c_str(), L"Collect", (LPWSTR)szCollectFnName) ) { goto Failed; }
if (ERROR_SUCCESS!=RegSetString( wszRegKey.c_str(), L"Close", (LPWSTR)szCloseFnName) ) { goto Failed; }
// Set up the Event Message File
wszRegKey = szEventLogRegKeyPrefix ; wszRegKey += szPerfSvc ;
// See if the EventMessageFile value is already set for this service
if (ERROR_SUCCESS==RegQueryString( wszRegKey.c_str(), L"EventMessageFile", szExistingPath, &cbExistingPath )) { if (_wcsicmp(szExistingPath, szPerfMsgFile)) {
hr = E_FAIL; goto Cleanup; } }
// Set the EventMessageFile value
if ( ERROR_SUCCESS!=RegSetString( wszRegKey.c_str(), L"EventMessageFile", (LPWSTR)szPerfMsgFile ) ) { goto Failed; }
if (ERROR_SUCCESS!=RegSetDWORD( wszRegKey.c_str(), L"TypesSupported", 0x07) ) // Error + Waring + Informational == 0x07
{ goto Failed; }
// Assume that the CategoryMessageFile is the same as
// the EventMessageFile.
if (ERROR_SUCCESS!=RegSetString( wszRegKey.c_str(), L"CategoryMessageFile", (LPWSTR)szPerfMsgFile ) ) { goto Failed; }
// NOTE: since we don't know what the count of categories is for the
// NOTE: CategoryMessageFile, do not set the CategoryCount value.
Cleanup: if (FAILED(hr)) { //EventLog ?? (L"RegisterPerfDllEx: Failed : (0x%08X)\n", hr);
}
return hr;
Failed: if (!FAILED(hr)) { hr = GetLastError(); } goto Cleanup; }
/*
- HrInitPerf - * Purpose: * Init data structure used to parameterize perfmon dll. Must be called * in DllMain for reason DLL_PROCESS_ATTACH. */
HRESULT HrInitPerf(PERF_DATA_INFO * pPDI) { HRESULT hr = S_OK;
if (!pPDI) return E_INVALIDARG;
if (g_fInitCalled) { return S_OK; }
CopyMemory(&g_PDI, pPDI, sizeof(PERF_DATA_INFO));
// Find the Service Name for using Event Logging on this Perfmon DLL
if (!(pPDI->wszPerfSvcName[0])) { wchar_t szFileName[_MAX_PATH+1] ; DWORD dwRet;
// Use WIN32 API's to get the path to the module name
// DEVNOTE - JMW - Since we need to make sure we have the instance handle of the DLL
// and not the executable, we will use VirtualQuery.
MEMORY_BASIC_INFORMATION mbi; VirtualQuery(HrInitPerf, &mbi, sizeof(mbi)); dwRet = GetModuleFileName( reinterpret_cast<HINSTANCE>(mbi.AllocationBase), szFileName, sizeof(szFileName)/sizeof(wchar_t)) ; if (dwRet == 0) { // Wow, don't know what happened
goto err; }
wchar_t szDrive[_MAX_DRIVE] ; wchar_t szDir[_MAX_DIR ] ; wchar_t szPerfFilename[ _MAX_FNAME ] ; wchar_t szExt[_MAX_EXT ] ; _wsplitpath( szFileName, szDrive, szDir, szPerfFilename, szExt ) ;
wcscpy(g_PDI.wszPerfSvcName, szPerfFilename); } // Safety: Need to alloc our own CounterTypes arrays
g_PDI.rgdwGlobalCounterTypes = NULL; g_PDI.rgdwInstCounterTypes = NULL;
DWORD cb; // Alloc & Copy Global Counter Types
if (g_PDI.cGlobalCounters) { cb = sizeof(DWORD) * g_PDI.cGlobalCounters;
g_PDI.rgdwGlobalCounterTypes = (DWORD *) malloc(cb); if(NULL == g_PDI.rgdwGlobalCounterTypes) { hr=E_OUTOFMEMORY; goto err; }
CopyMemory(g_PDI.rgdwGlobalCounterTypes, pPDI->rgdwGlobalCounterTypes, cb); g_PDI.rgdwGlobalCntrScale = (DWORD *) malloc(cb); if(NULL == g_PDI.rgdwGlobalCntrScale) { hr=E_OUTOFMEMORY; goto err; } CopyMemory(g_PDI.rgdwGlobalCntrScale, pPDI->rgdwGlobalCntrScale, cb); }
// Alloc & Copy Inst Counter Types
if (g_PDI.cInstCounters) { cb = sizeof(DWORD) * g_PDI.cInstCounters;
g_PDI.rgdwInstCounterTypes = (DWORD *) malloc(cb); if(NULL == g_PDI.rgdwInstCounterTypes) { hr=E_OUTOFMEMORY; goto err; }
CopyMemory(g_PDI.rgdwInstCounterTypes, pPDI->rgdwInstCounterTypes, cb); }
// Done!
g_fInitCalled = TRUE;
return S_OK;
err: if (g_PDI.rgdwGlobalCounterTypes) { free(g_PDI.rgdwGlobalCounterTypes); g_PDI.rgdwGlobalCounterTypes=NULL; } if (g_PDI.rgdwGlobalCntrScale) { free(g_PDI.rgdwGlobalCntrScale); g_PDI.rgdwGlobalCntrScale=NULL; }
if (g_PDI.rgdwInstCounterTypes) { free(g_PDI.rgdwInstCounterTypes); g_PDI.rgdwInstCounterTypes=NULL; }
return hr; }
/*
- HrShutdownPerf - * Purpose: * Release memory blocks allocated in HrInitPerf * */
HRESULT HrShutdownPerf(void) { HRESULT hr = S_OK;
if (g_cRef) { //EventLog ??(L"Warning: PERFDLL is being shut down with non-zero refcount!");
hr = E_UNEXPECTED; }
if (g_fInitCalled) { // We must invalidate the DLL if we release the memory in the g_PDI
g_fInitCalled = FALSE;
if (g_PDI.rgdwGlobalCounterTypes) { free(g_PDI.rgdwGlobalCounterTypes); g_PDI.rgdwGlobalCounterTypes=NULL; } if (g_PDI.rgdwGlobalCntrScale) { free(g_PDI.rgdwGlobalCntrScale); g_PDI.rgdwGlobalCntrScale=NULL; }
if (g_PDI.rgdwInstCounterTypes) { free(g_PDI.rgdwInstCounterTypes); g_PDI.rgdwInstCounterTypes=NULL;
} }
return hr; }
/*
- OpenPerformanceData - * Purpose: * Called by PerfMon to init the counters and this DLL. * * Parameters: * pszDeviceNames Ignored. * * Errors: * dwStat Indicates various errors that can occur during init */
DWORD APIENTRY OpenPerformanceData(LPWSTR pszDeviceNames) { DWORD dwStat = ERROR_SUCCESS; HANDLE hEventLog = NULL; BOOL fInMutex = FALSE; DWORD * rgdw; SECURITY_ATTRIBUTES sa; sa.lpSecurityDescriptor=NULL;
if (!g_fInitCalled) return E_FAIL;
// REVIEW: Assumes Open will be single-threaded. Verify?
if (g_cRef == 0) { DWORD idx; DWORD dwFirstCntr; DWORD dwFirstHelp; HRESULT hr = NOERROR;
// PERF_DATA_INFO wszSvcName is mandatory.
hEventLog = RegisterEventSource(NULL, g_PDI.wszPerfSvcName);
hr = HrInitializeSecurityAttribute(&sa);
if (FAILED(hr)) { //HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrInitSA);
dwStat = (DWORD)hr; goto err; }
if (g_PDI.cInstCounters) { // Create Controling Mutex
hr = HrCreatePerfMutex(&sa, g_PDI.wszInstMutexName, &g_hmtxInst);
if (FAILED(hr)) { //HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrInitSA);
dwStat = (DWORD)hr; goto err; }
fInMutex = TRUE; }
// Open shared memory blocks
dwStat = (DWORD) HrOpenSharedMemoryBlocks(hEventLog, &sa); if (FAILED(dwStat)) goto err;
// Allocate PERF_COUNTER_DEFINITION arrays for both g_perfdata
dwStat = (DWORD) HrAllocPerfCounterMem(hEventLog); if (FAILED(dwStat)) goto err;
// Get First Counter and First Help string offsets from Registry
dwStat = (DWORD) HrGetCounterIDsFromReg(hEventLog, &dwFirstCntr, &dwFirstHelp); if (FAILED(dwStat)) goto err;
//
// Fill in PerfMon structures to make Collect() faster
// Global Counters
if (g_PDI.cGlobalCounters) { PERF_COUNTER_DEFINITION * ppcd; DWORD cb;
PERF_OBJECT_TYPE * ppot = &g_perfdata.potGlobal;
cb = sizeof(PERF_OBJECT_TYPE) + (sizeof(PERF_COUNTER_DEFINITION) * g_PDI.cGlobalCounters) + sizeof(PERF_COUNTER_BLOCK) + (sizeof(DWORD) * g_PDI.cGlobalCounters);
ppot->TotalByteLength = ROUND_TO_8_BYTE(cb); ppot->DefinitionLength = sizeof(PERF_OBJECT_TYPE) + (g_PDI.cGlobalCounters * sizeof(PERF_COUNTER_DEFINITION));
ppot->HeaderLength = sizeof(PERF_OBJECT_TYPE); ppot->ObjectNameTitleIndex = dwFirstCntr; ppot->ObjectNameTitle = NULL; ppot->ObjectHelpTitleIndex = dwFirstHelp; ppot->ObjectHelpTitle = NULL; ppot->DetailLevel = PERF_DETAIL_NOVICE; ppot->NumCounters = g_PDI.cGlobalCounters; ppot->DefaultCounter = -1; ppot->NumInstances = PERF_NO_INSTANCES; ppot->CodePage = 0;
dwFirstCntr += 2; dwFirstHelp += 2;
rgdw = g_PDI.rgdwGlobalCounterTypes;
for (ppcd = g_perfdata.rgCntrDef, idx = 0; idx < g_PDI.cGlobalCounters; ppcd++, idx++) { ppcd->ByteLength = sizeof(PERF_COUNTER_DEFINITION); ppcd->CounterNameTitleIndex = dwFirstCntr; ppcd->CounterNameTitle = NULL; ppcd->CounterHelpTitleIndex = dwFirstHelp; ppcd->CounterHelpTitle = NULL; ppcd->DefaultScale = g_PDI.rgdwGlobalCntrScale[idx]; ppcd->DetailLevel = PERF_DETAIL_NOVICE; ppcd->CounterType = g_PDI.rgdwGlobalCounterTypes[idx]; ppcd->CounterSize = sizeof(DWORD); ppcd->CounterOffset = sizeof(PERF_COUNTER_BLOCK) + (idx * sizeof(DWORD)); dwFirstCntr += 2; dwFirstHelp += 2; }
// Last step: set the size of the counter block and data for this object
g_perfdata.CntrBlk.ByteLength = sizeof(PERF_COUNTER_BLOCK) + (g_PDI.cGlobalCounters * sizeof(DWORD));
} // Global Counters
// Instance Counters
if (g_PDI.cInstCounters) { PERF_COUNTER_DEFINITION * ppcd; PERF_OBJECT_TYPE * ppot = &g_perfinst.potInst;
// TotalByteLength will be overridden on each call to CollectPerfData().
ppot->DefinitionLength = sizeof(PERF_OBJECT_TYPE) + (g_PDI.cInstCounters * sizeof(PERF_COUNTER_DEFINITION)); ppot->HeaderLength = sizeof(PERF_OBJECT_TYPE); ppot->ObjectNameTitleIndex = dwFirstCntr; ppot->ObjectNameTitle = NULL; ppot->ObjectHelpTitleIndex = dwFirstHelp; ppot->ObjectHelpTitle = NULL; ppot->DetailLevel = PERF_DETAIL_NOVICE; ppot->NumCounters = g_PDI.cInstCounters; ppot->DefaultCounter = -1; ppot->NumInstances = 0; ppot->CodePage = 0;
dwFirstCntr += 2; dwFirstHelp += 2;
for (ppcd = g_perfinst.rgCntrDef, idx = 0; idx < g_PDI.cInstCounters; ppcd++, idx++) { ppcd->ByteLength = sizeof(PERF_COUNTER_DEFINITION); ppcd->CounterNameTitleIndex = dwFirstCntr; ppcd->CounterNameTitle = NULL; ppcd->CounterHelpTitleIndex = dwFirstHelp; ppcd->CounterHelpTitle = NULL; ppcd->DefaultScale = 0; ppcd->DetailLevel = PERF_DETAIL_NOVICE; ppcd->CounterType = g_PDI.rgdwInstCounterTypes[idx]; ppcd->CounterSize = sizeof(DWORD); ppcd->CounterOffset = sizeof(PERF_COUNTER_BLOCK) + (idx * sizeof(DWORD)); dwFirstCntr += 2; dwFirstHelp += 2; }
// Initialize generic INSTDATA for future use
g_instdata.InstDef.ByteLength = sizeof(PERF_INSTANCE_DEFINITION) + (MAX_PATH * sizeof(WCHAR)) ; g_instdata.InstDef.ParentObjectTitleIndex = 0; // No parent object
g_instdata.InstDef.ParentObjectInstance = 0; g_instdata.InstDef.UniqueID = PERF_NO_UNIQUE_ID; g_instdata.InstDef.NameOffset = sizeof(PERF_INSTANCE_DEFINITION); g_instdata.InstDef.NameLength = 0; // To be overriden in Collect
g_instdata.CntrBlk.ByteLength = (sizeof(PERF_COUNTER_BLOCK) + (g_PDI.cInstCounters * sizeof(DWORD)));
} // Instance Counters
// Done! Ready for business....
g_fInit = TRUE; }
// If we got here, we're okay!
dwStat = ERROR_SUCCESS; g_cRef++;
ret: if (fInMutex) ReleaseMutex(g_hmtxInst);
if (hEventLog) DeregisterEventSource(hEventLog);
if (sa.lpSecurityDescriptor) LocalFree(sa.lpSecurityDescriptor);
return dwStat;
err:
if (g_hsmGlobalCntr) { UnmapViewOfFile(g_rgdwGlobalCntr); CloseHandle(g_hsmGlobalCntr); }
if (g_hsmInstAdm) { UnmapViewOfFile(g_pic); CloseHandle(g_hsmInstAdm); }
if (g_hsmInstCntr) { UnmapViewOfFile(g_rgdwInstCntr); CloseHandle(g_hsmInstCntr); }
if (g_hmtxInst) CloseHandle(g_hmtxInst);
// TODO: release all that memory we Alloc'd
HrFreePerfCounterMem();
goto ret; }
/*
- CollectPerformanceData - * Purpose: * Called by PerfMon to collect performance counter data * * Parameters: * pszValueName * ppvData * pcbTotal * pcObjTypes * * Errors: * ERROR_SUCCESS * ERROR_MORE_DATA */
DWORD APIENTRY CollectPerformanceData( LPWSTR pszValueName, LPVOID *ppvData, LPDWORD pcbTotal, LPDWORD pcObjTypes) { BOOL fCollectGlobalData; BOOL fCollectInstData; DWORD dwQueryType; ULONG cbBuff = *pcbTotal; char * pcT;
// In case we have to bail out
*pcbTotal = 0; *pcObjTypes = 0;
if (!g_fInit) return ERROR_SUCCESS;
// Determine Query Type; we only support QUERY_ITEMS
dwQueryType = GetQueryType(pszValueName);
if (dwQueryType == QUERY_FOREIGN) return ERROR_SUCCESS;
// Assume PerfMon is collecting both until we prove otherwise
fCollectGlobalData = TRUE; fCollectInstData = TRUE;
if (dwQueryType == QUERY_ITEMS) { if (!IsNumberInUnicodeList(g_perfdata.potGlobal.ObjectNameTitleIndex, pszValueName)) fCollectGlobalData = FALSE; if (!IsNumberInUnicodeList(g_perfinst.potInst.ObjectNameTitleIndex, pszValueName)) fCollectInstData = FALSE;
if (!fCollectGlobalData && !fCollectInstData) return ERROR_SUCCESS; }
// Get a temporary pointer to the returned data buffer. If all goes well
// then we update ppvData before leaving, else we leave it unchanged and
// set *pcbTotal and pcObjTypes to zero and return ERROR_MORE_DATA.
pcT = (char *) *ppvData;
// Copy the data from the shared memory block for the system-wide
// counters into the buffer provided and update our OUT params.
if (g_rgdwGlobalCntr && fCollectGlobalData && g_PDI.cGlobalCounters) { DWORD cb; DWORD cbTotal;
// Estimate total size, and see if we can fit.
if (g_perfdata.potGlobal.TotalByteLength > cbBuff) return ERROR_MORE_DATA;
// Copy data in chunks.
cbTotal = 0;
// PERF_OBJECT_TYPE
cb = sizeof(PERF_OBJECT_TYPE); cbTotal += cb; CopyMemory((LPVOID) pcT, &g_perfdata.potGlobal, cb); pcT += cb;
// PERF_COUNTER_DEFINITION []
cb = g_PDI.cGlobalCounters * sizeof(PERF_COUNTER_DEFINITION); cbTotal += cb; CopyMemory((LPVOID) pcT, g_perfdata.rgCntrDef, cb); pcT += cb;
// PERF_COUNTER_BLOCK
cb = sizeof(PERF_COUNTER_BLOCK); cbTotal += cb; CopyMemory((LPVOID) pcT, &g_perfdata.CntrBlk , cb); pcT += cb;
// (counters) DWORD []
cb = g_PDI.cGlobalCounters * sizeof(DWORD); cbTotal += cb; CopyMemory((LPVOID) pcT, g_rgdwGlobalCntr, cb); pcT += cb;
// If we've done our math right, This assert should be valid.
//assert((DWORD)(pcT - (char *)*ppvData) == cbTotal);
if (g_perfdata.potGlobal.TotalByteLength > cbTotal) { cb = g_perfdata.potGlobal.TotalByteLength - cbTotal; pcT += cb; }
*pcbTotal += g_perfdata.potGlobal.TotalByteLength; (*pcObjTypes)++; }
// Copy the data from the shared memory block for the per-instance
// counters into the buffer provided and update our OUT params. We
// must enter the mutex here to prevent connection instances from
// being added to or removed from the list while we are copying data.
if (g_pic && fCollectInstData && g_PDI.cInstCounters) { DWORD cb; DWORD cbTotal; DWORD ism; DWORD ipd; DWORD cInst; PERF_OBJECT_TYPE * pPOT; INSTDATA * pInstData;
if (WaitForSingleObject(g_hmtxInst, INFINITE) != WAIT_OBJECT_0) { *pcbTotal = 0; *pcObjTypes = 0;
// BUGBUG: wrong return code for the problem. We timed out waiting for
// BUGBUG: the mutex; However, we may not return anything other than
// BUGBUG: ERROR_SUCCESS or ERROR_MORE_DATA without disabling the DLL.
return ERROR_SUCCESS; }
// Find out how many instances exist.
// NOTE: Zero instances is valid. We must still copy the "core" perf data.
cInst = g_pic->cInstRecInUse;
// Estimate total size, and see if we can fit.
cbTotal = sizeof(PERF_OBJECT_TYPE) + (g_PDI.cInstCounters * sizeof(PERF_COUNTER_DEFINITION)) + (cInst * sizeof(INSTDATA)) + (cInst * (g_PDI.cInstCounters * sizeof(DWORD))); // Must return data aligned on 8-byte boundaries.
cbTotal = ROUND_TO_8_BYTE(cbTotal);
if (cbTotal > (cbBuff - *pcbTotal)) { ReleaseMutex(g_hmtxInst);
*pcbTotal = 0; *pcObjTypes = 0;
return ERROR_MORE_DATA; }
// Keep a pointer to beginig so we can update the "total bytes" value at the end.
pPOT = (PERF_OBJECT_TYPE *) pcT;
// PERF_OBJECT_TYPE
CopyMemory(pPOT, &(g_perfinst.potInst), sizeof(PERF_OBJECT_TYPE));
pcT += sizeof(PERF_OBJECT_TYPE);
// PERF_COUNTER_DEFINITION []
cb = g_PDI.cInstCounters * sizeof(PERF_COUNTER_DEFINITION); CopyMemory(pcT, g_perfinst.rgCntrDef, cb); pcT += cb;
// Find instances and copy their counter data blocks
for (ism = 0, ipd = 0; (ism < g_pic->cMaxInstRec) && (ipd < cInst); ism++) { if (g_rgInstRec[ism].fInUse) { pInstData = (INSTDATA *) pcT;
// PERF_INSTANCE_DATA
cb = sizeof(INSTDATA); CopyMemory(pcT, &g_instdata, cb); pcT += cb;
// (inst name) WCHAR [] (inside INSTDATA block)
cb = (wcslen(g_rgInstRec[ism].szInstName) + 1) * sizeof(WCHAR);
//Assert( cb > 0 );
//Assert( cb < (MAX_PATH * sizeof(WCHAR)) );
CopyMemory(pInstData->szInstName, g_rgInstRec[ism].szInstName, cb);
// Update PERF_INSTANCE_DEFINITION with correct name length.
pInstData->InstDef.NameLength = cb;
// (counters) DWORD []
cb = g_PDI.cInstCounters * sizeof(DWORD); CopyMemory(pcT, &g_rgdwInstCntr[(ism * g_PDI.cInstCounters)], cb); pcT += cb;
ipd++; } }
// NOTE: we are deliberately ignoring the condition of
// (ipd < cInst), even though that may indicate corruption
// of the Shared Memory Block. Further note that this code
// will never trap the condition of (ipd > cInst).
// Done looking at Shared Memory Block.
ReleaseMutex(g_hmtxInst);
// Align data on 8-byte boundary
cb = (DWORD)((char *) pcT - (char *) pPOT); cbTotal = ROUND_TO_8_BYTE(cb); if (cbTotal > cb) pcT += (cbTotal - cb);
// Update PERF_OBJECT_TYPE with correct numbers
pPOT->TotalByteLength = cbTotal; pPOT->NumInstances = ipd; // Use the count of instances we actually *found*
*pcbTotal += cbTotal; (*pcObjTypes)++; }
// We only get here if nothing failed. It is now safe
// to update *ppvData and return a success indication.
*ppvData = (LPVOID) pcT;
return ERROR_SUCCESS; }
/*
- ClosePerformanceData - * Purpose: * Called by PerfMon to uninit the counter DLL. * * Parameters: * void * * Errors: * ERROR_SUCCESS Always! */
DWORD APIENTRY ClosePerformanceData(void) {
if (g_cRef > 0) { if (--g_cRef == 0) { // We're going to free stuff; make sure later calls
// don't de-ref bad pointers.
g_fInit = FALSE;
// Close down Global Counters
if (g_rgdwGlobalCntr) UnmapViewOfFile(g_rgdwGlobalCntr);
if (g_hsmGlobalCntr) CloseHandle(g_hsmGlobalCntr);
// Close down Instance Counters
// NOTE: g_pic is the starting offset of the SM block.
// NOTE: DO NOT Unmap on g_rgInstRec!
if (g_pic) UnmapViewOfFile(g_pic);
if (g_hsmInstAdm) CloseHandle(g_hsmInstAdm);
if (g_rgdwInstCntr) UnmapViewOfFile(g_rgdwInstCntr);
if (g_hsmInstCntr) CloseHandle(g_hsmInstCntr);
if (g_hmtxInst) CloseHandle(g_hmtxInst);
// Free all that memory we've alloc'd
HrFreePerfCounterMem();
g_rgdwGlobalCntr = NULL; g_hsmGlobalCntr = NULL; g_pic = NULL; g_rgInstRec = NULL; g_rgdwInstCntr = NULL; g_hsmInstAdm = NULL; g_hsmInstCntr = NULL; g_hmtxInst = NULL;
} } return ERROR_SUCCESS; }
/*
- GetQueryType - * Purpose: * Returns the type of query described in the lpValue string so that * the appropriate processing method may be used. * * Parameters: * lpValue String passed to PerfRegQuery Value for processing * * Returns: * QUERY_GLOBAL | QUERY_FOREIGN | QUERY_COSTLY | QUERY_ITEMS * */
static DWORD GetQueryType(LPWSTR lpValue) { WCHAR * pwcArgChar, *pwcTypeChar; BOOL bFound;
if (lpValue == 0) return QUERY_GLOBAL;
if (*lpValue == 0) return QUERY_GLOBAL;
// check for "Global" request
pwcArgChar = lpValue; pwcTypeChar = szGlobal; 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 = szForeign; 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 = szCostly; 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; }
/*
- IsNumberInUnicodeList - * Purpose: * Determines if dwNumber is in the pszUnicodeList. * * Parameters: * dwNumber Number to find in list * pszUnicodeList Space delimited list of decimal numbers * * Errors: * TRUE/FALSE If found/not found respectively * */
static BOOL IsNumberInUnicodeList(DWORD dwNumber, LPWSTR pszUnicodeList) { DWORD dwThisNumber = 0; BOOL bValidNumber = FALSE; BOOL bNewItem = TRUE; WCHAR wcDelimiter = (WCHAR)' '; WCHAR *pwcThisChar;
if (pszUnicodeList == NULL) return FALSE;
pwcThisChar = pszUnicodeList;
while (TRUE) /*lint !e774*/ { 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++; } }
/*
- HrLogEvent - * Purpose: * Wrap up the call to ReportEvent to make things look nicer. */ HRESULT HrLogEvent(HANDLE hEventLog, WORD wType, DWORD msgid) { if (g_fInitCalled) { DWORD cb = sizeof(WCHAR) * wcslen(g_PDI.wszSvcName); if (hEventLog) return ReportEvent(hEventLog, wType, // Event Type
(WORD)0, // Category
msgid, // Event ID
NULL, // User SID
0, // # strings to merge
cb, // size of binary data (bytes)
NULL, // array of strings to merge
g_PDI.wszSvcName); // binary data
else return E_FAIL; } else return E_FAIL; }
/*
- HrOpenSharedMemoryBlocks - * Purpose: * Encapsulate the grossness of opening the shared memory blocks. */
HRESULT HrOpenSharedMemoryBlocks(HANDLE hEventLog, SECURITY_ATTRIBUTES * psa) { HRESULT hr=S_OK; BOOL fExist;
if (!g_fInitCalled) return E_FAIL;
if (!psa) return E_INVALIDARG;
// Shared Memory for Global Counters
if (g_PDI.cGlobalCounters) {
hr = HrOpenSharedMemory(g_PDI.wszGlobalSMName, (sizeof(DWORD) * g_PDI.cGlobalCounters), psa, &g_hsmGlobalCntr, (LPVOID *) &g_rgdwGlobalCntr, &fExist);
if (FAILED(hr)) { //HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrInitSharedMemory1);
goto ret; }
if (!fExist) ZeroMemory(g_rgdwGlobalCntr, (g_PDI.cGlobalCounters * sizeof(DWORD))); }
// Shared Memory for Instance Counters
if (g_PDI.cInstCounters) { DWORD cbAdm; DWORD cbCntr; WCHAR szAdmName[MAX_PATH]; // Admin SM Name
WCHAR szCntrName[MAX_PATH]; // Counter SM Name
// Calc required memory sizes
// BUGBUG: Since we're puntin on dynamic instances, we use g_cMaxInst
cbAdm = sizeof(INSTCNTR_DATA) + (sizeof(INSTREC) * g_cMaxInst); cbCntr = ((sizeof(DWORD) * g_PDI.cInstCounters) * g_cMaxInst);
// Build SM names for Admin and Counters
wsprintf(szAdmName, L"%s_ADM", g_PDI.wszInstSMName); wsprintf(szCntrName,L"%s_CNTR", g_PDI.wszInstSMName);
// Open Instance Admin Memory
hr = HrOpenSharedMemory(szAdmName, cbAdm, psa, &g_hsmInstAdm, (LPVOID *)&g_pic, &fExist);
if (FAILED(hr)) { //HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrInitSharedMemory2);
goto ret; }
// Fixup Pointers
g_rgInstRec = (INSTREC *) ((LPBYTE) g_pic + sizeof(INSTCNTR_DATA));
if (!fExist) { ZeroMemory(g_pic, cbAdm); g_pic->cMaxInstRec = g_cMaxInst; g_pic->cInstRecInUse = 0; }
// Because we don't support dynamic instances, We should *always*
// have a MaxInstRec of g_cMaxInst
//Assert(g_cMaxInst == g_pic->cMaxInstRec);
// Open Instance Counter Memory
hr = HrOpenSharedMemory(szCntrName, cbCntr, psa, &g_hsmInstCntr, (LPVOID *)&g_rgdwInstCntr, &fExist);
if (FAILED(hr)) { //HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrInitSharedMemory2);
goto ret; }
if (!fExist) ZeroMemory(g_rgdwInstCntr, cbCntr); }
ret:
return hr;
}
/*
- HrGetCounterIDsFromReg - * Purpose: * Get the "First Name" and "First Help" inicies from the Registry in the * special place for the configured service. */
HRESULT HrGetCounterIDsFromReg(HANDLE hEventLog, DWORD * pdwFirstCntr, DWORD * pdwFirstHelp) { HRESULT hr; HKEY hKey = NULL; WCHAR wszServicePerfKey[MAX_PATH]; DWORD dwSize; DWORD dwType;
if (!g_fInitCalled) return E_FAIL;
if (!pdwFirstCntr || !pdwFirstHelp) return E_INVALIDARG;
// Get the First Counter and First Help from the registry
wsprintf(wszServicePerfKey, L"SYSTEM\\CurrentControlSet\\Services\\%s\\Performance", g_PDI.wszSvcName);
hr = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszServicePerfKey, 0L, KEY_READ, &hKey);
if (hr != ERROR_SUCCESS) { //HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrOpenRegistry);
goto ret; }
dwSize = sizeof(DWORD);
hr = RegQueryValueExW(hKey, L"First Counter", 0L, &dwType, (LPBYTE)pdwFirstCntr, &dwSize);
if (hr != ERROR_SUCCESS) { //HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrQueryRegistry1);
goto ret; }
dwSize = sizeof(DWORD);
hr = RegQueryValueExW(hKey, L"First Help", 0L, &dwType, (LPBYTE)pdwFirstHelp, &dwSize);
if (hr != ERROR_SUCCESS) { //HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrQueryRegistry2);
goto ret; }
ret:
if (hKey) RegCloseKey(hKey);
return hr;
}
/*
- HrAllocPerfCounterMem - * Purpose: * Allocates memory for PERF_COUNTER_DEFINITION arrays for both * g_perfdata and g_perfinst. * * Notes: * Uses ProcessHeap handle obtained in HrInit. */
HRESULT HrAllocPerfCounterMem(HANDLE hEventLog) { HRESULT hr; DWORD cb;
if (!g_fInitCalled) return E_FAIL;
// Global Counters
if (g_PDI.cGlobalCounters) { // Alloc Global PERF_COUNTER_DEFINITION array
cb = (sizeof(PERF_COUNTER_DEFINITION) * g_PDI.cGlobalCounters);
g_perfdata.rgCntrDef = (PERF_COUNTER_DEFINITION *) malloc(cb); if(NULL == g_perfdata.rgCntrDef) { hr = E_OUTOFMEMORY; goto err; }
}
// Instance Counters
if (g_PDI.cInstCounters) { // Alloc Inst PERF_COUNTER_DEFINITION array
cb = (sizeof(PERF_COUNTER_DEFINITION) * g_PDI.cInstCounters);
g_perfinst.rgCntrDef = (PERF_COUNTER_DEFINITION *) malloc(cb); if(NULL == g_perfinst.rgCntrDef ) { hr = E_OUTOFMEMORY; goto err; } }
return S_OK;
err:
//HrLogEvent(hEventLog, EVENTLOG_ERROR_TYPE, msgidCntrAlloc);
HrFreePerfCounterMem();
return hr; }
/*
- HrFreePerfCounterMem - * Purpose: * Companion to HrAllocPerfCounterMem * * Note: * Uses ProcessHeap handle obtained in HrInit. */
HRESULT HrFreePerfCounterMem(void) { if (!g_fInitCalled) return E_FAIL;
// We must invalidate the DLL if we release the memory in the g_PDI
g_fInitCalled = FALSE;
if (g_perfdata.rgCntrDef) { free(g_perfdata.rgCntrDef); g_perfdata.rgCntrDef = NULL; }
if (g_perfinst.rgCntrDef) { free(g_perfinst.rgCntrDef); g_perfinst.rgCntrDef = NULL; }
return S_OK; }
HRESULT HrUninstallPerfDll( IN LPCWSTR szService ) { // Make sure we've valid input
if( !szService ) return E_INVALIDARG;
// First, do unlodctr since removing the Performance key without removing
// counter names and descriptions may toast the perfmon system
std::wstring wszService = L"x "; // KB Article Q188769
wszService += szService; DWORD dwErr = UnloadPerfCounterTextStringsW(const_cast<LPWSTR>(wszService.c_str()), TRUE); if( dwErr != ERROR_SUCCESS ) { // Continue without error if unlodctr has already been called
if( (dwErr != ERROR_FILE_NOT_FOUND) && (dwErr != ERROR_BADKEY) ) { return HRESULT_FROM_WIN32(dwErr); } }
// Now that unlodctr has succeeded, we can start deleting registry keys
// Start with the Performance key of the service
std::wstring wszRegKey; wszRegKey = szServiceRegKeyPrefix; wszRegKey += szService; dwErr = SHDeleteKey(HKEY_LOCAL_MACHINE, wszRegKey.c_str()); if( (dwErr != ERROR_SUCCESS) && (dwErr != ERROR_FILE_NOT_FOUND) ) { return HRESULT_FROM_WIN32(dwErr); }
// Use WIN32 API's to get the path to the module name
wchar_t szFileName[_MAX_PATH+1] ; MEMORY_BASIC_INFORMATION mbi; VirtualQuery(HrUninstallPerfDll, &mbi, sizeof(mbi)); DWORD dwRet = GetModuleFileName( reinterpret_cast<HINSTANCE>(mbi.AllocationBase), szFileName, sizeof(szFileName)/sizeof(wchar_t)-1) ; if (dwRet == 0) { // Wow, don't know what happened,
return HRESULT_FROM_WIN32(::GetLastError()); }
szFileName[_MAX_PATH]=0; // Split the module path to get the filename
wchar_t szDrive[_MAX_DRIVE] ; wchar_t szDir[_MAX_DIR ] ; wchar_t szPerfFilename[ _MAX_FNAME ] ; wchar_t szExt[_MAX_EXT ] ; _wsplitpath( szFileName, szDrive, szDir, szPerfFilename, szExt ) ;
// Delete the Event Log key for the service
wszRegKey = szEventLogRegKeyPrefix; wszRegKey += szPerfFilename; dwErr = SHDeleteKey(HKEY_LOCAL_MACHINE, wszRegKey.c_str()); if( ERROR_FILE_NOT_FOUND == dwErr ) dwErr = ERROR_SUCCESS; return HRESULT_FROM_WIN32(dwErr); }
|