/*++ Copyright (C) 1995-1999 Microsoft Corporation Module Name: qutils.c Abstract: Query management utility functions --*/ #include #include "strsafe.h" #include #include "pdhitype.h" #include "pdhidef.h" #include "pdhmsg.h" #include "strings.h" #include "log_bin.h" #include "log_wmi.h" #include "perfdata.h" BOOL IsValidQuery( PDH_HQUERY hQuery ) { BOOL bReturn = FALSE; // assume it's not a valid query PPDHI_QUERY pQuery; __try { if (hQuery != NULL) { // see if a valid signature pQuery = (PPDHI_QUERY) hQuery; if ((* (DWORD *) & pQuery->signature[0] == SigQuery) && (pQuery->dwLength == sizeof(PDHI_QUERY))) { bReturn = TRUE; } } } __except (EXCEPTION_EXECUTE_HANDLER) { // something failed miserably so we can assume this is invalid } return bReturn; } BOOL AddMachineToQueryLists( PPERF_MACHINE pMachine, PPDHI_COUNTER pNewCounter ) { BOOL bReturn = FALSE; // assume failure PPDHI_QUERY pQuery; PPDHI_QUERY_MACHINE pQMachine; PPDHI_QUERY_MACHINE pLastQMachine; pQuery = pNewCounter->pOwner; if (IsValidQuery(pQuery)) { if (pQuery->pFirstQMachine != NULL) { // look for machine in list pLastQMachine = pQMachine = pQuery->pFirstQMachine; while (pQMachine != NULL) { if (pQMachine->pMachine == pMachine) { // found the machine already in the list so continue bReturn = TRUE; break; } else { pLastQMachine = pQMachine; pQMachine = pQMachine->pNext; } } if (pQMachine == NULL) { // add this machine to the end of the list pQMachine = G_ALLOC((sizeof(PDHI_QUERY_MACHINE) + (sizeof(WCHAR) * MAX_PATH))); if (pQMachine != NULL) { pQMachine->pMachine = pMachine; pQMachine->szObjectList = (LPWSTR) ( & pQMachine[1]); pQMachine->pNext = NULL; pQMachine->lQueryStatus = pMachine->dwStatus; pQMachine->llQueryTime = 0; bReturn = TRUE; // the pPerfData pointer will be tested prior to usage pQMachine->pPerfData = G_ALLOC(MEDIUM_BUFFER_SIZE); if (pQMachine->pPerfData == NULL) { G_FREE(pQMachine); pQMachine = NULL; bReturn = FALSE; SetLastError(PDH_MEMORY_ALLOCATION_FAILURE); } else { pLastQMachine->pNext = pQMachine; } } else { // unable to alloc memory block so machine cannot // be added SetLastError(PDH_MEMORY_ALLOCATION_FAILURE); } } } else { // add this as the first machine pQMachine = G_ALLOC ((sizeof(PDHI_QUERY_MACHINE) + (sizeof(WCHAR) * PDH_MAX_COUNTER_PATH))); if (pQMachine != NULL) { pQMachine->pMachine = pMachine; pQMachine->szObjectList = (LPWSTR) (& pQMachine[1]); pQMachine->pNext = NULL; pQMachine->lQueryStatus = pMachine->dwStatus; pQMachine->llQueryTime = 0; bReturn = TRUE; // the pPerfData pointer will be tested prior to usage pQMachine->pPerfData = G_ALLOC(MEDIUM_BUFFER_SIZE); if (pQMachine->pPerfData == NULL) { G_FREE(pQMachine); pQMachine = NULL; bReturn = FALSE; SetLastError(PDH_MEMORY_ALLOCATION_FAILURE); } else { pQuery->pFirstQMachine = pQMachine; } } else { // unable to alloc memory block so machine cannot // be added SetLastError(PDH_MEMORY_ALLOCATION_FAILURE); } } // here pQMachine should be the pointer to the correct machine // entry or NULL if unable to create if (pQMachine != NULL) { // save the new pointer pNewCounter->pQMachine = pQMachine; // increment reference count for this machine pMachine->dwRefCount ++; // update query perf. object list AppendObjectToValueList( pNewCounter->plCounterInfo.dwObjectId, pQMachine->szObjectList, PDH_MAX_COUNTER_PATH); } } else { SetLastError(PDH_INVALID_HANDLE); bReturn = FALSE; } return bReturn; } extern PDH_FUNCTION PdhiGetBinaryLogCounterInfo( PPDHI_LOG pLog, PPDHI_COUNTER pCounter ); extern PPERF_DATA_BLOCK PdhWmiMergeObjectBlock( PPDHI_LOG pLog, LPWSTR szMachine, PPDHI_BINARY_LOG_RECORD_HEADER pThisMasterRecord, PPDHI_BINARY_LOG_RECORD_HEADER pThisSubRecord ); PDH_FUNCTION PdhiGetCounterFromDataBlock( PPDHI_LOG pLog, PVOID pDataBuffer, PPDHI_COUNTER pCounter ) { PDH_STATUS pdhStatus = ERROR_SUCCESS; PERFLIB_COUNTER * pPerfCounter = & pCounter->plCounterInfo; PPDH_RAW_COUNTER pRawValue = & pCounter->ThisValue; LPWSTR szCompositeInstance = NULL; DWORD dwDataItemIndex; LPWSTR szThisInstanceName; PPDHI_BINARY_LOG_RECORD_HEADER pThisMasterRecord; PPDHI_BINARY_LOG_RECORD_HEADER pThisSubRecord; PPDHI_RAW_COUNTER_ITEM_BLOCK pDataBlock; PPDHI_RAW_COUNTER_ITEM pDataItem; PPDH_RAW_COUNTER pRawItem; PPERF_DATA_BLOCK pPerfData; PPERF_DATA_BLOCK pTmpBlock; FILETIME ftDataBlock; FILETIME ftGmtDataBlock; LONGLONG TimeStamp; ZeroMemory(& ftDataBlock, sizeof(FILETIME)); ZeroMemory(& ftGmtDataBlock, sizeof(FILETIME)); ZeroMemory(pRawValue, sizeof(PDH_RAW_COUNTER)); pThisMasterRecord = (PPDHI_BINARY_LOG_RECORD_HEADER) pDataBuffer; pThisSubRecord = PdhiGetSubRecord(pThisMasterRecord, pCounter->dwIndex); if (pThisSubRecord != NULL) { if (pThisSubRecord->dwType == BINLOG_TYPE_DATA_PSEUDO) { PDH_STATUS Status = ERROR_SUCCESS; DWORD dwOriginal = pCounter->dwIndex; DWORD dwPrevious; while (Status == ERROR_SUCCESS && pThisSubRecord) { if (pThisSubRecord->dwType != BINLOG_TYPE_DATA_PSEUDO) { break; } dwPrevious = pCounter->dwIndex; Status = PdhiGetBinaryLogCounterInfo(pLog, pCounter); if ( Status == ERROR_SUCCESS && dwPrevious != pCounter->dwIndex) { pThisSubRecord = PdhiGetSubRecord(pThisMasterRecord, pCounter->dwIndex); } } if (pThisSubRecord == NULL || Status == PDH_ENTRY_NOT_IN_LOG_FILE) { pCounter->dwIndex = 0; do { dwPrevious = pCounter->dwIndex; Status = PdhiGetBinaryLogCounterInfo(pLog, pCounter); if (Status == ERROR_SUCCESS && dwPrevious != pCounter->dwIndex) { pThisSubRecord = PdhiGetSubRecord(pThisMasterRecord, pCounter->dwIndex); } if (pThisSubRecord->dwType != BINLOG_TYPE_DATA_PSEUDO) { break; } } while (Status == ERROR_SUCCESS && pCounter->dwIndex < dwOriginal && pThisSubRecord); if (pThisSubRecord == NULL || pCounter->dwIndex >= dwOriginal) { Status = PDH_ENTRY_NOT_IN_LOG_FILE; } } if (Status == PDH_ENTRY_NOT_IN_LOG_FILE) { pCounter->dwIndex = dwOriginal; pThisSubRecord = PdhiGetSubRecord(pThisMasterRecord, pCounter->dwIndex); } } } if (pLog->pLastRecordRead != pDataBuffer) { pLog->pLastRecordRead = pDataBuffer; } if (pThisSubRecord != NULL) { switch (pThisSubRecord->dwType) { case BINLOG_TYPE_DATA_LOC_OBJECT: pTmpBlock = (PPERF_DATA_BLOCK) ((LPBYTE)pThisSubRecord + sizeof(PDHI_BINARY_LOG_RECORD_HEADER)); pPerfData = PdhWmiMergeObjectBlock( pLog, pCounter->pCounterPath->szMachineName, pThisMasterRecord, pThisSubRecord); SystemTimeToFileTime(& pPerfData->SystemTime, & ftDataBlock); TimeStamp = MAKELONGLONG(ftDataBlock.dwLowDateTime, ftDataBlock.dwHighDateTime); if (pCounter->dwFlags & PDHIC_MULTI_INSTANCE) { UpdateMultiInstanceCounterValue(pCounter, pPerfData, TimeStamp); } else { UpdateCounterValue(pCounter, pPerfData); pCounter->ThisValue.TimeStamp = ftDataBlock; } break; case BINLOG_TYPE_DATA_PSEUDO: case BINLOG_TYPE_DATA_SINGLE: pRawItem = (PPDH_RAW_COUNTER) ((LPBYTE)pThisSubRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); RtlCopyMemory(pRawValue, pRawItem, sizeof (PDH_RAW_COUNTER)); pdhStatus = ERROR_SUCCESS; break; case BINLOG_TYPE_DATA_MULTI: if (pCounter->dwFlags & PDHIC_MULTI_INSTANCE) { // this is a wild card query // ULONG i; ULONG CopySize = pThisSubRecord->dwLength - sizeof(PDHI_BINARY_LOG_RECORD_HEADER); PPDHI_RAW_COUNTER_ITEM_BLOCK pNewBlock = G_ALLOC(CopySize); if (pNewBlock == NULL) { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; } else if (pCounter->pThisRawItemList != NULL) { G_FREE(pCounter->pLastRawItemList); pCounter->pLastRawItemList = pCounter->pThisRawItemList; } pCounter->pThisRawItemList = pNewBlock; RtlCopyMemory(pNewBlock, (((LPBYTE) pThisSubRecord) + sizeof(PDHI_BINARY_LOG_RECORD_HEADER)), CopySize); } else if (pPerfCounter->szInstanceName != NULL) { DWORD dwInstanceId = pCounter->pCounterPath->dwIndex; DWORD dwInstanceSize = lstrlenW(pPerfCounter->szInstanceName) + 1; if (pPerfCounter->szParentInstanceName != NULL) { dwInstanceSize += lstrlenW(pPerfCounter->szParentInstanceName) + 1; } szCompositeInstance = G_ALLOC(sizeof(WCHAR) * dwInstanceSize); if (szCompositeInstance != NULL) { if (pPerfCounter->szParentInstanceName != NULL) { StringCchPrintfW(szCompositeInstance, dwInstanceSize, L"%ws/%ws", pPerfCounter->szParentInstanceName, pPerfCounter->szInstanceName); } else { StringCchCopyW(szCompositeInstance, dwInstanceSize, pPerfCounter->szInstanceName); } } else { pdhStatus = PDH_MEMORY_ALLOCATION_FAILURE; break; } pDataBlock = (PPDHI_RAW_COUNTER_ITEM_BLOCK) ((LPBYTE) pThisSubRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; pRawValue->CStatus = PDH_CSTATUS_NO_INSTANCE; for (dwDataItemIndex = 0; dwDataItemIndex < pDataBlock->dwItemCount; dwDataItemIndex++) { pDataItem = &pDataBlock->pItemArray[dwDataItemIndex]; szThisInstanceName = (LPWSTR) ((LPBYTE) pDataBlock + (DWORD_PTR) pDataItem->szName); if (lstrcmpiW(szThisInstanceName, szCompositeInstance) == 0) { if (dwInstanceId == 0) { pdhStatus = ERROR_SUCCESS; pRawValue->CStatus = pDataBlock->CStatus; pRawValue->TimeStamp = pDataBlock->TimeStamp; pRawValue->FirstValue = pDataItem->FirstValue; pRawValue->SecondValue = pDataItem->SecondValue; pRawValue->MultiCount = pDataItem->MultiCount; break; } else { dwInstanceId --; } } } } else { pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; pRawValue->CStatus = PDH_CSTATUS_INVALID_DATA; } break; default: pdhStatus = PDH_LOG_TYPE_NOT_FOUND; pRawValue->CStatus = PDH_CSTATUS_INVALID_DATA; break; } } else { pdhStatus = PDH_ENTRY_NOT_IN_LOG_FILE; pRawValue->CStatus = PDH_CSTATUS_INVALID_DATA; } G_FREE(szCompositeInstance); return pdhStatus; } LONG GetQueryPerfData( PPDHI_QUERY pQuery, LONGLONG * pTimeStamp ) { LONG lStatus = PDH_INVALID_DATA; PPDHI_COUNTER pCounter; PPDHI_QUERY_MACHINE pQMachine; LONGLONG llCurrentTime; LONGLONG llTimeStamp = 0; BOOLEAN bCounterCollected = FALSE; BOOL bLastLogEntry; if (pQuery->hLog == H_REALTIME_DATASOURCE) { FILETIME GmtFileTime; FILETIME LocFileTime; // this is a real-time query so // get the current data from each of the machines in the query // (after this "sequential" approach is perfected, then the // "parallel" approach of multiple threads can be developed // // get time stamp now so each machine will have the same time GetSystemTimeAsFileTime(& GmtFileTime); FileTimeToLocalFileTime(& GmtFileTime, & LocFileTime); llTimeStamp = MAKELONGLONG(LocFileTime.dwLowDateTime, LocFileTime.dwHighDateTime); pQMachine = pQuery->pFirstQMachine; while (pQMachine != NULL) { pQMachine->llQueryTime = llTimeStamp; lStatus = ValidateMachineConnection(pQMachine->pMachine); if (lStatus == ERROR_SUCCESS) { // machine is connected so get data lStatus = GetSystemPerfData( pQMachine->pMachine->hKeyPerformanceData, & pQMachine->pPerfData, pQMachine->szObjectList, FALSE); // never query the costly data objects as a group // save the machine's last status pQMachine->pMachine->dwStatus = lStatus; // if there was an error in the data collection, // set the retry counter and wait to try again. if (lStatus != ERROR_SUCCESS) { GetSystemTimeAsFileTime(& LocFileTime); llCurrentTime = MAKELONGLONG(LocFileTime.dwLowDateTime, LocFileTime.dwHighDateTime); pQMachine->pMachine->llRetryTime = llCurrentTime + RETRY_TIME_INTERVAL; } } pQMachine->lQueryStatus = lStatus; // get next machine in query pQMachine = pQMachine->pNext; } // now update the counters using this new data if ((pCounter = pQuery->pCounterListHead) != NULL) { DWORD dwCollected = 0; do { if (pCounter->dwFlags & PDHIC_COUNTER_OBJECT) { if (UpdateCounterObject(pCounter)) { dwCollected ++; } } else if (pCounter->dwFlags & PDHIC_MULTI_INSTANCE) { if (UpdateRealTimeMultiInstanceCounterValue (pCounter)) { dwCollected ++; } } else { // update single instance counter values if (UpdateRealTimeCounterValue(pCounter)) { dwCollected ++; } } pCounter = pCounter->next.flink; } while (pCounter != NULL && pCounter != pQuery->pCounterListHead); lStatus = (dwCollected > 0) ? ERROR_SUCCESS : PDH_NO_DATA; } else { // no counters in the query (?!) lStatus = PDH_NO_DATA; } } else { // read data from log file // get the next log record entry and update the // corresponding counter entries PPDHI_LOG pLog = NULL; DWORD dwLogType = 0; __try { pLog = (PPDHI_LOG) (pQuery->hLog); dwLogType = LOWORD(pLog->dwLogFormat); lStatus = ERROR_SUCCESS; } __except (EXCEPTION_EXECUTE_HANDLER) { pQuery->dwLastLogIndex = (ULONG) -1; lStatus = PDH_INVALID_HANDLE; } if (lStatus == ERROR_SUCCESS) { if (dwLogType == PDH_LOG_TYPE_BINARY) { if (pQuery->dwLastLogIndex == 0) { lStatus = PdhiReadTimeWmiRecord( pLog, * (ULONGLONG *) & pQuery->TimeRange.StartTime, NULL, 0); pQuery->dwLastLogIndex = BINLOG_FIRST_DATA_RECORD; } else { lStatus = PdhiReadNextWmiRecord(pLog, NULL, 0, TRUE); } if (lStatus != ERROR_SUCCESS && lStatus != PDH_MORE_DATA) { pQuery->dwLastLogIndex = (DWORD) -1; } else { pQuery->dwLastLogIndex --; } } else if (pQuery->dwLastLogIndex == 0) { // then the first matching entry needs to be // located in the log file lStatus = PdhiGetMatchingLogRecord( pQuery->hLog, (LONGLONG *) & pQuery->TimeRange.StartTime, & pQuery->dwLastLogIndex); if (lStatus != ERROR_SUCCESS) { // the matching time entry wasn't found in the log pQuery->dwLastLogIndex = (DWORD) -1; } else { // decrement the index so it can be incremented // below. 0 is not a valid entry so there's no // worry about -1 being attempted accidently. pQuery->dwLastLogIndex--; } } if (pQuery->dwLastLogIndex != (DWORD) -1) { bLastLogEntry = FALSE; pQuery->dwLastLogIndex ++; // go to next entry if ((pCounter = pQuery->pCounterListHead) != NULL) { DWORD dwCounter = 0; do { if (dwLogType == PDH_LOG_TYPE_BINARY) { // save current value as last value since we are getting // a new one, hopefully. pCounter->LastValue = pCounter->ThisValue; lStatus = PdhiGetCounterFromDataBlock( pLog, pLog->pLastRecordRead, pCounter); } else { lStatus = PdhiGetCounterValueFromLogFile( pQuery->hLog, pQuery->dwLastLogIndex, pCounter); } if (lStatus != ERROR_SUCCESS) { // see if this is because there's no more entries if (lStatus == PDH_NO_MORE_DATA) { bLastLogEntry = TRUE; break; } } else if (dwLogType == PDH_LOG_TYPE_BINARY) { if (pCounter->ThisValue.CStatus == PDH_CSTATUS_VALID_DATA) { llTimeStamp = pLog->llFileSize; if (pLog->llFileSize > pQuery->TimeRange.EndTime) { lStatus = PDH_NO_MORE_DATA; bLastLogEntry = TRUE; break; } dwCounter ++; } } else { // single entry or multiple entries // if (pCounter->ThisValue.CStatus == PDH_CSTATUS_VALID_DATA) { llTimeStamp = MAKELONGLONG(pCounter->ThisValue.TimeStamp.dwLowDateTime, pCounter->ThisValue.TimeStamp.dwHighDateTime); if (llTimeStamp > pQuery->TimeRange.EndTime) { lStatus = PDH_NO_MORE_DATA; bLastLogEntry = TRUE; break; } dwCounter ++; } bCounterCollected = TRUE; } // go to next counter in list pCounter = pCounter->next.flink; } while (pCounter != NULL && pCounter != pQuery->pCounterListHead); if (bLastLogEntry) { lStatus = PDH_NO_MORE_DATA; } else if (dwCounter == 0) { lStatus = PDH_NO_DATA; } else if (bCounterCollected) { lStatus = ERROR_SUCCESS; } } else { // no counters in the query (?!) lStatus = PDH_NO_DATA; } } else { // all samples in the requested time frame have // been returned. lStatus = PDH_NO_MORE_DATA; } } } * pTimeStamp = llTimeStamp; return lStatus; } DWORD WINAPI PdhiAsyncTimerThreadProc( LPVOID pArg ) { PPDHI_QUERY pQuery; DWORD dwMsWaitTime; PDH_STATUS Status; FILETIME ftStart; FILETIME ftStop; LONGLONG llAdjustment; DWORD dwInterval; LONG lStatus = ERROR_SUCCESS; LONGLONG llTimeStamp; pQuery = (PPDHI_QUERY) pArg; dwInterval = dwMsWaitTime = pQuery->dwInterval * 1000; // convert sec. to mS. // wait for timeout or exit event, then update the specified query while ((lStatus = WaitForSingleObject(pQuery->hExitEvent, dwMsWaitTime)) != WAIT_OBJECT_0) { // time out elapsed so get new sample. GetSystemTimeAsFileTime(& ftStart); lStatus = WAIT_FOR_AND_LOCK_MUTEX(pQuery->hMutex); if (lStatus == ERROR_SUCCESS) { if (pQuery->dwFlags & PDHIQ_WBEM_QUERY) { Status = GetQueryWbemData(pQuery, & llTimeStamp); } else { Status = GetQueryPerfData(pQuery, & llTimeStamp); } SetEvent (pQuery->hNewDataEvent); RELEASE_MUTEX(pQuery->hMutex); GetSystemTimeAsFileTime(& ftStop); llAdjustment = * (LONGLONG *) & ftStop; llAdjustment -= * (LONGLONG *) & ftStart; llAdjustment += 5000; // for rounding llAdjustment /= 10000; // convert 100ns Units to ms if (dwInterval > llAdjustment) { dwMsWaitTime = dwInterval - (DWORD) (llAdjustment & 0x00000000FFFFFFFF); } else { dwMsWaitTime = 0; // overdue so do it now. } } } return lStatus; }