//================================================================= // // PerfData.CPP -- Performance Data Helper class // // Copyright (c) 1996-2001 Microsoft Corporation, All Rights Reserved // // Revisions: 11/23/97 a-sanjes Created // //================================================================= #include "precomp.h" #include #include "perfdata.h" #include #include #ifdef NTONLY // Static Initialization bool CPerformanceData::m_fCloseKey = false; ////////////////////////////////////////////////////////// // // Function: CPerformanceData::CPerformanceData // // Default constructor // // Inputs: // None // // Outputs: // None // // Returns: // None // // Comments: // ////////////////////////////////////////////////////////// CPerformanceData::CPerformanceData( void ) { m_pBuff = NULL; } ////////////////////////////////////////////////////////// // // Function: CPerformanceData::~CPerformanceData // // Destructor // // Inputs: // None // // Outputs: // None // // Returns: // None // // Comments: // ////////////////////////////////////////////////////////// CPerformanceData::~CPerformanceData( void ) { if (m_pBuff != NULL) { delete [] m_pBuff; } } ////////////////////////////////////////////////////////// // // Function: CPerformanceData::RegQueryValueExExEx // // Inputs: HKEY hKey handle of key to query // LPTSTR lpValueName, address of name of value to query // LPDWORD lpReserved reserved // LPDWORD lpType, address of buffer for value type // LPBYTE lpData address of data buffer // LPDWORD lpcbData address of data buffer size // // // Returns: everything documented by RegQueryValueEx AND ERROR_SEM_TIMEOUT or ERROR_OPEN_FAILED // ////////////////////////////////////////////////////////// LONG CPerformanceData::RegQueryValueExExEx( HKEY hKey, LPTSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData) { LONG ret = -1; ret = RegQueryValueEx( hKey, lpValueName, lpReserved, lpType, lpData, lpcbData); return ret; } ////////////////////////////////////////////////////////// // // Function: CPerformanceData::Open // // Opens and retrieves data from the performance data // registry key. // // Inputs: // LPCTSTR pszValue - Value to retrieve // // Outputs: // LPDWORD pdwType - Type returned // LPBYTE lpData - Buffer // LPDWORD lpcbData - Amount of data returned // // Returns: // ERROR_SUCCESS if successful // // Comments: // ////////////////////////////////////////////////////////// DWORD CPerformanceData::Open( LPCTSTR pszValue, LPDWORD pdwType, LPBYTE *lppData, LPDWORD lpcbData ) { DWORD dwReturn = ERROR_OUTOFMEMORY; BOOL fStackTrashed = FALSE; LogMessage(_T("CPerformanceData::Open")); LPCTSTR pszOldValue = pszValue; LPDWORD pdwOldType = pdwType; LPBYTE* lppOldData = lppData; LPDWORD lpcbOldData = lpcbData; ASSERT_BREAK(*lppData == NULL); DWORD dwSize = 16384; *lpcbData = dwSize; *lppData = new byte [*lpcbData]; if (*lppData == NULL) { throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ; } if ( pszOldValue != pszValue || pdwOldType != pdwType || lppOldData != lppData || lpcbOldData != lpcbData ) { LogErrorMessage(_T("CPerformanceData::stack trashed after malloc")); fStackTrashed = TRUE; ASSERT_BREAK(0); } else { try { while ((*lppData != NULL) && // remember precedence & associativity? ((dwReturn = RegQueryValueEx( HKEY_PERFORMANCE_DATA, (LPTSTR)pszValue, NULL, pdwType, (LPBYTE) *lppData, lpcbData )) == ERROR_MORE_DATA) ) { if ( pszOldValue != pszValue || pdwOldType != pdwType || lppOldData != lppData || lpcbOldData != lpcbData ) { LogErrorMessage(_T("CPerformanceData::stack trashed after RegQueryValueEx")); fStackTrashed = TRUE; ASSERT_BREAK(0); break; } // Get a buffer that is big enough. LogMessage(_T("CPerformanceData::realloc")); dwSize += 16384; *lpcbData = dwSize ; if ( pszOldValue != pszValue || pdwOldType != pdwType || lppOldData != lppData || lpcbOldData != lpcbData ) { LogErrorMessage(_T("CPerformanceData::stack trashed after size reset")); fStackTrashed = TRUE; ASSERT_BREAK(0); break; } delete [] *lppData; *lppData = new BYTE [*lpcbData]; if (*lppData == NULL) { throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ; } if ( pszOldValue != pszValue || pdwOldType != pdwType || lppOldData != lppData || lpcbOldData != lpcbData ) { LogErrorMessage(_T("CPerformanceData::stack trashed after realloc")); fStackTrashed = TRUE; ASSERT_BREAK(0); break; } } // While } catch ( ... ) { if (*lppData != NULL) { delete [] *lppData; } throw ; } } if ( fStackTrashed ) { dwReturn = ERROR_INVALID_FUNCTION; } else { // if we got here in an error condition, try to recoup if ((dwReturn != ERROR_SUCCESS) && (*lppData != NULL)) { LogErrorMessage(_T("CPerformanceData::failed to alloc enough memory")); delete [] *lppData; *lppData = NULL; } if (!m_fCloseKey) { m_fCloseKey = ( ERROR_SUCCESS == dwReturn ); if (m_fCloseKey) LogMessage(_T("Opened perf counters")); } if ((dwReturn != ERROR_SUCCESS) && IsErrorLoggingEnabled()) { CHString sTemp; sTemp.Format(_T("Performance RegQueryValueEx returned %d\n"), dwReturn); LogErrorMessage(sTemp); } if (*lppData == NULL) { dwReturn = ERROR_OUTOFMEMORY; } } return dwReturn; } ////////////////////////////////////////////////////////// // // Function: CPerformanceData::Close // // Closes the performance data registry key if the // static value is TRUE. // // Inputs: // None. // // Outputs: // None. // // Returns: // None. // // Comments: // // Per the KB, calling RegCloseKey on HKEY_PERFORMANCE_DATA // causes a memory leak, so you do NOT want to do lots of // these. // ////////////////////////////////////////////////////////// #if 0 // From raid 48395 void CPerformanceData::Close( void ) { if ( m_fCloseKey ) { if ( m_fCloseKey ) { RegCloseKey( HKEY_PERFORMANCE_DATA ); m_fCloseKey = FALSE; LogMessage(_T("Closed Perf Counters")); } } } #endif ////////////////////////////////////////////////////////// // // Function: CPerformanceData::GetPerfIndex // // Given a perf object name, this function returns // the perf object number. // // Inputs: // Object name // // Outputs: // None // // Returns: // Associated Number or 0 on error. // // Comments: // // ////////////////////////////////////////////////////////// DWORD CPerformanceData::GetPerfIndex(LPCTSTR pszName) { DWORD dwRetVal = 0; if (m_pBuff == NULL) { LONG lRet = ERROR_SUCCESS; if (m_pBuff == NULL) { CRegistry RegInfo; // Hardcoding 009 should be ok since according to the docs: // "The langid is the ASCII representation of the 3-digit hexadecimal language identifier. " // "For example, the U.S. English langid is 009. In a non-English version of Windows NT, " // "counters are stored in both the native language of the system and in English. " if ((lRet = RegInfo.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009"), KEY_QUERY_VALUE)) == ERROR_SUCCESS) { // Get the size of the key DWORD dwSize; lRet = RegInfo.GetCurrentBinaryKeyValue(_T("Counter"), NULL, &dwSize); if (lRet == ERROR_SUCCESS) { // Allocate a buffer to hold it m_pBuff = new BYTE[dwSize]; if (m_pBuff != NULL) { // Get the actual data if ((lRet = RegInfo.GetCurrentBinaryKeyValue(_T("Counter"), m_pBuff, &dwSize)) != ERROR_SUCCESS) { delete [] m_pBuff; m_pBuff = NULL; } } else { throw CHeap_Exception ( CHeap_Exception :: E_ALLOCATION_ERROR ) ; } } } } if (lRet != ERROR_SUCCESS) { LogErrorMessage2(L"Failed to read Perflib key: %x", lRet); } } // If we got the registry key if (m_pBuff != NULL) { const TCHAR *pCounter; const TCHAR *ptemp; int stringlength; pCounter = (TCHAR *)m_pBuff; stringlength = _tcslen((LPCTSTR)pCounter); // Exit the loop when we hit the end while(stringlength) { // Strings are stored in the form \0\0. // What we want to return is the counter number. ptemp will point to the name ptemp = pCounter + stringlength+1; stringlength = _tcslen((LPCTSTR)ptemp); if (stringlength > 0) { // Did we find it if (_tcscmp((TCHAR *)ptemp, pszName) != 0) { // Nope, position to the next pair pCounter = ptemp + stringlength+1; stringlength = _tcslen((LPCTSTR)pCounter); } else { // Yup, calculate the value to return dwRetVal = _ttoi(pCounter); break; } } } } ASSERT_BREAK(dwRetVal > 0); return dwRetVal; } ////////////////////////////////////////////////////////// // // Function: CPerformanceData::GetValue // // Given a perf object index, counter index, and optional // instance name, returns the value and the time. // // Inputs: // Value, Time // // Outputs: // Value, Time // // Returns: // True if it finds the value // // Comments: // // ////////////////////////////////////////////////////////// bool CPerformanceData::GetValue(DWORD dwObjIndex, DWORD dwCtrIndex, const WCHAR *szInstanceName, PBYTE pbData, unsigned __int64 *pTime) { PPERF_DATA_BLOCK PerfData = NULL; DWORD dwBufferSize = 0; LONG lReturn = 0; BOOL fReturn = FALSE; TCHAR szBuff[MAXITOA]; bool bFound = false; PPERF_INSTANCE_DEFINITION pInstBlock; DWORD dwInstances; unsigned __int64 *pbCounterData; // The subsequent close happens in our destructor (read comment there). lReturn = Open( _itot(dwObjIndex, szBuff, 10), NULL, (LPBYTE *) (&PerfData), &dwBufferSize ); if ( NULL != PerfData && ERROR_SUCCESS == lReturn ) { try { // Surf through the objects returned until we find the one we are looking for. PPERF_OBJECT_TYPE pPerfObject = (PPERF_OBJECT_TYPE)((PBYTE)PerfData + PerfData->HeaderLength); for ( DWORD dwObjectCtr = 0; dwObjectCtr < PerfData->NumObjectTypes && pPerfObject->ObjectNameTitleIndex != dwObjIndex; dwObjectCtr++ ); // Did we find the Object? if ( dwObjectCtr < PerfData->NumObjectTypes ) { // Now surf through the Counter Definition Data until we locate the // counter we are hunting for. PPERF_COUNTER_DEFINITION pPerfCtrDef = (PPERF_COUNTER_DEFINITION)((PBYTE) pPerfObject + pPerfObject->HeaderLength); for ( DWORD dwCtr = 0; dwCtr < pPerfObject->NumCounters && pPerfCtrDef->CounterNameTitleIndex != dwCtrIndex; dwCtr++, // Go to the next counter pPerfCtrDef = (PPERF_COUNTER_DEFINITION)((PBYTE) pPerfCtrDef + pPerfCtrDef->ByteLength ) ); // Did we find the counter? if ( dwCtr < pPerfObject->NumCounters ) { // Finally go to the data offset we retrieved from the counter definitions // and access the data (finally). DWORD dwCounterOffset = pPerfCtrDef->CounterOffset; PPERF_COUNTER_BLOCK pPerfCtrBlock = NULL; // If we are looking for an instance if ((szInstanceName == NULL) && (pPerfObject->NumInstances == PERF_NO_INSTANCES)) { pPerfCtrBlock = (PPERF_COUNTER_BLOCK) ((PBYTE) pPerfObject + pPerfObject->DefinitionLength); bFound = true; } else if (pPerfObject->NumInstances != PERF_NO_INSTANCES) { // Walk the instances looking for the requested one pInstBlock = (PPERF_INSTANCE_DEFINITION) ((PBYTE)pPerfObject + pPerfObject->DefinitionLength); dwInstances = 1; while ((dwInstances <= pPerfObject->NumInstances) && (wcscmp((WCHAR *)((pInstBlock->NameOffset) + (PBYTE)pInstBlock), szInstanceName) != 0)) { pPerfCtrBlock = (PPERF_COUNTER_BLOCK) ((PBYTE)pInstBlock + pInstBlock->ByteLength); pInstBlock = (PPERF_INSTANCE_DEFINITION)((PBYTE) pInstBlock + (pInstBlock->ByteLength + pPerfCtrBlock->ByteLength)); dwInstances ++; } // Did we find it? if (dwInstances <= pPerfObject->NumInstances) { bFound = true; pPerfCtrBlock = (PPERF_COUNTER_BLOCK) ((PBYTE)pInstBlock + pInstBlock->ByteLength); } } // Grab the appropriate time field based on the counter definition if (bFound) { if (pPerfCtrDef->CounterType & PERF_TIMER_100NS) { *pTime = PerfData->PerfTime100nSec.QuadPart; } else { // Unverified *pTime = PerfData->PerfTime.QuadPart; } // Get a pointer to the data, then copy in the correct number of bytes (based on counter def) pbCounterData = (unsigned __int64 *)(((PBYTE) pPerfCtrBlock ) + dwCounterOffset); if (pPerfCtrDef->CounterType & PERF_SIZE_DWORD) { memcpy(pbData, pbCounterData, 4); } else if (pPerfCtrDef->CounterType & PERF_SIZE_LARGE) { memcpy(pbData, pbCounterData, 8); } } } // If Counter Definition found } // If Object found } // If memory allocated catch ( ... ) { delete [] PerfData ; throw ; } } // Free up any transient memory if ( NULL != PerfData ) { delete [] PerfData ; } ASSERT_BREAK(bFound); return bFound; } #endif