2025-04-27 07:49:33 -04:00

1347 lines
34 KiB
C++

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// CTRIPC.CPP
//
// Perf counter data IPC
//
// Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
//
//
// PCLIB uses shared memory to communicate between one process
// generating performance data and any number of processes gathering
// the data. Performance data is stored in shared memory in separate
// structures whose layout is compatible with system-defined perf data
// structures (PERF_OBJECT_TYPE, PERF_INSTANCE_DEFINITION, etc.).
// Data collection is thus a simple matter of iterating over the data
// in shared memory and copying it into the buffer provided by the
// data-gathering process.
//
// Additional structures -- links -- tie the data structures together
// into doubly-linked lists to make the iteration possible and to make
// removal of an individual structure an O(constant) operation.
// Since pointers are meaningless in the context of shared memory,
// links refer to one another using SharedHandles (see inc\smh.h).
//
// Every item (data and links) in shared memory is refcounted and
// all references between items are strong (i.e. "AddRef'd"). This
// property simplifies memory management: an object is always freed
// when its last reference goes away, regardless which process held
// it. It also eliminates a level of synchronization that would
// otherwise be necessary to ensure that a process can always bind
// a pointer to any given item in shared memory without having to
// deal with the possibility of that item going away at the same time.
//
// On the perf data generation side, classes are provided which encapsulate
// access to the per-instance perf data in shared memory. This decouples
// PCLIB consumers from the format of the perf data in shared memory.
// It also provides a way to manipulate the perf data through pointers
// rather than through handles.
//
#include "_pclib.h"
#include "perfutil.h"
// !!! IMPORTANT !!!
//
// If you make ANY change to shared memory data structures (including
// the CPerfHeader class), you MUST increment the version below.
// Doing this prevents potentially incompatible publishers and monitors
// from binding to one another and thus avoids the possibility of
// crashes due to mismatched expectations about the format or
// contents of those structures.
//
static WCHAR gsc_wszDataVersion[] = L"Version 1";
// ========================================================================
//
// CLASS IPerfCounterBlock
// CLASS IPerfObject
// CLASS ICounterData
//
// ------------------------------------------------------------------------
//
// Out of line virtual destructors for interface classes.
//
IPerfCounterBlock::~IPerfCounterBlock() {}
IPerfObject::~IPerfObject() {}
ICounterData::~ICounterData() {}
// ========================================================================
//
// TEMPLATE CLASS CLink
//
// Class used to implement linked lists in shared memory. Making the
// link itself a separate item from the data being linked together
// means the list insertion/removal code is written once.
//
template<class _X>
class CLink
{
//
// Handles to the previous and next links
//
SharedHandle<CLink<_X> > m_shLinkNext;
SharedHandle<CLink<_X> > m_shLinkPrev;
//
// Handle to the data
//
SharedHandle<_X> m_shX;
// NOT IMPLEMENTED
//
CLink& operator=( const CLink& );
CLink( const CLink& );
public:
// CREATORS
//
CLink() {}
// STATICS
//
static VOID Link( SharedPtr<CLink<_X> >& pLinkHead,
SharedPtr<CLink<_X> >& pLinkNew )
{
SharedPtr<CLink<_X> > pLinkHeadNext;
//
// Insert the new link at the head of the list.
// Note that the link representing the list head
// DOES NOT MOVE. Rather, the new link is inserted
// immediately after the head. This allows
// references to the head of the list to remain
// constant throughout insertions and deletions.
//
pLinkNew->m_shLinkPrev = pLinkHead.GetHandle();
pLinkNew->m_shLinkNext = pLinkHead->m_shLinkNext;
if ( pLinkHeadNext.FBind( pLinkHead->m_shLinkNext ) )
pLinkHeadNext->m_shLinkPrev = pLinkNew.GetHandle();
pLinkHead->m_shLinkNext = pLinkNew.GetHandle();
}
static VOID Unlink( SharedPtr<CLink<_X> >& pLink )
{
SharedPtr<CLink<_X> > pLinkPrev;
if ( pLinkPrev.FBind( pLink->m_shLinkPrev ) )
pLinkPrev->m_shLinkNext = pLink->m_shLinkNext;
SharedPtr<CLink<_X> > pLinkNext;
if ( pLinkNext.FBind( pLink->m_shLinkNext ) )
pLinkNext->m_shLinkPrev = pLink->m_shLinkPrev;
}
// MANIPULATORS
//
VOID SetData( SharedPtr<_X>& spX )
{
m_shX = spX.GetHandle();
}
SharedHandle<_X>& ShData()
{
return m_shX;
}
SharedHandle<CLink<_X> >& ShLinkNext()
{
return m_shLinkNext;
}
};
// ========================================================================
//
// CLASS CPerfHeader
//
// Counter data header. The sole instance of this class exists in
// the named file mapping (not shared memory) agreed upon by the
// data-generating and data-gathering processes. That instance exists
// exactly as long as a configured view of the file exists; it is never
// explicitly delete()'d.
//
// A refcounting mechanism similar to what is used for singletons in
// global memory provides guarded process-independent access to the
// counter data without synchronization. I.e. the last process to
// finish using the counter data is responsible for its cleanup,
// regardless whether that process is data-generating or data-gathering.
//
//$OPT The initialization mechanism of this class should be identical
//$OPT to the RefCountedGlobal template in \cal\src\inc\singlton.h.
//$OPT Unfortunately, we can't use the template because it requires
//$OPT that the class be defined in static (global) memory. This
//$OPT class exists only in shared memory. We should find a way
//$OPT to eliminate that limitation and commonize the code.
//
struct SObjectDefinition;
class CPerfHeader
{
//
// The object's reference count.
//
LONG m_lcRef;
//
// Set of states which describe the state
// of initialization. The state is
// STATE_UNKNOWN while we are initializing
// or deinitializing.
//
enum
{
STATE_UNINITIALIZED,
STATE_INITIALIZED,
STATE_UNKNOWN
};
LONG m_lState;
//
// Handle to perf object list
//
SharedHandle<CLink<SObjectDefinition> > m_shLinkObjectList;
// MANIPULATORS
//
BOOL FInit()
{
SharedPtr<CLink<SObjectDefinition> > spLinkObjectList;
if ( !spLinkObjectList.FCreate() )
return FALSE;
m_shLinkObjectList = spLinkObjectList.GetHandle();
return TRUE;
}
VOID Deinit()
{
SharedHandle<CLink<SObjectDefinition> > shNull;
m_shLinkObjectList = shNull;
}
// CREATORS
//
// The only instance of this class should never
// be destroyed, so declare the destructor as private.
//
~CPerfHeader();
public:
// STATICS
//
// Define placement operator new so that we can instantiate
// the perf header in the mapped view of the named
// file mapping.
//
static void * operator new(size_t, void * pv)
{
Assert( pv != NULL );
return pv;
}
// CREATORS
//
CPerfHeader() :
m_lcRef(0),
m_lState(STATE_UNINITIALIZED)
{
}
// ACCESSORS
//
SharedHandle<CLink<SObjectDefinition> >& ShLinkObjectList()
{
Assert( m_lcRef > 0 );
Assert( m_lState == STATE_INITIALIZED );
return m_shLinkObjectList;
}
// MANIPULATORS
//
DWORD DwInitRef()
{
//
// Assert the invariant condition that we never have a
// reference without the state being initialized.
//
Assert( m_lState != STATE_INITIALIZED || m_lcRef >= 1 );
//
// Add the reference for the instance we're about
// to initialize. Doing this now simplifies the
// code below at the expense of having to decrement
// if first time initialization (FInit()) fails.
// The only thing critical to the design is that
// at any point, when m_lState is STATE_INITIALIZED,
// m_lcRef is at least 1.
//
LONG lcRef = InterlockedIncrement( &m_lcRef );
Assert( lcRef > 0 );
//
// Don't proceed until the object is initialized.
//
while ( m_lState != STATE_INITIALIZED )
{
//
// Simultaneously check whether initialization has
// started and, if it has not, start it.
//
LONG lStatePrev = InterlockedCompareExchange(
&m_lState,
STATE_UNKNOWN,
STATE_UNINITIALIZED );
//
// If we're starting first time initialization,
// then create and initialize the sole instance.
//
if ( lStatePrev == STATE_UNINITIALIZED )
{
if ( FInit() )
{
m_lState = STATE_INITIALIZED;
break;
}
// We failed to init.
// Tear down now.
//
Assert( lcRef == 1 );
Assert( m_lState == STATE_UNKNOWN );
// Let go of our ref on the object.
// Destroy the object.
// And LAST, set the state to UNINITIALIZED.
// NOTE: This will let the next caller through the
// InterlockedCompare above.
//
InterlockedDecrement( &m_lcRef );
Deinit();
m_lState = STATE_UNINITIALIZED;
return 0;
}
//
// If first time initialization is in progress on
// another thread, then get out of the way so
// it can finish.
//
//$OPT We should probably spin rather than Sleep()
//$OPT on multi-proc machines on the assumption that
//$OPT we only get here on a processor other than
//$OPT the one which is doing the initialization
//$OPT and we don't want to invite a task switch
//$OPT by calling Sleep() while we are waiting
//$OPT for initialization to complete.
//
if ( lStatePrev == STATE_UNKNOWN )
Sleep(0);
}
//
// At this point, there must be at least
// one initialized reference.
//
Assert( m_lState == STATE_INITIALIZED );
Assert( m_lcRef > 0 );
return static_cast<DWORD>(lcRef);
}
VOID DeinitRef()
{
//
// At this point, there must be at least
// one initialized reference.
//
Assert( m_lState == STATE_INITIALIZED );
Assert( m_lcRef > 0 );
//
// Remove that reference. If it is the last
// then deinit the object.
//
if ( 0 == InterlockedDecrement( &m_lcRef ) )
{
//
// After releasing the last reference, declare that
// the object is in an unknown state. This prevents
// other threads trying to re-initialize the object
// from proceeding until we're done.
//
m_lState = STATE_UNKNOWN;
//
// There is a tiny window between decrementing the
// refcount and changing the state where another
// initialization could have come through. Test this
// by rechecking the refcount.
//
if ( 0 == m_lcRef )
{
//
// If the refcount is still zero, then no
// initializations happened before we changed
// states. At this point, if an initialization
// starts, it will block until we change state,
// so it is safe to actually deinit the instance.
//
Deinit();
//
// Once the object has been deinitialized, update
// the state information. This unblocks any
// initializations waiting to happen.
//
m_lState = STATE_UNINITIALIZED;
}
else // refcount is now non-zero
{
//
// If the refcount is no longer zero, then an
// initialization happened between decrementing
// the refcount above and entering the unknown
// state. When that happens, DO NOT deinit --
// there is now another valid reference somewhere.
// Instead, just restore the object's state to let
// other references proceed.
//
m_lState = STATE_INITIALIZED;
}
}
//
// Assert the invariant condition that we never have a
// reference without the state being initialized.
//
Assert( m_lState != STATE_INITIALIZED || m_lcRef >= 1 );
}
};
// ========================================================================
//
// STRUCT SObjectDefinition
//
// Encapsulates the perf object type definition and the
// counter definitions that follow it.
//
// NOTE: Instances of this structure exist only in shared memory.
//
struct SInstanceDefinition;
struct SObjectDefinition
{
//
// Handle to the instance list
//
SharedHandle<CLink<SInstanceDefinition> > shLinkInstanceList;
//
// Object type
//
PERF_OBJECT_TYPE pot;
//
// Array of counter definitions
//
PERF_COUNTER_DEFINITION rgcd[1];
DWORD DwCollectData( DWORD dwFirstCounter,
LPBYTE * plpbPerfData,
LPDWORD lpdwcbPerfData );
};
// ========================================================================
//
// STRUCT SInstanceDefinition
//
// Counter instance definition which includes the instance name
// and counter block that follows it.
//
// NOTE: Instances of this structure exist only in shared memory.
//
struct SCounterBlock;
struct SInstanceDefinition
{
//
// Instance definition, including instance name
// which must follow it immediately
//
PERF_INSTANCE_DEFINITION pid;
WCHAR rgwchName[1];
//
// Counter data beginning with the PERF_COUNTER_BLOCK resides
// at the next DWORD-aligned boundary following the instance name.
// However, since the length of the instance name is unknown at
// compile time, the address of the first counter cannot be identified
// by a symbol. PerfCounterBlock() provides this information at
// runtime by returning the location of the counter block.
//
SCounterBlock& PerfCounterBlock()
{
return *reinterpret_cast<SCounterBlock *>(
reinterpret_cast<BYTE *>(&pid) + pid.ByteLength );
}
DWORD DwCollectData( LPBYTE * plpbPerfData,
LPDWORD lpdwcbPerfData );
};
// ========================================================================
//
// STRUCT SCounterBlock
//
// Counter block definition which includes a symbol for the
// array of counter values that follow it.
//
// NOTE: Instances of this structure exist only in shared memory.
//
struct SCounterBlock
{
PERF_COUNTER_BLOCK pcb;
LONG rglValues[1];
};
// ========================================================================
//
// CLASS CCounterData
//
class CCounterData :
public ICounterData,
private Singleton<CCounterData>
{
//
// Friend declarations required by Singleton template
//
friend class Singleton<CCounterData>;
//
// Named file mapping containing a handle to the
// location in shared memory of the counter data header
//
auto_handle<HANDLE> m_hMapping;
//
// Pointer to the perf data header in the mapped
// view of shared memory.
//
BOOL m_fInitializedPerfHeader;
CPerfHeader * m_pPerfHeader;
//
// Flag to indicate whether this counter data instance
// is initialized as a publisher. If it is, then we
// need to make sure we set the appropriate bit in the
// perf header when we're done so that the next publisher
// to come will know whether we crashed.
//
BOOL m_fInitializedPublisher;
//
// Pointer to the object list
//
SharedPtr<CLink<SObjectDefinition> > m_spLinkObjectList;
//
// Event that is signalled whenever the mapped view
// is ready for use. This event is non-signalled
// while the mapping is being set up.
//
CEvent m_evtViewReady;
// CREATORS
//
CCounterData() :
m_fInitializedPerfHeader(FALSE),
m_fInitializedPublisher(FALSE),
m_pPerfHeader(NULL)
{
}
~CCounterData();
BOOL FInitialize( LPCWSTR lpwszSignature );
// NOT IMPLEMENTED
//
CCounterData& operator=( const CCounterData& );
CCounterData( const CCounterData& );
public:
using Singleton<CCounterData>::CreateInstance;
using Singleton<CCounterData>::DestroyInstance;
using Singleton<CCounterData>::Instance;
// CREATORS
//
BOOL FInitializePublisher( LPCWSTR lpwszSignature )
{
m_fInitializedPublisher = FInitialize( lpwszSignature );
return m_fInitializedPublisher;
}
BOOL FInitializeMonitor( LPCWSTR lpwszSignature )
{
return FInitialize( lpwszSignature );
}
// MANIPULATORS
//
IPerfObject * CreatePerfObject( const PERF_OBJECT_TYPE& pot );
DWORD DwCollectData( LPCWSTR lpwszCounterIndices,
DWORD dwFirstCounter,
LPVOID * plpvPerfData,
LPDWORD lpdwcbPerfData,
LPDWORD lpcObjectTypes );
};
// ========================================================================
//
// CLASS CPerfObject
//
class CPerfObject : public IPerfObject
{
//
// Pointer to the object definition in shared memory
//
SharedPtr<SObjectDefinition> m_spDefinition;
//
// Pointer to the link to that definition
//
SharedPtr<CLink<SObjectDefinition> > m_spLinkObject;
//
// Pointer to the list of instances of that definition
//
SharedPtr<CLink<SInstanceDefinition> > m_spLinkInstanceList;
//
// Critical section to protect the instance list from
// multiple threads simultaneously trying to add instances.
//
CCriticalSection m_csInstanceList;
// CREATORS
//
CPerfObject() {}
BOOL FInitialize( const PERF_OBJECT_TYPE& pot );
// NOT IMPLEMENTED
//
CPerfObject& operator=( const CPerfObject& );
CPerfObject( const CPerfObject& );
public:
// STATICS
//
static CPerfObject *
Create( const PERF_OBJECT_TYPE& pot );
// CREATORS
//
~CPerfObject();
// ACCESSORS
//
SharedPtr<CLink<SObjectDefinition> >& SpLink()
{
return m_spLinkObject;
}
// MANIPULATORS
//
IPerfCounterBlock *
NewInstance( const PERF_INSTANCE_DEFINITION& pid,
const PERF_COUNTER_BLOCK& pcb );
};
// ========================================================================
//
// CLASS CPerfInstance
//
class CPerfInstance : public IPerfCounterBlock
{
//
// Pointer to the instance definition in shared memory
//
SharedPtr<SInstanceDefinition> m_spDefinition;
//
// Pointer to the link to that definition
//
SharedPtr<CLink<SInstanceDefinition> > m_spLinkInstance;
// CREATORS
//
CPerfInstance() {}
BOOL FInitialize( const PERF_INSTANCE_DEFINITION& pid,
const PERF_COUNTER_BLOCK& pcb );
// NOT IMPLEMENTED
//
CPerfInstance& operator=( const CPerfInstance& );
CPerfInstance( const CPerfInstance& );
public:
// STATICS
//
static CPerfInstance *
Create( const PERF_INSTANCE_DEFINITION& pid,
const PERF_COUNTER_BLOCK& pcb );
// CREATORS
//
~CPerfInstance();
// ACCESSORS
//
SharedPtr<CLink<SInstanceDefinition> >& SpLink()
{
return m_spLinkInstance;
}
// MANIPULATORS
//
VOID IncrementCounter( UINT iCounter );
VOID DecrementCounter( UINT iCounter );
VOID SetCounter( UINT iCounter, LONG lValue );
};
// ========================================================================
//
// CLASS CPerfInstance
//
// ------------------------------------------------------------------------
//
// CPerfInstance::Create()
//
CPerfInstance *
CPerfInstance::Create(
const PERF_INSTANCE_DEFINITION& pid,
const PERF_COUNTER_BLOCK& pcb )
{
CPerfInstance * pPerfInstance = new CPerfInstance();
if ( pPerfInstance->FInitialize( pid, pcb ) )
return pPerfInstance;
delete pPerfInstance;
return NULL;
}
// ------------------------------------------------------------------------
//
// CPerfInstance::FInitialize()
//
BOOL
CPerfInstance::FInitialize(
const PERF_INSTANCE_DEFINITION& pid,
const PERF_COUNTER_BLOCK& pcb )
{
if ( !m_spDefinition.FCreate( pid.ByteLength + pcb.ByteLength ) )
return FALSE;
memcpy( &m_spDefinition->pid,
&pid,
pid.ByteLength );
memcpy( &m_spDefinition->PerfCounterBlock().pcb,
&pcb,
pcb.ByteLength );
if ( !m_spLinkInstance.FCreate() )
return FALSE;
m_spLinkInstance->SetData( m_spDefinition );
return TRUE;
}
// ------------------------------------------------------------------------
//
// CPerfInstance::~CPerfInstance()
//
CPerfInstance::~CPerfInstance()
{
//
// Unlink the instance definition.
//
CLink<SInstanceDefinition>::Unlink( m_spLinkInstance );
}
// ------------------------------------------------------------------------
//
// CPerfInstance::IncrementCounter()
//
VOID
CPerfInstance::IncrementCounter( UINT iCounter )
{
InterlockedIncrement( &m_spDefinition->PerfCounterBlock().rglValues[iCounter] );
}
// ------------------------------------------------------------------------
//
// CPerfInstance::DecrementCounter()
//
VOID
CPerfInstance::DecrementCounter( UINT iCounter )
{
InterlockedDecrement( &m_spDefinition->PerfCounterBlock().rglValues[iCounter] );
}
// ------------------------------------------------------------------------
//
// CPerfInstance::SetCounter()
//
VOID
CPerfInstance::SetCounter( UINT iCounter, LONG lValue )
{
m_spDefinition->PerfCounterBlock().rglValues[iCounter] = lValue;
}
// ========================================================================
//
// CLASS CPerfObject
//
// ------------------------------------------------------------------------
//
// CPerfObject::Create()
//
CPerfObject *
CPerfObject::Create( const PERF_OBJECT_TYPE& pot )
{
CPerfObject * pPerfObject = new CPerfObject();
if ( pPerfObject->FInitialize( pot ) )
return pPerfObject;
delete pPerfObject;
return NULL;
}
// ------------------------------------------------------------------------
//
// CPerfObject::FInitialize()
//
BOOL
CPerfObject::FInitialize( const PERF_OBJECT_TYPE& pot )
{
if ( !m_spDefinition.FCreate( pot.DefinitionLength ) )
return FALSE;
memcpy( &m_spDefinition->pot,
&pot,
pot.DefinitionLength );
if ( !m_spLinkObject.FCreate() )
return FALSE;
m_spLinkObject->SetData( m_spDefinition );
if ( !m_spLinkInstanceList.FCreate() )
return FALSE;
m_spDefinition->shLinkInstanceList = m_spLinkInstanceList.GetHandle();
return TRUE;
}
// ------------------------------------------------------------------------
//
// CPerfObject::~CPerfObject()
//
CPerfObject::~CPerfObject()
{
//
// Unlink the object definition.
//
CLink<SObjectDefinition>::Unlink( m_spLinkObject );
}
// ------------------------------------------------------------------------
//
// CPerfObject::NewInstance()
//
IPerfCounterBlock *
CPerfObject::NewInstance(
const PERF_INSTANCE_DEFINITION& pid,
const PERF_COUNTER_BLOCK& pcb )
{
CPerfInstance * pPerfInstance = CPerfInstance::Create( pid, pcb );
if ( pPerfInstance )
{
//
// Lock down the instance list so that multiple threads
// adding instances simultaneously don't mess it up.
//
CSynchronizedBlock sb(m_csInstanceList);
//
// Link the new instance into the instance list
//
CLink<SInstanceDefinition>::Link(
m_spLinkInstanceList,
pPerfInstance->SpLink() );
}
return pPerfInstance;
}
// ========================================================================
//
// CLASS CCounterData
//
// ------------------------------------------------------------------------
//
// CCounterData::~CCounterData()
//
CCounterData::~CCounterData()
{
if ( m_fInitializedPerfHeader )
{
Assert( m_pPerfHeader );
m_pPerfHeader->DeinitRef();
}
if ( m_pPerfHeader )
(void) UnmapViewOfFile( m_pPerfHeader );
}
// ------------------------------------------------------------------------
//
// CCounterData::FInitialize()
//
// Initialize by binding to the block of shared memory where
// the counter data is stored and getting or initializing the
// handle to the counter data header.
//
BOOL
CCounterData::FInitialize( LPCWSTR lpwszSignature )
{
SECURITY_DESCRIPTOR sdAllAccess;
SECURITY_ATTRIBUTES saAllAccess;
DWORD dwLastError = 0;
LPSECURITY_ATTRIBUTES lpsa = NULL;
//
// First, set up security descriptor and attributes so that
// the system objects created below are accessible from both
// the process that produces the counter data and the process
// that consumes it.
//
//$HACK - Special case for HTTPEXT
//
// To meet C2 security requirements, we cannot have any completely
// open objects. Fortunately, HTTPEXT only uses this code for its
// perf counters which are both generated and gathered in the same
// process (W3SVC) and same security context (local system). Access
// can thus be left to the default (local system or admin only).
//
if (lpwszSignature != wcsstr(lpwszSignature, L"HTTPEXT"))
{
(void) InitializeSecurityDescriptor( &sdAllAccess, SECURITY_DESCRIPTOR_REVISION );
SetSecurityDescriptorDacl( &sdAllAccess, TRUE, NULL, FALSE );
saAllAccess.nLength = sizeof(saAllAccess);
saAllAccess.lpSecurityDescriptor = &sdAllAccess;
saAllAccess.bInheritHandle = FALSE;
lpsa = &saAllAccess;
}
//
// Bind to a named file mapping containing the perf data header.
//
static const WCHAR sc_wszHeaderFileMappingName[] =
L"Global\\PCLIB/%s/CCounterData/FInit/sc_wszHeaderFileMappingName: %s";
WCHAR lpwszMappingName[256];
Assert (sizeof(lpwszMappingName) >=
(sizeof(sc_wszHeaderFileMappingName) +
sizeof(gsc_wszDataVersion) +
sizeof(WCHAR) * wcslen(lpwszSignature)));
swprintf( lpwszMappingName,
sc_wszHeaderFileMappingName,
gsc_wszDataVersion,
lpwszSignature );
m_hMapping = CreateFileMappingW( INVALID_HANDLE_VALUE,
lpsa,
PAGE_READWRITE,
0,
sizeof(CPerfHeader),
lpwszMappingName );
if ( !m_hMapping.get() )
{
DebugTrace( "CCounterData::FInitialize() - CreateFileMappingW() failed (%d)\n", GetLastError() );
return FALSE;
}
// Remember the last error before the resource registration
// call since I have seen that GLE value may change inside
// this debug only function.
//
dwLastError = GetLastError();
//
// Determine if the file mapping existed before we bound
// to it. This tells us whether we need to initialize
// counter data structures in shared memory.
//
// If the file mapping did not exist until we bound to it,
// then initialize the counter data by creating an empty
// counter data header. Stuff the shared memory handle
// to the header into the mapping for all to use.
//
// If the file mapping already exists, then another process
// has either already initialized the counter data or is
// in the process of doing so. Wait on a named initialization
// completion event to be sure it's done before proceeding.
//
BOOL fCreatedView = (ERROR_SUCCESS == dwLastError);
Assert( fCreatedView || ERROR_ALREADY_EXISTS == dwLastError );
m_pPerfHeader = static_cast<CPerfHeader *>(
MapViewOfFile( m_hMapping.get(),
FILE_MAP_ALL_ACCESS,
0,
0,
0 ) );
if ( !m_pPerfHeader )
{
DebugTrace( "CCounterData::FInitialize() - MapViewOfFile() failed (%d)\n", GetLastError() );
return FALSE;
}
static const WCHAR sc_wszEvtViewReadyFmt[] =
L"Global\\PCLIB/%s/CCounterData/sc_wszEvtViewReady: %s";
WCHAR lpwszEvtViewReady[256];
Assert (sizeof(lpwszEvtViewReady) >=
(sizeof(sc_wszEvtViewReadyFmt) +
sizeof(gsc_wszDataVersion) +
sizeof(WCHAR) * wcslen(lpwszSignature)));
swprintf( lpwszEvtViewReady,
sc_wszEvtViewReadyFmt,
gsc_wszDataVersion,
lpwszSignature );
if ( !m_evtViewReady.FCreate( "PCLIB/CCounterData/m_evtViewReady",
lpsa, // global accessibility
TRUE, // manual reset
FALSE, // initially non-signalled
lpwszEvtViewReady,
TRUE) ) // don't munge the event name! It has a '\' in
// it intentionally.
{
DebugTrace( "CCounterData::FInitialize() - Error creating m_evtViewReady (%d)\n", GetLastError() );
return FALSE;
}
//
// If we created the view then we'll need to initialize
// it to a known state before we (or any other process)
// can do anything useful with it.
//
if ( fCreatedView )
{
new(m_pPerfHeader) CPerfHeader();
m_evtViewReady.Set();
}
else
{
m_evtViewReady.Wait();
}
//
// Get a reference to the perf header. First thread/process in
// creates the header. Note that the process that creates the
// header is not necessarily the same as the process that created
// and initialized the view above.
//
m_fInitializedPerfHeader = !!m_pPerfHeader->DwInitRef();
if ( !m_fInitializedPerfHeader )
return FALSE;
//
// Bind a local pointer to the perf object list rooted at the header
//
if ( !m_spLinkObjectList.FBind( m_pPerfHeader->ShLinkObjectList() ) )
return FALSE;
return TRUE;
}
// ------------------------------------------------------------------------
//
// CCounterData::CreatePerfObject()
//
IPerfObject *
CCounterData::CreatePerfObject( const PERF_OBJECT_TYPE& pot )
{
CPerfObject * pPerfObject = CPerfObject::Create( pot );
if ( pPerfObject )
{
CLink<SObjectDefinition>::Link(
m_spLinkObjectList,
pPerfObject->SpLink() );
}
return pPerfObject;
}
// ------------------------------------------------------------------------
//
// CCounterData::DwCollectData()
//
DWORD
CCounterData::DwCollectData( LPCWSTR lpwszCounterIndices,
DWORD dwFirstCounter,
LPVOID * plpvPerfData,
LPDWORD lpdwcbPerfData,
LPDWORD lpcObjectTypes )
{
LPBYTE lpbPerfDataStart = static_cast<LPBYTE>(*plpvPerfData);
LPBYTE lpbPerfDataCur = lpbPerfDataStart;
DWORD dwcbPerfDataBufRemaining = *lpdwcbPerfData;
DWORD cObjectTypes = 0;
//
// Initialize the return values in case of failure
//
*lpdwcbPerfData = 0;
*lpcObjectTypes = 0;
//
// Figure out what kind of data is being asked for
//
DWORD dwQueryType = GetQueryType(lpwszCounterIndices);
//
// We don't have any foreign data, so return immediately
// if that's what was asked for.
//
if ( QUERY_FOREIGN == dwQueryType )
return ERROR_SUCCESS;
SharedHandle<CLink<SObjectDefinition> > shLink;
SharedPtr<CLink<SObjectDefinition> > spLink;
for ( shLink = m_spLinkObjectList->ShLinkNext();
spLink.Clear(), spLink.FBind( shLink );
shLink = spLink->ShLinkNext() )
{
SharedPtr<SObjectDefinition> spDefinition;
spDefinition.FBind( spLink->ShData() );
if ( QUERY_GLOBAL == dwQueryType ||
IsNumberInUnicodeList( spDefinition->pot.ObjectNameTitleIndex + dwFirstCounter,
lpwszCounterIndices ) )
{
DWORD dwResult =
spDefinition->DwCollectData( dwFirstCounter,
&lpbPerfDataCur,
&dwcbPerfDataBufRemaining );
if ( dwResult != ERROR_SUCCESS )
return dwResult;
// update the object count only if we collect data for the object.
++cObjectTypes;
}
}
//
// Fill in the successful return values
//
*plpvPerfData = static_cast<LPVOID>(lpbPerfDataCur);
*lpdwcbPerfData = static_cast<DWORD>(lpbPerfDataCur - lpbPerfDataStart);
*lpcObjectTypes = cObjectTypes;
return ERROR_SUCCESS;
}
// ========================================================================
//
// CLASS SInstanceDefinition
//
// ------------------------------------------------------------------------
//
// SInstanceDefinition::DwCollectData()
//
DWORD
SInstanceDefinition::DwCollectData( LPBYTE * plpbPerfData,
LPDWORD lpdwcbPerfData )
{
DWORD dwcbInstance = pid.ByteLength + PerfCounterBlock().pcb.ByteLength;
if ( *lpdwcbPerfData < dwcbInstance )
return ERROR_MORE_DATA;
memcpy( *plpbPerfData,
&pid,
dwcbInstance );
*plpbPerfData += dwcbInstance;
*lpdwcbPerfData -= dwcbInstance;
return ERROR_SUCCESS;
}
// ========================================================================
//
// CLASS SObjectDefinition
//
// ------------------------------------------------------------------------
//
// SObjectDefinition::DwCollectData()
//
DWORD
SObjectDefinition::DwCollectData( DWORD dwFirstCounter,
LPBYTE * plpbPerfData,
LPDWORD lpdwcbPerfData )
{
//
// Determine whether we have enough room for at least the
// object definition (including the counter definitions).
//
if ( *lpdwcbPerfData < pot.DefinitionLength )
return ERROR_MORE_DATA;
//
// We've got enough room, so prepare to copy the data. Set up
// pointers to the object type and counter defintions in the
// perf data being returned so that we can fixup things
// like total byte length and counter indices in the
// perf data before returning it.
//
PERF_OBJECT_TYPE * ppot =
reinterpret_cast<PERF_OBJECT_TYPE *>( *plpbPerfData );
PERF_COUNTER_DEFINITION * pcd =
reinterpret_cast<PERF_COUNTER_DEFINITION *>( *plpbPerfData + pot.HeaderLength );
//
// Copy over object type and counter definitions
//
memcpy( ppot,
&pot,
pot.DefinitionLength );
*plpbPerfData += pot.DefinitionLength;
*lpdwcbPerfData -= pot.DefinitionLength;
//
// Copy over counter instances
//
SharedHandle<CLink<SInstanceDefinition> > shLink;
SharedPtr<CLink<SInstanceDefinition> > spLink;
SideAssert(spLink.FBind( shLinkInstanceList ));
for ( shLink = spLink->ShLinkNext();
spLink.Clear(), spLink.FBind(shLink);
shLink = spLink->ShLinkNext()
)
{
SharedPtr<SInstanceDefinition> spDefinition;
spDefinition.FBind( spLink->ShData() );
DWORD dwResult = spDefinition->DwCollectData( plpbPerfData, lpdwcbPerfData );
if ( dwResult != ERROR_SUCCESS )
return dwResult;
++ppot->NumInstances;
}
//
// Compute the total byte length of the counter object and
// fixup the name/help title indices of the object and counters.
//
ppot->TotalByteLength = static_cast<DWORD>(*plpbPerfData - reinterpret_cast<LPBYTE>(ppot));
Assert( ppot->TotalByteLength > 0 );
ppot->ObjectNameTitleIndex += dwFirstCounter;
ppot->ObjectHelpTitleIndex += dwFirstCounter;
//
// Fixup the counters' title indices
//
for ( DWORD i = ppot->NumCounters; i-- > 0; )
{
pcd[i].CounterNameTitleIndex += dwFirstCounter;
pcd[i].CounterHelpTitleIndex += dwFirstCounter;
}
return ERROR_SUCCESS;
}
// ========================================================================
//
// FREE FUNCTIONS
//
// ------------------------------------------------------------------------
//
// NewCounterPublisher()
//
ICounterData * __fastcall
NewCounterPublisher( LPCWSTR lpwszSignature )
{
if ( CCounterData::CreateInstance().FInitializePublisher( lpwszSignature ) )
return &CCounterData::Instance();
CCounterData::DestroyInstance();
return NULL;
}
// ------------------------------------------------------------------------
//
// NewCounterMonitor()
//
ICounterData * __fastcall
NewCounterMonitor( LPCWSTR lpwszSignature )
{
if ( CCounterData::CreateInstance().FInitializeMonitor( lpwszSignature ) )
return &CCounterData::Instance();
CCounterData::DestroyInstance();
return NULL;
}