windows-nt/Source/XPSP1/NT/admin/snapin/dsadmin/profile.h
2020-09-26 16:20:57 +08:00

2358 lines
67 KiB
C++

/*********************************************************************************
/* File:
/* PROFILE.H
/* Author:
/* Max-H. Windisch, SDE-T
/* Date:
/* October 1996
/* Macros:
/* BEGIN_PROFILING_BLOCK
/* END_PROFILING_BLOCK
/* DUMP_PROFILING_RESULTS
/* IMPLEMENT_PROFILING
/* IMPLEMENT_PROFILING_CONDITIONAL
/* Classes:
/* CMaxLargeInteger
/* CMaxTimerAbstraction
/* CMaxMiniProfiler_Node_Base
/* CMaxMiniProfiler_Node_Standard
/* CMaxMiniProfiler_Node_NoHistory
/* CMaxMiniProfiler_Base
/* CMaxMiniProfiler_Standard
/* CMaxMiniProfiler_NoHistory
/* CMaxMultithreadProfiler
/* CMaxProfilingDLLWrapper
/* CMaxProfilingObject
/* CMaxProfilingBlockWrapper
/* Summary:
/* This mini profiler allows you to place BEGIN_PROFILING_BLOCK and
/* END_PROFILING_BLOCK directives in your code, or use the
/* CMaxProfilingBlockWrapper object, and collect results
/* in a logfile on termination of the profiled application (or by
/* using the DUMP_PROFILING_RESULTS macro). The
/* profiling blocks can be nested. Each module (DLL/EXE) using
/* the profiler must use IMPLEMENT_PROFILING or
/* IMPLEMENT_PROFILING_CONDITIONAL exactly once (defines
/* static variables for the profiler)
/* More details:
/* The default result file is c:/result.txt. It is not erased
/* automatically. For each completed instance of a profiler, it
/* contains: 1) a header, 2) the history of all profiled blocks (optional),
/* 3) merged results. For merging, results are sorted by {level, name},
/* merged, then sorted again by {full name}. Therefore, block names
/* must be unique. In any case, absolute results are always
/* given (in seconds)
/* How to enable in your code:
/* To enable the profiler, define MAX_PROFILING_ENABLED before including
/* this file. To use the profiler through s:/ds/util/maxprof.dll
/* (built in release), define MAX_PROFILING_ENABLED_DLL instead. This
/* allows to use one single instance of a profiler from multiple
/* modules
/* Other comments:
/* At runtime, you can disable history output by defining the following
/* environment variable to YES: MAX_DISABLE_PROFILING_HISTORY.
/* In DLL mode, if you define MAX_PROFILING_CONDITIONAL before including
/* this file, the profiler will work only if the following environment
/* variable is defined to YES: MAX_ENABLE_PROFILING
/* Note:
/* It's on purpose that I avoid using virtual methods here
/*
/* (c) Copyright 1996 Microsoft-Softimage Inc.
/********************************************************************************/
#ifndef __MAX_PROFILING_H // {
#define __MAX_PROFILING_H
#include <afx.h> // for CTime and CString
#include <assert.h> // for asserts
#include <fstream.h> // for streams
#include <iomanip.h>
//#pragma warning( disable : 4786 ) // stl antivirus ;-)
//#include <dsstlmfc.h> // for STL
#define MAX_ENV_ENABLE_PROFILING _T( "MAX_ENABLE_PROFILING" )
#define MAX_ENV_DISABLE_PROFILING_HISTORY _T( "MAX_DISABLE_PROFILING_HISTORY" )
#define MAX_ENV_YES _T( "YES" )
#define MAX_ENV_ALL _T( "ALL" )
#if !defined( DS_ON_AXP ) && !defined( _NO_THROW )
#define MAXPROFNOTHROW __declspec( nothrow )
#else
#define MAXPROFNOTHROW
#endif
#define MAX_PROFTAGNODE_TOP "PROFILER: ALL"
#define MAX_PROFTAGNODE_HEAPALLOCATION "PROFILER: HEAPALLOCATION"
#define MAX_PROFTAGNODE_BIAS "PROFILER: BIAS"
#define MAX_PROFTAGNODE_NOTHINGNESS "PROFILER: NOTHINGNESS"
// Note: disable profiling in _SHIP (unless specified otherwise by DS_PROFILE_SHIP),
// and in unix (not sure why)
#if ( defined _SHIP && !defined DS_PROFILE_SHIP ) || defined unix
#undef MAX_PROFILING_ENABLED_DLL
#undef MAX_PROFILING_ENABLED
#endif
/*********************************************************************************
/* Macros:
/* BEGIN_PROFILING_BLOCK
/* END_PROFILING_BLOCK
/* DUMP_PROFILING_RESULTS
/* IMPLEMENT_PROFILING
/* IMPLEMENT_PROFILING_CONDITIONAL
/* Comments:
/* . For simplified use of CMaxMiniProfiler's.
/* . For the comment parameter, use a non-unicode string, without "return"
/* character
/* . For the enabler parameter, use a unicode string (the name of your
/* environment variable)
/* . Use unique comments, since the profiler might use them as sorting keys
/* . The use of DUMP_PROFILING_RESULTS is not compulsory, since profilings
/* are always dumped at the end of a profiling session
/********************************************************************************/
#ifndef unix
#define __MAX_RESULTFILE_NAME "c:\\result.txt"
#else
#define __MAX_RESULTFILE_NAME "result.txt"
#endif
#ifdef MAX_PROFILING_ENABLED_DLL
#define __MAX_MINIPROFILER_IMPLEMENTATION ;
#else
#define __MAX_MINIPROFILER_IMPLEMENTATION \
const char *CMaxMiniProfiler_Base::s_poDefaultFileName = __MAX_RESULTFILE_NAME; \
CMaxTimerAbstraction CMaxMiniProfiler_Base::s_oOutOfBraceBiasApproximation; \
CMaxTimerAbstraction CMaxMiniProfiler_Base::s_oInOfBraceBiasApproximation; \
bool CMaxMiniProfiler_Base::s_bBiasIsKnown = false; \
unsigned long CMaxMiniProfiler_Base::s_lHeapBlockSize = 5000;
#endif
#if defined MAX_PROFILING_ENABLED || defined MAX_PROFILING_ENABLED_DLL // {{
#define BEGIN_PROFILING_BLOCK( comment ) \
CMaxProfilingObject::SCreateNewNode( comment );
#define END_PROFILING_BLOCK \
CMaxProfilingObject::SCloseCurrentNode();
#define DUMP_PROFILING_RESULTS \
CMaxProfilingObject::SDumpResults();
#define IMPLEMENT_PROFILING \
__MAX_MINIPROFILER_IMPLEMENTATION \
CMaxProfilingObject::MPOProfiler CMaxProfilingObject::s_oProfiler; \
CMaxProfilingObject::__CBiasApproximation CMaxProfilingObject::s_oBiasApproximation;
#define IMPLEMENT_PROFILING_CONDITIONAL( enabler ) \
__MAX_MINIPROFILER_IMPLEMENTATION \
CMaxProfilingObject::MPOProfiler CMaxProfilingObject::s_oProfiler( enabler ); \
CMaxProfilingObject::__CBiasApproximation CMaxProfilingObject::s_oBiasApproximation;
#else // }{
#define BEGIN_PROFILING_BLOCK( comment ) ( void )( comment );
#define END_PROFILING_BLOCK ;
#define DUMP_PROFILING_RESULTS ;
#define IMPLEMENT_PROFILING ;
#define IMPLEMENT_PROFILING_CONDITIONAL( enabler ) ;
#endif // }}
#if defined MAX_PROFILING_ENABLED || defined MAX_PROFILING_ENABLED_DLL || defined MAX_PROFILING_DLL_IMPLEMENTATION // {
/*********************************************************************************
/* Helper function:
/* bGIsEnabledEnvVar
/* Comments:
/********************************************************************************/
MAXPROFNOTHROW static inline bool bGIsEnabledEnvVar(
const TCHAR *pszEnvironmentVariableName,
const TCHAR *pszCriteria = MAX_ENV_YES )
{
const int nLength = 80;
TCHAR szBuffer[ nLength ];
DWORD dwValue;
// NULL string means enabled (default)
if ( NULL == pszEnvironmentVariableName )
return true;
dwValue = ::GetEnvironmentVariable(
pszEnvironmentVariableName, szBuffer, nLength );
if ( dwValue > 0 && _tcsicmp( szBuffer, pszCriteria ) == 0 )
return true;
return false;
};
#endif // }
#if defined MAX_PROFILING_ENABLED || defined MAX_PROFILING_DLL_IMPLEMENTATION // {
/*********************************************************************************
/* Class:
/* CMaxLargeInteger
/* Comments:
/* Minimal encapsulation of LARGE_INTEGER, considered as a time value
/********************************************************************************/
class CMaxLargeInteger
{
protected:
LARGE_INTEGER m_oValue;
public:
MAXPROFNOTHROW CMaxLargeInteger( LONG lHighPart = 0, DWORD dwLowPart = 0 )
{
m_oValue.u.HighPart = lHighPart;
m_oValue.u.LowPart = dwLowPart;
}
MAXPROFNOTHROW CMaxLargeInteger( LONGLONG llQuadPart )
{
m_oValue.QuadPart = llQuadPart;
}
MAXPROFNOTHROW CMaxLargeInteger operator +( const CMaxLargeInteger &roAdded ) const
{
return CMaxLargeInteger( m_oValue.QuadPart + roAdded.m_oValue.QuadPart );
}
MAXPROFNOTHROW CMaxLargeInteger operator -( const CMaxLargeInteger &roSubstracted ) const
{
return CMaxLargeInteger( m_oValue.QuadPart - roSubstracted.m_oValue.QuadPart );
}
MAXPROFNOTHROW CMaxLargeInteger operator /( unsigned long lDivisor ) const
{
return CMaxLargeInteger( m_oValue.QuadPart / ( LONGLONG )lDivisor );
}
MAXPROFNOTHROW bool operator <( const CMaxLargeInteger &roCompared ) const
{
return m_oValue.QuadPart < roCompared.m_oValue.QuadPart;
}
MAXPROFNOTHROW operator LARGE_INTEGER*()
{
return &m_oValue;
}
MAXPROFNOTHROW LONG lFGetHighPart() const
{
return m_oValue.u.HighPart;
}
MAXPROFNOTHROW DWORD dwFGetLowPart() const
{
return m_oValue.u.LowPart;
}
MAXPROFNOTHROW double dFInSecondsF( const CMaxLargeInteger &roFreq ) const
{
const DWORD dwMaxDword = 0xffffffff;
double highunit;
assert( 0 == roFreq.m_oValue.u.HighPart && 0 != roFreq.m_oValue.u.LowPart );
highunit = ( ( double )dwMaxDword + 1.0 ) / ( double )roFreq.m_oValue.u.LowPart;
return ( ( ( double )m_oValue.u.HighPart * highunit ) + ( ( double )m_oValue.u.LowPart / roFreq.m_oValue.u.LowPart ) );
}
};
MAXPROFNOTHROW inline ostream& operator<<( ostream &os, const CMaxLargeInteger &val )
{
return os << "(" << ( unsigned long )val.lFGetHighPart() << ";" << ( unsigned long )val.dwFGetLowPart() << ")";
};
/*********************************************************************************
/* Class:
/* CMaxTimerAbstraction
/* Comments:
/* Defines the interface CMaxMiniProfiler's expect from any timer
/* implementation
/********************************************************************************/
class CMaxTimerAbstraction
{
protected:
CMaxLargeInteger m_oTime;
static const CMaxLargeInteger s_oFrequency;
public:
MAXPROFNOTHROW CMaxTimerAbstraction(){ /* assumed to zero its internal value */ }
MAXPROFNOTHROW CMaxTimerAbstraction( int ){ ::QueryPerformanceCounter( m_oTime ); }
MAXPROFNOTHROW CMaxTimerAbstraction( const CMaxTimerAbstraction &roSrc ) : m_oTime( roSrc.m_oTime ){}
MAXPROFNOTHROW const CMaxTimerAbstraction& operator =( const CMaxTimerAbstraction &roSrc ){ m_oTime = roSrc.m_oTime; return *this; }
protected:
// Note: not part of the interface; for internal use only
MAXPROFNOTHROW CMaxTimerAbstraction( const CMaxLargeInteger &roSrc ) : m_oTime( roSrc ){};
public:
MAXPROFNOTHROW void FLog()
{
::QueryPerformanceCounter( m_oTime );
}
MAXPROFNOTHROW double dFInSeconds() const
{
return m_oTime.dFInSecondsF( s_oFrequency );
}
public:
MAXPROFNOTHROW void FAdd( const CMaxTimerAbstraction &roAdded )
{
m_oTime = m_oTime + roAdded.m_oTime;
}
MAXPROFNOTHROW void FSubstract( const CMaxTimerAbstraction &roSubstracted )
{
#if 0
// special case for negative differences - hide them
if ( m_oTime < roSubstracted.m_oTime )
{
m_oTime = CMaxLargeInteger( 0, 1 );
return;
}
#endif
m_oTime = m_oTime - roSubstracted.m_oTime;
}
MAXPROFNOTHROW void FDivide( unsigned long lDivisor )
{
m_oTime = m_oTime / lDivisor;
}
public:
MAXPROFNOTHROW static CMaxTimerAbstraction oSSum( const CMaxTimerAbstraction &roArg1, const CMaxTimerAbstraction &roArg2 )
{
CMaxTimerAbstraction sum;
sum.m_oTime = roArg1.m_oTime + roArg2.m_oTime;
return sum;
}
MAXPROFNOTHROW static CMaxTimerAbstraction oSDifference( const CMaxTimerAbstraction &roArg1, const CMaxTimerAbstraction &roArg2 )
{
CMaxTimerAbstraction difference;
#if 0
// special case for negative differences - hide them
if ( roArg1.m_oTime < roArg2.m_oTime )
return CMaxTimerAbstraction( CMaxLargeInteger( 0, 1 ) );
#endif
difference.m_oTime = roArg1.m_oTime - roArg2.m_oTime;
return difference;
}
MAXPROFNOTHROW static bool bSLess( const CMaxTimerAbstraction &roArg1, const CMaxTimerAbstraction &roArg2 )
{
return roArg1.m_oTime < roArg2.m_oTime;
}
MAXPROFNOTHROW static CMaxTimerAbstraction oSFrequency()
{
return CMaxTimerAbstraction( s_oFrequency );
}
private:
MAXPROFNOTHROW static CMaxLargeInteger oSCentralFrequency()
{
CMaxLargeInteger frequency;
::QueryPerformanceFrequency( frequency );
return frequency;
}
friend ostream& operator<<( ostream &os, const CMaxTimerAbstraction &val );
};
MAXPROFNOTHROW inline ostream& operator<<( ostream &os, const CMaxTimerAbstraction &val )
{
return os << val.m_oTime;
};
/*********************************************************************************
/* Class:
/* CMaxMiniProfiler_Node_Base
/* Comments:
/* Basic profiling node that behaves like a chronometer, and provides
/* standard logging services. Both the Standard and NoHistory profilers
/* use this basic implementation
/********************************************************************************/
class CMaxMiniProfiler_Node_Base
{
public:
typedef CString MMPNBString;
public:
// comparison by index
// -------------------
class CCompareIndexes
{
public:
MAXPROFNOTHROW bool operator()( const CMaxMiniProfiler_Node_Base &o1, const CMaxMiniProfiler_Node_Base &o2 ) const
{
assert( &o1 != &o2 );
return ( o1.m_lIndex < o2.m_lIndex );
};
};
friend CCompareIndexes;
protected:
// acquired at initialization
// --------------------------
unsigned long m_lLevel;
const char *m_pszTitle;
unsigned long m_lIndex;
// internal time counting mechanism
// --------------------------------
CMaxTimerAbstraction m_taOrigin;
CMaxTimerAbstraction m_taDelta;
unsigned int m_nCount;
#ifdef _DEBUG
bool m_bIsCounting;
#endif
// for final output
// ----------------
double m_dDelta;
public:
// constructor etc.
// ----------------
// Note: uses default assignment and copy constructor
// Note: it doesn't cost anything to initialize lots of things here - this
// is not done within profiling braces
MAXPROFNOTHROW CMaxMiniProfiler_Node_Base()
: m_lLevel( 0 )
, m_pszTitle( NULL )
, m_lIndex( 0 )
, m_nCount( 0 )
, m_dDelta( 0 )
#ifdef _DEBUG
, m_bIsCounting( false )
#endif
{
};
// chrono
// ------
MAXPROFNOTHROW void FStart()
{
#ifdef _DEBUG
assert( !m_bIsCounting );
m_taOrigin.FLog();
m_nCount++;
m_bIsCounting = true;
#else
m_taOrigin.FLog();
m_nCount++;
#endif
};
MAXPROFNOTHROW void FStop()
{
CMaxTimerAbstraction destination( 1 );
#ifdef _DEBUG
assert( m_bIsCounting );
m_taDelta.FAdd( CMaxTimerAbstraction::oSDifference( destination, m_taOrigin ) );
m_bIsCounting = false;
#else
m_taDelta.FAdd( CMaxTimerAbstraction::oSDifference( destination, m_taOrigin ) );
#endif
};
// access to members
// -----------------
MAXPROFNOTHROW unsigned long lFGetLevel() const { return m_lLevel; };
MAXPROFNOTHROW const char *pszFGetTitle() const { return m_pszTitle; };
MAXPROFNOTHROW unsigned long lFGetIndex() const { return m_lIndex; };
MAXPROFNOTHROW const CMaxTimerAbstraction &roFGetOrigin() const { return m_taOrigin; };
MAXPROFNOTHROW CMaxTimerAbstraction &roFGetDelta() { return m_taDelta; };
MAXPROFNOTHROW unsigned int nFGetCount() const { return m_nCount; };
MAXPROFNOTHROW double dFGetDelta()
{
if ( 0 == m_dDelta )
FComputeDelta();
return m_dDelta;
};
// misc. services
// --------------
MAXPROFNOTHROW bool bFIsIn( const CMaxMiniProfiler_Node_Base &roNode ) const
{
// Note: times cannot be equal, so we don't need to worry about that
if ( CMaxTimerAbstraction::bSLess( m_taOrigin, roNode.m_taOrigin ) )
{
CMaxTimerAbstraction d1 = m_taOrigin;
CMaxTimerAbstraction d2 = roNode.m_taOrigin;
d1.FAdd( m_taDelta );
d2.FAdd( roNode.m_taDelta );
if ( CMaxTimerAbstraction::bSLess( d2, d1 ) )
return true;
}
return false;
};
MAXPROFNOTHROW void FConditionalRemove( const CMaxMiniProfiler_Node_Base &roNode, const CMaxTimerAbstraction &roBias )
{
if ( bFIsIn( roNode ) )
{
CMaxTimerAbstraction d = roNode.m_taDelta;
d.FAdd( roBias );
m_taDelta.FSubstract( d );
}
};
// output to file
// --------------
void FOutput( ostream &os )
{
// don't output dead (merged) nodes
if ( 0 == m_nCount )
return;
// output our index
os << setw( 10 ) << m_lIndex << ": ";
// indent
STab( os, m_lLevel );
// output our title
os << "@@Name=";
if ( NULL != m_pszTitle )
os << m_pszTitle;
// output our block count
os << " @@Count=" << m_nCount;
// output our delta t
os << " @@Duration=";
SStampDeltaInSeconds( os, dFGetDelta() );
};
void FStampAbsoluteRange( ostream &os ) const
{
SStampAbsoluteRange( os, m_taOrigin, m_taDelta );
};
protected:
// computations at output time (outside of profiling)
// --------------------------------------------------
MAXPROFNOTHROW void FComputeDelta()
{
m_dDelta = m_taDelta.dFInSeconds();
};
public:
// mini helpers for facilitated and standardized output of results
// ---------------------------------------------------------------
static ostream& STab( ostream &os, int level )
{
for ( int i = 0; i < level; i++ )
os << " ";
return os;
};
static ostream& SStampDeltaInSeconds( ostream &os, double delta )
{
os << delta << "s";
return os;
};
static ostream& SStampAbsoluteRange( ostream &os, const CMaxTimerAbstraction &rO, const CMaxTimerAbstraction &rD )
{
os << "[origin" << rO;
os << ",duration" << rD << "]";
return os;
};
};
/*********************************************************************************
/* Classes:
/* CMaxMiniProfiler_Node_Standard
/* Comments:
/********************************************************************************/
class CMaxMiniProfiler_Node_Standard
: public CMaxMiniProfiler_Node_Base
{
public:
// comparison by full titles
// -------------------------
class CCompareFullTitles
{
public:
MAXPROFNOTHROW bool operator()( const CMaxMiniProfiler_Node_Standard &o1, const CMaxMiniProfiler_Node_Standard &o2 ) const
{
assert( &o1 != &o2 );
return ( o1.m_oFullTitle < o2.m_oFullTitle );
};
};
friend CCompareFullTitles;
// comparison for node merging (a) level, b) full title, c) index)
// ---------------------------------------------------------------
class CCompareForNodeMerging
{
public:
MAXPROFNOTHROW bool operator()( const CMaxMiniProfiler_Node_Standard &o1, const CMaxMiniProfiler_Node_Standard &o2 ) const
{
assert( &o1 != &o2 );
if ( o1.m_lLevel < o2.m_lLevel )
return true;
else if ( o1.m_lLevel == o2.m_lLevel )
{
if ( o1.m_oFullTitle < o2.m_oFullTitle )
return true;
else if ( o1.m_oFullTitle == o2.m_oFullTitle )
{
if ( o1.m_lIndex < o2.m_lIndex )
return true;
}
}
return false;
};
};
friend CCompareForNodeMerging;
// for the unique algorithm; modifies the parameters
// -------------------------------------------------
class CMergeSimilarNodes
{
public:
MAXPROFNOTHROW bool operator()( CMaxMiniProfiler_Node_Standard &o1, CMaxMiniProfiler_Node_Standard &o2 )
{
assert( &o1 != &o2 );
if ( ( o1.m_lLevel == o2.m_lLevel ) &&
( o1.m_oFullTitle == o2.m_oFullTitle ) )
{
if ( o1.m_nCount > 0 && o2.m_nCount > 0 )
{
CMaxMiniProfiler_Node_Standard &kept = ( o1.m_lIndex < o2.m_lIndex ) ? o1 : o2;
CMaxMiniProfiler_Node_Standard &thrown = ( o1.m_lIndex < o2.m_lIndex ) ? o2 : o1;
kept.m_nCount++;
kept.m_taDelta.FAdd( thrown.m_taDelta );
kept.m_dDelta = 0;
thrown.m_nCount = 0;
thrown.m_taDelta = CMaxTimerAbstraction();
thrown.m_dDelta = 0;
}
return true;
}
return false;
};
};
friend CMergeSimilarNodes;
protected:
MMPNBString m_oFullTitle;
public:
// initialization
// --------------
MAXPROFNOTHROW void FInitialize( unsigned long lLevel, const char *pszTitle )
{
m_lLevel = lLevel;
m_pszTitle = pszTitle;
};
MAXPROFNOTHROW void FIndex( unsigned long lIndex )
{
m_lIndex = lIndex;
};
MAXPROFNOTHROW void FSetFullTitle( const MMPNBString &roFullTitle )
{
m_oFullTitle = roFullTitle;
};
// access to members
// -----------------
MAXPROFNOTHROW const MMPNBString &roFGetFullTitle() const { return m_oFullTitle; };
};
/*********************************************************************************
/* Class:
/* CMaxMiniProfiler_Node_NoHistory
/* Comments:
/********************************************************************************/
class CMaxMiniProfiler_Node_NoHistory
: public CMaxMiniProfiler_Node_Base
{
public:
// unique key to a profiler node
// -----------------------------
class CKey
{
public:
unsigned long m_lLevel;
ULONG_PTR m_lCheckSum;
const char *m_pszTitle;
public:
MAXPROFNOTHROW CKey(
unsigned long lLevel = 0,
const char *pszTitle = NULL,
ULONG_PTR lCheckSum = 0 )
: m_lLevel( lLevel )
, m_lCheckSum( lCheckSum )
, m_pszTitle( pszTitle )
{
};
};
// comparison of unique keys
// -------------------------
class CCompareKeys
{
public:
MAXPROFNOTHROW bool operator()( const CKey &o1, const CKey &o2 ) const
{
assert( &o1 != &o2 );
if ( o1.m_lLevel < o2.m_lLevel )
return true;
else if ( o1.m_lLevel == o2.m_lLevel )
{
if ( o1.m_pszTitle < o2.m_pszTitle )
return true;
else if ( o1.m_pszTitle == o2.m_pszTitle )
{
if ( o1.m_lCheckSum < o2.m_lCheckSum )
return true;
}
}
return false;
};
};
protected:
CMaxTimerAbstraction m_oInternalOverhead;
ULONG_PTR m_lCheckSum;
public:
MAXPROFNOTHROW CMaxMiniProfiler_Node_NoHistory()
: CMaxMiniProfiler_Node_Base()
, m_lCheckSum( 0 )
{
};
// initialization
// --------------
MAXPROFNOTHROW void FInitialize(
unsigned long lLevel,
const char *pszTitle,
unsigned long lIndex,
const CMaxTimerAbstraction oInternalOverhead )
{
if ( 0 == m_lIndex )
{
m_lLevel = lLevel;
m_pszTitle = pszTitle;
m_lIndex = lIndex;
}
#ifdef _DEBUG
else
{
assert( lLevel == m_lLevel );
assert( pszTitle == m_pszTitle );
}
#endif
m_oInternalOverhead.FAdd( oInternalOverhead );
};
MAXPROFNOTHROW void FSetCheckSum(
ULONG_PTR lCheckSum )
{
m_lCheckSum = lCheckSum;
};
// access to members
// -----------------
MAXPROFNOTHROW const CMaxTimerAbstraction &roFGetInternalOverhead() const { return m_oInternalOverhead; };
MAXPROFNOTHROW ULONG_PTR lFGetCheckSum() const { return m_lCheckSum; };
};
/*********************************************************************************
/* Class:
/* CMaxMiniProfiler_Base
/* Comments:
/********************************************************************************/
class CMaxMiniProfiler_Base
{
protected:
// output file name
const char *m_poFileName;
// internal info
DWORD m_dwThreadId;
CTime m_oStartTimeOfProfilings;
protected:
// Note: the lock in CMaxMultithreadProfiler takes care of protecting
// the static data below in multithread mode
// default values for initialization
static const char *s_poDefaultFileName;
static unsigned long s_lHeapBlockSize;
// BIAS values
static CMaxTimerAbstraction s_oOutOfBraceBiasApproximation;
static CMaxTimerAbstraction s_oInOfBraceBiasApproximation;
static bool s_bBiasIsKnown;
public:
// constructor / destructor
// ------------------------
CMaxMiniProfiler_Base(
const TCHAR * = NULL )
: m_poFileName( s_poDefaultFileName )
, m_dwThreadId( ::GetCurrentThreadId() )
, m_oStartTimeOfProfilings( CTime::GetCurrentTime() )
{
};
~CMaxMiniProfiler_Base()
{
};
// locking - public interface
// --------------------------
void FLockProfiler(){};
void FUnlockProfiler(){};
// bias approximation
// ------------------
// Note: the result of this operation is used at output time uniquely
bool bFIsBiasKnown() const { return s_bBiasIsKnown; };
protected:
// for final output
// ----------------
void FOutputEmptySession()
{
// open the output file
ofstream os( m_poFileName, ios::out | ios::ate );
// just stamp a message saying that there was nothing to profile
CTime t = CTime::GetCurrentTime();
os << endl;
os << "PROFILER INSTANTIATED THE ";
os << t.GetYear() << "/" << t.GetMonth() << "/" << t.GetDay() << " BETWEEN ";
SStampCTime( os, m_oStartTimeOfProfilings ) << " AND ";
SStampCTime( os, t ) << " WAS NOT USED." << endl;
};
void FOutputHeaderCore(
ostream &os,
unsigned long lNumberOfOpenNodes,
const CMaxMiniProfiler_Node_Base &roRootNode,
unsigned long lTotalNumberOfNodes )
{
// stamp the current time in our logfile
CTime t = CTime::GetCurrentTime();
os << endl;
os << "***************************" << endl;
os << "*** @@ProfilingDate=" << t.GetYear() << "/" << t.GetMonth() << "/" << t.GetDay() << endl;
os << "*** @@ProfilingStartTime=";
SStampCTime( os, m_oStartTimeOfProfilings ) << endl;
os << "*** @@ProfilingEndTime=";
SStampCTime( os, t ) << endl;
os << "*** @@ProfilingRange=";
roRootNode.FStampAbsoluteRange( os );
os << endl;
if ( 0 != lNumberOfOpenNodes )
os << "*** "<< lNumberOfOpenNodes << " NODES WERE NOT CLOSED BY THE USER" << endl;
os << "***************************" << endl;
// output the counter's frequency and thread id
os << "*** @@CounterFrequency=" << CMaxTimerAbstraction::oSFrequency() << endl;
os << "*** @@ThreadId=" << ( unsigned long )m_dwThreadId << endl;
// output the profiler's finest possible unit of measurement
CMaxTimerAbstraction origin( 1 ), destination( 1 );
CMaxTimerAbstraction delta( CMaxTimerAbstraction::oSDifference( destination, origin ) );
os << "*** @@FinestMeasurement=";
CMaxMiniProfiler_Node_Base::SStampDeltaInSeconds( os, delta.dFInSeconds() ) << "=" << delta << endl;
// output the profiler's approximated bias
assert( s_bBiasIsKnown );
os << "*** @@OutsideBias=";
CMaxMiniProfiler_Node_Base::SStampDeltaInSeconds( os, s_oOutOfBraceBiasApproximation.dFInSeconds() ) << endl;
os << "*** @@InsideBias=";
CMaxMiniProfiler_Node_Base::SStampDeltaInSeconds( os, s_oInOfBraceBiasApproximation.dFInSeconds() ) << endl;
// output the total number of blocks
os << "*** @@TotalNumberOfBlocks=" << lTotalNumberOfNodes << endl;
};
void FOutputMergedSectionHeader( ostream &os ) const
{
os << "*** @@MergedResults=" << endl;
};
bool bFHistoryOutputDisabled() const
{
return bGIsEnabledEnvVar( MAX_ENV_DISABLE_PROFILING_HISTORY );
};
public:
static ostream& SStampCTime( ostream &os, const CTime &roTime )
{
os << roTime.GetHour() << ":" << roTime.GetMinute() << ":" << roTime.GetSecond();
return os;
};
private:
CMaxMiniProfiler_Base( const CMaxMiniProfiler_Base &o );
const CMaxMiniProfiler_Base& operator =( const CMaxMiniProfiler_Base & );
};
/*********************************************************************************
/* Functions:
/* GOutputProfilings
/* lGGetNumberOfProfilingSubNodes
/* lGDetermineMaxLevelOfProfilings
/* GRemoveInAndOutBiasFromProfilingNodes
/* Comments:
/* Done this way to avoid virtuals at node level (and to have common
/* output code for Standard and NoHistory profilers)
/********************************************************************************/
template <class TVectorItem>
void GOutputProfilings(
ostream &os,
std::vector<TVectorItem> &roProfilings,
unsigned long lMaxLevel,
double dPrecisionThreshold,
bool bOutputAbsoluteTimeRange )
{
std::vector<TVectorItem>::iterator i;
std::vector<TVectorItem>::size_type n;
std::vector<std::vector<TVectorItem>::size_type> parents( 1 + lMaxLevel );
parents[ 0 ] = 0;
for ( i = roProfilings.begin(), n = 0; roProfilings.end() != i; i++, n++ )
{
// signal the validity of the node
assert( 0 != ( *i ).nFGetCount() );
os << ( ( ( ( *i ).dFGetDelta() / ( *i ).nFGetCount() ) < dPrecisionThreshold ) ? "X" : " " );
// output the node
( *i ).FOutput( os );
// register it as the last parent of its level
long currentlevel = ( *i ).lFGetLevel();
parents[ currentlevel ] = n;
// output the % for all parents of the node
os << " @@PERCENT=";
double deltat = ( *i ).dFGetDelta();
for ( long j = currentlevel - 1; j >= 0; j-- )
os << 100.0 * deltat / roProfilings[ parents[ j ] ].dFGetDelta() << "% ";
// output the time range in units
if ( bOutputAbsoluteTimeRange )
{
os << " @@Range=";
( *i ).FStampAbsoluteRange( os );
}
// finish output for this node
os << endl;
}
};
template <class TVectorItem, class TVectorIterator>
unsigned long lGGetNumberOfProfilingSubNodes(
const std::vector<TVectorItem> &roProfilings,
TVectorIterator &roOrg )
{
unsigned long level = ( *roOrg ).lFGetLevel();
unsigned long n;
TVectorIterator i = roOrg;
i++;
for ( n = 0; roProfilings.end() != i; i++, n++ )
if ( ( *i ).lFGetLevel() <= level )
break;
return n;
};
template <class TVectorItem>
unsigned long lGDetermineMaxLevelOfProfilings(
const std::vector<TVectorItem> &roProfilings )
{
unsigned long l = 0;
std::vector<TVectorItem>::const_iterator i;
for ( i = roProfilings.begin(); roProfilings.end() != i; i++ )
if ( ( *i ).lFGetLevel() > l )
l = ( *i ).lFGetLevel();
return l;
};
template <class TVectorItem>
void GRemoveInAndOutBiasFromProfilingNodes(
std::vector<TVectorItem> &roProfilings,
const CMaxTimerAbstraction &roOutOfBraceBiasApproximation,
const CMaxTimerAbstraction &roInOfBraceBiasApproximation )
{
std::vector<TVectorItem>::iterator i;
unsigned long t, k;
for ( i = roProfilings.begin(); roProfilings.end() != i; i++ )
{
CMaxTimerAbstraction &rtaDelta = ( *i ).roFGetDelta();
t = ::lGGetNumberOfProfilingSubNodes( roProfilings, i );
for ( k = 0; k < t; k++ )
rtaDelta.FSubstract( roOutOfBraceBiasApproximation );
for ( k = 0; k < t + 1; k++ )
rtaDelta.FSubstract( roInOfBraceBiasApproximation );
}
};
/*********************************************************************************
/* Class:
/* CMaxMiniProfiler_Standard
/* Comments:
/********************************************************************************/
class CMaxMiniProfiler_Standard
: public CMaxMiniProfiler_Base
{
protected:
typedef std::vector<CMaxMiniProfiler_Node_Standard> MMPNodes;
typedef MMPNodes::size_type MMPNodesRandomAccess;
typedef std::vector<MMPNodesRandomAccess> MMPNodesReferences;
typedef std::stack<MMPNodesRandomAccess, MMPNodesReferences> MMPStack;
typedef MMPStack::size_type MMPStackSizeType;
protected:
// profiling nodes
MMPNodes m_oProfilings;
MMPNodesRandomAccess m_oLastNode;
// stack for nested blocks
MMPStack m_oStack;
// heap acquisition timings
MMPNodes m_oHeapAcquisitionTimings;
public:
// constructor / destructor
// ------------------------
CMaxMiniProfiler_Standard(
const TCHAR *pszSpecificEnabler = NULL )
: CMaxMiniProfiler_Base( pszSpecificEnabler )
, m_oProfilings( 0 )
, m_oLastNode( 0 )
, m_oHeapAcquisitionTimings( 0 )
{
FInitDumpingSession();
};
~CMaxMiniProfiler_Standard()
{
FDumpSession();
FTermDumpingSession();
};
// dumping results - public interface
// ----------------------------------
void FDumpResults( bool bForced = false, bool = true )
{
if ( !bForced )
{
// can dump results only when all profiling nodes are closed
// (except the main one); we don't want to artificially close the nodes
// here at this point
if ( 1 != m_oStack.size() )
{
assert( false );
return;
}
}
// dump
FDumpSession();
FTermDumpingSession();
// prepare for next dump
FInitDumpingSession();
};
// profiling nodes generation
// --------------------------
// Note: FCreateNewNode and FCloseCurrentNode are meant to be as fast as possible;
// also, the bracket between FStart and FStop is as small as possible
void FCreateNewNode( const char *pszTitle )
{
assert( ( 0 == m_oStack.size() ) || ( ::GetCurrentThreadId() == m_dwThreadId ) );
if ( m_oProfilings.size() == m_oLastNode )
FReserveMoreHeap();
// Note: this is time constant
m_oStack.push( m_oLastNode );
CMaxMiniProfiler_Node_Standard &roNode = m_oProfilings[ m_oLastNode++ ];
roNode.FInitialize( static_cast<ULONG>(m_oStack.size()) - 1, pszTitle );
roNode.FStart();
};
void FCloseCurrentNode()
{
assert( ( 1 == m_oStack.size() ) || ( ::GetCurrentThreadId() == m_dwThreadId ) );
// Note: this is time constant
if ( m_oStack.size() > 0 )
{
m_oProfilings[ m_oStack.top() ].FStop();
m_oStack.pop();
}
else
assert( false );
};
// bias approximation
// ------------------
// Note: the result of this operation is used at output time uniquely
void FSetBiasApproximationFrom( unsigned long lBiasSample )
{
unsigned int i;
assert( !s_bBiasIsKnown );
// Note: this function should be called immediately after having created
// 1 BIAS (b) node
// and x NOTHINGNESS (N) subnodes (n1 ... nx),
// where x = lBiasSample
assert( m_oLastNode > 1 + lBiasSample );
// our out of brace bias is equal to (b - (n1 + n2 + ... + nx)) / x
s_oOutOfBraceBiasApproximation = m_oProfilings[ m_oLastNode - ( 1 + lBiasSample ) ].roFGetDelta();
for ( i = lBiasSample; i > 0; i-- )
s_oOutOfBraceBiasApproximation.FSubstract( m_oProfilings[ m_oLastNode - i ].roFGetDelta() );
s_oOutOfBraceBiasApproximation.FDivide( lBiasSample );
// our in of brace bias is equal to ((n1 + n2 + ... + nx) - N.x) / x
// Note: on purpose, we re-evaluate N as many times as there are samples
s_oInOfBraceBiasApproximation = CMaxTimerAbstraction();
CMaxTimerAbstraction delta;
for ( i = lBiasSample; i > 0; i-- )
{
CMaxTimerAbstraction origin( 1 ), destination( 1 );
delta.FAdd( CMaxTimerAbstraction::oSDifference( destination, origin ) );
s_oInOfBraceBiasApproximation.FAdd( m_oProfilings[ m_oLastNode - i ].roFGetDelta() );
}
s_oInOfBraceBiasApproximation.FSubstract( delta );
s_oInOfBraceBiasApproximation.FDivide( lBiasSample );
#if 1
// remove those BIAS and NOTHINGNESS nodes from the profiler's output nodes
MMPNodes::iterator iter;
MMPNodesRandomAccess n;
for ( iter = m_oProfilings.begin(), n = 0; ( m_oProfilings.end() != iter ) && ( n < m_oLastNode - ( 1 + lBiasSample ) ); iter++, n++ );
std::fill( iter, m_oProfilings.end(), CMaxMiniProfiler_Node_Standard() );
m_oLastNode -= ( 1 + lBiasSample );
#endif
s_bBiasIsKnown = true;
};
protected:
// dumping session management
// --------------------------
void FInitDumpingSession()
{
// prepare some heap
FReserveMoreHeap();
// put a main node
FCreateNewNode( MAX_PROFTAGNODE_TOP );
// verify that we start cleanly
assert( 1 == m_oStack.size() );
assert( 0 == m_oStack.top() );
};
void FDumpSession()
{
MMPStackSizeType lNumberOfOpenNodes;
// terminate our main node
FCloseCurrentNode();
// make sure all nodes are closed
lNumberOfOpenNodes = m_oStack.size();
while ( !m_oStack.empty() )
FCloseCurrentNode();
if ( m_oLastNode > 1 )
{
unsigned long lMaxLevel;
// final trimming and initializations
FTrimProfilings();
FIndexProfilings();
lMaxLevel = ::lGDetermineMaxLevelOfProfilings( m_oProfilings );
FComputeFullTitles( lMaxLevel );
// open the output file
ofstream os( m_poFileName, ios::out | ios::ate );
// output the raw profilings
FOutputHeader( os, lNumberOfOpenNodes );
if ( !bFHistoryOutputDisabled() )
FOutputProfilings( os, true, lMaxLevel );
// merge nodes and output merged results
FMergeProfilings();
FOutputMergedSectionHeader( os );
FOutputProfilings( os, false, lMaxLevel );
}
else
FOutputEmptySession();
};
void FTermDumpingSession()
{
while ( !m_oStack.empty() )
m_oStack.pop();
m_oLastNode = 0;
m_oProfilings.erase( m_oProfilings.begin(), m_oProfilings.end() );
m_oHeapAcquisitionTimings.erase( m_oHeapAcquisitionTimings.begin(), m_oHeapAcquisitionTimings.end() );
};
protected:
// for final output
// ----------------
void FOutputHeader( ostream &os, MMPStackSizeType lNumberOfOpenNodes )
{
FOutputHeaderCore( os, static_cast<ULONG>(lNumberOfOpenNodes), m_oProfilings[ 0 ], static_cast<ULONG>(m_oLastNode) );
// output the total number of heap allocations
double dTotalTimeInAllocations = m_oHeapAcquisitionTimings[ 0 ].dFGetDelta();
for ( MMPNodes::iterator i = m_oHeapAcquisitionTimings.begin(); m_oHeapAcquisitionTimings.end() != i; i++ )
dTotalTimeInAllocations += ( *i ).dFGetDelta();
os << "*** @@TotalNumberOfHeapAllocations=" << static_cast<ULONG>(m_oHeapAcquisitionTimings.size()) << "=";
CMaxMiniProfiler_Node_Base::SStampDeltaInSeconds( os, dTotalTimeInAllocations ) << endl;
// output the total profiling overhead
double dTotalOverhead =
( ( double )( m_oLastNode - 1.0 ) * s_oOutOfBraceBiasApproximation.dFInSeconds() ) +
( ( double )m_oLastNode * s_oInOfBraceBiasApproximation.dFInSeconds() );
double dTotalOverheadPercent =
100.0 * ( dTotalOverhead / ( dTotalOverhead + m_oProfilings[ 0 ].dFGetDelta() ) );
os << "*** @@TotalProfilerOverhead=" << dTotalOverheadPercent << "%=";
CMaxMiniProfiler_Node_Base::SStampDeltaInSeconds( os, dTotalOverhead ) << endl;
// that's it
os << "***************************" << endl;
os << "*** @@History=" << endl;
};
void FOutputProfilings( ostream &os, bool bOutputAbsoluteTimeRange, unsigned long lMaxLevel )
{
double dPrecisionThreshold = 2.0 * ( s_oOutOfBraceBiasApproximation.dFInSeconds() + s_oInOfBraceBiasApproximation.dFInSeconds() );
::GOutputProfilings( os, m_oProfilings, lMaxLevel, dPrecisionThreshold, bOutputAbsoluteTimeRange );
};
// final management of profiling nodes
// -----------------------------------
void FTrimProfilings()
{
MMPNodes::iterator i, j;
MMPNodesRandomAccess n;
// find the iterator that corresponds to the last node
for ( i = m_oProfilings.begin(), n = 0; ( m_oProfilings.end() != i ) && ( n < m_oLastNode ); i++, n++ );
// remove uninitialized nodes
m_oProfilings.erase( i, m_oProfilings.end() );
// remove heap allocation timings from affected nodes
for ( i = m_oHeapAcquisitionTimings.begin(); m_oHeapAcquisitionTimings.end() != i; i++ )
for ( j = m_oProfilings.begin(); m_oProfilings.end() != j; j++ )
( *j ).FConditionalRemove( *i, s_oOutOfBraceBiasApproximation );
// remove from nodes the profiling bias
::GRemoveInAndOutBiasFromProfilingNodes(
m_oProfilings, s_oOutOfBraceBiasApproximation, s_oInOfBraceBiasApproximation );
};
void FIndexProfilings()
{
MMPNodes::iterator i;
unsigned long n;
for ( i = m_oProfilings.begin(), n = 1; m_oProfilings.end() != i; i++, n++ )
( *i ).FIndex( n );
};
void FComputeFullTitles( unsigned long lMaxLevel )
{
MMPNodes::iterator i;
MMPNodesRandomAccess j, n;
MMPNodesReferences parents( 1 + lMaxLevel );
parents[ 0 ] = 0;
for ( i = m_oProfilings.begin(), n = 0; m_oProfilings.end() != i; i++, n++ )
{
// register the node as the last parent of its level
unsigned long currentlevel = ( *i ).lFGetLevel();
parents[ currentlevel ] = n;
// compute the iterated node's full title
CMaxMiniProfiler_Node_Base::MMPNBString fulltitle;
for ( j = 0; j <= currentlevel; j++ )
fulltitle += CMaxMiniProfiler_Node_Base::MMPNBString( m_oProfilings[ parents[ j ] ].pszFGetTitle() );
( *i ).FSetFullTitle( fulltitle );
}
};
void FMergeProfilings()
{
MMPNodes::iterator i;
// sort by level/name/index
std::sort( m_oProfilings.begin(), m_oProfilings.end(), CMaxMiniProfiler_Node_Standard::CCompareForNodeMerging() );
// merge the nodes that have same level/name
i = std::unique( m_oProfilings.begin(), m_oProfilings.end(), CMaxMiniProfiler_Node_Standard::CMergeSimilarNodes() );
m_oProfilings.erase( i, m_oProfilings.end() );
// sort by full name
std::sort( m_oProfilings.begin(), m_oProfilings.end(), CMaxMiniProfiler_Node_Standard::CCompareFullTitles() );
};
protected:
// heap management
// ---------------
void FReserveMoreHeap()
{
CMaxMiniProfiler_Node_Standard node;
// log the time we used to generate new heap
node.FStart();
node.FInitialize( 0, MAX_PROFTAGNODE_HEAPALLOCATION );
// reserve a new chunk of nodes
m_oProfilings.reserve( m_oProfilings.size() + s_lHeapBlockSize );
m_oProfilings.insert( m_oProfilings.end(),
m_oProfilings.capacity() - m_oProfilings.size(),
CMaxMiniProfiler_Node_Standard() );
// that's it
m_oHeapAcquisitionTimings.push_back( node );
m_oHeapAcquisitionTimings.back().FStop();
};
private:
CMaxMiniProfiler_Standard( const CMaxMiniProfiler_Standard &o );
const CMaxMiniProfiler_Standard& operator =( const CMaxMiniProfiler_Standard & );
};
/*********************************************************************************
/* Class:
/* CMaxMiniProfiler_NoHistory
/* Comments:
/* This implementation is targetted for massive amounts of nodes
/********************************************************************************/
class CMaxMiniProfiler_NoHistory
: public CMaxMiniProfiler_Base
{
protected:
typedef CMaxMiniProfiler_Node_NoHistory::CKey MMPNHKey;
typedef CMaxMiniProfiler_Node_NoHistory::CCompareKeys MMPNHKeyCompare;
typedef std::map<MMPNHKey, CMaxMiniProfiler_Node_NoHistory, MMPNHKeyCompare> MMPNHNodes;
typedef MMPNHNodes::iterator MMPNHNodesIterator;
typedef std::vector<MMPNHNodesIterator> MMPNHNodesReferences;
typedef std::stack<MMPNHNodesIterator, MMPNHNodesReferences> MMPNHStack;
typedef MMPNHStack::size_type MMPNHStackSizeType;
protected:
typedef std::vector<CMaxMiniProfiler_Node_NoHistory> MMPNHFinalNodes;
typedef MMPNHFinalNodes::iterator MMPNHFinalNodesIterator;
protected:
// profiling nodes
MMPNHNodes m_oProfilings;
unsigned long m_lLastNode;
// stack for nested blocks
MMPNHStack m_oStack;
public:
// constructor / destructor
// ------------------------
CMaxMiniProfiler_NoHistory(
const TCHAR *pszSpecificEnabler = NULL )
: CMaxMiniProfiler_Base( pszSpecificEnabler )
, m_lLastNode( 0 )
{
FInitDumpingSession();
};
~CMaxMiniProfiler_NoHistory()
{
FDumpSession();
FTermDumpingSession();
};
// dumping results - public interface
// ----------------------------------
void FDumpResults( bool bForced = false, bool = true )
{
if ( !bForced )
{
// can dump results only when all profiling nodes are closed
// (except the main one); we don't want to artificially close the nodes
// here at this point
if ( 1 != m_oStack.size() )
{
assert( false );
return;
}
}
// dump
FDumpSession();
FTermDumpingSession();
// prepare for next dump
FInitDumpingSession();
};
// profiling nodes generation
// --------------------------
// Note: FCreateNewNode and FCloseCurrentNode are meant to be as fast as possible;
// also, the bracket between FStart and FStop is as small as possible
void FCreateNewNode( const char *pszTitle )
{
MMPNHNodesIterator i;
assert( ( 0 == m_oStack.size() ) || ( ::GetCurrentThreadId() == m_dwThreadId ) );
// A) this is not time constant
// ----------------------------
// Note: therefore we measure how much time we spend here
CMaxTimerAbstraction before( 1 );
{
// compute the checksum
ULONG_PTR lCheckSum = ( ULONG_PTR )pszTitle;
if ( !m_oStack.empty() )
lCheckSum += ( *m_oStack.top() ).first.m_lCheckSum;
// compute the key
MMPNHKey oKey( static_cast<unsigned long>(m_oStack.size()), pszTitle, lCheckSum );
// get the corresponding node, if any
i = m_oProfilings.find( oKey );
// otherwise, create a new node
if ( m_oProfilings.end() == i )
i = m_oProfilings.insert( MMPNHNodes::value_type( oKey, CMaxMiniProfiler_Node_NoHistory() ) ).first;
}
CMaxTimerAbstraction after( 1 );
// B) this is time constant
// ------------------------
// Note: therefore taken care of by bias computation
CMaxTimerAbstraction oInternalOverhead( CMaxTimerAbstraction::oSDifference( after, before ) );
m_lLastNode++;
( *i ).second.FInitialize( static_cast<unsigned long>(m_oStack.size()), pszTitle, m_lLastNode, oInternalOverhead );
m_oStack.push( i );
( *i ).second.FStart();
};
void FCloseCurrentNode()
{
assert( ( 1 == m_oStack.size() ) || ( ::GetCurrentThreadId() == m_dwThreadId ) );
// Note: this is time constant
if ( m_oStack.size() > 0 )
{
( *m_oStack.top() ).second.FStop();
m_oStack.pop();
}
else
assert( false );
};
// bias approximation
// ------------------
// Note: the result of this operation is used at output time uniquely
void FSetBiasApproximationFrom( unsigned long lBiasSample )
{
unsigned int i;
MMPNHNodes::iterator j, j1, j2;
CMaxTimerAbstraction b, n, ib;
assert( !s_bBiasIsKnown );
// Note: this function should be called immediately after having created
// 1 BIAS (b) node
// and x NOTHINGNESS (N) subnodes (n1 ... nx),
// where x = lBiasSample
assert( m_lLastNode > 1 + lBiasSample );
// find bias and nothingness nodes
// Note: here we search by name, because it's not time critical and
// we don't know the checksum
CMaxMiniProfiler_Node_Base::MMPNBString id_bias( MAX_PROFTAGNODE_BIAS );
CMaxMiniProfiler_Node_Base::MMPNBString id_nothingness( MAX_PROFTAGNODE_NOTHINGNESS );
char cDone = 0;
for ( j = m_oProfilings.begin(); ( m_oProfilings.end() != j ) && ( ( 1 | 2 ) != cDone ); j++ )
{
CMaxMiniProfiler_Node_Base::MMPNBString id_iterated( ( *j ).second.pszFGetTitle() );
if ( id_iterated == id_bias )
{
assert( !( cDone & 1 ) );
b = ( *j ).second.roFGetDelta();
j1 = j;
cDone |= 1;
}
else if ( id_iterated == id_nothingness )
{
assert( !( cDone & 2 ) );
n = ( *j ).second.roFGetDelta();
ib = ( *j ).second.roFGetInternalOverhead();
j2 = j;
cDone |= 2;
}
}
assert( ( 1 | 2 ) == cDone );
if ( cDone & 1 )
m_oProfilings.erase( j1 );
if ( cDone & 2 )
m_oProfilings.erase( j2 );
// our out of brace bias is equal to (b - (n1 + n2 + ... + nx) - (ib1 + ib2 + ... + ibx)) / x
// Note: ib is the internal bias (or overhead), and is taken care of separately
s_oOutOfBraceBiasApproximation = b;
s_oOutOfBraceBiasApproximation.FSubstract( n );
s_oOutOfBraceBiasApproximation.FSubstract( ib );
s_oOutOfBraceBiasApproximation.FDivide( lBiasSample );
// our in of brace bias is equal to ((n1 + n2 + ... + nx) - N.x) / x
// Note: on purpose, we re-evaluate N as many times as there are samples
CMaxTimerAbstraction delta;
for ( i = lBiasSample; i > 0; i-- )
{
CMaxTimerAbstraction origin( 1 ), destination( 1 );
delta.FAdd( CMaxTimerAbstraction::oSDifference( destination, origin ) );
}
s_oInOfBraceBiasApproximation = n;
s_oInOfBraceBiasApproximation.FSubstract( delta );
s_oInOfBraceBiasApproximation.FDivide( lBiasSample );
s_bBiasIsKnown = true;
};
protected:
// dumping session management
// --------------------------
void FInitDumpingSession()
{
// put a main node
FCreateNewNode( MAX_PROFTAGNODE_TOP );
// verify that we start cleanly
assert( 1 == m_oStack.size() );
};
void FDumpSession()
{
MMPNHStackSizeType lNumberOfOpenNodes;
MMPNHFinalNodes oFinalNodes;
unsigned long lMaxLevel;
// terminate our main node
FCloseCurrentNode();
// make sure all nodes are closed
lNumberOfOpenNodes = m_oStack.size();
while ( !m_oStack.empty() )
FCloseCurrentNode();
// get the final list of nodes, sorted by index
FGetFinalNodes( oFinalNodes );
if ( oFinalNodes.size() > 1 )
{
// final trimming and initializations
::GRemoveInAndOutBiasFromProfilingNodes(
oFinalNodes, s_oOutOfBraceBiasApproximation, s_oInOfBraceBiasApproximation );
lMaxLevel = ::lGDetermineMaxLevelOfProfilings( oFinalNodes );
CMaxTimerAbstraction oTotalInternalOverhead = oFRemoveInternalOverheadFromFinalNodes( oFinalNodes );
// open the output file
ofstream os( m_poFileName, ios::out | ios::ate );
// output the raw profilings
FOutputHeader( oFinalNodes, os, lNumberOfOpenNodes, oTotalInternalOverhead );
FOutputFinalNodes( oFinalNodes, os, lMaxLevel );
}
else
FOutputEmptySession();
};
void FTermDumpingSession()
{
while ( !m_oStack.empty() )
m_oStack.pop();
m_oProfilings.erase( m_oProfilings.begin(), m_oProfilings.end() );
};
protected:
// for final output
// ----------------
void FOutputHeader(
MMPNHFinalNodes &roFinalNodes,
ostream &os,
MMPNHStackSizeType lNumberOfOpenNodes,
const CMaxTimerAbstraction &roTotalInternalOverhead )
{
FOutputHeaderCore( os, static_cast<unsigned long>(lNumberOfOpenNodes), roFinalNodes[ 0 ], m_lLastNode );
// output the total profiling overhead
double dTotalOverhead =
( ( double )( m_lLastNode - 1.0 ) * s_oOutOfBraceBiasApproximation.dFInSeconds() ) +
( ( double )m_lLastNode * s_oInOfBraceBiasApproximation.dFInSeconds() ) +
roTotalInternalOverhead.dFInSeconds();
double dTotalOverheadPercent =
100.0 * ( dTotalOverhead / ( dTotalOverhead + roFinalNodes[ 0 ].dFGetDelta() ) );
os << "*** @@TotalProfilerOverhead=" << dTotalOverheadPercent << "%=";
CMaxMiniProfiler_Node_Base::SStampDeltaInSeconds( os, dTotalOverhead ) << endl;
// that's it
os << "***************************" << endl;
FOutputMergedSectionHeader( os );
};
// final management of profiling nodes
// -----------------------------------
void FGetFinalNodes( MMPNHFinalNodes &roFinalNodes )
{
assert( !m_oProfilings.empty() );
// copy the map of profiling nodes into a simple vector
for ( MMPNHNodes::iterator i = m_oProfilings.begin(); m_oProfilings.end() != i; i++ )
{
( *i ).second.FSetCheckSum( ( *i ).first.m_lCheckSum );
roFinalNodes.push_back( ( *i ).second );
}
// sort the vector by nodes indexes
std::sort( roFinalNodes.begin(), roFinalNodes.end(), CMaxMiniProfiler_Node_Base::CCompareIndexes() );
// reparent the lost nodes
// Note: sorting by nodes indexes is not good enough when the profiled code has some
// conditional branches; suppose a new node appears in a branch, its index might
// be greater than nodes that don't belong to that branch; therefore reparenting
// those lost nodes is necessary
// Note: top node doesn't have a parent, so skip it
// Note: this algorithm is O(n2) right now, and could be improved, but since it is
// executed at output time only, I don't care
MMPNHFinalNodesIterator j = roFinalNodes.begin();
j++;
while ( roFinalNodes.end() != j )
{
const MMPNHFinalNodesIterator oldj = j;
bool bWrongParent = false;
unsigned long lTargetLevel = ( *j ).lFGetLevel() - 1;
ULONG_PTR lTargetCheckSum = ( *j ).lFGetCheckSum() - ( ULONG_PTR )( *j ).pszFGetTitle();
// find the real parent of j (must appear before j in the sorted vector)
for ( MMPNHFinalNodesIterator k = j; roFinalNodes.end() != k; k-- )
{
unsigned long lIteratedLevel = ( *k ).lFGetLevel();
// the real parent must have a level equal to lTargetLevel
if ( lIteratedLevel != lTargetLevel )
{
// maybe j didn't even have an immediate wrong parent
if ( lIteratedLevel < lTargetLevel )
bWrongParent = true;
continue;
}
// the parent must have a checksum equal to lTargetCheckSum,
// otherwise it is a wrong parent
if ( ( *k ).lFGetCheckSum() != lTargetCheckSum )
bWrongParent = true;
// we found the real parent
else
{
// if no wrong parent was encountered, nothing to do
if ( !bWrongParent )
{
j++;
break;
}
// otherwise, we must move the node below its real parent
else
{
CMaxMiniProfiler_Node_NoHistory nodecopy = *j;
j++;
k++;
roFinalNodes.erase( oldj );
roFinalNodes.insert( k, nodecopy );
bWrongParent = false;
break;
}
}
}
assert( !bWrongParent );
assert( oldj != j );
}
}
CMaxTimerAbstraction oFRemoveInternalOverheadFromFinalNodes( MMPNHFinalNodes &roFinalNodes )
{
CMaxTimerAbstraction oTotalOverhead;
MMPNHFinalNodes::iterator i;
std::vector<MMPNHFinalNodesIterator> parents;
std::vector<MMPNHFinalNodesIterator>::iterator j;
unsigned long l, s;
for ( i = roFinalNodes.begin(); roFinalNodes.end() != i; i++ )
{
// get the current node level (l) and stack of parents size (s)
l = ( *i ).lFGetLevel();
s = static_cast<unsigned long>(parents.size());
// get the iterated node's internal overhead
const CMaxTimerAbstraction &roOverhead = ( *i ).roFGetInternalOverhead();
oTotalOverhead.FAdd( roOverhead );
// update the stack of parents
if ( s > 0 )
{
while ( s > l )
{
parents.pop_back();
s--;
}
}
assert( l == s );
// remove internal overhead from all parents
for ( j = parents.begin(); parents.end() != j; j++ )
{
assert( ( *j ) != i );
CMaxTimerAbstraction &rtaDelta = ( *( *j ) ).roFGetDelta();
rtaDelta.FSubstract( roOverhead );
}
// insert the current node in the stack of parents
parents.push_back( i );
}
return oTotalOverhead;
};
void FOutputFinalNodes( MMPNHFinalNodes &roFinalNodes, ostream &os, unsigned long lMaxLevel )
{
double dPrecisionThreshold = 2.0 * ( s_oOutOfBraceBiasApproximation.dFInSeconds() + s_oInOfBraceBiasApproximation.dFInSeconds() );
::GOutputProfilings( os, roFinalNodes, lMaxLevel, dPrecisionThreshold, false );
};
private:
CMaxMiniProfiler_NoHistory( const CMaxMiniProfiler_NoHistory &o );
const CMaxMiniProfiler_NoHistory& operator =( const CMaxMiniProfiler_NoHistory & );
};
/*********************************************************************************
/* Class:
/* CMaxMultithreadProfiler
/* Comments:
/* Instantiates and manages one CMaxMiniProfiler per calling thread
/********************************************************************************/
template <class TMiniProfiler>
class CMaxMultithreadProfiler
{
protected:
typedef std::less<DWORD> MTPThreadIdsCompare;
typedef std::map<DWORD, TMiniProfiler*, MTPThreadIdsCompare> MTPMap;
protected:
class __CMaxCriticalSection
{
protected:
CRITICAL_SECTION m_oNTCriticalSection;
public:
__CMaxCriticalSection(){ ::InitializeCriticalSection( &m_oNTCriticalSection ); };
~__CMaxCriticalSection(){ ::DeleteCriticalSection( &m_oNTCriticalSection ); };
bool Lock() const { ::EnterCriticalSection( &( ( __CMaxCriticalSection * )this )->m_oNTCriticalSection ); return true; };
bool Unlock() const { ::LeaveCriticalSection( &( ( __CMaxCriticalSection * )this )->m_oNTCriticalSection ); return true; };
operator CRITICAL_SECTION*() const { return ( CRITICAL_SECTION* )&m_oNTCriticalSection; };
};
protected:
MTPMap m_oProfilers;
__CMaxCriticalSection m_oLockProfilers;
public:
CMaxMultithreadProfiler(
const TCHAR * = NULL )
{
m_oProfilers[ ::GetCurrentThreadId() ] = new TMiniProfiler();
};
~CMaxMultithreadProfiler()
{
if ( !m_oProfilers.empty() )
FFlushProfilers();
};
void FLockProfiler()
{
m_oLockProfilers.Lock();
};
void FUnlockProfiler()
{
m_oLockProfilers.Unlock();
};
void FDumpResults( bool bForced = false, bool bCurrentThreadOnly = true )
{
m_oLockProfilers.Lock();
{
DWORD id = ::GetCurrentThreadId();
MTPMap::iterator i;
if ( m_oProfilers.empty() )
{
m_oLockProfilers.Unlock();
return;
}
for ( i = m_oProfilers.begin(); m_oProfilers.end() != i; i++ )
if ( !bCurrentThreadOnly || ( ( *i ).first == id ) )
( *i ).second->FDumpResults( bForced );
if ( bForced )
FFlushProfilers();
}
m_oLockProfilers.Unlock();
};
void FCreateNewNode( const char *pszTitle )
{
m_oLockProfilers.Lock();
{
DWORD id = ::GetCurrentThreadId();
MTPMap::iterator i = m_oProfilers.find( id );
if ( m_oProfilers.end() != i )
( *i ).second->FCreateNewNode( pszTitle );
else
{
TMiniProfiler *pNewProfiler = new TMiniProfiler();
m_oProfilers[ id ] = pNewProfiler;
pNewProfiler->FCreateNewNode( pszTitle );
}
}
m_oLockProfilers.Unlock();
};
void FCloseCurrentNode()
{
m_oLockProfilers.Lock();
{
DWORD id = ::GetCurrentThreadId();
MTPMap::iterator i = m_oProfilers.find( id );
assert( m_oProfilers.end() != i );
( *i ).second->FCloseCurrentNode();
}
m_oLockProfilers.Unlock();
};
bool bFIsBiasKnown() const
{
bool b;
m_oLockProfilers.Lock();
assert( !m_oProfilers.empty() );
b = ( *m_oProfilers.begin() ).second->bFIsBiasKnown();
m_oLockProfilers.Unlock();
return b;
};
void FSetBiasApproximationFrom( unsigned long lBiasSample )
{
m_oLockProfilers.Lock();
assert( !m_oProfilers.empty() );
( *m_oProfilers.begin() ).second->FSetBiasApproximationFrom( lBiasSample );
m_oLockProfilers.Unlock();
};
protected:
void FFlushProfilers()
{
m_oLockProfilers.Lock();
assert( !m_oProfilers.empty() );
for ( MTPMap::iterator i = m_oProfilers.begin(); m_oProfilers.end() != i; i++ )
delete ( *i ).second;
m_oProfilers.erase( m_oProfilers.begin(), m_oProfilers.end() );
m_oLockProfilers.Unlock();
};
private:
CMaxMultithreadProfiler( const CMaxMultithreadProfiler &o );
const CMaxMultithreadProfiler& operator =( const CMaxMultithreadProfiler & );
};
#endif // }
#ifdef MAX_PROFILING_ENABLED_DLL // {
/*********************************************************************************
/* Class:
/* CMaxProfilingDLLWrapper
/* Comments:
/* For simplified use through the macros defined above
/********************************************************************************/
class CMaxProfilingDLLWrapper
{
protected:
class __CMaxLoadLibrary
{
protected:
HINSTANCE m_hLibrary;
public:
__CMaxLoadLibrary( LPCTSTR pszLibraryFileName )
: m_hLibrary( NULL )
{
if ( NULL != pszLibraryFileName )
m_hLibrary = ::LoadLibrary( pszLibraryFileName );
};
~__CMaxLoadLibrary()
{
if ( NULL != m_hLibrary )
::FreeLibrary( m_hLibrary );
};
operator HINSTANCE() const
{
return m_hLibrary;
};
};
protected:
__CMaxLoadLibrary m_oLibrary;
protected:
void ( *m_pfn_LockProfiler )();
void ( *m_pfn_UnlockProfiler )();
void ( *m_pfn_DumpResults )( bool, bool );
void ( *m_pfn_CreateNewNode )( const char * );
void ( *m_pfn_CloseCurrentNode )();
bool ( *m_pfn_IsBiasKnown )();
void ( *m_pfn_SetBiasApproximationFrom )( unsigned long );
protected:
static void SLockProfiler_Bogus(){};
static void SUnlockProfiler_Bogus(){};
static void SDumpResults_Bogus( bool, bool ){};
static void SCreateNewNode_Bogus( const char * ){};
static void SCloseCurrentNode_Bogus(){};
static bool bSIsBiasKnown_Bogus(){ return true; };
static void SSetBiasApproximationFrom_Bogus( unsigned long ){};
public:
CMaxProfilingDLLWrapper(
const TCHAR *pszSpecificEnabler = NULL )
#ifndef unix
: m_oLibrary( _T( "s:\\ds\\util\\maxprof.dll" ) )
#else
: m_oLibrary( NULL )
#endif
, m_pfn_LockProfiler( NULL )
, m_pfn_UnlockProfiler( NULL )
, m_pfn_DumpResults( NULL )
, m_pfn_CreateNewNode( NULL )
, m_pfn_CloseCurrentNode( NULL )
, m_pfn_IsBiasKnown( NULL )
, m_pfn_SetBiasApproximationFrom( NULL )
{
// if the profiler is enabled, get the dll's entry points for profiling
// (if possible)
if ( bFProfilerEnabled( pszSpecificEnabler ) &&
( NULL != ( HINSTANCE )m_oLibrary ) )
{
m_pfn_LockProfiler = ( void ( * )() )::GetProcAddress( m_oLibrary, "LockProfiler" );
assert( NULL != m_pfn_LockProfiler );
m_pfn_UnlockProfiler = ( void ( * )() )::GetProcAddress( m_oLibrary, "UnlockProfiler" );
assert( NULL != m_pfn_UnlockProfiler );
m_pfn_DumpResults = ( void ( * )( bool, bool ) )::GetProcAddress( m_oLibrary, "DumpResults" );
assert( NULL != m_pfn_DumpResults );
m_pfn_CreateNewNode = ( void ( * )( const char * ) )::GetProcAddress( m_oLibrary, "CreateNewNode" );
assert( NULL != m_pfn_CreateNewNode );
m_pfn_CloseCurrentNode = ( void ( * )() )::GetProcAddress( m_oLibrary, "CloseCurrentNode" );
assert( NULL != m_pfn_CloseCurrentNode );
m_pfn_IsBiasKnown = ( bool ( * )() )::GetProcAddress( m_oLibrary, "IsBiasKnown" );
assert( NULL != m_pfn_IsBiasKnown );
m_pfn_SetBiasApproximationFrom = ( void ( * )( unsigned long ) )::GetProcAddress( m_oLibrary, "SetBiasApproximationFrom" );
assert( NULL != m_pfn_SetBiasApproximationFrom );
}
// otherwise, create bogus entry points
// Note: this technique is preferred to using "if"s on each call, so this
// switch does not affect the profiling mode at all
else
{
m_pfn_LockProfiler = SLockProfiler_Bogus;
m_pfn_UnlockProfiler = SUnlockProfiler_Bogus;
m_pfn_DumpResults = SDumpResults_Bogus;
m_pfn_CreateNewNode = SCreateNewNode_Bogus;
m_pfn_CloseCurrentNode = SCloseCurrentNode_Bogus;
m_pfn_IsBiasKnown = bSIsBiasKnown_Bogus;
m_pfn_SetBiasApproximationFrom = SSetBiasApproximationFrom_Bogus;
}
};
// Note: this is the easiest way to avoid a severe bug in the DLL version of the
// profiler; basically, if a client DLL detaches the profiled process, the
// "titles" maintained by address by the profiling nodes become invalid, and
// can no longer be dereferenced; therefore, to avoid this problem, I make
// sure all profiling nodes are dumped when a client DLL detaches. This
// may affect results in some circumstances, but should be OK in most cases
~CMaxProfilingDLLWrapper()
{ FDumpResults( true, false ); };
void FLockProfiler()
{ ( *m_pfn_LockProfiler )(); };
void FUnlockProfiler()
{ ( *m_pfn_UnlockProfiler )(); };
void FDumpResults( bool bForced = false, bool bCurrentThreadOnly = true )
{ ( *m_pfn_DumpResults )( bForced, bCurrentThreadOnly ); };
void FCreateNewNode( const char *pszTitle )
{ ( *m_pfn_CreateNewNode )( pszTitle ); };
void FCloseCurrentNode()
{ ( *m_pfn_CloseCurrentNode )(); };
bool bFIsBiasKnown() const
{ return ( *m_pfn_IsBiasKnown )(); };
void FSetBiasApproximationFrom( unsigned long lBiasSample )
{ ( *m_pfn_SetBiasApproximationFrom )( lBiasSample ); };
protected:
#ifdef MAX_PROFILING_CONDITIONAL
bool bFProfilerEnabled(
const TCHAR *pszSpecificEnabler ) const
{
// Note: the global enabler allows you to enable/disable
// all "subsystems" at once
// Note: the specific enabler allows you to enable/disable
// specific "subsystems", given that the global
// enabler is set
return ( bGIsEnabledEnvVar( MAX_ENV_ENABLE_PROFILING, MAX_ENV_ALL ) ||
( bGIsEnabledEnvVar( pszSpecificEnabler ) && bGIsEnabledEnvVar( MAX_ENV_ENABLE_PROFILING ) ) );
}
#else
bool bFProfilerEnabled( const TCHAR * ) const
{
return true;
}
#endif
};
#endif // }
#if defined MAX_PROFILING_ENABLED || defined MAX_PROFILING_ENABLED_DLL // {
//#ifdef _DEBUG
// #pragma message( "MAXPROFILER Warning: beware of profilings generated with a DEBUG build." )
//#endif
/*********************************************************************************
/* Class:
/* CMaxProfilingObject
/* Comments:
/* For simplified use through the macros defined above. The typedef
/* allows easy substitution between multi-threaded (default) and
/* single-threaded profilers
/********************************************************************************/
class CMaxProfilingObject
{
public:
#ifdef MAX_PROFILING_ENABLED_DLL
typedef CMaxProfilingDLLWrapper MPOProfiler;
#else
typedef CMaxMultithreadProfiler<CMaxMiniProfiler_Standard> MPOProfiler;
#endif
protected:
static MPOProfiler s_oProfiler;
protected:
class __CBiasApproximation
{
public:
__CBiasApproximation()
{
const unsigned long lBiasSample = 20;
// if bias has already been computed once, do nothing
if ( CMaxProfilingObject::s_oProfiler.bFIsBiasKnown() )
return;
// compute bias through the used profiler
CMaxProfilingObject::s_oProfiler.FLockProfiler();
{
CMaxProfilingObject::SCreateNewNode( MAX_PROFTAGNODE_BIAS );
for ( int i = 0; i < lBiasSample; i++ )
{
CMaxProfilingObject::SCreateNewNode( MAX_PROFTAGNODE_NOTHINGNESS );
CMaxProfilingObject::SCloseCurrentNode();
}
CMaxProfilingObject::SCloseCurrentNode();
CMaxProfilingObject::s_oProfiler.FSetBiasApproximationFrom( lBiasSample );
}
CMaxProfilingObject::s_oProfiler.FUnlockProfiler();
};
};
friend __CBiasApproximation;
protected:
static __CBiasApproximation s_oBiasApproximation;
public:
static void SDumpResults( bool bForced = false, bool bCurrentThreadOnly = true )
{ s_oProfiler.FDumpResults( bForced, bCurrentThreadOnly ); };
static void SCreateNewNode( const char *pszTitle )
{ s_oProfiler.FCreateNewNode( pszTitle ); };
static void SCloseCurrentNode()
{ s_oProfiler.FCloseCurrentNode(); };
};
#endif // }
#ifndef MAX_PROFILING_DLL_IMPLEMENTATION // {
/*********************************************************************************
/* Class:
/* CMaxProfilingBlockWrapper
/* Comments:
/* As a substitute to the macros (Ray's request). Hoping that those inlines
/* disappear completely when profiling is turned off. The alternative
/* would have been to define a new set of macros taking an additional
/* parameter (a unique name for each instance of the wrapper within the
/* same scope)
/* Note:
/* I use nothrow here, but the current implementations don't guaranty
/* that no exception will be thrown
/********************************************************************************/
class CMaxProfilingBlockWrapper
{
public:
MAXPROFNOTHROW CMaxProfilingBlockWrapper( const char *pszTitle )
{ BEGIN_PROFILING_BLOCK( pszTitle ); };
MAXPROFNOTHROW ~CMaxProfilingBlockWrapper()
{ END_PROFILING_BLOCK; };
public:
MAXPROFNOTHROW static void SDump()
{ DUMP_PROFILING_RESULTS; };
};
#endif // }
/*********************************************************************************
/* Comments:
/* Here is the code used to generate maxprof.dll
/*********************************************************************************
#define MAX_PROFILING_DLL_IMPLEMENTATION
#include <iomanip.h>
#include <profile.h>
#pragma warning( disable : 4786 )
__MAX_MINIPROFILER_IMPLEMENTATION
typedef CMaxMultithreadProfiler<CMaxMiniProfiler_NoHistory> GDllProfiler_Type1;
typedef CMaxMultithreadProfiler<CMaxMiniProfiler_Standard> GDllProfiler_Type2;
GDllProfiler_Type1 g_oProfiler;
__declspec(dllexport) void LockProfiler()
{ g_oProfiler.FLockProfiler(); }
__declspec(dllexport) void UnlockProfiler()
{ g_oProfiler.FUnlockProfiler(); }
__declspec(dllexport) void DumpResults( bool bForced, bool bCurrentThreadOnly )
{ g_oProfiler.FDumpResults( bForced, bCurrentThreadOnly ); }
__declspec(dllexport) void CreateNewNode( const char *pszTitle )
{ g_oProfiler.FCreateNewNode( pszTitle ); }
__declspec(dllexport) void CloseCurrentNode()
{ g_oProfiler.FCloseCurrentNode(); }
__declspec(dllexport) bool IsBiasKnown()
{ return g_oProfiler.bFIsBiasKnown(); }
__declspec(dllexport) void SetBiasApproximationFrom( unsigned long lBiasSample )
{ g_oProfiler.FSetBiasApproximationFrom( lBiasSample ); }
#pragma warning( default : 4786 )
/*********************************************************************************
/* Comments:
/* Here is the DEF file used to generate maxprof.dll
/*********************************************************************************
EXPORTS
LockProfiler @1
UnlockProfiler @2
DumpResults @3
CreateNewNode @4
CloseCurrentNode @5
IsBiasKnown @6
SetBiasApproximationFrom @7
/********************************************************************************/
// Note: I don't reenable C4786, and I know it...
#endif // }