656 lines
23 KiB
C++
656 lines
23 KiB
C++
/*
|
|
** SSOPERF.CPP
|
|
**
|
|
** Perfmon Counters for the SSO Framework
|
|
**
|
|
** davidsan -- 03/01/96
|
|
*/
|
|
|
|
#pragma warning(disable: 4237) // disable "bool" reserved
|
|
|
|
#include <ssobase.h>
|
|
#include <winperf.h>
|
|
#include <iis64.h>
|
|
#include "ssoperf.h"
|
|
|
|
// Okay, this is how the perfmon gunk works. Some of this is distilled from
|
|
// the MSDN documentation on perfmon counters (in the win32 SDK and the win32
|
|
// DDK). Other parts of it are explanations of how this implementation works.
|
|
//
|
|
// Basically, the API to provide perfmon counters works like this: assuming
|
|
// everything's set up correctly in the registry, when your object is selected
|
|
// by a perfmon client, SSOPerfOpen() is called. Then SSOPerfCollect() is
|
|
// repeatedly called to provide the actual counter data.
|
|
//
|
|
// SSOPerfCollect() is given a buffer, which it's supposed to fill with a
|
|
// PERF_OBJECT_TYPE structure, followed by a set of PERF_COUNTER_DEFINITION
|
|
// structures (one per counter), which are in turn followed by a set of
|
|
// PERF_INSTANCE_DEFINITION structures, one per instance, which contain
|
|
// the actual counter data. Whew. Since this huge structure has to be
|
|
// spewed into the buffer for every SSOPerfCollect() call, we keep as much
|
|
// of it as we can pre-initialized and just ready to go. The main structure
|
|
// we use is the SPD structure, which is pre-initialized below. The only
|
|
// problem is that there's some stuff we don't know at compile time. The
|
|
// really important one is: as part of this perfmon API, every
|
|
// perfmon counter source gets two magic reg values put into its reg key
|
|
// as part of installation of the counter. The reg values are a "first
|
|
// counter" and "first help" index. At SSOPerfOpen() time, these values
|
|
// have to be added to every index in the SPD structure (there are two counter
|
|
// indices per object and two per counter).
|
|
//
|
|
// Perfmon objects can provide COUNTERS and INSTANCES. A counter is an
|
|
// abstract measurement of some quantity, like # of times Invoke() is called
|
|
// per second. An instance is an instantiation of a counter. In this code,
|
|
// we map "instance" onto "SSO method". One limitation of the perfmon API
|
|
// is that every counter must have data for every instance. This means that
|
|
// every counter in this code has to compute its value PER SSO METHOD. We
|
|
// can't add global counters without adding a second PERF_OBJECT_TYPE, which
|
|
// just isn't worth it no matter what.
|
|
//
|
|
// The only other real complication is the fact that the perfmon.exe process
|
|
// and the Gibraltar process have to share the perfmon counter data via a
|
|
// shared memory-mapped file. The way we do this is to export a set of
|
|
// pointers to arrays of counter data within the MMF. That way, once the
|
|
// Gibraltar side has called InitPerfSource(), it can just do things like
|
|
// g_rgcInvokes[instance]++;
|
|
// to set the counters. The perfmon.exe side of the MMF uses the same names
|
|
// for the variables and everything, so once InitPerfSink() is called, it
|
|
// can get the value by just accessing g_rgcInvokes[instance] directly.
|
|
|
|
// Steps to add a new counter:
|
|
// 1) add a PERF_COUNTER_DEFINITION to g_spd
|
|
// 2) increase g_cCounters
|
|
// 3) add its instance computations in Open
|
|
// 4) add entry in ssoperf.h and ssoperf.ini
|
|
// 5) add new arrays for the shared memory block, if necessary
|
|
// 6) add their computation/storage in Collect
|
|
// 7) add code in ssobase.cpp to actually touch the counter values
|
|
// 8) add setting of arrays in InitPerfSource/FInitPerfSink
|
|
|
|
#include <pshpack4.h>
|
|
typedef struct _SSO_PERF_DATA
|
|
{
|
|
PERF_OBJECT_TYPE potSSO;
|
|
PERF_COUNTER_DEFINITION pcdInvokes;
|
|
PERF_COUNTER_DEFINITION pcdInvokesTotal;
|
|
PERF_COUNTER_DEFINITION pcdGetNames;
|
|
PERF_COUNTER_DEFINITION pcdGetNamesTotal;
|
|
PERF_COUNTER_DEFINITION pcdLatency;
|
|
PERF_COUNTER_DEFINITION pcdMaxLatency;
|
|
} SPD, *PSPD;
|
|
#include <poppack.h>
|
|
|
|
const int g_cCounters = 6;
|
|
|
|
// This structure is preinitialized here, but there are some adjustments that
|
|
// have to be made in the Open routine, because at compile time we don't know
|
|
// how many instances (SSO methods) we have. Also, the Index pieces are
|
|
// relative to some values that get stored in the registry by lodctr.exe, so
|
|
// those have to be adjusted also. Here's a list of everything to adjust:
|
|
// TotalByteLength += size of instance defs (includes data)
|
|
// ObjectNameTitleIndex/ObjectHelpTitleIndex += base offset from registry
|
|
// NumInstances
|
|
// each CounterNameTitleIndex/CounterHelpTitleIndex
|
|
SPD g_spd =
|
|
{
|
|
{
|
|
sizeof(SPD), // TotalByteLength
|
|
sizeof(SPD), // DefinitionLength
|
|
sizeof(PERF_OBJECT_TYPE), // HeaderLength
|
|
SSO_PERF_OBJECT, // ObjectNameTitleIndex
|
|
0, // ObjectNameTitle
|
|
SSO_PERF_OBJECT, // ObjectHelpTitleIndex
|
|
0, // ObjectHelpTitle
|
|
PERF_DETAIL_NOVICE, // DetailLevel
|
|
g_cCounters, // NumCounters
|
|
0, // DefaultCounter
|
|
0, // NumInstances
|
|
0, // CodePage
|
|
},
|
|
{
|
|
sizeof(PERF_COUNTER_DEFINITION), // ByteLength
|
|
SSO_NUM_INVOKES, // CounterNameTitleIndex
|
|
0, // CounterNameTitle
|
|
SSO_NUM_INVOKES, // CounterHelpTitleIndex
|
|
0, // CounterHelpTitle
|
|
0, // DefaultScale
|
|
PERF_DETAIL_NOVICE, // DetailLevel
|
|
PERF_COUNTER_COUNTER, // CounterType -- means dword rate counter with per/sec display
|
|
sizeof(DWORD), // CounterSize
|
|
sizeof(DWORD), // CounterOffset
|
|
},
|
|
{
|
|
sizeof(PERF_COUNTER_DEFINITION), // ByteLength
|
|
SSO_NUM_INVOKES_TOTAL, // CounterNameTitleIndex
|
|
0, // CounterNameTitle
|
|
SSO_NUM_INVOKES_TOTAL, // CounterHelpTitleIndex
|
|
0, // CounterHelpTitle
|
|
0, // DefaultScale
|
|
PERF_DETAIL_NOVICE, // DetailLevel
|
|
PERF_COUNTER_RAWCOUNT, // CounterType -- means dword counter
|
|
sizeof(DWORD), // CounterSize
|
|
2 * sizeof(DWORD), // CounterOffset
|
|
},
|
|
{
|
|
sizeof(PERF_COUNTER_DEFINITION), // ByteLength
|
|
SSO_NUM_GETNAMES, // CounterNameTitleIndex
|
|
0, // CounterNameTitle
|
|
SSO_NUM_GETNAMES, // CounterHelpTitleIndex
|
|
0, // CounterHelpTitle
|
|
0, // DefaultScale
|
|
PERF_DETAIL_NOVICE, // DetailLevel
|
|
PERF_COUNTER_COUNTER, // CounterType -- means dword rate counter with per/sec display
|
|
sizeof(DWORD), // CounterSize
|
|
3 * sizeof(DWORD), // CounterOffset
|
|
},
|
|
{
|
|
sizeof(PERF_COUNTER_DEFINITION), // ByteLength
|
|
SSO_NUM_GETNAMES_TOTAL, // CounterNameTitleIndex
|
|
0, // CounterNameTitle
|
|
SSO_NUM_GETNAMES_TOTAL, // CounterHelpTitleIndex
|
|
0, // CounterHelpTitle
|
|
0, // DefaultScale
|
|
PERF_DETAIL_NOVICE, // DetailLevel
|
|
PERF_COUNTER_RAWCOUNT, // CounterType -- means dword counter
|
|
sizeof(DWORD), // CounterSize
|
|
4 * sizeof(DWORD), // CounterOffset
|
|
},
|
|
{
|
|
sizeof(PERF_COUNTER_DEFINITION), // ByteLength
|
|
SSO_LATENCY_INVOKES_AVG, // CounterNameTitleIndex
|
|
0, // CounterNameTitle
|
|
SSO_LATENCY_INVOKES_AVG, // CounterHelpTitleIndex
|
|
0, // CounterHelpTitle
|
|
0, // DefaultScale
|
|
PERF_DETAIL_NOVICE, // DetailLevel
|
|
PERF_COUNTER_RAWCOUNT, // CounterType -- just a number, don't munge it
|
|
sizeof(DWORD), // CounterSize
|
|
5 * sizeof(DWORD), // CounterOffset
|
|
},
|
|
{
|
|
sizeof(PERF_COUNTER_DEFINITION), // ByteLength
|
|
SSO_LATENCY_INVOKES_MAX, // CounterNameTitleIndex
|
|
0, // CounterNameTitle
|
|
SSO_LATENCY_INVOKES_MAX, // CounterHelpTitleIndex
|
|
0, // CounterHelpTitle
|
|
0, // DefaultScale
|
|
PERF_DETAIL_NOVICE, // DetailLevel
|
|
PERF_COUNTER_RAWCOUNT, // CounterType -- just a number, don't munge it
|
|
sizeof(DWORD), // CounterSize
|
|
6 * sizeof(DWORD), // CounterOffset
|
|
},
|
|
};
|
|
|
|
BOOL g_fInited = FALSE;
|
|
int g_cInstances = 0;
|
|
|
|
// add new counter arrays here
|
|
int *g_rgcInvokes = NULL;
|
|
int *g_rgcGetNames = NULL;
|
|
DWORD *g_rgctixLatencyMax = NULL;
|
|
int *g_rgcTimeSamples = NULL;
|
|
int *g_rgiTimeSampleCurrent = NULL;
|
|
DWORD *g_rgTimeSamples = NULL;
|
|
|
|
extern BOOL FInitPerfSink();
|
|
|
|
PERF_INSTANCE_DEFINITION **g_rgppid = NULL;
|
|
|
|
BOOL
|
|
_FAddInstance(OLECHAR *wszName, int *pcbInstanceData, int iInstance, int iFirstCounter)
|
|
{
|
|
int cchName, cbName;
|
|
int cbThisInstance;
|
|
void *pv;
|
|
|
|
cchName = lstrlenW(wszName) + 1;
|
|
// pad to 4-byte (2-wchar) boundary
|
|
if (cchName & 1)
|
|
cchName++;
|
|
cbName = 2 * cchName;
|
|
|
|
// allocate space for PID + name + PERF_COUNTER_BLOCK + data
|
|
cbThisInstance = sizeof(PERF_INSTANCE_DEFINITION) + cbName + sizeof(DWORD) + (sizeof(DWORD) * g_cCounters);
|
|
|
|
if (!(pv = (void *)new BYTE[cbThisInstance]))
|
|
return(FALSE);
|
|
|
|
*pcbInstanceData += cbThisInstance;
|
|
g_rgppid[iInstance] = (PERF_INSTANCE_DEFINITION *)pv;
|
|
g_rgppid[iInstance]->ByteLength = sizeof(PERF_INSTANCE_DEFINITION) + cbName;
|
|
g_rgppid[iInstance]->ParentObjectTitleIndex = iFirstCounter;
|
|
g_rgppid[iInstance]->ParentObjectInstance = iFirstCounter;
|
|
g_rgppid[iInstance]->UniqueID = PERF_NO_UNIQUE_ID;
|
|
g_rgppid[iInstance]->NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
|
|
g_rgppid[iInstance]->NameLength = cbName;
|
|
|
|
lstrcpyW((WORD *)(((char *)pv) + sizeof(PERF_INSTANCE_DEFINITION)), wszName);
|
|
return(TRUE);
|
|
}
|
|
|
|
extern "C" DWORD APIENTRY
|
|
SSOPerfOpen(LPWSTR lpDeviceNames)
|
|
{
|
|
LONG lRet = ERROR_SUCCESS;
|
|
char szKey[MAX_PATH];
|
|
HKEY hkey;
|
|
DWORD dwType;
|
|
DWORD cb;
|
|
DWORD iFirstCounter;
|
|
DWORD iFirstHelp;
|
|
SSOMETHOD *psm;
|
|
int iInstance = 0;
|
|
int cbInstanceData;
|
|
|
|
if (!g_fInited)
|
|
{
|
|
// count the number of methods (instances) we have
|
|
psm = g_rgssomethod;
|
|
while (psm->wszName)
|
|
{
|
|
g_cInstances++;
|
|
psm++;
|
|
}
|
|
|
|
if (g_pfnssoDynamic)
|
|
++g_cInstances; // one for the dynamic method
|
|
|
|
g_spd.potSSO.NumInstances = g_cInstances;
|
|
|
|
if (!FInitPerfSink())
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
// get the magic counter offset values from the registry
|
|
wsprintf(szKey, "SYSTEM\\CurrentControlSet\\Services\\%s\\Performance", g_szSSOProgID);
|
|
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_READ | KEY_WRITE, &hkey);
|
|
if (ERROR_SUCCESS != lRet)
|
|
goto LBail;
|
|
|
|
cb = sizeof(DWORD);
|
|
lRet = RegQueryValueEx(hkey, "First Counter", 0, &dwType, (LPBYTE)&iFirstCounter, &cb);
|
|
if (ERROR_SUCCESS != lRet || cb != sizeof(DWORD))
|
|
{
|
|
RegCloseKey(hkey);
|
|
goto LBail;
|
|
}
|
|
|
|
lRet = RegQueryValueEx(hkey, "First Help", 0, &dwType, (LPBYTE)&iFirstHelp, &cb);
|
|
if (ERROR_SUCCESS != lRet || cb != sizeof(DWORD))
|
|
{
|
|
RegCloseKey(hkey);
|
|
goto LBail;
|
|
}
|
|
|
|
// add the magic counter offset values to all our indices
|
|
g_spd.potSSO.ObjectNameTitleIndex += iFirstCounter;
|
|
g_spd.potSSO.ObjectHelpTitleIndex += iFirstHelp;
|
|
g_spd.pcdInvokes.CounterNameTitleIndex += iFirstCounter;
|
|
g_spd.pcdInvokes.CounterHelpTitleIndex += iFirstHelp;
|
|
g_spd.pcdInvokesTotal.CounterNameTitleIndex += iFirstCounter;
|
|
g_spd.pcdInvokesTotal.CounterHelpTitleIndex += iFirstHelp;
|
|
g_spd.pcdGetNames.CounterNameTitleIndex += iFirstCounter;
|
|
g_spd.pcdGetNames.CounterHelpTitleIndex += iFirstHelp;
|
|
g_spd.pcdGetNamesTotal.CounterNameTitleIndex += iFirstCounter;
|
|
g_spd.pcdGetNamesTotal.CounterHelpTitleIndex += iFirstHelp;
|
|
g_spd.pcdLatency.CounterNameTitleIndex += iFirstCounter;
|
|
g_spd.pcdLatency.CounterHelpTitleIndex += iFirstHelp;
|
|
g_spd.pcdMaxLatency.CounterNameTitleIndex += iFirstCounter;
|
|
g_spd.pcdMaxLatency.CounterHelpTitleIndex += iFirstHelp;
|
|
|
|
RegCloseKey(hkey);
|
|
|
|
// build up an array of pointers to PERF_INSTANCE_DEFINITION structures,
|
|
// one per instance.
|
|
g_rgppid = new PERF_INSTANCE_DEFINITION *[g_cInstances];
|
|
if (!g_rgppid)
|
|
{
|
|
lRet = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto LBail;
|
|
}
|
|
|
|
iInstance = 0;
|
|
cbInstanceData = 0;
|
|
|
|
// dynamic method
|
|
if (g_pfnssoDynamic)
|
|
{
|
|
if (!_FAddInstance(L"Dynamic Methods", &cbInstanceData, iInstance, iFirstCounter))
|
|
{
|
|
lRet = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto LBail;
|
|
}
|
|
|
|
iInstance++;
|
|
}
|
|
|
|
// static methods
|
|
psm = g_rgssomethod;
|
|
while (psm->wszName)
|
|
{
|
|
if (!_FAddInstance(psm->wszName, &cbInstanceData, iInstance, iFirstCounter))
|
|
{
|
|
lRet = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto LBail;
|
|
}
|
|
|
|
psm++;
|
|
iInstance++;
|
|
}
|
|
|
|
g_spd.potSSO.TotalByteLength += cbInstanceData;
|
|
g_fInited = TRUE;
|
|
}
|
|
|
|
LBail:
|
|
return lRet;
|
|
}
|
|
|
|
extern "C" DWORD APIENTRY
|
|
SSOPerfClose()
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
const WCHAR *g_wszGlobal = L"Global";
|
|
const WCHAR *g_wszForeign = L"Foreign";
|
|
const WCHAR *g_wszCostly = L"Costly";
|
|
|
|
BOOL
|
|
FWszStartsWith(LPWSTR wsz, LPCWSTR wszPrefix)
|
|
{
|
|
WCHAR *pwc;
|
|
WCHAR *pwcPrefix;
|
|
|
|
if (!wsz)
|
|
return FALSE;
|
|
|
|
pwc = wsz;
|
|
pwcPrefix = (WCHAR *)wszPrefix;
|
|
while (*pwc && *pwcPrefix)
|
|
{
|
|
if (*pwc++ != *pwcPrefix++)
|
|
return FALSE;
|
|
}
|
|
if (!*pwc && *pwcPrefix)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
FWszNumberListContains(LPWSTR wsz, DWORD dw)
|
|
{
|
|
WCHAR *pwc;
|
|
DWORD dwCur;
|
|
|
|
if (!wsz)
|
|
return FALSE;
|
|
|
|
pwc = wsz;
|
|
dwCur = 0;
|
|
|
|
while (*pwc)
|
|
{
|
|
if (*pwc >= (WCHAR)'0' && *pwc <= (WCHAR)'9')
|
|
{
|
|
dwCur *= 10;
|
|
dwCur += *pwc - (WCHAR)'0';
|
|
}
|
|
else if (*pwc == (WCHAR)' ')
|
|
{
|
|
if (dw == dwCur)
|
|
return TRUE;
|
|
dwCur = 0;
|
|
}
|
|
else
|
|
return FALSE; // if not digit or space, bail
|
|
|
|
pwc++;
|
|
}
|
|
if (dw == dwCur)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD
|
|
DwAvgLatency(int iInstance)
|
|
{
|
|
DWORD *rgSamples = &g_rgTimeSamples[iInstance * cTimeSamplesMax];
|
|
int i;
|
|
int iMax = g_rgcTimeSamples[iInstance]; // only take this out of the array once,
|
|
// because it can change in the array
|
|
// during this loop!
|
|
__int64 i64Sum = 0;
|
|
|
|
if (iMax == 0)
|
|
return 0;
|
|
|
|
for (i = 0; i < iMax; i++)
|
|
i64Sum += rgSamples[i];
|
|
|
|
return (DWORD)(unsigned __int64)(i64Sum / (__int64)iMax);
|
|
}
|
|
|
|
|
|
// In order to specify what object(s) the perfmon client wants data for, the
|
|
// API passes information in in the form of lpwszValue. Yes, that's right,
|
|
// a string. This string can be:
|
|
// L"global" -- meaning give me all the objects you have
|
|
// L"foreign" -- give me data on another computer. we ignore this.
|
|
// L"costly" -- give me everything that's hard to compute. we ignore this also.
|
|
// Or, my favorite, it can be a list of object indices, provided as a space-
|
|
// separated list of stringized numbers which we're supposed to grovel through
|
|
// to find our ObjectNameTitleIndex value.
|
|
extern "C" DWORD APIENTRY
|
|
SSOPerfCollect(LPWSTR lpwszValue, LPVOID *lppData, LPDWORD lpcbBytes, LPDWORD lpcObjectTypes)
|
|
{
|
|
int iInstance;
|
|
BYTE *pb = (BYTE *)*lppData;
|
|
|
|
if (!g_fInited)
|
|
{
|
|
*lpcbBytes = 0;
|
|
*lpcObjectTypes = 0;
|
|
return ERROR_SUCCESS; // can't return any errors from this routine, whee
|
|
}
|
|
|
|
if (FWszStartsWith(lpwszValue, g_wszForeign) || FWszStartsWith(lpwszValue, g_wszCostly))
|
|
{
|
|
// we don't handle foreign requests, and i don't know what `costly' means
|
|
*lpcbBytes = 0;
|
|
*lpcObjectTypes = 0;
|
|
return ERROR_SUCCESS; // can't return any errors from this routine, whee
|
|
}
|
|
if (!FWszStartsWith(lpwszValue, g_wszGlobal))
|
|
{
|
|
// not a global request, so it better be a list of object indices. if ours isn't
|
|
// in the list, bail out.
|
|
if (!FWszNumberListContains(lpwszValue, g_spd.potSSO.ObjectNameTitleIndex))
|
|
{
|
|
*lpcbBytes = 0;
|
|
*lpcObjectTypes = 0;
|
|
return ERROR_SUCCESS; // can't return any errors from this routine, whee
|
|
}
|
|
}
|
|
|
|
if (*lpcbBytes < g_spd.potSSO.TotalByteLength)
|
|
{
|
|
*lpcbBytes = 0;
|
|
*lpcObjectTypes = 0;
|
|
return ERROR_MORE_DATA;
|
|
}
|
|
|
|
// okay, if we got to here, it means that perfmon really wants our data
|
|
// and that we have enough room to put it in. first, copy the SPD into
|
|
// the buffer.
|
|
CopyMemory(pb, &g_spd, sizeof(g_spd));
|
|
pb += sizeof(g_spd);
|
|
|
|
// then, for each instance, put in the PERF_INSTANCE_DEFINITION and all
|
|
// the counter data.
|
|
for (iInstance = 0; iInstance < g_cInstances; iInstance++)
|
|
{
|
|
CopyMemory(pb, g_rgppid[iInstance], g_rgppid[iInstance]->ByteLength);
|
|
pb += g_rgppid[iInstance]->ByteLength;
|
|
|
|
// this is the ByteLength of the PERF_COUNTER_BLOCK
|
|
*(DWORD *)pb = sizeof(DWORD) + (g_cCounters * sizeof(DWORD));
|
|
pb += sizeof(DWORD);
|
|
|
|
// first two counters are same value with different representations
|
|
*(DWORD *)pb = g_rgcInvokes[iInstance];
|
|
pb += sizeof(DWORD);
|
|
*(DWORD *)pb = g_rgcInvokes[iInstance];
|
|
pb += sizeof(DWORD);
|
|
|
|
// second two counters are same value with different representations
|
|
*(DWORD *)pb = g_rgcGetNames[iInstance];
|
|
pb += sizeof(DWORD);
|
|
*(DWORD *)pb = g_rgcGetNames[iInstance];
|
|
pb += sizeof(DWORD);
|
|
|
|
// next: avg latency. this one is medium-hard to compute, so we don't
|
|
// do it here.
|
|
*(DWORD *)pb = DwAvgLatency(iInstance);
|
|
pb += sizeof(DWORD);
|
|
|
|
// max latency
|
|
*(DWORD *)pb = g_rgctixLatencyMax[iInstance];
|
|
pb += sizeof(DWORD);
|
|
|
|
// add other counters here!
|
|
}
|
|
*lpcbBytes = DIFF(pb - (BYTE *)*lppData);
|
|
*lppData = pb;
|
|
*lpcObjectTypes = 1;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
const char *g_szMMFFormat = "%s.PerfData";
|
|
HANDLE g_hmmf = NULL;
|
|
|
|
// the shared MMF has the following chunks, in order:
|
|
// list of invokes counters (one DWORD per instance)
|
|
// list of getnames counters (one DWORD per instance)
|
|
// list of max latency values (one DWORD per instance)
|
|
// list of counts of time samples (one DWORD per instance)
|
|
// list of indices of current time samples (one DWORD per instance)
|
|
// list of arrays of time samples (one array of cTimeSamplesMax DWORDS per instance)
|
|
HANDLE
|
|
HmmfShared()
|
|
{
|
|
char szMMF[MAX_PATH];
|
|
SECURITY_DESCRIPTOR sd;
|
|
SECURITY_ATTRIBUTES sa;
|
|
int cbChunk = g_cInstances * sizeof(DWORD);
|
|
|
|
// 5 in next line is the number of one-dword-per-instance things we have
|
|
int cbMMF = (5 * cbChunk) + (cTimeSamplesMax * cbChunk);
|
|
|
|
if (g_hmmf)
|
|
return g_hmmf;
|
|
|
|
wsprintf(szMMF, g_szMMFFormat, g_szSSOProgID);
|
|
g_hmmf = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, szMMF);
|
|
if (!g_hmmf)
|
|
{
|
|
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
|
|
SetSecurityDescriptorDacl(&sd, TRUE, (PACL)NULL, FALSE);
|
|
sa.nLength = sizeof(sa);
|
|
sa.lpSecurityDescriptor = &sd;
|
|
sa.bInheritHandle = FALSE;
|
|
g_hmmf = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, cbMMF, szMMF);
|
|
}
|
|
return g_hmmf;
|
|
}
|
|
|
|
BOOL
|
|
FInitPerfSink()
|
|
{
|
|
char *pch;
|
|
int cbChunk = (g_cInstances * sizeof(DWORD));
|
|
|
|
if (g_rgcInvokes)
|
|
return TRUE;
|
|
|
|
if (!HmmfShared())
|
|
return FALSE;
|
|
|
|
pch = (char *)MapViewOfFile(g_hmmf,
|
|
FILE_MAP_ALL_ACCESS,
|
|
0, 0, 0);
|
|
if (!pch)
|
|
return FALSE;
|
|
g_rgcInvokes = (int *)pch;
|
|
pch += cbChunk;
|
|
g_rgcGetNames = (int *)pch;
|
|
pch += cbChunk;
|
|
g_rgctixLatencyMax = (DWORD *)pch;
|
|
pch += cbChunk;
|
|
g_rgcTimeSamples = (int *)pch;
|
|
pch += cbChunk;
|
|
g_rgiTimeSampleCurrent = (int *)pch;
|
|
pch += cbChunk;
|
|
g_rgTimeSamples = (DWORD *)pch;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
InitPerfSource()
|
|
{
|
|
SSOMETHOD *psm;
|
|
char *pch;
|
|
int cbChunk;
|
|
int cbMMF;
|
|
|
|
if (g_rgcInvokes)
|
|
return;
|
|
|
|
g_cInstances = (g_pfnssoDynamic ? 1 : 0);
|
|
|
|
psm = g_rgssomethod;
|
|
while (psm->wszName)
|
|
{
|
|
psm->iMethod = g_cInstances++;
|
|
psm++;
|
|
}
|
|
|
|
if (!HmmfShared())
|
|
return;
|
|
|
|
cbChunk = (g_cInstances * sizeof(DWORD));
|
|
cbMMF = (4 * cbChunk) + (cTimeSamplesMax * cbChunk);
|
|
|
|
pch = (char *)MapViewOfFile(g_hmmf,
|
|
FILE_MAP_ALL_ACCESS,
|
|
0, 0, 0);
|
|
if (!pch)
|
|
return;
|
|
|
|
g_rgcInvokes = (int *)pch;
|
|
pch += cbChunk;
|
|
g_rgcGetNames = (int *)pch;
|
|
pch += cbChunk;
|
|
g_rgctixLatencyMax = (DWORD *)pch;
|
|
pch += cbChunk;
|
|
g_rgcTimeSamples = (int *)pch;
|
|
pch += cbChunk;
|
|
g_rgiTimeSampleCurrent = (int *)pch;
|
|
pch += cbChunk;
|
|
g_rgTimeSamples = (DWORD *)pch;
|
|
|
|
FillMemory(g_rgcInvokes, g_cInstances * g_cCounters * sizeof(DWORD), 0);
|
|
}
|
|
|
|
void
|
|
FreePerfGunk()
|
|
{
|
|
if (g_rgcInvokes)
|
|
UnmapViewOfFile(g_rgcInvokes);
|
|
if (g_hmmf)
|
|
CloseHandle(g_hmmf);
|
|
}
|