windows-nt/Source/XPSP1/NT/sdktools/pdh/pdhdll/qutils.c
2020-09-26 16:20:57 +08:00

647 lines
24 KiB
C

/*++
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;
}