/*++ BUILD Version: 0001 // Increment this if a change has global effects Copyright (c) 1992-1994 Microsoft Corporation Module Name: perflib.c Abstract: This file implements the Configuration Registry for the purposes of the Performance Monitor. This file contains the code which implements the Performance part of the Configuration Registry. Author: Russ Blake 11/15/91 Revision History: 04/20/91 - russbl - Converted to lib in Registry from stand-alone .dll form. 11/04/92 - a-robw - added pagefile and image counter routines 11/01/96 - bobw - revamped to support dynamic loading and unloading of performance modules --*/ #define UNICODE // // Include files // #pragma warning(disable:4306) #include #include #include #include #include #include #include #include #include #include #include "regrpc.h" #include "ntconreg.h" #include "prflbmsg.h" // event log messages #include #include #include #define _INIT_WINPERFP_ #include "perflib.h" #pragma warning (default:4306) #define NUM_VALUES 2 // // performance gathering thead priority // #define DEFAULT_THREAD_PRIORITY THREAD_BASE_PRIORITY_LOWRT // // constants // const WCHAR DLLValue[] = L"Library"; const CHAR OpenValue[] = "Open"; const CHAR CloseValue[] = "Close"; const CHAR CollectValue[] = "Collect"; const CHAR QueryValue[] = "Query"; const WCHAR ObjListValue[] = L"Object List"; const WCHAR LinkageKey[] = L"\\Linkage"; const WCHAR ExportValue[] = L"Export"; const WCHAR PerflibKey[] = L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib"; const WCHAR HKLMPerflibKey[] = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib"; const WCHAR CounterValue[] = L"Counter"; const WCHAR HelpValue[] = L"Help"; const WCHAR PerfSubKey[] = L"\\Performance"; const WCHAR ExtPath[] = L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services"; const WCHAR OpenTimeout[] = L"Open Timeout"; const WCHAR CollectTimeout[] = L"Collect Timeout"; const WCHAR EventLogLevel[] = L"EventLogLevel"; const WCHAR ExtCounterTestLevel[] = L"ExtCounterTestLevel"; const WCHAR OpenProcedureWaitTime[] = L"OpenProcedureWaitTime"; const WCHAR TotalInstanceName[] = L"TotalInstanceName"; const WCHAR LibraryUnloadTime[] = L"Library Unload Time"; const WCHAR KeepResident[] = L"Keep Library Resident"; const WCHAR NULL_STRING[] = L"\0"; // pointer to null string const WCHAR UseCollectionThread[] = L"UseCollectionThread"; const WCHAR cszLibraryValidationData[] = L"Library Validation Code"; const WCHAR cszSuccessfulFileData[] = L"Successful File Date"; const WCHAR cszPerflibFlags[] = L"Configuration Flags"; const WCHAR FirstCounter[] = L"First Counter"; const WCHAR LastCounter[] = L"Last Counter"; const WCHAR FirstHelp[] = L"First Help"; const WCHAR LastHelp[] = L"Last Help"; const WCHAR cszFailureCount[] = L"Error Count"; const WCHAR cszFailureLimit[] = L"Error Count Limit"; const WCHAR cszBusy[] = L"Updating"; // // external variables defined in perfname.c // extern WCHAR DefaultLangId[]; WCHAR NativeLangId[8] = L"\0"; // // Data collection thread variables // #define COLLECTION_WAIT_TIME 10000L // 10 seconds to get all the data HANDLE hCollectThread = NULL; #define COLLECT_THREAD_PROCESS_EVENT 0 #define COLLECT_THREAD_EXIT_EVENT 1 #define COLLECT_THREAD_LOOP_EVENT_COUNT 2 #define COLLECT_THREAD_DONE_EVENT 2 #define COLLECT_THREAD_EVENT_COUNT 3 HANDLE hCollectEvents[COLLECT_THREAD_EVENT_COUNT]; BOOL bThreadHung = FALSE; DWORD CollectThreadFunction (LPVOID dwArg); #define COLL_FLAG_USE_SEPARATE_THREAD 1 DWORD dwCollectionFlags = 0; // // Global variable Definitions // // event log handle for perflib generated errors // HANDLE hEventLog = NULL; // // used to count concurrent opens. // LONG NumberOfOpens = 0; // // Synchronization objects for Multi-threaded access // HANDLE hGlobalDataMutex = NULL; // sync for ctr object list // // computer name cache buffers. Initialized in predefh.c // DWORD ComputerNameLength; LPWSTR pComputerName = NULL; // The next pointer is used to point to an array of addresses of // Open/Collect/Close routines found by searching the Configuration Registry. // object list head PEXT_OBJECT ExtensibleObjects = NULL; // // count of active list users (threads) DWORD dwExtObjListRefCount = 0; // // event to indicate the object list is not in use HANDLE hExtObjListIsNotInUse = NULL; // // Number of Extensible Objects found during the "open" call DWORD NumExtensibleObjects = 0; // // see if the perflib data is restricted to ADMIN's ONLY or just anyone // LONG lCheckProfileSystemRight = CPSR_NOT_DEFINED; // // flag to see if the ProfileSystemPerformance priv should be set. // if it is attempted and the caller does not have permission to use this priv. // it won't be set. This is only attempted once. // BOOL bEnableProfileSystemPerfPriv = FALSE; // // timeout value (in mS) for timing threads & libraries // DWORD dwThreadAndLibraryTimeout = PERFLIB_TIMING_THREAD_TIMEOUT; // global key for access to HKLM\Software\....\Perflib // HKEY ghKeyPerflib = NULL; // // Error report frequency DWORD dwErrorFrequency = 1; LONG lEventLogLevel = LOG_USER; LONG lPerflibConfigFlags = PLCF_DEFAULT; DWORD dwErrorCount = 0; ERROR_LOG PerfpErrorLog; // performance data block entries WCHAR szPerflibSectionFile[MAX_PATH]; WCHAR szPerflibSectionName[MAX_PATH]; WCHAR szUpdatingServiceName[MAX_PATH]; HANDLE hPerflibSectionFile = NULL; HANDLE hPerflibSectionMap = NULL; LPVOID lpPerflibSectionAddr = NULL; BOOL bPerflibOpen = FALSE; DWORD dwBoostPriority = 1; #define dwPerflibSectionMaxEntries 127L const DWORD dwPerflibSectionSize = (sizeof(PERFDATA_SECTION_HEADER) + \ (sizeof(PERFDATA_SECTION_RECORD) * dwPerflibSectionMaxEntries)); // forward function references LONG PerfEnumTextValue ( IN HKEY hKey, IN DWORD dwIndex, OUT PUNICODE_STRING lpValueName, OUT LPDWORD lpReserved OPTIONAL, OUT LPDWORD lpType OPTIONAL, OUT LPBYTE lpData, IN OUT LPDWORD lpcbData, OUT LPDWORD lpcbLen OPTIONAL ); #if 0 // collection thread functions are not supported DWORD OpenCollectionThread ( ) { BOOL bError = FALSE; DWORD dwThreadID; assert (hCollectThread == NULL); // if it's already created, then just return if (hCollectThread != NULL) return ERROR_SUCCESS; bThreadHung = FALSE; hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] = CreateEvent ( NULL, // default security FALSE, // auto reset FALSE, // non-signaled NULL); // no name bError = hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] == NULL; assert (hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] != NULL); hCollectEvents[COLLECT_THREAD_EXIT_EVENT] = CreateEvent ( NULL, // default security FALSE, // auto reset FALSE, // non-signaled NULL); // no name bError = (hCollectEvents[COLLECT_THREAD_EXIT_EVENT] == NULL) | bError; assert (hCollectEvents[COLLECT_THREAD_EXIT_EVENT] != NULL); hCollectEvents[COLLECT_THREAD_DONE_EVENT] = CreateEvent ( NULL, // default security FALSE, // auto reset FALSE, // non-signaled NULL); // no name bError = (hCollectEvents[COLLECT_THREAD_DONE_EVENT] == NULL) | bError; assert (hCollectEvents[COLLECT_THREAD_DONE_EVENT] != NULL); if (!bError) { // create data collection thread hCollectThread = CreateThread ( NULL, // default security 0, // default stack size (LPTHREAD_START_ROUTINE)CollectThreadFunction, NULL, // no argument 0, // no flags &dwThreadID); // we don't need the ID so it's in an automatic variable if (hCollectThread == NULL) { bError = TRUE; } assert (hCollectThread != NULL); } if (bError) { if (hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] != NULL) { CloseHandle (hCollectEvents[COLLECT_THREAD_PROCESS_EVENT]); hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] = NULL; } if (hCollectEvents[COLLECT_THREAD_EXIT_EVENT] != NULL) { CloseHandle (hCollectEvents[COLLECT_THREAD_EXIT_EVENT]); hCollectEvents[COLLECT_THREAD_EXIT_EVENT] = NULL; } if (hCollectEvents[COLLECT_THREAD_DONE_EVENT] != NULL) { CloseHandle (hCollectEvents[COLLECT_THREAD_DONE_EVENT] = NULL); hCollectEvents[COLLECT_THREAD_DONE_EVENT] = NULL; } if (hCollectThread != NULL) { CloseHandle (hCollectThread); hCollectThread = NULL; } return (GetLastError()); } else { return ERROR_SUCCESS; } } DWORD CloseCollectionThread ( ) { if (hCollectThread != NULL) { // close the data collection thread if (bThreadHung) { // then kill it the hard way // this might cause problems, but it's better than // a thread leak TerminateThread (hCollectThread, ERROR_TIMEOUT); } else { // then ask it to leave SetEvent (hCollectEvents[COLLECT_THREAD_EXIT_EVENT]); } // wait for thread to leave WaitForSingleObject (hCollectThread, COLLECTION_WAIT_TIME); // close the handles and clear the variables CloseHandle (hCollectThread); hCollectThread = NULL; CloseHandle (hCollectEvents[COLLECT_THREAD_PROCESS_EVENT]); hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] = NULL; CloseHandle (hCollectEvents[COLLECT_THREAD_EXIT_EVENT]); hCollectEvents[COLLECT_THREAD_EXIT_EVENT] = NULL; CloseHandle (hCollectEvents[COLLECT_THREAD_DONE_EVENT]); hCollectEvents[COLLECT_THREAD_DONE_EVENT] = NULL; } else { // nothing was opened } return ERROR_SUCCESS; } #endif DWORD PerfOpenKey ( IN HKEY hKey ) { LARGE_INTEGER liPerfDataWaitTime; PLARGE_INTEGER pTimeout; NTSTATUS status = STATUS_SUCCESS; DWORD dwFnStatus = ERROR_SUCCESS; // status code to be returned DWORD dwError = ERROR_SUCCESS; DWORD dwType, dwSize, dwValue; HANDLE hDataMutex; OSVERSIONINFOEXW OsVersion; if (hGlobalDataMutex == NULL) { hDataMutex = CreateMutex(NULL, FALSE, NULL); if (hDataMutex == NULL) { DebugPrint((0, "Perf Data Mutex Not Initialized\n")); goto OPD_Error_Exit_NoSemaphore; } if (InterlockedCompareExchangePointer( &hGlobalDataMutex, hDataMutex, NULL) != NULL) { CloseHandle(hDataMutex); // mutex just got created by another thread hDataMutex = NULL; } } if ((dwThreadAndLibraryTimeout == 0) || (dwThreadAndLibraryTimeout == INFINITE)) { pTimeout = NULL; } else { liPerfDataWaitTime.QuadPart = MakeTimeOutValue(dwThreadAndLibraryTimeout); pTimeout = &liPerfDataWaitTime; } status = NtWaitForSingleObject ( hGlobalDataMutex, // Mutex FALSE, // not alertable pTimeout); // wait time if (status != STATUS_SUCCESS) { // unable to contine, return error; dwFnStatus = PerfpDosError(status); DebugPrint((0, "Status=%X in waiting for global mutex", status)); goto OPD_Error_Exit_NoSemaphore; } // if here, then the data semaphore has been acquired by this thread if (InterlockedIncrement(& NumberOfOpens) == 1) { if (ghKeyPerflib == NULL) { HKEY lhKeyPerflib = NULL; dwFnStatus = (DWORD) RegOpenKeyExW(HKEY_LOCAL_MACHINE, HKLMPerflibKey, 0L, KEY_READ, & lhKeyPerflib); if (dwFnStatus != ERROR_SUCCESS) { DebugPrint((0, "Error=%d in RegOpenKeyExW call (%d)", dwFnStatus, __LINE__)); goto OPD_Error_Exit_NoSemaphore; } else { if (InterlockedCompareExchangePointer(& ghKeyPerflib, lhKeyPerflib, NULL) != NULL) { RegCloseKey(lhKeyPerflib); lhKeyPerflib = NULL; } } } assert (ghKeyPerflib != NULL); // check if we are in the middle of Lodctr/unlodctr. If so, don't open the performance data stuff. // dwSize = MAX_PATH * sizeof(WCHAR); dwType = 0; ZeroMemory(szUpdatingServiceName, dwSize); dwFnStatus = PrivateRegQueryValueExW(ghKeyPerflib, cszBusy, NULL, & dwType, (LPBYTE) szUpdatingServiceName, & dwSize); if (dwFnStatus == ERROR_SUCCESS) { // someone is running lodctr/unlodctr, bail out now. // InterlockedDecrement(& NumberOfOpens); if (hGlobalDataMutex != NULL) { ReleaseMutex(hGlobalDataMutex); } dwFnStatus = ERROR_SUCCESS; goto OPD_Error_Exit_NoSemaphore; } dwSize = sizeof(dwValue); dwValue = dwType = 0; dwFnStatus = PrivateRegQueryValueExW ( ghKeyPerflib, DisablePerformanceCounters, NULL, &dwType, (LPBYTE)&dwValue, &dwSize); if ((dwFnStatus == ERROR_SUCCESS) && (dwType == REG_DWORD) && (dwValue == 1)) { // then DON'T Load any libraries and unload any that have been // loaded InterlockedDecrement(&NumberOfOpens); // since it didn't open. dwFnStatus = ERROR_SERVICE_DISABLED; } else { dwFnStatus = ERROR_SUCCESS; ComputerNameLength = 0; GetComputerNameW(pComputerName, &ComputerNameLength); ComputerNameLength++; // account for the NULL terminator pComputerName = ALLOCMEM(ComputerNameLength * sizeof(WCHAR)); if (pComputerName == NULL) { ComputerNameLength = 0; } else { if ( !GetComputerNameW(pComputerName, &ComputerNameLength) ) { // // Signal failure to data collection routine // ComputerNameLength = 0; } else { pComputerName[ComputerNameLength] = UNICODE_NULL; ComputerNameLength = (ComputerNameLength+1) * sizeof(WCHAR); } } WinPerfStartTrace(ghKeyPerflib); // create event and indicate the list is busy hExtObjListIsNotInUse = CreateEvent (NULL, TRUE, FALSE, NULL); // read collection thread flag dwType = 0; dwSize = sizeof(DWORD); dwError = PrivateRegQueryValueExW (ghKeyPerflib, cszPerflibFlags, NULL, &dwType, (LPBYTE)&lPerflibConfigFlags, &dwSize); if ((dwError == ERROR_SUCCESS) && (dwType == REG_DWORD)) { // then keep it } else { // apply default value lPerflibConfigFlags = PLCF_DEFAULT; } // // Create global section for perf data on perflibs // NOTE: This is optional only // if ((hPerflibSectionFile == NULL) && (lPerflibConfigFlags & PLCF_ENABLE_PERF_SECTION)) { PPERFDATA_SECTION_HEADER pHead; WCHAR szPID[32]; HRESULT hErr; size_t nDestSize, nCharsLeft; PWCHAR szSectionName, szTail; dwError = ERROR_SUCCESS; // create section name nDestSize = MAX_PATH; _ultow ((ULONG)GetCurrentProcessId(), szPID, 16); // create filename szSectionName = &szPerflibSectionName[0]; hErr = StringCchCopyExW(szSectionName, nDestSize, (LPCWSTR)L"%TEMP%\\Perflib_Perfdata_", &szTail, &nCharsLeft, STRSAFE_NULL_ON_FAILURE); if (SUCCEEDED(hErr)) { szSectionName = szTail; nDestSize = nCharsLeft; hErr = StringCchCopyExW(szSectionName, nDestSize, szPID, &szTail, &nCharsLeft, STRSAFE_NULL_ON_FAILURE); } if (SUCCEEDED(hErr)) { szSectionName = szTail; nDestSize = nCharsLeft; hErr = StringCchCopyExW(szSectionName, nDestSize, (LPCWSTR)L".dat", &szTail, &nCharsLeft, STRSAFE_NULL_ON_FAILURE); } if (SUCCEEDED(hErr)) { nDestSize = ExpandEnvironmentStrings (szPerflibSectionName, szPerflibSectionFile, MAX_PATH); if ((nDestSize == 0) || (nDestSize > MAX_PATH)) { dwError = ERROR_MORE_DATA; } } else { dwError = ERROR_MORE_DATA; } if (dwError == ERROR_SUCCESS) { hPerflibSectionFile = CreateFile (szPerflibSectionFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_TEMPORARY, NULL); } if ((hPerflibSectionFile != INVALID_HANDLE_VALUE) && (hPerflibSectionFile != NULL)) { // create file mapping object hPerflibSectionMap = CreateFileMapping ( hPerflibSectionFile, NULL, PAGE_READWRITE, 0, dwPerflibSectionSize, szPerflibSectionName); if (hPerflibSectionMap != NULL) { // map view of file lpPerflibSectionAddr = MapViewOfFile ( hPerflibSectionMap, FILE_MAP_WRITE, 0,0, dwPerflibSectionSize); if (lpPerflibSectionAddr != NULL) { // init section if not already pHead = (PPERFDATA_SECTION_HEADER)lpPerflibSectionAddr; if (pHead->dwInitSignature != PDSH_INIT_SIG) { // then init // clear file to 0 memset (pHead, 0, dwPerflibSectionSize); pHead->dwEntriesInUse = 0; pHead->dwMaxEntries = dwPerflibSectionMaxEntries; pHead->dwMissingEntries = 0; pHead->dwInitSignature = PDSH_INIT_SIG; } else { // already initialized so leave it } } else { // unable to map file so close TRACE((WINPERF_DBG_TRACE_WARNING), (&PerflibGuid, __LINE__, PERF_OPEN_KEY, 0, 0, NULL)); CloseHandle (hPerflibSectionMap); hPerflibSectionMap = NULL; CloseHandle (hPerflibSectionFile); hPerflibSectionFile = NULL; } } else { // unable to create file mapping so close file TRACE((WINPERF_DBG_TRACE_WARNING), (&PerflibGuid, __LINE__, PERF_OPEN_KEY, 0, 0, NULL)); CloseHandle (hPerflibSectionFile); hPerflibSectionFile = NULL; } } else { // unable to open file so no perf stats available TRACE((WINPERF_DBG_TRACE_WARNING), (&PerflibGuid, __LINE__, PERF_OPEN_KEY, 0, 0, NULL)); hPerflibSectionFile = NULL; } } // find and open perf counters OpenExtensibleObjects(); bPerflibOpen = TRUE; dwExtObjListRefCount = 0; SetEvent (hExtObjListIsNotInUse); // indicate the list is not busy // read collection thread flag dwType = 0; dwSize = sizeof(DWORD); dwError = PrivateRegQueryValueExW (ghKeyPerflib, UseCollectionThread, NULL, &dwType, (LPBYTE)&dwCollectionFlags, &dwSize); if ((dwError == ERROR_SUCCESS) && (dwType == REG_DWORD)) { // validate the answer switch (dwCollectionFlags) { case 0: // this is a valid value break; case COLL_FLAG_USE_SEPARATE_THREAD: // this feature is not supported so skip through default: // this is for invalid values dwCollectionFlags = 0; // dwCollectionFlags = COLL_FLAG_USE_SEPARATE_THREAD; break; } } if (dwError != ERROR_SUCCESS) { dwCollectionFlags = 0; // dwCollectionFlags = COLL_FLAG_USE_SEPARATE_THREAD; } if (dwCollectionFlags == COLL_FLAG_USE_SEPARATE_THREAD) { // create data collection thread // a seperate thread is required for COM/OLE compatibity as some // client threads may be COM initialized incorrectly for the // extensible counter DLL's that may be called // status = OpenCollectionThread (); } else { hCollectEvents[COLLECT_THREAD_PROCESS_EVENT] = NULL; hCollectEvents[COLLECT_THREAD_EXIT_EVENT] = NULL; hCollectEvents[COLLECT_THREAD_DONE_EVENT] = NULL; hCollectThread = NULL; } dwError = ERROR_SUCCESS; } RtlZeroMemory(&OsVersion, sizeof(OsVersion)); OsVersion.dwOSVersionInfoSize = sizeof(OsVersion); status = RtlGetVersion((POSVERSIONINFOW) &OsVersion); if (NT_SUCCESS(status)) { if (OsVersion.wProductType == VER_NT_WORKSTATION) { dwBoostPriority = 0; } } } if ((hKey != HKEY_PERFORMANCE_DATA) && (dwFnStatus != ERROR_SERVICE_DISABLED)) { InterlockedDecrement(&NumberOfOpens); } // KdPrint(("PERFLIB: [Open] Pid: %d, Number Of PerflibHandles: %d\n", // GetCurrentProcessId(), NumberOfOpens)); if (hGlobalDataMutex != NULL) ReleaseMutex (hGlobalDataMutex); OPD_Error_Exit_NoSemaphore: TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_OPEN_KEY, 0, status, &NumberOfOpens, sizeof(NumberOfOpens), NULL)); return dwFnStatus; } LONG PerfRegQueryValue ( IN HKEY hKey, IN PUNICODE_STRING lpValueName, OUT LPDWORD lpReserved OPTIONAL, OUT LPDWORD lpType OPTIONAL, OUT LPBYTE lpData, OUT LPDWORD lpcbData, OUT LPDWORD lpcbLen OPTIONAL ) /*++ PerfRegQueryValue - Get data Inputs: hKey - Predefined handle to open remote machine lpValueName - Name of the value to be returned; could be "ForeignComputer: or perhaps some other objects, separated by ~; must be Unicode string lpReserved - should be omitted (NULL) lpType - should be omitted (NULL) lpData - pointer to a buffer to receive the performance data lpcbData - pointer to a variable containing the size in bytes of the output buffer; on output, will receive the number of bytes actually returned lpcbLen - Return the number of bytes to transmit to the client (used by RPC) (optional). Return Value: DOS error code indicating status of call or ERROR_SUCCESS if all ok --*/ { DWORD dwQueryType; // type of request DWORD TotalLen; // Length of the total return block DWORD Win32Error; // Failure code DWORD lFnStatus = ERROR_SUCCESS; // Win32 status to return to caller DWORD dwcbData = 0; // Content of *lpcbData DWORD dwcbLen = 0; // Content of *lpcbLen LPVOID pDataDefinition; // Pointer to next object definition UNICODE_STRING usLocalValue = {0,0, NULL}; PERF_DATA_BLOCK *pPerfDataBlock = (PERF_DATA_BLOCK *)lpData; LARGE_INTEGER liQueryWaitTime ; THREAD_BASIC_INFORMATION tbiData; LONG lOldPriority, lNewPriority; NTSTATUS status = STATUS_SUCCESS; LPWSTR lpLangId = NULL; DBG_UNREFERENCED_PARAMETER(lpReserved); HEAP_PROBE(); lOldPriority = lNewPriority = -1; // make a local copy of the value string if the arg references // the static buffer since it can be overwritten by // some of the RegistryEventSource call made by this routine pDataDefinition = NULL; if (lpValueName != NULL) { if (lpValueName->Buffer == NULL) { lFnStatus = ERROR_INVALID_PARAMETER; goto PRQV_ErrorExit1; } if (lpValueName == &NtCurrentTeb( )->StaticUnicodeString) { if (RtlCreateUnicodeString ( &usLocalValue, lpValueName->Buffer)) { lFnStatus = ERROR_SUCCESS; } else { // unable to create string lFnStatus = ERROR_INVALID_PARAMETER; } } else { // copy the arg to the local structure try { memcpy (&usLocalValue, lpValueName, sizeof(UNICODE_STRING)); } except (EXCEPTION_EXECUTE_HANDLER) { lFnStatus = GetExceptionCode(); } } } else { lFnStatus = ERROR_INVALID_PARAMETER; goto PRQV_ErrorExit1; } if (lFnStatus != ERROR_SUCCESS) { goto PRQV_ErrorExit1; } if (hGlobalDataMutex == NULL || bPerflibOpen == FALSE) { // if a Mutex was not allocated then the key needs to be opened. // Without synchronization, it's too easy for threads to get // tangled up lFnStatus = PerfOpenKey(hKey); if (lFnStatus == ERROR_SUCCESS) { if (!TestClientForAccess ()) { if (THROTTLE_PERFLIB(PERFLIB_ACCESS_DENIED)) { LPTSTR szMessageArray[2]; TCHAR szUserName[128]; TCHAR szModuleName[MAX_PATH]; DWORD dwUserNameLength; dwUserNameLength = sizeof(szUserName)/sizeof(TCHAR); if (!GetUserName (szUserName, &dwUserNameLength)) { szUserName[0] = 0; } if (!GetModuleFileName (NULL, szModuleName, sizeof(szModuleName)/sizeof(TCHAR))) { szModuleName[0] = 0; } szMessageArray[0] = szUserName; szMessageArray[1] = szModuleName; ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_ACCESS_DENIED, // event, NULL, // SID (not used), 2, // number of strings 0, // sizeof raw data szMessageArray, // message text array NULL); // raw data } lFnStatus = ERROR_ACCESS_DENIED; TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, lFnStatus, NULL)); } } } if (lFnStatus != ERROR_SUCCESS) { // goto the exit point goto PRQV_ErrorExit1; } if (dwBoostPriority != 0) { status = NtQueryInformationThread ( NtCurrentThread(), ThreadBasicInformation, &tbiData, sizeof(tbiData), NULL); if (NT_SUCCESS(status)) { lOldPriority = tbiData.Priority; } else { TRACE((WINPERF_DBG_TRACE_WARNING), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL)); lOldPriority = -1; } lNewPriority = DEFAULT_THREAD_PRIORITY; // perfmon's favorite priority // // Only RAISE the priority here. Don't lower it if it's high // if ((lOldPriority > 0) && (lOldPriority < lNewPriority)) { status = NtSetInformationThread( NtCurrentThread(), ThreadPriority, &lNewPriority, sizeof(lNewPriority) ); if (!NT_SUCCESS(status)) { TRACE((WINPERF_DBG_TRACE_WARNING), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL)); lOldPriority = -1; } } else { lOldPriority = -1; // to save resetting at the end } } // // Set the length parameter to zero so that in case of an error, // nothing will be transmitted back to the client and the client won't // attempt to unmarshall anything. // dwcbData = 0; dwcbLen = 0; try { if( ARGUMENT_PRESENT( lpcbLen )) { *lpcbLen = 0; } if( lpcbData != NULL ) { dwcbData = *lpcbData; } } except (EXCEPTION_EXECUTE_HANDLER) { lFnStatus = Win32Error = GetExceptionCode(); } // if here, then assume the caller has the necessary access /* determine query type, can be one of the following Global get all objects List get objects in list (usLocalValue) Foreign Computer call extensible Counter Routine only Costly costly object items Counter get counter names for the specified language Id Help get help names for the specified language Id */ dwQueryType = GetQueryType (usLocalValue.Buffer); TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, dwQueryType, NULL)); if (dwQueryType == QUERY_COUNTER || dwQueryType == QUERY_HELP || dwQueryType == QUERY_ADDCOUNTER || dwQueryType == QUERY_ADDHELP ) { liQueryWaitTime.QuadPart = MakeTimeOutValue(QUERY_WAIT_TIME); status = NtWaitForSingleObject ( hGlobalDataMutex, // semaphore FALSE, // not alertable &liQueryWaitTime); // wait 'til timeout if (status != STATUS_SUCCESS) { lFnStatus = ERROR_BUSY; Win32Error = ERROR_BUSY; TotalLen = dwcbData; TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL)); } else { try { TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL)); if (hKey == HKEY_PERFORMANCE_DATA) { lpLangId = NULL; } else if (hKey == HKEY_PERFORMANCE_TEXT) { lpLangId = DefaultLangId; } else if (hKey == HKEY_PERFORMANCE_NLSTEXT) { RtlZeroMemory(NativeLangId, 8 * sizeof(WCHAR)); lpLangId = &NativeLangId[0]; PerfGetLangId(NativeLangId); } status = PerfGetNames ( dwQueryType, &usLocalValue, lpData, lpcbData, lpcbLen, lpLangId); TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, &hKey, sizeof(hKey), NULL)); if (! NT_SUCCESS(status) && (hKey == HKEY_PERFORMANCE_NLSTEXT)) { // Sublanguage doesn't exist, so try the real one // TRACE((WINPERF_DBG_TRACE_WARNING), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL)); RtlZeroMemory(NativeLangId, 8 * sizeof(WCHAR)); PerfGetPrimaryLangId(GetUserDefaultUILanguage(), NativeLangId); if (lpcbData != NULL) * lpcbData = dwcbData; if (lpcbLen != NULL) * lpcbLen = dwcbLen; status = PerfGetNames ( dwQueryType, &usLocalValue, lpData, lpcbData, lpcbLen, lpLangId); } if (!NT_SUCCESS(status)) { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL)); // convert error to win32 for return } lFnStatus = PerfpDosError(status); if (ARGUMENT_PRESENT (lpType)) { // test for optional value *lpType = REG_MULTI_SZ; } } except (EXCEPTION_EXECUTE_HANDLER) { lFnStatus = Win32Error = GetExceptionCode(); } ReleaseMutex (hGlobalDataMutex); } } else { // define info block for data collection COLLECT_THREAD_DATA CollectThreadData = {0, NULL, NULL, NULL, NULL, NULL, 0, 0}; liQueryWaitTime.QuadPart = MakeTimeOutValue(QUERY_WAIT_TIME); status = NtWaitForSingleObject ( hGlobalDataMutex, // semaphore FALSE, // not alertable &liQueryWaitTime); // wait 'til timeout if (status != STATUS_SUCCESS) { lFnStatus = ERROR_BUSY; Win32Error = ERROR_BUSY; TotalLen = dwcbData; TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL)); } else { TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, status, NULL)); // // Format Return Buffer: start with basic data block // TotalLen = sizeof(PERF_DATA_BLOCK) + ((CNLEN+sizeof(UNICODE_NULL))*sizeof(WCHAR)); if ( dwcbData < TotalLen ) { Win32Error = ERROR_MORE_DATA; TRACE((WINPERF_DBG_TRACE_ERROR), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, TotalLen, dwcbData, sizeof(DWORD), NULL)); } else { // foreign data provider will return the perf data header Win32Error = ERROR_SUCCESS; try { if (dwQueryType == QUERY_FOREIGN) { // reset the values to avoid confusion // *lpcbData = 0; // 0 bytes (removed to enable foreign computers) if (lpData == NULL) { Win32Error = ERROR_MORE_DATA; } else { pDataDefinition = (LPVOID) lpData; memset(lpData, 0, sizeof(PERF_DATA_BLOCK)); // clear out header } } else { if (pPerfDataBlock == NULL) { // this is actually lpData Win32Error = ERROR_MORE_DATA; } else { MonBuildPerfDataBlock(pPerfDataBlock, (PVOID *) &pDataDefinition, 0, PROCESSOR_OBJECT_TITLE_INDEX); } } } except (EXCEPTION_EXECUTE_HANDLER) { Win32Error = GetExceptionCode(); } if (Win32Error == ERROR_SUCCESS) { CollectThreadData.dwQueryType = dwQueryType; CollectThreadData.lpValueName = usLocalValue.Buffer, CollectThreadData.lpData = lpData; CollectThreadData.lpcbData = lpcbData; CollectThreadData.lppDataDefinition = &pDataDefinition; CollectThreadData.pCurrentExtObject = NULL; CollectThreadData.lReturnValue = ERROR_SUCCESS; CollectThreadData.dwActionFlags = CTD_AF_NO_ACTION; if (hCollectThread == NULL) { // then call the function directly and hope for the best Win32Error = QueryExtensibleData ( &CollectThreadData); } else { // collect the data in a separate thread // load the args // set event to get things going SetEvent (hCollectEvents[COLLECT_THREAD_PROCESS_EVENT]); // now wait for the thread to return Win32Error = WaitForSingleObject ( hCollectEvents[COLLECT_THREAD_DONE_EVENT], COLLECTION_WAIT_TIME); if (Win32Error == WAIT_TIMEOUT) { bThreadHung = TRUE; // log error TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, Win32Error, NULL)); if (THROTTLE_PERFLIB (PERFLIB_COLLECTION_HUNG)) { LPSTR szMessageArray[2]; WORD wStringIndex; // load data for eventlog message wStringIndex = 0; if (CollectThreadData.pCurrentExtObject != NULL) { szMessageArray[wStringIndex++] = CollectThreadData.pCurrentExtObject->szCollectProcName; } else { szMessageArray[wStringIndex++] = "Unknown"; } ReportEventA (hEventLog, EVENTLOG_ERROR_TYPE, // error type 0, // category (not used) (DWORD)PERFLIB_COLLECTION_HUNG, // event, NULL, // SID (not used), wStringIndex, // number of strings 0, // sizeof raw data szMessageArray, // message text array NULL); // raw data } DisablePerfLibrary(CollectThreadData.pCurrentExtObject, PERFLIB_DISABLE_ALL); // DebugPrint((0, "Collection thread is hung in %s\n", // CollectThreadData.pCurrentExtObject->szCollectProcName != NULL ? // CollectThreadData.pCurrentExtObject->szCollectProcName : "Unknown")); // and then wait forever for the thread to return // this is done to prevent the function from returning // while the collection thread is using the buffer // passed in by the calling function and causing // all kind of havoc should the buffer be changed and/or // deleted and then have the thread continue for some reason Win32Error = WaitForSingleObject ( hCollectEvents[COLLECT_THREAD_DONE_EVENT], INFINITE); } bThreadHung = FALSE; // in case it was true, but came out // here the thread has returned so continue on Win32Error = CollectThreadData.lReturnValue; } #if 0 if (CollectThreadData.dwActionFlags != CTD_AF_NO_ACTION) { if (CollectThreadData.dwActionFlags == CTD_AF_OPEN_THREAD) { OpenCollectionThread(); } else if (CollectThreadData.dwActionFlags == CTD_AF_CLOSE_THREAD) { CloseCollectionThread(); } else { assert (CollectThreadData.dwActionFlags != 0); } } #endif } } // if (Win32Error == ERROR_SUCCESS) ReleaseMutex (hGlobalDataMutex); } // if an error was encountered, return it if (Win32Error != ERROR_SUCCESS) { lFnStatus = Win32Error; } else { // // Final housekeeping for data return: note data size // TotalLen = (DWORD) ((PCHAR) pDataDefinition - (PCHAR) lpData); lFnStatus = ERROR_SUCCESS; try { if (lpcbData != NULL) { *lpcbData = TotalLen; } } except (EXCEPTION_EXECUTE_HANDLER) { lFnStatus = GetExceptionCode(); } pPerfDataBlock->TotalByteLength = TotalLen; } try { if (ARGUMENT_PRESENT (lpcbLen)) { // test for optional parameter *lpcbLen = TotalLen; } if (ARGUMENT_PRESENT (lpType)) { // test for optional value *lpType = REG_BINARY; } } except (EXCEPTION_EXECUTE_HANDLER) { lFnStatus = GetExceptionCode(); } } PRQV_ErrorExit1: if (dwBoostPriority != 0) { // reset thread to original priority if ((lOldPriority > 0) && (lOldPriority != lNewPriority)) { NtSetInformationThread( NtCurrentThread(), ThreadPriority, &lOldPriority, sizeof(lOldPriority) ); } } if (usLocalValue.Buffer != NULL) { // restore the value string if it was from the local static buffer // then free the local buffer if (lpValueName == &NtCurrentTeb( )->StaticUnicodeString) { USHORT Length = lpValueName->MaximumLength; if (Length > usLocalValue.MaximumLength) { Length = usLocalValue.MaximumLength; } memcpy (lpValueName->Buffer, usLocalValue.Buffer, Length); lpValueName->Buffer[(Length/sizeof(WCHAR))-1] = UNICODE_NULL; RtlFreeUnicodeString (&usLocalValue); } } HEAP_PROBE(); TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_QUERY_VALUE, 0, lFnStatus, NULL)); return (LONG) lFnStatus; } LONG PerfRegCloseKey ( IN OUT PHKEY phKey ) /*++ Routine Description: Closes all performance handles when the usage count drops to 0. Arguments: phKey - Supplies a handle to an open key to be closed. Return Value: Returns ERROR_SUCCESS (0) for success; error-code for failure. --*/ { NTSTATUS status; LARGE_INTEGER liQueryWaitTime ; HANDLE hObjMutex; LONG lReturn = ERROR_SUCCESS; HKEY hKey; PEXT_OBJECT pThisExtObj, pNextExtObj; // // Set the handle to NULL so that RPC knows that it has been closed. // hKey = *phKey; *phKey = NULL; if (hKey != HKEY_PERFORMANCE_DATA) { // no need to check HKEY_PERFORMANCE_TEXT and HKEY_PERFORMANCE_NLSTEXT. // Only HKEY_PERFORMANCE_DATA affects NumberOfOpens value. // return ERROR_SUCCESS; } if (NumberOfOpens <= 0) { // KdPrint(("PERFLIB: [Close] Pid: %d, Number Of PerflibHandles: %d\n", // GetCurrentProcessId(), NumberOfOpens)); return ERROR_SUCCESS; } // wait for ext obj list to be "un"-busy liQueryWaitTime.QuadPart = MakeTimeOutValue (CLOSE_WAIT_TIME); status = NtWaitForSingleObject ( hExtObjListIsNotInUse, FALSE, &liQueryWaitTime); if (status == STATUS_SUCCESS) { TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, status, NULL)); // then the list is inactive so continue if (hGlobalDataMutex != NULL) { // if a mutex was allocated, then use it // if here, then assume a mutex is ready liQueryWaitTime.QuadPart = MakeTimeOutValue(CLOSE_WAIT_TIME); status = NtWaitForSingleObject ( hGlobalDataMutex, // semaphore FALSE, // not alertable &liQueryWaitTime); // wait forever if (status == STATUS_SUCCESS) { TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, status, &NumberOfOpens, sizeof(NumberOfOpens), &hKey, sizeof(hKey), NULL)); // now we have a lock on the global data, so continue if (hKey == HKEY_PERFORMANCE_DATA) { if (InterlockedDecrement(&NumberOfOpens) == 0) { // walk down list of known objects and close and delete each one pNextExtObj = ExtensibleObjects; while (pNextExtObj != NULL) { // close and destroy each entry in the list pThisExtObj = pNextExtObj; hObjMutex = pThisExtObj->hMutex; status = NtWaitForSingleObject ( hObjMutex, FALSE, &liQueryWaitTime); if (status == STATUS_SUCCESS) { TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, status, pThisExtObj->szServiceName, WSTRSIZE(pThisExtObj->szServiceName), NULL)); InterlockedIncrement((LONG *)&pThisExtObj->dwLockoutCount); status = CloseExtObjectLibrary(pThisExtObj, TRUE); // close the handle to the perf subkey NtClose (pThisExtObj->hPerfKey); ReleaseMutex (hObjMutex); // release CloseHandle (hObjMutex); // and free pNextExtObj = pThisExtObj->pNext; // toss the memory for this object FREEMEM (pThisExtObj); } else { TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, status, pThisExtObj->szServiceName, WSTRSIZE(pThisExtObj->szServiceName), NULL)); // this shouldn't happen since we've locked the // list of objects pNextExtObj = pThisExtObj->pNext; } } // while // close the global objects FREEMEM(pComputerName); ComputerNameLength = 0; pComputerName = NULL; ExtensibleObjects = NULL; NumExtensibleObjects = 0; // close the timer thread DestroyPerflibFunctionTimer (); if (hEventLog != NULL) { DeregisterEventSource (hEventLog); hEventLog = NULL; } // else the event log has already been closed // release event handle CloseHandle (hExtObjListIsNotInUse); hExtObjListIsNotInUse = NULL; // CloseCollectionThread(); if (ghKeyPerflib != NULL) { RegCloseKey(ghKeyPerflib); ghKeyPerflib = NULL; } if (lpPerflibSectionAddr != NULL) { UnmapViewOfFile (lpPerflibSectionAddr); lpPerflibSectionAddr = NULL; CloseHandle (hPerflibSectionMap); hPerflibSectionMap = NULL; CloseHandle (hPerflibSectionFile); hPerflibSectionFile = NULL; } ReleaseMutex(hGlobalDataMutex); } else { // this isn't the last open call so return success assert(NumberOfOpens != 0); ReleaseMutex (hGlobalDataMutex); } } // if (hKey == HKEY_PERFORMANCE_DATA) } else { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, status, NULL)); // unable to lock the global data mutex in a timely fashion // so return lReturn = ERROR_BUSY; } } else { // if there's no mutex then something's fishy. It probably hasn't // been opened, yet. lReturn = ERROR_NOT_READY; } } else { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, status, NULL)); // the object list is still in use so return and let the // caller try again later lReturn = WAIT_TIMEOUT; } // KdPrint(("PERFLIB: [Close] Pid: %d, Number Of PerflibHandles: %d\n", // GetCurrentProcessId(), NumberOfOpens)); TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_CLOSE_KEY, 0, lReturn, &NumberOfOpens, sizeof(NumberOfOpens), NULL)); return lReturn; } LONG PerfRegSetValue ( IN HKEY hKey, IN LPWSTR lpValueName, IN DWORD Reserved, IN DWORD dwType, IN LPBYTE lpData, IN DWORD cbData ) /*++ PerfRegSetValue - Set data Inputs: hKey - Predefined handle to open remote machine lpValueName - Name of the value to be returned; could be "ForeignComputer: or perhaps some other objects, separated by ~; must be Unicode string lpReserved - should be omitted (NULL) lpType - should be REG_MULTI_SZ lpData - pointer to a buffer containing the performance name lpcbData - pointer to a variable containing the size in bytes of the input buffer; Return Value: DOS error code indicating status of call or ERROR_SUCCESS if all ok --*/ { DWORD dwQueryType; // type of request LPWSTR lpLangId = NULL; NTSTATUS status; UNICODE_STRING String; LONG lReturn = ERROR_SUCCESS; DWORD cbTmpData = cbData; UNREFERENCED_PARAMETER(dwType); UNREFERENCED_PARAMETER(Reserved); try { dwQueryType = GetQueryType (lpValueName); } except (EXCEPTION_EXECUTE_HANDLER) { lReturn = GetExceptionCode(); goto Error_exit; } TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_SET_VALUE, 0, dwQueryType, &hKey, sizeof(hKey), NULL)); // convert the query to set commands if ((dwQueryType == QUERY_COUNTER) || (dwQueryType == QUERY_ADDCOUNTER)) { dwQueryType = QUERY_ADDCOUNTER; } else if ((dwQueryType == QUERY_HELP) || (dwQueryType == QUERY_ADDHELP)) { dwQueryType = QUERY_ADDHELP; } else { lReturn = ERROR_BADKEY; goto Error_exit; } if (hKey == HKEY_PERFORMANCE_TEXT) { lpLangId = DefaultLangId; } else if (hKey == HKEY_PERFORMANCE_NLSTEXT) { lpLangId = &NativeLangId[0]; PerfGetLangId(NativeLangId); } else { lReturn = ERROR_BADKEY; goto Error_exit; } try { RtlInitUnicodeString(&String, lpValueName); status = PerfGetNames ( dwQueryType, &String, lpData, &cbData, NULL, lpLangId); if (!NT_SUCCESS(status) && (hKey == HKEY_PERFORMANCE_NLSTEXT)) { TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_SET_VALUE, 0, status, NULL)); // Sublanguage doesn't exist, so try the real one // PerfGetPrimaryLangId(GetUserDefaultUILanguage(), NativeLangId); cbData = cbTmpData; status = PerfGetNames ( dwQueryType, &String, lpData, &cbData, NULL, lpLangId); } } except (EXCEPTION_EXECUTE_HANDLER) { lReturn = GetExceptionCode(); goto Error_exit; } if (!NT_SUCCESS(status)) { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_REG_SET_VALUE, 0, status, NULL)); lReturn = (error_status_t)PerfpDosError(status); } Error_exit: TRACE((WINPERF_DBG_TRACE_INFO), (&PerflibGuid, __LINE__, PERF_REG_SET_VALUE, 0, lReturn, NULL)); return (lReturn); } LONG PerfRegEnumKey ( IN HKEY hKey, IN DWORD dwIndex, OUT PUNICODE_STRING lpName, OUT LPDWORD lpReserved OPTIONAL, OUT PUNICODE_STRING lpClass OPTIONAL, OUT PFILETIME lpftLastWriteTime OPTIONAL ) /*++ Routine Description: Enumerates keys under HKEY_PERFORMANCE_DATA. Arguments: Same as RegEnumKeyEx. Returns that there are no such keys. Return Value: Returns ERROR_SUCCESS (0) for success; error-code for failure. --*/ { UNREFERENCED_PARAMETER(hKey); UNREFERENCED_PARAMETER(dwIndex); UNREFERENCED_PARAMETER(lpReserved); try { lpName->Length = 0; if (ARGUMENT_PRESENT (lpClass)) { lpClass->Length = 0; } if ( ARGUMENT_PRESENT(lpftLastWriteTime) ) { lpftLastWriteTime->dwLowDateTime = 0; lpftLastWriteTime->dwHighDateTime = 0; } } except (EXCEPTION_EXECUTE_HANDLER) { return 0; } return ERROR_NO_MORE_ITEMS; } LONG PerfRegQueryInfoKey ( IN HKEY hKey, OUT PUNICODE_STRING lpClass, OUT LPDWORD lpReserved OPTIONAL, OUT LPDWORD lpcSubKeys, OUT LPDWORD lpcbMaxSubKeyLen, OUT LPDWORD lpcbMaxClassLen, OUT LPDWORD lpcValues, OUT LPDWORD lpcbMaxValueNameLen, OUT LPDWORD lpcbMaxValueLen, OUT LPDWORD lpcbSecurityDescriptor, OUT PFILETIME lpftLastWriteTime ) /*++ Routine Description: This returns information concerning the predefined handle HKEY_PERFORMANCE_DATA Arguments: Same as RegQueryInfoKey. Return Value: Returns ERROR_SUCCESS (0) for success. --*/ { DWORD TempLength=0; DWORD MaxValueLen=0; UNICODE_STRING Null; SECURITY_DESCRIPTOR SecurityDescriptor; HKEY hPerflibKey; OBJECT_ATTRIBUTES Obja; NTSTATUS Status; DWORD PerfStatus = ERROR_SUCCESS; UNICODE_STRING PerflibSubKeyString; BOOL bGetSACL = TRUE; UNREFERENCED_PARAMETER(lpReserved); try { if (lpClass->MaximumLength >= sizeof(UNICODE_NULL)) { lpClass->Length = 0; *lpClass->Buffer = UNICODE_NULL; } *lpcSubKeys = 0; *lpcbMaxSubKeyLen = 0; *lpcbMaxClassLen = 0; *lpcValues = NUM_VALUES; *lpcbMaxValueNameLen = VALUE_NAME_LENGTH; *lpcbMaxValueLen = 0; if ( ARGUMENT_PRESENT(lpftLastWriteTime) ) { lpftLastWriteTime->dwLowDateTime = 0; lpftLastWriteTime->dwHighDateTime = 0; } } except (EXCEPTION_EXECUTE_HANDLER) { PerfStatus = GetExceptionCode(); } if (PerfStatus == ERROR_SUCCESS) { if ((hKey == HKEY_PERFORMANCE_TEXT) || (hKey == HKEY_PERFORMANCE_NLSTEXT)) { // // We have to go enumerate the values to determine the answer for // the MaxValueLen parameter. // Null.Buffer = NULL; Null.Length = 0; Null.MaximumLength = 0; PerfStatus = PerfEnumTextValue(hKey, 0, &Null, NULL, NULL, NULL, &MaxValueLen, NULL); if (PerfStatus == ERROR_SUCCESS) { PerfStatus = PerfEnumTextValue(hKey, 1, &Null, NULL, NULL, NULL, &TempLength, NULL); } try { if (PerfStatus == ERROR_SUCCESS) { if (TempLength > MaxValueLen) { MaxValueLen = TempLength; } *lpcbMaxValueLen = MaxValueLen; } else { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_REG_QUERY_INFO_KEY, 0, PerfStatus, NULL)); // unable to successfully enum text values for this // key so return 0's and the error code *lpcValues = 0; *lpcbMaxValueNameLen = 0; } } except (EXCEPTION_EXECUTE_HANDLER) { PerfStatus = GetExceptionCode(); } } } if (PerfStatus == ERROR_SUCCESS) { // continune if all is OK // now get the size of SecurityDescriptor for Perflib key RtlInitUnicodeString ( &PerflibSubKeyString, PerflibKey); // // Initialize the OBJECT_ATTRIBUTES structure and open the key. // InitializeObjectAttributes( &Obja, &PerflibSubKeyString, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey( &hPerflibKey, MAXIMUM_ALLOWED | ACCESS_SYSTEM_SECURITY, &Obja ); if ( ! NT_SUCCESS( Status )) { Status = NtOpenKey( &hPerflibKey, MAXIMUM_ALLOWED, &Obja ); bGetSACL = FALSE; } if ( ! NT_SUCCESS( Status )) { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_REG_QUERY_INFO_KEY, 0, Status, NULL)); } else { try { *lpcbSecurityDescriptor = 0; if (bGetSACL == FALSE) { // // Get the size of the key's SECURITY_DESCRIPTOR for OWNER, GROUP // and DACL. These three are always accessible (or inaccesible) // as a set. // Status = NtQuerySecurityObject( hPerflibKey, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, &SecurityDescriptor, 0, lpcbSecurityDescriptor ); } else { // // Get the size of the key's SECURITY_DESCRIPTOR for OWNER, GROUP, // DACL, and SACL. // Status = NtQuerySecurityObject( hPerflibKey, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, &SecurityDescriptor, 0, lpcbSecurityDescriptor ); } if( Status != STATUS_BUFFER_TOO_SMALL ) { *lpcbSecurityDescriptor = 0; } else { // this is expected so set status to success Status = STATUS_SUCCESS; } } except (EXCEPTION_EXECUTE_HANDLER) { PerfStatus = GetExceptionCode(); } NtClose(hPerflibKey); } // else return status if (NT_SUCCESS( Status )) { PerfStatus = ERROR_SUCCESS; } else { TRACE((WINPERF_DBG_TRACE_FATAL), (&PerflibGuid, __LINE__, PERF_REG_QUERY_INFO_KEY, 0, Status, NULL)); // return error PerfStatus = PerfpDosError(Status); } } return (LONG) PerfStatus; } LONG PerfRegEnumValue ( IN HKEY hKey, IN DWORD dwIndex, OUT PUNICODE_STRING lpValueName, OUT LPDWORD lpReserved OPTIONAL, OUT LPDWORD lpType OPTIONAL, OUT LPBYTE lpData, IN OUT LPDWORD lpcbData, OUT LPDWORD lpcbLen OPTIONAL ) /*++ Routine Description: Enumerates Values under HKEY_PERFORMANCE_DATA. Arguments: Same as RegEnumValue. Returns the values. Return Value: Returns ERROR_SUCCESS (0) for success; error-code for failure. --*/ { USHORT cbNameSize; LONG ErrorCode; // table of names used by enum values UNICODE_STRING ValueNames[NUM_VALUES]; ValueNames [0].Length = (WORD)(lstrlenW (GLOBAL_STRING) * sizeof(WCHAR)); ValueNames [0].MaximumLength = (WORD)(ValueNames [0].Length + sizeof(UNICODE_NULL)); ValueNames [0].Buffer = (LPWSTR)GLOBAL_STRING; ValueNames [1].Length = (WORD)(lstrlenW(COSTLY_STRING) * sizeof(WCHAR)); ValueNames [1].MaximumLength = (WORD)(ValueNames [1].Length + sizeof(UNICODE_NULL)); ValueNames [1].Buffer = (LPWSTR)COSTLY_STRING; if (lpValueName == NULL || lpValueName->Buffer == NULL) { return ERROR_INVALID_PARAMETER; } if ((hKey == HKEY_PERFORMANCE_TEXT) || (hKey == HKEY_PERFORMANCE_NLSTEXT)) { // // Assumes PerfEnumTextValue will use try block // return(PerfEnumTextValue(hKey, dwIndex, lpValueName, lpReserved, lpType, lpData, lpcbData, lpcbLen)); } if ( dwIndex >= NUM_VALUES ) { // // This is a request for data from a non-existent value name // try { *lpcbData = 0; } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } return ERROR_NO_MORE_ITEMS; } cbNameSize = ValueNames[dwIndex].Length; ErrorCode = ERROR_SUCCESS; try { if ( lpValueName->MaximumLength < cbNameSize ) { ErrorCode = ERROR_MORE_DATA; } else { lpValueName->Length = cbNameSize; RtlCopyUnicodeString(lpValueName, &ValueNames[dwIndex]); if (ARGUMENT_PRESENT (lpType)) { *lpType = REG_BINARY; } } } except (EXCEPTION_EXECUTE_HANDLER) { ErrorCode = GetExceptionCode(); } if (ErrorCode == ERROR_SUCCESS) { ErrorCode = PerfRegQueryValue(hKey, lpValueName, NULL, lpType, lpData, lpcbData, lpcbLen); } return ErrorCode; } LONG PerfEnumTextValue ( IN HKEY hKey, IN DWORD dwIndex, OUT PUNICODE_STRING lpValueName, OUT LPDWORD lpReserved OPTIONAL, OUT LPDWORD lpType OPTIONAL, OUT LPBYTE lpData, IN OUT LPDWORD lpcbData, OUT LPDWORD lpcbLen OPTIONAL ) /*++ Routine Description: Enumerates Values under Perflib\lang Arguments: Same as RegEnumValue. Returns the values. Return Value: Returns ERROR_SUCCESS (0) for success; error-code for failure. --*/ { UNICODE_STRING FullValueName; LONG lReturn = ERROR_SUCCESS; // // Only two values, "Counter" and "Help" // try { if (dwIndex==0) { lpValueName->Length = 0; RtlInitUnicodeString(&FullValueName, CounterValue); } else if (dwIndex==1) { lpValueName->Length = 0; RtlInitUnicodeString(&FullValueName, HelpValue); } else { lReturn = ERROR_NO_MORE_ITEMS; } if (lReturn == ERROR_SUCCESS) { RtlCopyUnicodeString(lpValueName, &FullValueName); // // We need to NULL terminate the name to make RPC happy. // if (lpValueName->Length+sizeof(WCHAR) <= lpValueName->MaximumLength) { lpValueName->Buffer[lpValueName->Length / sizeof(WCHAR)] = UNICODE_NULL; lpValueName->Length += sizeof(UNICODE_NULL); } } } except (EXCEPTION_EXECUTE_HANDLER) { lReturn = GetExceptionCode(); } if (lReturn == ERROR_SUCCESS) { lReturn = PerfRegQueryValue(hKey, &FullValueName, lpReserved, lpType, lpData, lpcbData, lpcbLen); } return lReturn; } #if 0 DWORD CollectThreadFunction ( LPDWORD dwArg ) { DWORD dwWaitStatus = 0; BOOL bExit = FALSE; NTSTATUS status = STATUS_SUCCESS; THREAD_BASIC_INFORMATION tbiData; LONG lOldPriority, lNewPriority; LONG lStatus; UNREFERENCED_PARAMETER (dwArg); // KdPrint(("PERFLIB: Entering Data Collection Thread: PID: %d, TID: %d\n", // GetCurrentProcessId(), GetCurrentThreadId())); // raise the priority of this thread status = NtQueryInformationThread ( NtCurrentThread(), ThreadBasicInformation, &tbiData, sizeof(tbiData), NULL); if (NT_SUCCESS(status)) { lOldPriority = tbiData.Priority; lNewPriority = DEFAULT_THREAD_PRIORITY; // perfmon's favorite priority // // Only RAISE the priority here. Don't lower it if it's high // if (lOldPriority < lNewPriority) { status = NtSetInformationThread( NtCurrentThread(), ThreadPriority, &lNewPriority, sizeof(lNewPriority) ); if (status != STATUS_SUCCESS) { DebugPrint((0, "Set Thread Priority failed: 0x%8.8x\n", status)); } } } // wait for flags while (!bExit) { dwWaitStatus = WaitForMultipleObjects ( COLLECT_THREAD_LOOP_EVENT_COUNT, hCollectEvents, FALSE, // wait for ANY event to go INFINITE); // wait for ever // see why the wait returned: if (dwWaitStatus == (WAIT_OBJECT_0 + COLLECT_THREAD_PROCESS_EVENT)) { // the event is cleared automatically // collect data lStatus = QueryExtensibleData ( &CollectThreadData); CollectThreadData.lReturnValue = lStatus; SetEvent (hCollectEvents[COLLECT_THREAD_DONE_EVENT]); } else if (dwWaitStatus == (WAIT_OBJECT_0 + COLLECT_THREAD_EXIT_EVENT)) { bExit = TRUE; continue; // go up and bail out } else { // who knows, so output message KdPrint(("\nPERFLILB: Collect Thread wait returned unknown value: 0x%8.8x",dwWaitStatus)); bExit = TRUE; continue; } } // KdPrint(("PERFLIB: Leaving Data Collection Thread: PID: %d, TID: %d\n", // GetCurrentProcessId(), GetCurrentThreadId())); return ERROR_SUCCESS; } #endif BOOL PerfRegInitialize() { RtlInitializeCriticalSection(&PerfpCritSect); InitializeListHead((PLIST_ENTRY) &PerfpErrorLog); return TRUE; } BOOL PerfRegCleanup() /*++ Routine Description: Cleans up anything that perflib uses before it unloads. Assumes that there are queries or perf reg opens outstanding. Arguments: None Return Value: Returns TRUE if succeed. FALSE otherwise. --*/ { if (hGlobalDataMutex != NULL) { if (NumberOfOpens != 0) return FALSE; CloseHandle(hGlobalDataMutex); hGlobalDataMutex = NULL; } PerfpDeleteErrorLogs(&PerfpErrorLog); RtlDeleteCriticalSection(&PerfpCritSect); return TRUE; }