|
|
/*++
Copyright (C) 1995-1999 Microsoft Corporation
Module Name:
qutils.c
Abstract:
Query management utility functions
--*/
#include <windows.h>
#include <assert.h>
#include <pdh.h>
#include "pdhitype.h"
#include "pdhidef.h"
#include "pdhmsg.h"
#include "strings.h"
#include "log_bin.h"
#include "log_wmi.h"
#include "perftype.h"
#include "perfdata.h"
BOOL IsValidQuery ( IN HQUERY hQuery ) { BOOL bReturn = FALSE; // assume it's not a valid query
PPDHI_QUERY pQuery; #if DBG
LONG lStatus = ERROR_SUCCESS; #endif
__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; } else { // this is not a valid query because the sig is bad
} } else { // this is not a valid query because the handle is NULL
} } __except (EXCEPTION_EXECUTE_HANDLER) { // something failed miserably so we can assume this is invalid
#if DBG
lStatus = GetExceptionCode(); #endif
} return bReturn; }
BOOL AddMachineToQueryLists ( IN PPERF_MACHINE pMachine, IN 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)) { assert (!(pQuery->dwFlags & PDHIQ_WBEM_QUERY)); 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) * 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 { 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) { assert (bReturn == TRUE); // 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); } } else { SetLastError (PDH_INVALID_HANDLE); bReturn = FALSE; }
return bReturn; }
extern PDH_FUNCTION PdhiGetBinaryLogCounterInfo ( IN PPDHI_LOG pLog, IN PPDHI_COUNTER pCounter );
PDH_FUNCTION PdhiGetCounterFromDataBlock( IN PPDHI_LOG pLog, IN PVOID pDataBuffer, IN PPDHI_COUNTER pCounter) { PDH_STATUS pdhStatus = ERROR_SUCCESS; PERFLIB_COUNTER * pPerfCounter = & pCounter->plCounterInfo; PPDH_RAW_COUNTER pRawValue = & pCounter->ThisValue; WCHAR szCompositeInstance[1024]; 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; FILETIME ftDataBlock; FILETIME ftGmtDataBlock; LONGLONG TimeStamp;
memset(pRawValue, 0, sizeof(PDH_RAW_COUNTER)); pThisMasterRecord = (PPDHI_BINARY_LOG_RECORD_HEADER) pDataBuffer; assert (pThisMasterRecord->dwType == BINLOG_TYPE_DATA); 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: case BINLOG_TYPE_DATA_OBJECT: pPerfData = (PPERF_DATA_BLOCK) ((LPBYTE)pThisSubRecord + sizeof (PDHI_BINARY_LOG_RECORD_HEADER)); if (pThisSubRecord->dwType == BINLOG_TYPE_DATA_OBJECT) { SystemTimeToFileTime(& pPerfData->SystemTime, & ftGmtDataBlock); FileTimeToLocalFileTime(& ftGmtDataBlock, & ftDataBlock); } else { 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) { if (pCounter->pLastRawItemList != NULL) { G_FREE(pCounter->pLastRawItemList); } pCounter->pLastRawItemList = pCounter->pThisRawItemList; } pCounter->pThisRawItemList = pNewBlock; RtlCopyMemory(pNewBlock, ( ((LPBYTE) pThisSubRecord) + sizeof(PDHI_BINARY_LOG_RECORD_HEADER)), CopySize); assert(CopySize == pNewBlock->dwLength); } else if (pPerfCounter->szInstanceName != NULL) { DWORD dwInstanceId = pCounter->pCounterPath->dwIndex; if (pPerfCounter->szParentInstanceName != NULL) { lstrcpyW(szCompositeInstance, pPerfCounter->szParentInstanceName); lstrcatW(szCompositeInstance, cszSlash); lstrcatW(szCompositeInstance, pPerfCounter->szInstanceName); } else { lstrcpyW(szCompositeInstance, pPerfCounter->szInstanceName); }
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; }
return pdhStatus; }
LONG GetQueryPerfData ( IN PPDHI_QUERY pQuery, IN 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 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(& LocFileTime); llTimeStamp = MAKELONGLONG(LocFileTime.dwLowDateTime, LocFileTime.dwHighDateTime);
assert (!(pQuery->dwFlags & PDHIQ_WBEM_QUERY)); //
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) { GetLocalFileTime (&llCurrentTime); 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--; } } else { // not WMI and not a time record no positioning required
}
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 { // 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; }
|