573 lines
17 KiB
C++
573 lines
17 KiB
C++
//========= Copyright © 1996-2004, Valve LLC, All rights reserved. ============
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================
|
|
|
|
#include "stdafx.h"
|
|
#ifdef _WIN32
|
|
#include "winperfcounter.h"
|
|
#ifdef _WIN32
|
|
#include <pdh.h>
|
|
#include <pdhmsg.h>
|
|
#endif
|
|
|
|
#ifdef GC
|
|
#include "gclogger.h"
|
|
using namespace GCSDK;
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: threaded reading & access of PDH data
|
|
//-----------------------------------------------------------------------------
|
|
class CWinPerfCounterReadThread : public CThread
|
|
{
|
|
public:
|
|
CWinPerfCounterReadThread( CWinPerfCountersPriv *pPrivData ) : m_pPrivData( pPrivData ), m_pPerfCounterMap( NULL )
|
|
{
|
|
}
|
|
|
|
void SetPerfCounterMap( const PerfCounter_t *pPerfCounterMap )
|
|
{
|
|
m_pPerfCounterMap = pPerfCounterMap;
|
|
}
|
|
|
|
virtual int Run();
|
|
|
|
private:
|
|
CWinPerfCountersPriv *m_pPrivData;
|
|
const PerfCounter_t *m_pPerfCounterMap;
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Pimpl pattern - Hides all the Pdh* stuff from the global include
|
|
//-----------------------------------------------------------------------------
|
|
class CWinPerfCountersPriv
|
|
{
|
|
#pragma warning(suppress : 4355) // warning C4355: 'this' : used in base member initializer list
|
|
CWinPerfCountersPriv( int nCounters ) : m_threadQueryPerfCounters( this )
|
|
{
|
|
m_ppdhHCounters = NULL;
|
|
#ifdef _WIN32
|
|
m_ppdhHCounters = new PDH_HCOUNTER[nCounters];
|
|
#endif
|
|
}
|
|
|
|
~CWinPerfCountersPriv()
|
|
{
|
|
delete[] m_ppdhHCounters;
|
|
}
|
|
|
|
#ifdef DBGFLAG_VALIDATE
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Run a global validation pass on all of our data structures and memory
|
|
// allocations.
|
|
// Input: validator - Our global validator object
|
|
// pchName - Our name (typically a member var in our container)
|
|
//-----------------------------------------------------------------------------
|
|
void CWinPerfCountersPriv::Validate( CValidator &validator, const char *pchName )
|
|
{
|
|
VALIDATE_SCOPE();
|
|
|
|
AUTO_LOCK( m_mutexDataAccess );
|
|
if ( m_ppdhHCounters )
|
|
validator.ClaimArrayMemory( m_ppdhHCounters );
|
|
|
|
ValidateObj( m_vecData );
|
|
ValidateObj( m_vecDataInFlight );
|
|
}
|
|
#endif // DBGFLAG_VALIDATE
|
|
|
|
friend class CWinPerfCounters;
|
|
friend class CWinPerfCounterReadThread;
|
|
|
|
protected:
|
|
#ifdef _WIN32
|
|
PDH_HQUERY m_pdhHQuery;
|
|
PDH_HCOUNTER *m_ppdhHCounters;
|
|
CUtlVector<uint32> m_vecData, m_vecDataInFlight;
|
|
CThreadMutex m_mutexDataAccess;
|
|
CThreadEvent m_eventStartQuery;
|
|
volatile bool m_bThreadRunning;
|
|
CWinPerfCounterReadThread m_threadQueryPerfCounters;
|
|
#endif
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: c'tor
|
|
//-----------------------------------------------------------------------------
|
|
CWinPerfCounters::CWinPerfCounters( )
|
|
{
|
|
m_pPrivData = NULL;
|
|
m_bInited = false;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: d'tor
|
|
//-----------------------------------------------------------------------------
|
|
CWinPerfCounters::~CWinPerfCounters()
|
|
{
|
|
Shutdown();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: registers the interesting counters via the Pdh (perf data handling) API
|
|
//-----------------------------------------------------------------------------
|
|
bool CWinPerfCounters::Init( const PerfCounter_t *counterMap, int nCounters )
|
|
{
|
|
VPROF_BUDGET( "CWinPerfCounters::Init", VPROF_BUDGETGROUP_STEAM );
|
|
Assert( nCounters > 0 );
|
|
Assert( counterMap );
|
|
Assert( !m_pPrivData );
|
|
|
|
bool bRet = false;
|
|
#ifdef _WIN32
|
|
|
|
m_pPerfCounterMap = counterMap;
|
|
m_nCounters = nCounters;
|
|
m_pPrivData = new CWinPerfCountersPriv( nCounters );
|
|
AUTO_LOCK( m_pPrivData->m_mutexDataAccess );
|
|
m_pPrivData->m_threadQueryPerfCounters.SetPerfCounterMap( counterMap );
|
|
m_pPrivData->m_vecData.SetSize( m_nCounters );
|
|
m_pPrivData->m_vecDataInFlight.SetSize( m_nCounters );
|
|
m_pPrivData->m_bThreadRunning = true;
|
|
|
|
bRet = true;
|
|
PDH_STATUS pdhStat = PdhOpenQuery( NULL, NULL, &m_pPrivData->m_pdhHQuery );
|
|
AssertMsg( ERROR_SUCCESS == pdhStat, "CWinPerfCounters::Init(): Failed to initalize performance data gathering.\n" );
|
|
for ( int i = 0; i < m_nCounters; ++i )
|
|
{
|
|
pdhStat = PdhAddCounter( m_pPrivData->m_pdhHQuery, m_pPerfCounterMap[i].m_rgchPerfObject, NULL, &(m_pPrivData->m_ppdhHCounters[i]) );
|
|
if ( ERROR_SUCCESS != pdhStat && NULL != m_pPerfCounterMap[i].m_rgchPerfObjectAlternative )
|
|
{
|
|
// first counter name didn't work; try our backup if we have one
|
|
pdhStat = PdhAddCounter( m_pPrivData->m_pdhHQuery, m_pPerfCounterMap[i].m_rgchPerfObjectAlternative, NULL, &(m_pPrivData->m_ppdhHCounters[i]) );
|
|
}
|
|
|
|
m_pPrivData->m_vecData[i] = 0;
|
|
m_pPrivData->m_vecDataInFlight[i] = 0;
|
|
|
|
if ( ERROR_SUCCESS != pdhStat )
|
|
{
|
|
char rgchCounterNames[ 1024 ];
|
|
|
|
if ( NULL != m_pPerfCounterMap[ i ].m_rgchPerfObjectAlternative )
|
|
{
|
|
Q_snprintf( rgchCounterNames, Q_ARRAYSIZE( rgchCounterNames ), "%s or %s",
|
|
m_pPerfCounterMap[ i ].m_rgchPerfObject,
|
|
m_pPerfCounterMap[ i ].m_rgchPerfObjectAlternative );
|
|
}
|
|
else
|
|
{
|
|
Q_snprintf( rgchCounterNames, Q_ARRAYSIZE( rgchCounterNames ), "%s", m_pPerfCounterMap[ i ].m_rgchPerfObject );
|
|
}
|
|
if ( m_pPerfCounterMap[i].m_bAssertOnFailure )
|
|
AssertMsg1( false, "CWinPerfCounters::Init():Failed to add %s to performance monitoring.",
|
|
rgchCounterNames );
|
|
else
|
|
{
|
|
#ifdef GC
|
|
EmitError( SPEW_GC, "CWinPerfCounters::Init():Failed to add %s to performance monitoring.\n", rgchCounterNames );
|
|
#else
|
|
Warning( "CWinPerfCounters::Init():Failed to add %s to performance monitoring.\n", rgchCounterNames );
|
|
#endif
|
|
pdhStat = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
bRet &= ( ERROR_SUCCESS == pdhStat );
|
|
}
|
|
m_bInited = true;
|
|
Assert( m_pPrivData->m_vecDataInFlight.Count() == m_nCounters );
|
|
Assert( m_pPrivData->m_vecData.Count() == m_nCounters );
|
|
|
|
// take a sample immediately
|
|
TakeSample();
|
|
#endif
|
|
return bRet;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Performs the read of the perf counters being monitored
|
|
//-----------------------------------------------------------------------------
|
|
bool CWinPerfCounters::TakeSample()
|
|
{
|
|
#ifdef _WIN32
|
|
if ( m_bInited ) // this gets called before InitOnServerRunning
|
|
{
|
|
if ( !m_pPrivData->m_threadQueryPerfCounters.IsAlive() )
|
|
{
|
|
m_pPrivData->m_threadQueryPerfCounters.Start();
|
|
}
|
|
m_pPrivData->m_eventStartQuery.Set();
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: pokes the measured values into the provided stats struct
|
|
//-----------------------------------------------------------------------------
|
|
bool CWinPerfCounters::WriteStats( void *pStatsStruct )
|
|
{
|
|
bool bRet = false;
|
|
#ifdef _WIN32
|
|
AUTO_LOCK( m_pPrivData->m_mutexDataAccess );
|
|
|
|
// pull out our already retrieved data
|
|
for ( int i = 0; i < m_nCounters; ++i )
|
|
{
|
|
//Get destination
|
|
byte *dst = (byte *) pStatsStruct + m_pPerfCounterMap[i].m_statsOffset;
|
|
*(int32 *)dst = m_pPrivData->m_vecData[i];
|
|
|
|
//perform rollup if necessary
|
|
if ( m_pPerfCounterMap[i].m_bCounterRequiresRollup == true && ( i + 1 < m_nCounters ) )
|
|
{
|
|
while( m_pPerfCounterMap[i].m_rgchPerfObject == m_pPerfCounterMap[i+1].m_rgchPerfObject )
|
|
{
|
|
++i;
|
|
*(int32 *)dst += m_pPrivData->m_vecData[i];
|
|
if ( m_nCounters == i ) break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bRet = true;
|
|
|
|
#endif // _WIN32
|
|
return bRet;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: collects data on a thread
|
|
//-----------------------------------------------------------------------------
|
|
int CWinPerfCounterReadThread::Run()
|
|
{
|
|
while ( m_pPrivData->m_bThreadRunning )
|
|
{
|
|
// wait to be signalled
|
|
m_pPrivData->m_eventStartQuery.Wait();
|
|
if ( !m_pPrivData->m_bThreadRunning )
|
|
break;
|
|
|
|
PDH_STATUS pdhStat = PdhCollectQueryData( m_pPrivData->m_pdhHQuery );
|
|
|
|
// pull out all the stats
|
|
PDH_FMT_COUNTERVALUE pdhCounterVal;
|
|
for ( int i = 0; i < m_pPrivData->m_vecDataInFlight.Count(); ++i )
|
|
{
|
|
DWORD fmt = PDH_FMT_LONG;
|
|
switch ( m_pPerfCounterMap[i].m_eFmt )
|
|
{
|
|
case k_EFormatInt:
|
|
fmt = PDH_FMT_LONG;
|
|
break;
|
|
case k_EFormatFloat:
|
|
fmt = PDH_FMT_DOUBLE;
|
|
break;
|
|
default:
|
|
Assert(false);
|
|
}
|
|
|
|
pdhStat = PdhGetFormattedCounterValue( m_pPrivData->m_ppdhHCounters[i], fmt, NULL, &pdhCounterVal );
|
|
byte *dst = (byte *)&m_pPrivData->m_vecDataInFlight[i];
|
|
switch ( m_pPerfCounterMap[i].m_eFmt )
|
|
{
|
|
case k_EFormatInt:
|
|
*(int32 *)dst = ERROR_SUCCESS == pdhStat ? pdhCounterVal.longValue : (int) m_pPerfCounterMap[i].m_fUnsetValue;
|
|
break;
|
|
case k_EFormatFloat:
|
|
*(float *)dst = ERROR_SUCCESS == pdhStat ? (float) pdhCounterVal.doubleValue : (float) m_pPerfCounterMap[i].m_fUnsetValue;
|
|
break;
|
|
default:
|
|
Assert(false);
|
|
}
|
|
}
|
|
|
|
// swap in the new data
|
|
AUTO_LOCK( m_pPrivData->m_mutexDataAccess );
|
|
m_pPrivData->m_vecData.Swap( m_pPrivData->m_vecDataInFlight );
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: closes the Pdh Query (which frees system resources)
|
|
//-----------------------------------------------------------------------------
|
|
void CWinPerfCounters::Shutdown()
|
|
{
|
|
if ( m_pPrivData )
|
|
{
|
|
m_pPrivData->m_bThreadRunning = false;
|
|
if ( m_pPrivData->m_threadQueryPerfCounters.IsAlive() )
|
|
{
|
|
m_pPrivData->m_eventStartQuery.Set();
|
|
m_pPrivData->m_threadQueryPerfCounters.Join( 200 );
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
if (m_bInited)
|
|
{
|
|
PdhCloseQuery( m_pPrivData->m_pdhHQuery );
|
|
m_bInited = false;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
m_pPerfCounterMap = NULL;
|
|
|
|
SAFE_DELETE( m_pPrivData );
|
|
}
|
|
|
|
|
|
#ifdef DBGFLAG_VALIDATE
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Run a global validation pass on all of our data structures and memory
|
|
// allocations.
|
|
// Input: validator - Our global validator object
|
|
// pchName - Our name (typically a member var in our container)
|
|
//-----------------------------------------------------------------------------
|
|
void CWinPerfCounters::Validate( CValidator &validator, const char *pchName )
|
|
{
|
|
VALIDATE_SCOPE();
|
|
ValidatePtr( m_pPrivData );
|
|
}
|
|
#endif // DBGFLAG_VALIDATE
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CWinNetworkPerfCounters::CWinNetworkPerfCounters( )
|
|
{
|
|
m_unNumInterfaces = 0;
|
|
m_bInited = false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
CWinNetworkPerfCounters::~CWinNetworkPerfCounters()
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CWinNetworkPerfCounters::Init()
|
|
{
|
|
// Enumerate all network interfaces and create counters for each
|
|
|
|
bool bRet = false;
|
|
HQUERY hQuery;
|
|
PDH_STATUS pdhStatus = PdhOpenQuery( NULL, 1, & hQuery );
|
|
|
|
if ( pdhStatus != ERROR_SUCCESS )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CUtlBuffer bufCounterList;
|
|
DWORD dwCounterListSize = 0;
|
|
CUtlBuffer bufInstanceList;
|
|
DWORD dwInstanceListSize = 0;
|
|
LPTSTR pszThisInstance = NULL;
|
|
|
|
// Determine the required buffer size for the data.
|
|
pdhStatus = PdhEnumObjectItems
|
|
(
|
|
NULL, // reserved
|
|
NULL, // local machine
|
|
TEXT("Network Interface"), // object to enumerate
|
|
NULL, // pass in NULL buffers
|
|
& dwCounterListSize, // an 0 length to get
|
|
NULL,// required size
|
|
& dwInstanceListSize, // of the buffers in chars
|
|
PERF_DETAIL_WIZARD, // counter detail level
|
|
0
|
|
);
|
|
|
|
// Note: old MSDN example tests against ERROR_SUCCESS (works on Win2k, fails on XP,
|
|
// new (.NET) MSDN example tests against PDH_MORE_DATA (works on XP, fails on Win2k Server).
|
|
// A usenet post has code that tests against both.
|
|
if ( pdhStatus != ERROR_SUCCESS
|
|
&& pdhStatus != PDH_MORE_DATA
|
|
) //lint !e650 !e737 constant out of range for '!=' (code from MSDN)
|
|
{
|
|
// failed to determine buffer size
|
|
return bRet;
|
|
}
|
|
else
|
|
{
|
|
// Allocate the buffers and try the call again.
|
|
bufCounterList.EnsureCapacity( dwCounterListSize * sizeof(TCHAR) );
|
|
bufInstanceList.EnsureCapacity( dwInstanceListSize * sizeof (TCHAR) );
|
|
|
|
pdhStatus = PdhEnumObjectItems
|
|
(
|
|
NULL, // reserved
|
|
NULL, // local machine
|
|
TEXT("Network Interface"), // object to enumerate
|
|
(LPTSTR) bufCounterList.Base(),
|
|
& dwCounterListSize,
|
|
(LPTSTR) bufInstanceList.Base(),
|
|
& dwInstanceListSize,
|
|
PERF_DETAIL_WIZARD, // counter detail level
|
|
0
|
|
);
|
|
|
|
if ( pdhStatus != ERROR_SUCCESS )
|
|
{
|
|
return bRet;
|
|
}
|
|
else
|
|
{
|
|
// If the machine has multiple network cards with identical names then we need
|
|
// to count them and append '#n' to the name, beginning with #1 for the first
|
|
// duplicate.
|
|
typedef CUtlDict< uint > mapIdenticalInstanceCount_t;
|
|
mapIdenticalInstanceCount_t mapIdenticalInstanceCount;
|
|
|
|
// Walk the return instance list.
|
|
for (
|
|
pszThisInstance = (LPTSTR) bufInstanceList.Base();
|
|
* pszThisInstance != 0;
|
|
pszThisInstance += lstrlen( pszThisInstance ) + 1
|
|
)
|
|
{
|
|
// reached our limit
|
|
if ( m_unNumInterfaces >= sm_unMaxNetworkInterfacesToMeasure )
|
|
break;
|
|
|
|
CUtlString sThisInstance;
|
|
|
|
// If the machine has multiple network cards with identical names then we need
|
|
// to count them and append '#n' to the name, beginning with #1 for the first
|
|
// duplicate.
|
|
// note 11/20/2012 I'm not sure this is true anymore, Windows might finally
|
|
// be giving us the instance names we actually need. Which is good because sometimes
|
|
// it's reall " 2" or " _2" on the end. . .
|
|
int iDict = mapIdenticalInstanceCount.Find( pszThisInstance );
|
|
if ( iDict == mapIdenticalInstanceCount.InvalidIndex() )
|
|
{
|
|
mapIdenticalInstanceCount.Insert( pszThisInstance, 1 );
|
|
sThisInstance = pszThisInstance;
|
|
}
|
|
else
|
|
{
|
|
mapIdenticalInstanceCount[iDict]++;
|
|
sThisInstance.Format( "%s #%d", pszThisInstance, mapIdenticalInstanceCount[iDict] );
|
|
}
|
|
|
|
CUtlString sBytesSentCounterName;
|
|
sBytesSentCounterName.Format( "\\Network Interface(%s)\\Bytes Sent/sec", sThisInstance.String() );
|
|
CUtlString sBytesRecvCounterName;
|
|
sBytesRecvCounterName.Format( "\\Network Interface(%s)\\Bytes Received/sec", sThisInstance.String() );
|
|
|
|
PerfCounter_t &BytesSentCounter = m_rgPerfCounterInfo[ 2 * m_unNumInterfaces ];
|
|
PerfCounter_t &BytesReceivedCounter = m_rgPerfCounterInfo[ 2 * m_unNumInterfaces + 1 ];
|
|
|
|
BytesSentCounter.m_rgchPerfObject = strdup( sBytesSentCounterName.Get() );
|
|
BytesSentCounter.m_rgchPerfObjectAlternative = NULL;
|
|
BytesSentCounter.m_statsOffset = offsetof( Stats_t, m_rgunNetworkBytesSentStats ) + ( m_unNumInterfaces * sizeof( uint32 ) );
|
|
BytesSentCounter.m_eFmt = k_EFormatInt;
|
|
BytesSentCounter.m_fUnsetValue = 0;
|
|
BytesSentCounter.m_bAssertOnFailure = false;
|
|
BytesSentCounter.m_bCounterRequiresRollup = false;
|
|
|
|
BytesReceivedCounter.m_rgchPerfObject = strdup( sBytesRecvCounterName.Get() );
|
|
BytesReceivedCounter.m_rgchPerfObjectAlternative = NULL;
|
|
BytesReceivedCounter.m_statsOffset = offsetof( Stats_t, m_rgunNetworkBytesReceivedStats ) + ( m_unNumInterfaces * sizeof( uint32 ) );
|
|
BytesReceivedCounter.m_eFmt = k_EFormatInt;
|
|
BytesReceivedCounter.m_fUnsetValue = 0;
|
|
BytesReceivedCounter.m_bAssertOnFailure = false;
|
|
BytesReceivedCounter.m_bCounterRequiresRollup = false;
|
|
|
|
m_unNumInterfaces++;
|
|
|
|
}
|
|
|
|
bRet = m_PerfCounters.Init( m_rgPerfCounterInfo, m_unNumInterfaces*2 );
|
|
m_bInited = bRet;
|
|
}
|
|
}
|
|
|
|
PdhCloseQuery( hQuery );
|
|
return bRet;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CWinNetworkPerfCounters::TakeSample()
|
|
{
|
|
if ( m_bInited )
|
|
return m_PerfCounters.TakeSample();
|
|
else
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool CWinNetworkPerfCounters::WriteStats( uint64 *pu64BytesSentPerSec, uint64 *pu64BytesRecvPerSec )
|
|
{
|
|
if ( !m_bInited )
|
|
return false;
|
|
|
|
bool bRet = false;
|
|
if ( m_PerfCounters.WriteStats( &m_Stats ) )
|
|
{
|
|
*pu64BytesSentPerSec = 0;
|
|
*pu64BytesRecvPerSec = 0;
|
|
for ( uint32 i=0; i < m_unNumInterfaces; ++i )
|
|
{
|
|
*pu64BytesSentPerSec += m_Stats.m_rgunNetworkBytesSentStats[i];
|
|
*pu64BytesRecvPerSec += m_Stats.m_rgunNetworkBytesReceivedStats[i];
|
|
}
|
|
|
|
bRet = true;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CWinNetworkPerfCounters::Shutdown()
|
|
{
|
|
if ( m_bInited )
|
|
m_PerfCounters.Shutdown();
|
|
|
|
}
|
|
#ifdef DBGFLAG_VALIDATE
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void CWinNetworkPerfCounters::Validate( CValidator &validator, const char *pchName )
|
|
{
|
|
ValidateObj( m_PerfCounters );
|
|
for ( uint32 i=0; i < m_unNumInterfaces; ++i )
|
|
{
|
|
validator.ClaimMemory( (void*) m_rgPerfCounterInfo[2*i].m_rgchPerfObject );
|
|
validator.ClaimMemory( (void*) m_rgPerfCounterInfo[2*i + 1].m_rgchPerfObject );
|
|
}
|
|
}
|
|
|
|
#endif // DBGFLAG_VALIDATE
|
|
|
|
#endif // _WIN32
|