|
|
/*++
Copyright (C) 1995-1999 Microsoft Corporation
Module Name: qutils.c
Abstract: Query management utility functions --*/
#include <windows.h>
#include "strsafe.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 "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; }
|