600 lines
19 KiB
C++
600 lines
19 KiB
C++
/*++
|
|
|
|
Copyright (C) 1997-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Abstract:
|
|
|
|
History:
|
|
|
|
--*/
|
|
|
|
//***************************************************************************
|
|
//
|
|
// PERFHELP.CPP
|
|
//
|
|
// Registry-based performance counter reading helper
|
|
//
|
|
//***************************************************************************
|
|
|
|
#include "precomp.h"
|
|
#include <stdio.h>
|
|
|
|
#include <wbemidl.h>
|
|
|
|
#include <wbemint.h>
|
|
|
|
#include "flexarry.h"
|
|
#include "ntperf.h"
|
|
#include "perfhelp.h"
|
|
#include "refreshr.h"
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// PerfHelper::GetInstances
|
|
//
|
|
// This is called to retrieve all instances of a given class.
|
|
//
|
|
// Parameters:
|
|
// <pBuf> The perf blob retrieved from HKEY_PERFORMANCE_DATA.
|
|
// <pClassMap> A map object of the class required.
|
|
// <pSink> The sink to which to deliver the objects.
|
|
//
|
|
//***************************************************************************
|
|
|
|
void PerfHelper::GetInstances(
|
|
LPBYTE pBuf,
|
|
CClassMapInfo *pClassMap,
|
|
IWbemObjectSink *pSink
|
|
)
|
|
{
|
|
PPERF_OBJECT_TYPE PerfObj = 0;
|
|
PPERF_INSTANCE_DEFINITION PerfInst = 0;
|
|
PPERF_COUNTER_DEFINITION PerfCntr = 0, CurCntr = 0;
|
|
PPERF_COUNTER_BLOCK PtrToCntr = 0;
|
|
PPERF_DATA_BLOCK PerfData = (PPERF_DATA_BLOCK) pBuf;
|
|
DWORD i, j, k;
|
|
|
|
// Get the first object type.
|
|
// ==========================
|
|
|
|
PerfObj = (PPERF_OBJECT_TYPE)((PBYTE)PerfData +
|
|
PerfData->HeaderLength);
|
|
|
|
// Process all objects.
|
|
// ====================
|
|
|
|
for (i = 0; i < PerfData->NumObjectTypes; i++ )
|
|
{
|
|
// Within each PERF_OBJECT_TYPE is a series of
|
|
// PERF_COUNTER_DEFINITION blocks.
|
|
// ==========================================
|
|
|
|
PerfCntr = (PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj +
|
|
PerfObj->HeaderLength);
|
|
|
|
// If the current object isn't of the class we requested,
|
|
// simply skip over it. I am not sure if this can really
|
|
// happen or not in practice.
|
|
// ======================================================
|
|
|
|
if (PerfObj->ObjectNameTitleIndex != pClassMap->m_dwObjectId)
|
|
{
|
|
PerfObj = (PPERF_OBJECT_TYPE)((PBYTE)PerfObj +
|
|
PerfObj->TotalByteLength);
|
|
continue;
|
|
}
|
|
|
|
if (PerfObj->NumInstances > 0)
|
|
{
|
|
// Get the first instance.
|
|
// =======================
|
|
|
|
PerfInst = (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj +
|
|
PerfObj->DefinitionLength);
|
|
|
|
// Retrieve all instances.
|
|
// =======================
|
|
|
|
for (k = 0; k < DWORD(PerfObj->NumInstances); k++ )
|
|
{
|
|
CurCntr = PerfCntr;
|
|
|
|
// Get the first counter.
|
|
// ======================
|
|
|
|
PtrToCntr = (PPERF_COUNTER_BLOCK)((PBYTE)PerfInst +
|
|
PerfInst->ByteLength);
|
|
|
|
// Quickly clone a new instance to send back to the user.
|
|
// Since SpawnInstance() returns an IWbemClassObject and
|
|
// we really need an IWbemObjectAccess,we have to QI
|
|
// after the spawn. We need to fix this, as this number
|
|
// of calls is too time consuming.
|
|
// ======================================================
|
|
|
|
IWbemObjectAccess *pNewInst = 0;
|
|
IWbemClassObject *pClsObj = 0;
|
|
|
|
pClassMap->m_pClassDef->SpawnInstance(0, &pClsObj);
|
|
pClsObj->QueryInterface(IID_IWbemObjectAccess, (LPVOID *) &pNewInst);
|
|
|
|
pClsObj->Release(); // We only need the IWbemObjectAccess pointer
|
|
|
|
// Locate the instance name.
|
|
// ==========================
|
|
|
|
LPWSTR pName = (LPWSTR) ((PBYTE)PerfInst + PerfInst->NameOffset);
|
|
|
|
// Retrieve all counters.
|
|
// ======================
|
|
|
|
for(j = 0; j < PerfObj->NumCounters; j++ )
|
|
{
|
|
// Find the WBEM property handle based on the counter title index.
|
|
// This function does a quick binary search of the class map object
|
|
// to find the handle that goes with this counter.
|
|
// ================================================================
|
|
|
|
LONG hPropHandle = pClassMap->GetPropHandle(CurCntr->CounterNameTitleIndex);
|
|
if (hPropHandle == 0)
|
|
continue;
|
|
|
|
// Data is (LPVOID)((PBYTE)PtrToCntr + CurCntr->CounterOffset);
|
|
|
|
// Only supporting simple DWORD types for now. BobW knows more about
|
|
// all this stuff and can extend it properly.
|
|
// ==================================================================
|
|
|
|
if ((CurCntr->CounterType & 0x700) == 0)
|
|
{
|
|
LPDWORD pdwVal = LPDWORD((LPVOID)((PBYTE)PtrToCntr + CurCntr->CounterOffset));
|
|
HRESULT hRes = pNewInst->WriteDWORD(hPropHandle, *pdwVal);
|
|
}
|
|
|
|
// Get next counter.
|
|
// =================
|
|
|
|
CurCntr = (PPERF_COUNTER_DEFINITION)((PBYTE)CurCntr +
|
|
CurCntr->ByteLength);
|
|
}
|
|
|
|
// Write the instance 'name'
|
|
// =========================
|
|
|
|
if (pName && pClassMap->m_dwNameHandle)
|
|
{
|
|
pNewInst->WritePropertyValue(
|
|
pClassMap->m_dwNameHandle,
|
|
(wcslen(pName) + 1) * 2,
|
|
LPBYTE(pName)
|
|
);
|
|
}
|
|
|
|
// Deliver the instance to the user.
|
|
// =================================
|
|
|
|
pSink->Indicate(1, (IWbemClassObject **) &pNewInst);
|
|
pNewInst->Release();
|
|
|
|
// Move to the next perf instance.
|
|
// ================================
|
|
PerfInst = (PPERF_INSTANCE_DEFINITION)((PBYTE)PtrToCntr +
|
|
PtrToCntr->ByteLength);
|
|
}
|
|
}
|
|
|
|
// Cases where the counters have no instances.
|
|
// ===========================================
|
|
|
|
else
|
|
{
|
|
// Get the first counter.
|
|
// ======================
|
|
|
|
PtrToCntr = (PPERF_COUNTER_BLOCK) ((PBYTE)PerfObj +
|
|
PerfObj->DefinitionLength );
|
|
|
|
// Quickly clone a new instance to send back to the user.
|
|
// Since SpawnInstance() returns an IWbemClassObject and
|
|
// we really need an IWbemObjectAccess,we have to QI
|
|
// after the spawn. We need to fix this, as this number
|
|
// of calls is too time consuming.
|
|
// ======================================================
|
|
|
|
IWbemObjectAccess *pNewInst = 0;
|
|
IWbemClassObject *pClsObj = 0;
|
|
pClassMap->m_pClassDef->SpawnInstance(0, &pClsObj);
|
|
pClsObj->QueryInterface(IID_IWbemObjectAccess, (LPVOID *) &pNewInst);
|
|
pClsObj->Release();
|
|
|
|
// Retrieve all counters.
|
|
// ======================
|
|
|
|
for( j=0; j < PerfObj->NumCounters; j++ )
|
|
{
|
|
// Find the WBEM property handle based on the counter title index.
|
|
// This function does a quick binary search of the class map object
|
|
// to find the handle that goes with this counter.
|
|
// ================================================================
|
|
|
|
LONG hPropHandle = pClassMap->GetPropHandle(PerfCntr->CounterNameTitleIndex);
|
|
if (hPropHandle == 0)
|
|
continue;
|
|
|
|
// Data is (LPVOID)((PBYTE)PtrToCntr + PerfCntr->CounterOffset);
|
|
|
|
// We will ignore non-DWORD types for now.
|
|
// =======================================
|
|
|
|
if ((PerfCntr->CounterType & 0x700) == 0)
|
|
{
|
|
LPDWORD pdwVal = LPDWORD((LPVOID)((PBYTE)PtrToCntr + PerfCntr->CounterOffset));
|
|
HRESULT hRes = pNewInst->WriteDWORD(hPropHandle, *pdwVal);
|
|
}
|
|
|
|
PerfCntr = (PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr +
|
|
PerfCntr->ByteLength);
|
|
}
|
|
|
|
// Since IWbemObjectAccess derives from IWbemClassObject, the following
|
|
// cast is legal. Note that indicate wants IWbemClassObject objects.
|
|
// ====================================================================
|
|
|
|
pSink->Indicate(1, (IWbemClassObject **) &pNewInst);
|
|
pNewInst->Release();
|
|
}
|
|
|
|
// Get the next object type.
|
|
// =========================
|
|
|
|
PerfObj = (PPERF_OBJECT_TYPE)((PBYTE)PerfObj +
|
|
PerfObj->TotalByteLength);
|
|
}
|
|
}
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// PerfHelper::RefreshInstances
|
|
//
|
|
//***************************************************************************
|
|
|
|
void PerfHelper::RefreshInstances(
|
|
LPBYTE pBuf,
|
|
CNt5Refresher *pRef
|
|
)
|
|
{
|
|
PPERF_OBJECT_TYPE PerfObj = 0;
|
|
PPERF_INSTANCE_DEFINITION PerfInst = 0;
|
|
PPERF_COUNTER_DEFINITION PerfCntr = 0, CurCntr = 0;
|
|
PPERF_COUNTER_BLOCK PtrToCntr = 0;
|
|
PPERF_DATA_BLOCK PerfData = (PPERF_DATA_BLOCK) pBuf;
|
|
DWORD i, j, k;
|
|
|
|
// Get the first object type.
|
|
// ==========================
|
|
|
|
PerfObj = (PPERF_OBJECT_TYPE)((PBYTE)PerfData +
|
|
PerfData->HeaderLength);
|
|
|
|
// Process all objects.
|
|
// ====================
|
|
|
|
for (i = 0; i < PerfData->NumObjectTypes; i++ )
|
|
{
|
|
// Within each PERF_OBJECT_TYPE is a series of
|
|
// PERF_COUNTER_DEFINITION blocks.
|
|
// ==========================================
|
|
|
|
PerfCntr = (PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj +
|
|
PerfObj->HeaderLength);
|
|
|
|
if (PerfObj->NumInstances > 0)
|
|
{
|
|
// Get the first instance.
|
|
// =======================
|
|
|
|
PerfInst = (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj +
|
|
PerfObj->DefinitionLength);
|
|
|
|
// Retrieve all instances.
|
|
// =======================
|
|
|
|
for (k = 0; k < DWORD(PerfObj->NumInstances); k++ )
|
|
{
|
|
CurCntr = PerfCntr;
|
|
|
|
// Get the first counter.
|
|
// ======================
|
|
|
|
PtrToCntr = (PPERF_COUNTER_BLOCK)((PBYTE)PerfInst +
|
|
PerfInst->ByteLength);
|
|
|
|
|
|
// Locate the instance name.
|
|
// ==========================
|
|
|
|
LPWSTR pName = (LPWSTR) ((PBYTE)PerfInst + PerfInst->NameOffset);
|
|
|
|
// Find the instance in the refresher, if there is one, which
|
|
// corresponds to the instance we are looking at.
|
|
// ==========================================================
|
|
|
|
CClassMapInfo *pClassMap = 0;
|
|
IWbemObjectAccess *pInst = 0;
|
|
|
|
BOOL bRes = pRef->FindInst(
|
|
PerfObj->ObjectNameTitleIndex, // Object type (WBEM Class)
|
|
pName, // Instance name
|
|
&pInst,
|
|
&pClassMap
|
|
);
|
|
|
|
// Retrieve all counters for the instance if it was one of the instances
|
|
// we are supposed to be refreshing.
|
|
// =====================================================================
|
|
|
|
if (bRes)
|
|
{
|
|
for (j = 0; j < PerfObj->NumCounters; j++ )
|
|
{
|
|
LONG hPropHandle = pClassMap->GetPropHandle(CurCntr->CounterNameTitleIndex);
|
|
if (hPropHandle == 0)
|
|
continue;
|
|
|
|
// Data is (LPVOID)((PBYTE)PtrToCntr + CurCntr->CounterOffset);
|
|
|
|
if ((CurCntr->CounterType & 0x700) == 0)
|
|
{
|
|
LPDWORD pdwVal = LPDWORD((LPVOID)((PBYTE)PtrToCntr + CurCntr->CounterOffset));
|
|
HRESULT hRes = pInst->WriteDWORD(hPropHandle, *pdwVal);
|
|
}
|
|
|
|
// Get next counter.
|
|
// =================
|
|
CurCntr = (PPERF_COUNTER_DEFINITION)((PBYTE)CurCntr +
|
|
CurCntr->ByteLength);
|
|
}
|
|
}
|
|
|
|
// Get the next instance.
|
|
// ======================
|
|
PerfInst = (PPERF_INSTANCE_DEFINITION)((PBYTE)PtrToCntr +
|
|
PtrToCntr->ByteLength);
|
|
}
|
|
}
|
|
|
|
// Cases where the counters have no instances.
|
|
// ===========================================
|
|
|
|
else
|
|
{
|
|
// Get the first counter.
|
|
// ======================
|
|
|
|
PtrToCntr = (PPERF_COUNTER_BLOCK) ((PBYTE)PerfObj +
|
|
PerfObj->DefinitionLength );
|
|
|
|
// Find the singleton WBEM instance which correponds to the singleton perf instance
|
|
// along with its class def so that we have the property handles.
|
|
//
|
|
// Note that since the perf object index translates to a WBEM class and there
|
|
// can only be one instance, all that is required to find the instance in the
|
|
// refresher is the perf object title index.
|
|
// =================================================================================
|
|
|
|
CClassMapInfo *pClassMap = 0;
|
|
IWbemObjectAccess *pInst = 0;
|
|
|
|
BOOL bRes = pRef->FindSingletonInst(
|
|
PerfObj->ObjectNameTitleIndex,
|
|
&pInst,
|
|
&pClassMap
|
|
);
|
|
|
|
// Retrieve all counters if the instance is one we are supposed to be refreshing.
|
|
// ==============================================================================
|
|
|
|
if (bRes)
|
|
{
|
|
for( j=0; j < PerfObj->NumCounters; j++ )
|
|
{
|
|
// Get the property handle for the counter.
|
|
// ========================================
|
|
|
|
LONG hPropHandle = pClassMap->GetPropHandle(PerfCntr->CounterNameTitleIndex);
|
|
if (hPropHandle == 0)
|
|
continue;
|
|
|
|
// Data is (LPVOID)((PBYTE)PtrToCntr + PerfCntr->CounterOffset);
|
|
|
|
// We will ignore non-DWORD types for now.
|
|
// =======================================
|
|
|
|
if ((PerfCntr->CounterType & 0x700) == 0)
|
|
{
|
|
LPDWORD pdwVal = LPDWORD((LPVOID)((PBYTE)PtrToCntr + PerfCntr->CounterOffset));
|
|
HRESULT hRes = pInst->WriteDWORD(hPropHandle, *pdwVal);
|
|
}
|
|
|
|
PerfCntr = (PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr +
|
|
PerfCntr->ByteLength);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the next object type.
|
|
// =========================
|
|
|
|
PerfObj = (PPERF_OBJECT_TYPE)((PBYTE)PerfObj +
|
|
PerfObj->TotalByteLength);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// QueryInstances
|
|
//
|
|
// Used to send back all instances of a perf counter. The counter
|
|
// is specified by the <pClassMap> object, which is tightly bound to
|
|
// a particular counter.
|
|
//
|
|
//***************************************************************************
|
|
|
|
|
|
BOOL PerfHelper::QueryInstances(
|
|
CClassMapInfo *pClassMap,
|
|
IWbemObjectSink *pSink
|
|
)
|
|
{
|
|
DWORD dwBufSize = 0;
|
|
DWORD dwType = 0;
|
|
LPBYTE pBuf = 0;
|
|
|
|
for (;;)
|
|
{
|
|
dwBufSize += 0x20000; // 128K
|
|
|
|
pBuf = new BYTE[dwBufSize];
|
|
|
|
wchar_t ID[32];
|
|
LONG lStatus;
|
|
|
|
swprintf(ID, L"%d", pClassMap->m_dwObjectId);
|
|
|
|
lStatus = RegQueryValueExW(
|
|
HKEY_PERFORMANCE_DATA,
|
|
ID,
|
|
0,
|
|
&dwType,
|
|
pBuf,
|
|
&dwBufSize
|
|
);
|
|
|
|
if (lStatus == ERROR_MORE_DATA)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (lStatus)
|
|
return FALSE;
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
// Decode the instances and send them back.
|
|
// ========================================
|
|
|
|
GetInstances(pBuf, pClassMap, pSink);
|
|
|
|
// Cleanup.
|
|
// ========
|
|
|
|
|
|
delete [] pBuf;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
//
|
|
// RefreshInstances
|
|
//
|
|
// Used to refresh a set of instances.
|
|
//
|
|
//***************************************************************************
|
|
|
|
|
|
BOOL PerfHelper::RefreshInstances(
|
|
CNt5Refresher *pRef
|
|
)
|
|
{
|
|
DWORD dwBufSize = 0;
|
|
DWORD dwType = 0;
|
|
LPBYTE pBuf = 0;
|
|
|
|
// Build up the Perf Object ID list.
|
|
// =================================
|
|
|
|
DWORD dwNumIds;
|
|
DWORD *pdwIdList;
|
|
pRef->GetObjectIds(&dwNumIds, &pdwIdList);
|
|
|
|
wchar_t *IDList = new wchar_t[dwNumIds * 8]; // Allow 8 wide chars per id
|
|
IDList[0] = 0;
|
|
|
|
for (DWORD n = 0; n < dwNumIds; n++)
|
|
{
|
|
wchar_t Tmp[32];
|
|
swprintf(Tmp, L"%d", pdwIdList[n]);
|
|
if (n > 0)
|
|
wcscat(IDList, L" ");
|
|
wcscat(IDList, Tmp);
|
|
}
|
|
|
|
delete [] pdwIdList;
|
|
|
|
for (;;)
|
|
{
|
|
dwBufSize += 0x20000; // 128K
|
|
|
|
pBuf = new BYTE[dwBufSize];
|
|
|
|
LONG lStatus;
|
|
|
|
lStatus = RegQueryValueExW(
|
|
HKEY_PERFORMANCE_DATA,
|
|
IDList,
|
|
0,
|
|
&dwType,
|
|
pBuf,
|
|
&dwBufSize
|
|
);
|
|
|
|
if (lStatus == ERROR_MORE_DATA)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (lStatus)
|
|
{
|
|
delete [] pBuf;
|
|
delete [] IDList;
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
// Decode the instances and send them back.
|
|
// ========================================
|
|
|
|
RefreshInstances(pBuf, pRef);
|
|
|
|
// Cleanup.
|
|
// ========
|
|
|
|
delete [] pBuf;
|
|
delete [] IDList;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|