windows-nt/Source/XPSP1/NT/net/ias/system/perfmon/perflib.cpp
2020-09-26 16:20:57 +08:00

435 lines
12 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1998, Microsoft Corp. All rights reserved.
//
// FILE
//
// perflib.cpp
//
// SYNOPSIS
//
// Defines classes for implementing a PerfMon DLL.
//
// MODIFICATION HISTORY
//
// 09/06/1998 Original version.
// 10/19/1998 Throw LONG's on error.
// 03/18/1999 Data buffer must be 8-byte aligned.
// 05/13/1999 Fix offset to first help text.
//
///////////////////////////////////////////////////////////////////////////////
#include <ias.h>
#include <align.h>
#include <perflib.h>
/////////
// Application offsets into the string title database.
/////////
DWORD theFirstCounter;
DWORD theFirstHelp;
/////////
// Reads the name offsets for a given application.
/////////
LONG GetCounterOffsets(PCWSTR appName) throw ()
{
// Build the name of the performance key.
WCHAR keyPath[MAX_PATH + 1] = L"SYSTEM\\CurrentControlSet\\Services\\";
wcscat(keyPath, appName);
wcscat(keyPath, L"\\Performance");
// Open the performance key.
LONG status;
HKEY hKey;
status = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
keyPath,
0,
KEY_READ,
&hKey
);
if (status != NO_ERROR) { return status; }
// Get the first counter offset.
DWORD type, cbData = sizeof(DWORD);
status = RegQueryValueExW(
hKey,
L"First Counter",
0,
&type,
(PBYTE)&theFirstCounter,
&cbData
);
if (status != ERROR_SUCCESS) { goto close_key; };
// Make sure it's a DWORD.
if (type != REG_DWORD || cbData != sizeof(DWORD))
{
status = ERROR_BADKEY;
goto close_key;
}
// Get the first help offset.
status = RegQueryValueExW(
hKey,
L"First Help",
0,
&type,
(PBYTE)&theFirstHelp,
&cbData
);
if (status != ERROR_SUCCESS) { goto close_key; };
// Make sure it's a DWORD.
if (type != REG_DWORD || cbData != sizeof(DWORD))
{
status = ERROR_BADKEY;
goto close_key;
}
close_key:
RegCloseKey(hKey);
return status;
}
PBYTE PerfCounterBlock::collect(PBYTE first, PBYTE last)
{
PBYTE retval = first + pcb.ByteLength;
if (retval > last) { throw ERROR_MORE_DATA; }
memcpy(first, this, pcb.ByteLength);
return retval;
}
PerfCounterBlock* PerfCounterBlock::create(DWORD numDWORDs)
{
// Compute various lengths.
DWORD cntrLength = sizeof(DWORD) * numDWORDs;
DWORD byteLength = sizeof(PERF_COUNTER_BLOCK) + cntrLength;
// Allocate enough extra space for the counters.
PerfCounterBlock* pcb = new (operator new(byteLength)) PerfCounterBlock;
// Initialize the PERF_COUNTER_BLOCK.
pcb->pcb.ByteLength = byteLength;
// Initialize the counters.
memset(pcb->counters, 0, cntrLength);
return pcb;
}
PBYTE PerfInstanceDefinition::collect(PBYTE first, PBYTE last)
{
PBYTE retval = first + pid.ByteLength;
if (retval > last) { throw ERROR_MORE_DATA; }
memcpy(first, this, pid.ByteLength);
return retval;
}
PerfInstanceDefinition* PerfInstanceDefinition::create(
PCWSTR name,
LONG uniqueID
)
{
// Compute various lengths.
DWORD nameLength = name ? (wcslen(name) + 1) * sizeof(WCHAR) : 0;
DWORD byteLength = sizeof(PERF_INSTANCE_DEFINITION) + nameLength;
// Keep everything DWORD aligned.
byteLength = ROUND_UP_COUNT(byteLength, ALIGN_DWORD);
// Allocate enough extra space for the name.
PerfInstanceDefinition* pid = new (operator new(byteLength))
PerfInstanceDefinition;
// Initialize the PERF_INSTANCE_DEFINITION.
pid->pid.ByteLength = byteLength;
pid->pid.ParentObjectTitleIndex = 0;
pid->pid.ParentObjectInstance = 0;
pid->pid.UniqueID = uniqueID;
pid->pid.NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
pid->pid.NameLength = nameLength;
// Initialize the name.
memcpy(pid->name, name, nameLength);
return pid;
}
PBYTE PerfInstance::collect(PBYTE first, PBYTE last)
{
return pcb->collect((pid.get() ? pid->collect(first, last) : first), last);
}
PerfObjectType::~PerfObjectType() throw ()
{
for (MyVec::iterator i = instances.begin(); i != instances.end(); ++i)
{
delete *i;
}
}
void PerfObjectType::clear() throw ()
{
// Never clear the default instance.
if (pot.NumInstances != -1)
{
for (MyVec::iterator i = instances.begin(); i != instances.end(); ++i)
{
delete *i;
}
instances.clear();
}
}
void PerfObjectType::addInstance(PCWSTR name, LONG uniqueID)
{
// Resize first. If we threw an exception in push_back, we'd leak.
instances.reserve(size() + 1);
instances.push_back(new PerfInstance(name, uniqueID, numDWORDs));
}
PBYTE PerfObjectType::collect(PBYTE first, PBYTE last)
{
// Reserve enough room for the type definition.
PBYTE retval = first + pot.DefinitionLength;
if (retval > last) { throw ERROR_MORE_DATA; }
// Give the user a chance to fill-in the data.
if (dataSource) { dataSource(*this); }
if (pot.NumInstances == -1)
{
// We always have exactly one instance.
retval = at(0).collect(retval, last);
}
else if (instances.empty())
{
// If we're empty, then we right one instance's worth of zeros.
// Otherwise, PerfMon.exe has a tendency to display garbage.
DWORD nbyte = sizeof(PERF_INSTANCE_DEFINITION) +
sizeof(PERF_COUNTER_BLOCK) +
numDWORDs * sizeof(DWORD);
if (retval + nbyte > last) { throw ERROR_MORE_DATA; }
memset(retval, 0, nbyte);
retval += nbyte;
pot.NumInstances = 0;
}
else
{
// iterate through and collect all instances.
for (size_type i = 0; i < size(); ++i)
{
retval = at(i).collect(retval, last);
}
pot.NumInstances = (DWORD)size();
}
// Now that we've collected all the data, we can finish the definition ...
retval = (PBYTE)ROUND_UP_POINTER(retval, ALIGN_QUAD);
pot.TotalByteLength = retval - first;
QueryPerformanceCounter(&pot.PerfTime);
// ... and copy in the data.
memcpy(first, &pot, pot.DefinitionLength);
return retval;
}
PerfObjectType* PerfObjectType::create(const PerfObjectTypeDef& def)
{
// Allocate a new object.
size_t counterLength = def.numCounters * sizeof(PERF_COUNTER_DEFINITION);
size_t nbyte = sizeof(PerfObjectType) + counterLength;
std::auto_ptr<PerfObjectType> po(new (operator new(nbyte)) PerfObjectType);
// Save the data source.
po->dataSource = def.dataSource;
// Fill in the PERF_COUNTER_DEFINITION structs first since we also
// need to compute the object detail level.
DWORD detailLevel = PERF_DETAIL_WIZARD;
PERF_COUNTER_DEFINITION* dst = po->pcd;
PerfCounterDef* src = def.counters;
DWORD offset = sizeof(PERF_COUNTER_BLOCK);
for (DWORD i = 0; i < def.numCounters; ++i, ++dst, ++src)
{
dst->ByteLength = sizeof(PERF_COUNTER_DEFINITION);
dst->CounterNameTitleIndex = src->nameTitleOffset + theFirstCounter;
dst->CounterNameTitle = 0;
dst->CounterHelpTitleIndex = src->nameTitleOffset + theFirstHelp;
dst->CounterHelpTitle = 0;
dst->DefaultScale = src->defaultScale;
dst->DetailLevel = src->detailLevel;
dst->CounterType = src->counterType;
dst->CounterOffset = offset;
// Compute the counter size.
switch (dst->CounterOffset & 0x300)
{
case PERF_SIZE_DWORD:
dst->CounterSize = sizeof(DWORD);
break;
case PERF_SIZE_LARGE:
dst->CounterSize = sizeof(LARGE_INTEGER);
break;
default:
dst->CounterSize = 0;
}
// Update the offset based on the size.
offset += dst->CounterSize;
// The object detail level is the minimum counter detail level.
if (dst->DetailLevel < detailLevel)
{
detailLevel = dst->DetailLevel;
}
}
// Calculate the number of DWORD's of counter data.
po->numDWORDs = (offset - sizeof(PERF_COUNTER_BLOCK)) / sizeof(DWORD);
// Fill in the PERF_OBJECT_TYPE struct.
po->pot.DefinitionLength = sizeof(PERF_OBJECT_TYPE) + counterLength;
po->pot.HeaderLength = sizeof(PERF_OBJECT_TYPE);
po->pot.ObjectNameTitleIndex = def.nameTitleOffset + theFirstCounter;
po->pot.ObjectNameTitle = 0;
po->pot.ObjectHelpTitleIndex = def.nameTitleOffset + theFirstHelp;
po->pot.ObjectHelpTitle = 0;
po->pot.DetailLevel = detailLevel;
po->pot.NumCounters = def.numCounters;
po->pot.DefaultCounter = def.defaultCounter;
po->pot.NumInstances = 0;
po->pot.CodePage = 0;
QueryPerformanceFrequency(&(po->pot.PerfFreq));
// If it doesn't support multiple instances, then it must have exactly one.
if (!def.multipleInstances)
{
po->pot.NumInstances = -1;
po->instances.reserve(1);
po->instances.push_back(new PerfInstance(po->numDWORDs));
}
return po.release();
}
PerfCollector::~PerfCollector() throw ()
{
close();
}
void PerfCollector::clear() throw ()
{
for (PerfObjectType** i = types; *i; ++i)
{
(*i)->clear();
}
}
void PerfCollector::open(const PerfCollectorDef& def)
{
// Read the registry.
LONG success = GetCounterOffsets(def.name);
if (success != NO_ERROR) { throw success; }
// Allocate a null terminated array to hold the object types.
DWORD len = def.numTypes + 1;
types = new PerfObjectType*[len];
memset(types, 0, sizeof(PerfObjectType*) * len);
// Create the various object types.
for (DWORD i = 0; i < def.numTypes; ++i)
{
types[i] = PerfObjectType::create(def.types[i]);
}
}
void PerfCollector::collect(
PCWSTR values,
PVOID& data,
DWORD& numBytes,
DWORD& numTypes
)
{
PBYTE cursor = (PBYTE)data;
PBYTE last = cursor + numBytes;
numBytes = 0;
numTypes = 0;
if (values == NULL || *values == L'\0' || !wcscmp(values, L"Global"))
{
// For global we get everything.
for (PerfObjectType** i = types; *i; ++i)
{
cursor = (*i)->collect(cursor, last);
++numTypes;
}
}
else if (wcsncmp(values, L"Foreign", 7) && wcscmp(values, L"Costly"))
{
// It's not Global, Foreign, or Costly, so we parse the tokens and
// convert them to title indices.
PWSTR endptr;
PCWSTR nptr = values;
ULONG index = wcstoul(nptr, &endptr, 10);
while (endptr != nptr)
{
// We got a valid index, so find the object ...
for (PerfObjectType** i = types; *i; ++i)
{
if ((*i)->getIndex() == index)
{
// ... and collect the data.
cursor = (*i)->collect(cursor, last);
++numTypes;
break;
}
}
index = wcstoul(nptr = endptr, &endptr, 10);
}
}
numBytes = cursor - (PBYTE)data;
data = cursor;
}
void PerfCollector::close() throw ()
{
if (types)
{
for (PerfObjectType** i = types ; *i; ++i)
{
delete *i;
}
delete[] types;
types = NULL;
}
}