// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // 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 CLink { // // Handles to the previous and next links // SharedHandle > m_shLinkNext; SharedHandle > 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 >& pLinkHead, SharedPtr >& pLinkNew ) { SharedPtr > 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 >& pLink ) { SharedPtr > pLinkPrev; if ( pLinkPrev.FBind( pLink->m_shLinkPrev ) ) pLinkPrev->m_shLinkNext = pLink->m_shLinkNext; SharedPtr > 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 >& 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 > m_shLinkObjectList; // MANIPULATORS // BOOL FInit() { SharedPtr > spLinkObjectList; if ( !spLinkObjectList.FCreate() ) return FALSE; m_shLinkObjectList = spLinkObjectList.GetHandle(); return TRUE; } VOID Deinit() { SharedHandle > 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 >& 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(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 > 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( reinterpret_cast(&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 { // // Friend declarations required by Singleton template // friend class Singleton; // // Named file mapping containing a handle to the // location in shared memory of the counter data header // auto_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 > 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::CreateInstance; using Singleton::DestroyInstance; using Singleton::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 m_spDefinition; // // Pointer to the link to that definition // SharedPtr > m_spLinkObject; // // Pointer to the list of instances of that definition // SharedPtr > 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 >& 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 m_spDefinition; // // Pointer to the link to that definition // SharedPtr > 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 >& 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::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::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::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( 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::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(*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 > shLink; SharedPtr > spLink; for ( shLink = m_spLinkObjectList->ShLinkNext(); spLink.Clear(), spLink.FBind( shLink ); shLink = spLink->ShLinkNext() ) { SharedPtr 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(lpbPerfDataCur); *lpdwcbPerfData = static_cast(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( *plpbPerfData ); PERF_COUNTER_DEFINITION * pcd = reinterpret_cast( *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 > shLink; SharedPtr > spLink; SideAssert(spLink.FBind( shLinkInstanceList )); for ( shLink = spLink->ShLinkNext(); spLink.Clear(), spLink.FBind(shLink); shLink = spLink->ShLinkNext() ) { SharedPtr 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(*plpbPerfData - reinterpret_cast(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; }