Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2357 lines
67 KiB

  1. /*********************************************************************************
  2. /* File:
  3. /* PROFILE.H
  4. /* Author:
  5. /* Max-H. Windisch, SDE-T
  6. /* Date:
  7. /* October 1996
  8. /* Macros:
  9. /* BEGIN_PROFILING_BLOCK
  10. /* END_PROFILING_BLOCK
  11. /* DUMP_PROFILING_RESULTS
  12. /* IMPLEMENT_PROFILING
  13. /* IMPLEMENT_PROFILING_CONDITIONAL
  14. /* Classes:
  15. /* CMaxLargeInteger
  16. /* CMaxTimerAbstraction
  17. /* CMaxMiniProfiler_Node_Base
  18. /* CMaxMiniProfiler_Node_Standard
  19. /* CMaxMiniProfiler_Node_NoHistory
  20. /* CMaxMiniProfiler_Base
  21. /* CMaxMiniProfiler_Standard
  22. /* CMaxMiniProfiler_NoHistory
  23. /* CMaxMultithreadProfiler
  24. /* CMaxProfilingDLLWrapper
  25. /* CMaxProfilingObject
  26. /* CMaxProfilingBlockWrapper
  27. /* Summary:
  28. /* This mini profiler allows you to place BEGIN_PROFILING_BLOCK and
  29. /* END_PROFILING_BLOCK directives in your code, or use the
  30. /* CMaxProfilingBlockWrapper object, and collect results
  31. /* in a logfile on termination of the profiled application (or by
  32. /* using the DUMP_PROFILING_RESULTS macro). The
  33. /* profiling blocks can be nested. Each module (DLL/EXE) using
  34. /* the profiler must use IMPLEMENT_PROFILING or
  35. /* IMPLEMENT_PROFILING_CONDITIONAL exactly once (defines
  36. /* static variables for the profiler)
  37. /* More details:
  38. /* The default result file is c:/result.txt. It is not erased
  39. /* automatically. For each completed instance of a profiler, it
  40. /* contains: 1) a header, 2) the history of all profiled blocks (optional),
  41. /* 3) merged results. For merging, results are sorted by {level, name},
  42. /* merged, then sorted again by {full name}. Therefore, block names
  43. /* must be unique. In any case, absolute results are always
  44. /* given (in seconds)
  45. /* How to enable in your code:
  46. /* To enable the profiler, define MAX_PROFILING_ENABLED before including
  47. /* this file. To use the profiler through s:/ds/util/maxprof.dll
  48. /* (built in release), define MAX_PROFILING_ENABLED_DLL instead. This
  49. /* allows to use one single instance of a profiler from multiple
  50. /* modules
  51. /* Other comments:
  52. /* At runtime, you can disable history output by defining the following
  53. /* environment variable to YES: MAX_DISABLE_PROFILING_HISTORY.
  54. /* In DLL mode, if you define MAX_PROFILING_CONDITIONAL before including
  55. /* this file, the profiler will work only if the following environment
  56. /* variable is defined to YES: MAX_ENABLE_PROFILING
  57. /* Note:
  58. /* It's on purpose that I avoid using virtual methods here
  59. /*
  60. /* (c) Copyright 1996 Microsoft-Softimage Inc.
  61. /********************************************************************************/
  62. #ifndef __MAX_PROFILING_H // {
  63. #define __MAX_PROFILING_H
  64. #include <afx.h> // for CTime and CString
  65. #include <assert.h> // for asserts
  66. #include <fstream.h> // for streams
  67. #include <iomanip.h>
  68. //#pragma warning( disable : 4786 ) // stl antivirus ;-)
  69. //#include <dsstlmfc.h> // for STL
  70. #define MAX_ENV_ENABLE_PROFILING _T( "MAX_ENABLE_PROFILING" )
  71. #define MAX_ENV_DISABLE_PROFILING_HISTORY _T( "MAX_DISABLE_PROFILING_HISTORY" )
  72. #define MAX_ENV_YES _T( "YES" )
  73. #define MAX_ENV_ALL _T( "ALL" )
  74. #if !defined( DS_ON_AXP ) && !defined( _NO_THROW )
  75. #define MAXPROFNOTHROW __declspec( nothrow )
  76. #else
  77. #define MAXPROFNOTHROW
  78. #endif
  79. #define MAX_PROFTAGNODE_TOP "PROFILER: ALL"
  80. #define MAX_PROFTAGNODE_HEAPALLOCATION "PROFILER: HEAPALLOCATION"
  81. #define MAX_PROFTAGNODE_BIAS "PROFILER: BIAS"
  82. #define MAX_PROFTAGNODE_NOTHINGNESS "PROFILER: NOTHINGNESS"
  83. // Note: disable profiling in _SHIP (unless specified otherwise by DS_PROFILE_SHIP),
  84. // and in unix (not sure why)
  85. #if ( defined _SHIP && !defined DS_PROFILE_SHIP ) || defined unix
  86. #undef MAX_PROFILING_ENABLED_DLL
  87. #undef MAX_PROFILING_ENABLED
  88. #endif
  89. /*********************************************************************************
  90. /* Macros:
  91. /* BEGIN_PROFILING_BLOCK
  92. /* END_PROFILING_BLOCK
  93. /* DUMP_PROFILING_RESULTS
  94. /* IMPLEMENT_PROFILING
  95. /* IMPLEMENT_PROFILING_CONDITIONAL
  96. /* Comments:
  97. /* . For simplified use of CMaxMiniProfiler's.
  98. /* . For the comment parameter, use a non-unicode string, without "return"
  99. /* character
  100. /* . For the enabler parameter, use a unicode string (the name of your
  101. /* environment variable)
  102. /* . Use unique comments, since the profiler might use them as sorting keys
  103. /* . The use of DUMP_PROFILING_RESULTS is not compulsory, since profilings
  104. /* are always dumped at the end of a profiling session
  105. /********************************************************************************/
  106. #ifndef unix
  107. #define __MAX_RESULTFILE_NAME "c:\\result.txt"
  108. #else
  109. #define __MAX_RESULTFILE_NAME "result.txt"
  110. #endif
  111. #ifdef MAX_PROFILING_ENABLED_DLL
  112. #define __MAX_MINIPROFILER_IMPLEMENTATION ;
  113. #else
  114. #define __MAX_MINIPROFILER_IMPLEMENTATION \
  115. const char *CMaxMiniProfiler_Base::s_poDefaultFileName = __MAX_RESULTFILE_NAME; \
  116. CMaxTimerAbstraction CMaxMiniProfiler_Base::s_oOutOfBraceBiasApproximation; \
  117. CMaxTimerAbstraction CMaxMiniProfiler_Base::s_oInOfBraceBiasApproximation; \
  118. bool CMaxMiniProfiler_Base::s_bBiasIsKnown = false; \
  119. unsigned long CMaxMiniProfiler_Base::s_lHeapBlockSize = 5000;
  120. #endif
  121. #if defined MAX_PROFILING_ENABLED || defined MAX_PROFILING_ENABLED_DLL // {{
  122. #define BEGIN_PROFILING_BLOCK( comment ) \
  123. CMaxProfilingObject::SCreateNewNode( comment );
  124. #define END_PROFILING_BLOCK \
  125. CMaxProfilingObject::SCloseCurrentNode();
  126. #define DUMP_PROFILING_RESULTS \
  127. CMaxProfilingObject::SDumpResults();
  128. #define IMPLEMENT_PROFILING \
  129. __MAX_MINIPROFILER_IMPLEMENTATION \
  130. CMaxProfilingObject::MPOProfiler CMaxProfilingObject::s_oProfiler; \
  131. CMaxProfilingObject::__CBiasApproximation CMaxProfilingObject::s_oBiasApproximation;
  132. #define IMPLEMENT_PROFILING_CONDITIONAL( enabler ) \
  133. __MAX_MINIPROFILER_IMPLEMENTATION \
  134. CMaxProfilingObject::MPOProfiler CMaxProfilingObject::s_oProfiler( enabler ); \
  135. CMaxProfilingObject::__CBiasApproximation CMaxProfilingObject::s_oBiasApproximation;
  136. #else // }{
  137. #define BEGIN_PROFILING_BLOCK( comment ) ( void )( comment );
  138. #define END_PROFILING_BLOCK ;
  139. #define DUMP_PROFILING_RESULTS ;
  140. #define IMPLEMENT_PROFILING ;
  141. #define IMPLEMENT_PROFILING_CONDITIONAL( enabler ) ;
  142. #endif // }}
  143. #if defined MAX_PROFILING_ENABLED || defined MAX_PROFILING_ENABLED_DLL || defined MAX_PROFILING_DLL_IMPLEMENTATION // {
  144. /*********************************************************************************
  145. /* Helper function:
  146. /* bGIsEnabledEnvVar
  147. /* Comments:
  148. /********************************************************************************/
  149. MAXPROFNOTHROW static inline bool bGIsEnabledEnvVar(
  150. const TCHAR *pszEnvironmentVariableName,
  151. const TCHAR *pszCriteria = MAX_ENV_YES )
  152. {
  153. const int nLength = 80;
  154. TCHAR szBuffer[ nLength ];
  155. DWORD dwValue;
  156. // NULL string means enabled (default)
  157. if ( NULL == pszEnvironmentVariableName )
  158. return true;
  159. dwValue = ::GetEnvironmentVariable(
  160. pszEnvironmentVariableName, szBuffer, nLength );
  161. if ( dwValue > 0 && _tcsicmp( szBuffer, pszCriteria ) == 0 )
  162. return true;
  163. return false;
  164. };
  165. #endif // }
  166. #if defined MAX_PROFILING_ENABLED || defined MAX_PROFILING_DLL_IMPLEMENTATION // {
  167. /*********************************************************************************
  168. /* Class:
  169. /* CMaxLargeInteger
  170. /* Comments:
  171. /* Minimal encapsulation of LARGE_INTEGER, considered as a time value
  172. /********************************************************************************/
  173. class CMaxLargeInteger
  174. {
  175. protected:
  176. LARGE_INTEGER m_oValue;
  177. public:
  178. MAXPROFNOTHROW CMaxLargeInteger( LONG lHighPart = 0, DWORD dwLowPart = 0 )
  179. {
  180. m_oValue.u.HighPart = lHighPart;
  181. m_oValue.u.LowPart = dwLowPart;
  182. }
  183. MAXPROFNOTHROW CMaxLargeInteger( LONGLONG llQuadPart )
  184. {
  185. m_oValue.QuadPart = llQuadPart;
  186. }
  187. MAXPROFNOTHROW CMaxLargeInteger operator +( const CMaxLargeInteger &roAdded ) const
  188. {
  189. return CMaxLargeInteger( m_oValue.QuadPart + roAdded.m_oValue.QuadPart );
  190. }
  191. MAXPROFNOTHROW CMaxLargeInteger operator -( const CMaxLargeInteger &roSubstracted ) const
  192. {
  193. return CMaxLargeInteger( m_oValue.QuadPart - roSubstracted.m_oValue.QuadPart );
  194. }
  195. MAXPROFNOTHROW CMaxLargeInteger operator /( unsigned long lDivisor ) const
  196. {
  197. return CMaxLargeInteger( m_oValue.QuadPart / ( LONGLONG )lDivisor );
  198. }
  199. MAXPROFNOTHROW bool operator <( const CMaxLargeInteger &roCompared ) const
  200. {
  201. return m_oValue.QuadPart < roCompared.m_oValue.QuadPart;
  202. }
  203. MAXPROFNOTHROW operator LARGE_INTEGER*()
  204. {
  205. return &m_oValue;
  206. }
  207. MAXPROFNOTHROW LONG lFGetHighPart() const
  208. {
  209. return m_oValue.u.HighPart;
  210. }
  211. MAXPROFNOTHROW DWORD dwFGetLowPart() const
  212. {
  213. return m_oValue.u.LowPart;
  214. }
  215. MAXPROFNOTHROW double dFInSecondsF( const CMaxLargeInteger &roFreq ) const
  216. {
  217. const DWORD dwMaxDword = 0xffffffff;
  218. double highunit;
  219. assert( 0 == roFreq.m_oValue.u.HighPart && 0 != roFreq.m_oValue.u.LowPart );
  220. highunit = ( ( double )dwMaxDword + 1.0 ) / ( double )roFreq.m_oValue.u.LowPart;
  221. return ( ( ( double )m_oValue.u.HighPart * highunit ) + ( ( double )m_oValue.u.LowPart / roFreq.m_oValue.u.LowPart ) );
  222. }
  223. };
  224. MAXPROFNOTHROW inline ostream& operator<<( ostream &os, const CMaxLargeInteger &val )
  225. {
  226. return os << "(" << ( unsigned long )val.lFGetHighPart() << ";" << ( unsigned long )val.dwFGetLowPart() << ")";
  227. };
  228. /*********************************************************************************
  229. /* Class:
  230. /* CMaxTimerAbstraction
  231. /* Comments:
  232. /* Defines the interface CMaxMiniProfiler's expect from any timer
  233. /* implementation
  234. /********************************************************************************/
  235. class CMaxTimerAbstraction
  236. {
  237. protected:
  238. CMaxLargeInteger m_oTime;
  239. static const CMaxLargeInteger s_oFrequency;
  240. public:
  241. MAXPROFNOTHROW CMaxTimerAbstraction(){ /* assumed to zero its internal value */ }
  242. MAXPROFNOTHROW CMaxTimerAbstraction( int ){ ::QueryPerformanceCounter( m_oTime ); }
  243. MAXPROFNOTHROW CMaxTimerAbstraction( const CMaxTimerAbstraction &roSrc ) : m_oTime( roSrc.m_oTime ){}
  244. MAXPROFNOTHROW const CMaxTimerAbstraction& operator =( const CMaxTimerAbstraction &roSrc ){ m_oTime = roSrc.m_oTime; return *this; }
  245. protected:
  246. // Note: not part of the interface; for internal use only
  247. MAXPROFNOTHROW CMaxTimerAbstraction( const CMaxLargeInteger &roSrc ) : m_oTime( roSrc ){};
  248. public:
  249. MAXPROFNOTHROW void FLog()
  250. {
  251. ::QueryPerformanceCounter( m_oTime );
  252. }
  253. MAXPROFNOTHROW double dFInSeconds() const
  254. {
  255. return m_oTime.dFInSecondsF( s_oFrequency );
  256. }
  257. public:
  258. MAXPROFNOTHROW void FAdd( const CMaxTimerAbstraction &roAdded )
  259. {
  260. m_oTime = m_oTime + roAdded.m_oTime;
  261. }
  262. MAXPROFNOTHROW void FSubstract( const CMaxTimerAbstraction &roSubstracted )
  263. {
  264. #if 0
  265. // special case for negative differences - hide them
  266. if ( m_oTime < roSubstracted.m_oTime )
  267. {
  268. m_oTime = CMaxLargeInteger( 0, 1 );
  269. return;
  270. }
  271. #endif
  272. m_oTime = m_oTime - roSubstracted.m_oTime;
  273. }
  274. MAXPROFNOTHROW void FDivide( unsigned long lDivisor )
  275. {
  276. m_oTime = m_oTime / lDivisor;
  277. }
  278. public:
  279. MAXPROFNOTHROW static CMaxTimerAbstraction oSSum( const CMaxTimerAbstraction &roArg1, const CMaxTimerAbstraction &roArg2 )
  280. {
  281. CMaxTimerAbstraction sum;
  282. sum.m_oTime = roArg1.m_oTime + roArg2.m_oTime;
  283. return sum;
  284. }
  285. MAXPROFNOTHROW static CMaxTimerAbstraction oSDifference( const CMaxTimerAbstraction &roArg1, const CMaxTimerAbstraction &roArg2 )
  286. {
  287. CMaxTimerAbstraction difference;
  288. #if 0
  289. // special case for negative differences - hide them
  290. if ( roArg1.m_oTime < roArg2.m_oTime )
  291. return CMaxTimerAbstraction( CMaxLargeInteger( 0, 1 ) );
  292. #endif
  293. difference.m_oTime = roArg1.m_oTime - roArg2.m_oTime;
  294. return difference;
  295. }
  296. MAXPROFNOTHROW static bool bSLess( const CMaxTimerAbstraction &roArg1, const CMaxTimerAbstraction &roArg2 )
  297. {
  298. return roArg1.m_oTime < roArg2.m_oTime;
  299. }
  300. MAXPROFNOTHROW static CMaxTimerAbstraction oSFrequency()
  301. {
  302. return CMaxTimerAbstraction( s_oFrequency );
  303. }
  304. private:
  305. MAXPROFNOTHROW static CMaxLargeInteger oSCentralFrequency()
  306. {
  307. CMaxLargeInteger frequency;
  308. ::QueryPerformanceFrequency( frequency );
  309. return frequency;
  310. }
  311. friend ostream& operator<<( ostream &os, const CMaxTimerAbstraction &val );
  312. };
  313. MAXPROFNOTHROW inline ostream& operator<<( ostream &os, const CMaxTimerAbstraction &val )
  314. {
  315. return os << val.m_oTime;
  316. };
  317. /*********************************************************************************
  318. /* Class:
  319. /* CMaxMiniProfiler_Node_Base
  320. /* Comments:
  321. /* Basic profiling node that behaves like a chronometer, and provides
  322. /* standard logging services. Both the Standard and NoHistory profilers
  323. /* use this basic implementation
  324. /********************************************************************************/
  325. class CMaxMiniProfiler_Node_Base
  326. {
  327. public:
  328. typedef CString MMPNBString;
  329. public:
  330. // comparison by index
  331. // -------------------
  332. class CCompareIndexes
  333. {
  334. public:
  335. MAXPROFNOTHROW bool operator()( const CMaxMiniProfiler_Node_Base &o1, const CMaxMiniProfiler_Node_Base &o2 ) const
  336. {
  337. assert( &o1 != &o2 );
  338. return ( o1.m_lIndex < o2.m_lIndex );
  339. };
  340. };
  341. friend CCompareIndexes;
  342. protected:
  343. // acquired at initialization
  344. // --------------------------
  345. unsigned long m_lLevel;
  346. const char *m_pszTitle;
  347. unsigned long m_lIndex;
  348. // internal time counting mechanism
  349. // --------------------------------
  350. CMaxTimerAbstraction m_taOrigin;
  351. CMaxTimerAbstraction m_taDelta;
  352. unsigned int m_nCount;
  353. #ifdef _DEBUG
  354. bool m_bIsCounting;
  355. #endif
  356. // for final output
  357. // ----------------
  358. double m_dDelta;
  359. public:
  360. // constructor etc.
  361. // ----------------
  362. // Note: uses default assignment and copy constructor
  363. // Note: it doesn't cost anything to initialize lots of things here - this
  364. // is not done within profiling braces
  365. MAXPROFNOTHROW CMaxMiniProfiler_Node_Base()
  366. : m_lLevel( 0 )
  367. , m_pszTitle( NULL )
  368. , m_lIndex( 0 )
  369. , m_nCount( 0 )
  370. , m_dDelta( 0 )
  371. #ifdef _DEBUG
  372. , m_bIsCounting( false )
  373. #endif
  374. {
  375. };
  376. // chrono
  377. // ------
  378. MAXPROFNOTHROW void FStart()
  379. {
  380. #ifdef _DEBUG
  381. assert( !m_bIsCounting );
  382. m_taOrigin.FLog();
  383. m_nCount++;
  384. m_bIsCounting = true;
  385. #else
  386. m_taOrigin.FLog();
  387. m_nCount++;
  388. #endif
  389. };
  390. MAXPROFNOTHROW void FStop()
  391. {
  392. CMaxTimerAbstraction destination( 1 );
  393. #ifdef _DEBUG
  394. assert( m_bIsCounting );
  395. m_taDelta.FAdd( CMaxTimerAbstraction::oSDifference( destination, m_taOrigin ) );
  396. m_bIsCounting = false;
  397. #else
  398. m_taDelta.FAdd( CMaxTimerAbstraction::oSDifference( destination, m_taOrigin ) );
  399. #endif
  400. };
  401. // access to members
  402. // -----------------
  403. MAXPROFNOTHROW unsigned long lFGetLevel() const { return m_lLevel; };
  404. MAXPROFNOTHROW const char *pszFGetTitle() const { return m_pszTitle; };
  405. MAXPROFNOTHROW unsigned long lFGetIndex() const { return m_lIndex; };
  406. MAXPROFNOTHROW const CMaxTimerAbstraction &roFGetOrigin() const { return m_taOrigin; };
  407. MAXPROFNOTHROW CMaxTimerAbstraction &roFGetDelta() { return m_taDelta; };
  408. MAXPROFNOTHROW unsigned int nFGetCount() const { return m_nCount; };
  409. MAXPROFNOTHROW double dFGetDelta()
  410. {
  411. if ( 0 == m_dDelta )
  412. FComputeDelta();
  413. return m_dDelta;
  414. };
  415. // misc. services
  416. // --------------
  417. MAXPROFNOTHROW bool bFIsIn( const CMaxMiniProfiler_Node_Base &roNode ) const
  418. {
  419. // Note: times cannot be equal, so we don't need to worry about that
  420. if ( CMaxTimerAbstraction::bSLess( m_taOrigin, roNode.m_taOrigin ) )
  421. {
  422. CMaxTimerAbstraction d1 = m_taOrigin;
  423. CMaxTimerAbstraction d2 = roNode.m_taOrigin;
  424. d1.FAdd( m_taDelta );
  425. d2.FAdd( roNode.m_taDelta );
  426. if ( CMaxTimerAbstraction::bSLess( d2, d1 ) )
  427. return true;
  428. }
  429. return false;
  430. };
  431. MAXPROFNOTHROW void FConditionalRemove( const CMaxMiniProfiler_Node_Base &roNode, const CMaxTimerAbstraction &roBias )
  432. {
  433. if ( bFIsIn( roNode ) )
  434. {
  435. CMaxTimerAbstraction d = roNode.m_taDelta;
  436. d.FAdd( roBias );
  437. m_taDelta.FSubstract( d );
  438. }
  439. };
  440. // output to file
  441. // --------------
  442. void FOutput( ostream &os )
  443. {
  444. // don't output dead (merged) nodes
  445. if ( 0 == m_nCount )
  446. return;
  447. // output our index
  448. os << setw( 10 ) << m_lIndex << ": ";
  449. // indent
  450. STab( os, m_lLevel );
  451. // output our title
  452. os << "@@Name=";
  453. if ( NULL != m_pszTitle )
  454. os << m_pszTitle;
  455. // output our block count
  456. os << " @@Count=" << m_nCount;
  457. // output our delta t
  458. os << " @@Duration=";
  459. SStampDeltaInSeconds( os, dFGetDelta() );
  460. };
  461. void FStampAbsoluteRange( ostream &os ) const
  462. {
  463. SStampAbsoluteRange( os, m_taOrigin, m_taDelta );
  464. };
  465. protected:
  466. // computations at output time (outside of profiling)
  467. // --------------------------------------------------
  468. MAXPROFNOTHROW void FComputeDelta()
  469. {
  470. m_dDelta = m_taDelta.dFInSeconds();
  471. };
  472. public:
  473. // mini helpers for facilitated and standardized output of results
  474. // ---------------------------------------------------------------
  475. static ostream& STab( ostream &os, int level )
  476. {
  477. for ( int i = 0; i < level; i++ )
  478. os << " ";
  479. return os;
  480. };
  481. static ostream& SStampDeltaInSeconds( ostream &os, double delta )
  482. {
  483. os << delta << "s";
  484. return os;
  485. };
  486. static ostream& SStampAbsoluteRange( ostream &os, const CMaxTimerAbstraction &rO, const CMaxTimerAbstraction &rD )
  487. {
  488. os << "[origin" << rO;
  489. os << ",duration" << rD << "]";
  490. return os;
  491. };
  492. };
  493. /*********************************************************************************
  494. /* Classes:
  495. /* CMaxMiniProfiler_Node_Standard
  496. /* Comments:
  497. /********************************************************************************/
  498. class CMaxMiniProfiler_Node_Standard
  499. : public CMaxMiniProfiler_Node_Base
  500. {
  501. public:
  502. // comparison by full titles
  503. // -------------------------
  504. class CCompareFullTitles
  505. {
  506. public:
  507. MAXPROFNOTHROW bool operator()( const CMaxMiniProfiler_Node_Standard &o1, const CMaxMiniProfiler_Node_Standard &o2 ) const
  508. {
  509. assert( &o1 != &o2 );
  510. return ( o1.m_oFullTitle < o2.m_oFullTitle );
  511. };
  512. };
  513. friend CCompareFullTitles;
  514. // comparison for node merging (a) level, b) full title, c) index)
  515. // ---------------------------------------------------------------
  516. class CCompareForNodeMerging
  517. {
  518. public:
  519. MAXPROFNOTHROW bool operator()( const CMaxMiniProfiler_Node_Standard &o1, const CMaxMiniProfiler_Node_Standard &o2 ) const
  520. {
  521. assert( &o1 != &o2 );
  522. if ( o1.m_lLevel < o2.m_lLevel )
  523. return true;
  524. else if ( o1.m_lLevel == o2.m_lLevel )
  525. {
  526. if ( o1.m_oFullTitle < o2.m_oFullTitle )
  527. return true;
  528. else if ( o1.m_oFullTitle == o2.m_oFullTitle )
  529. {
  530. if ( o1.m_lIndex < o2.m_lIndex )
  531. return true;
  532. }
  533. }
  534. return false;
  535. };
  536. };
  537. friend CCompareForNodeMerging;
  538. // for the unique algorithm; modifies the parameters
  539. // -------------------------------------------------
  540. class CMergeSimilarNodes
  541. {
  542. public:
  543. MAXPROFNOTHROW bool operator()( CMaxMiniProfiler_Node_Standard &o1, CMaxMiniProfiler_Node_Standard &o2 )
  544. {
  545. assert( &o1 != &o2 );
  546. if ( ( o1.m_lLevel == o2.m_lLevel ) &&
  547. ( o1.m_oFullTitle == o2.m_oFullTitle ) )
  548. {
  549. if ( o1.m_nCount > 0 && o2.m_nCount > 0 )
  550. {
  551. CMaxMiniProfiler_Node_Standard &kept = ( o1.m_lIndex < o2.m_lIndex ) ? o1 : o2;
  552. CMaxMiniProfiler_Node_Standard &thrown = ( o1.m_lIndex < o2.m_lIndex ) ? o2 : o1;
  553. kept.m_nCount++;
  554. kept.m_taDelta.FAdd( thrown.m_taDelta );
  555. kept.m_dDelta = 0;
  556. thrown.m_nCount = 0;
  557. thrown.m_taDelta = CMaxTimerAbstraction();
  558. thrown.m_dDelta = 0;
  559. }
  560. return true;
  561. }
  562. return false;
  563. };
  564. };
  565. friend CMergeSimilarNodes;
  566. protected:
  567. MMPNBString m_oFullTitle;
  568. public:
  569. // initialization
  570. // --------------
  571. MAXPROFNOTHROW void FInitialize( unsigned long lLevel, const char *pszTitle )
  572. {
  573. m_lLevel = lLevel;
  574. m_pszTitle = pszTitle;
  575. };
  576. MAXPROFNOTHROW void FIndex( unsigned long lIndex )
  577. {
  578. m_lIndex = lIndex;
  579. };
  580. MAXPROFNOTHROW void FSetFullTitle( const MMPNBString &roFullTitle )
  581. {
  582. m_oFullTitle = roFullTitle;
  583. };
  584. // access to members
  585. // -----------------
  586. MAXPROFNOTHROW const MMPNBString &roFGetFullTitle() const { return m_oFullTitle; };
  587. };
  588. /*********************************************************************************
  589. /* Class:
  590. /* CMaxMiniProfiler_Node_NoHistory
  591. /* Comments:
  592. /********************************************************************************/
  593. class CMaxMiniProfiler_Node_NoHistory
  594. : public CMaxMiniProfiler_Node_Base
  595. {
  596. public:
  597. // unique key to a profiler node
  598. // -----------------------------
  599. class CKey
  600. {
  601. public:
  602. unsigned long m_lLevel;
  603. ULONG_PTR m_lCheckSum;
  604. const char *m_pszTitle;
  605. public:
  606. MAXPROFNOTHROW CKey(
  607. unsigned long lLevel = 0,
  608. const char *pszTitle = NULL,
  609. ULONG_PTR lCheckSum = 0 )
  610. : m_lLevel( lLevel )
  611. , m_lCheckSum( lCheckSum )
  612. , m_pszTitle( pszTitle )
  613. {
  614. };
  615. };
  616. // comparison of unique keys
  617. // -------------------------
  618. class CCompareKeys
  619. {
  620. public:
  621. MAXPROFNOTHROW bool operator()( const CKey &o1, const CKey &o2 ) const
  622. {
  623. assert( &o1 != &o2 );
  624. if ( o1.m_lLevel < o2.m_lLevel )
  625. return true;
  626. else if ( o1.m_lLevel == o2.m_lLevel )
  627. {
  628. if ( o1.m_pszTitle < o2.m_pszTitle )
  629. return true;
  630. else if ( o1.m_pszTitle == o2.m_pszTitle )
  631. {
  632. if ( o1.m_lCheckSum < o2.m_lCheckSum )
  633. return true;
  634. }
  635. }
  636. return false;
  637. };
  638. };
  639. protected:
  640. CMaxTimerAbstraction m_oInternalOverhead;
  641. ULONG_PTR m_lCheckSum;
  642. public:
  643. MAXPROFNOTHROW CMaxMiniProfiler_Node_NoHistory()
  644. : CMaxMiniProfiler_Node_Base()
  645. , m_lCheckSum( 0 )
  646. {
  647. };
  648. // initialization
  649. // --------------
  650. MAXPROFNOTHROW void FInitialize(
  651. unsigned long lLevel,
  652. const char *pszTitle,
  653. unsigned long lIndex,
  654. const CMaxTimerAbstraction oInternalOverhead )
  655. {
  656. if ( 0 == m_lIndex )
  657. {
  658. m_lLevel = lLevel;
  659. m_pszTitle = pszTitle;
  660. m_lIndex = lIndex;
  661. }
  662. #ifdef _DEBUG
  663. else
  664. {
  665. assert( lLevel == m_lLevel );
  666. assert( pszTitle == m_pszTitle );
  667. }
  668. #endif
  669. m_oInternalOverhead.FAdd( oInternalOverhead );
  670. };
  671. MAXPROFNOTHROW void FSetCheckSum(
  672. ULONG_PTR lCheckSum )
  673. {
  674. m_lCheckSum = lCheckSum;
  675. };
  676. // access to members
  677. // -----------------
  678. MAXPROFNOTHROW const CMaxTimerAbstraction &roFGetInternalOverhead() const { return m_oInternalOverhead; };
  679. MAXPROFNOTHROW ULONG_PTR lFGetCheckSum() const { return m_lCheckSum; };
  680. };
  681. /*********************************************************************************
  682. /* Class:
  683. /* CMaxMiniProfiler_Base
  684. /* Comments:
  685. /********************************************************************************/
  686. class CMaxMiniProfiler_Base
  687. {
  688. protected:
  689. // output file name
  690. const char *m_poFileName;
  691. // internal info
  692. DWORD m_dwThreadId;
  693. CTime m_oStartTimeOfProfilings;
  694. protected:
  695. // Note: the lock in CMaxMultithreadProfiler takes care of protecting
  696. // the static data below in multithread mode
  697. // default values for initialization
  698. static const char *s_poDefaultFileName;
  699. static unsigned long s_lHeapBlockSize;
  700. // BIAS values
  701. static CMaxTimerAbstraction s_oOutOfBraceBiasApproximation;
  702. static CMaxTimerAbstraction s_oInOfBraceBiasApproximation;
  703. static bool s_bBiasIsKnown;
  704. public:
  705. // constructor / destructor
  706. // ------------------------
  707. CMaxMiniProfiler_Base(
  708. const TCHAR * = NULL )
  709. : m_poFileName( s_poDefaultFileName )
  710. , m_dwThreadId( ::GetCurrentThreadId() )
  711. , m_oStartTimeOfProfilings( CTime::GetCurrentTime() )
  712. {
  713. };
  714. ~CMaxMiniProfiler_Base()
  715. {
  716. };
  717. // locking - public interface
  718. // --------------------------
  719. void FLockProfiler(){};
  720. void FUnlockProfiler(){};
  721. // bias approximation
  722. // ------------------
  723. // Note: the result of this operation is used at output time uniquely
  724. bool bFIsBiasKnown() const { return s_bBiasIsKnown; };
  725. protected:
  726. // for final output
  727. // ----------------
  728. void FOutputEmptySession()
  729. {
  730. // open the output file
  731. ofstream os( m_poFileName, ios::out | ios::ate );
  732. // just stamp a message saying that there was nothing to profile
  733. CTime t = CTime::GetCurrentTime();
  734. os << endl;
  735. os << "PROFILER INSTANTIATED THE ";
  736. os << t.GetYear() << "/" << t.GetMonth() << "/" << t.GetDay() << " BETWEEN ";
  737. SStampCTime( os, m_oStartTimeOfProfilings ) << " AND ";
  738. SStampCTime( os, t ) << " WAS NOT USED." << endl;
  739. };
  740. void FOutputHeaderCore(
  741. ostream &os,
  742. unsigned long lNumberOfOpenNodes,
  743. const CMaxMiniProfiler_Node_Base &roRootNode,
  744. unsigned long lTotalNumberOfNodes )
  745. {
  746. // stamp the current time in our logfile
  747. CTime t = CTime::GetCurrentTime();
  748. os << endl;
  749. os << "***************************" << endl;
  750. os << "*** @@ProfilingDate=" << t.GetYear() << "/" << t.GetMonth() << "/" << t.GetDay() << endl;
  751. os << "*** @@ProfilingStartTime=";
  752. SStampCTime( os, m_oStartTimeOfProfilings ) << endl;
  753. os << "*** @@ProfilingEndTime=";
  754. SStampCTime( os, t ) << endl;
  755. os << "*** @@ProfilingRange=";
  756. roRootNode.FStampAbsoluteRange( os );
  757. os << endl;
  758. if ( 0 != lNumberOfOpenNodes )
  759. os << "*** "<< lNumberOfOpenNodes << " NODES WERE NOT CLOSED BY THE USER" << endl;
  760. os << "***************************" << endl;
  761. // output the counter's frequency and thread id
  762. os << "*** @@CounterFrequency=" << CMaxTimerAbstraction::oSFrequency() << endl;
  763. os << "*** @@ThreadId=" << ( unsigned long )m_dwThreadId << endl;
  764. // output the profiler's finest possible unit of measurement
  765. CMaxTimerAbstraction origin( 1 ), destination( 1 );
  766. CMaxTimerAbstraction delta( CMaxTimerAbstraction::oSDifference( destination, origin ) );
  767. os << "*** @@FinestMeasurement=";
  768. CMaxMiniProfiler_Node_Base::SStampDeltaInSeconds( os, delta.dFInSeconds() ) << "=" << delta << endl;
  769. // output the profiler's approximated bias
  770. assert( s_bBiasIsKnown );
  771. os << "*** @@OutsideBias=";
  772. CMaxMiniProfiler_Node_Base::SStampDeltaInSeconds( os, s_oOutOfBraceBiasApproximation.dFInSeconds() ) << endl;
  773. os << "*** @@InsideBias=";
  774. CMaxMiniProfiler_Node_Base::SStampDeltaInSeconds( os, s_oInOfBraceBiasApproximation.dFInSeconds() ) << endl;
  775. // output the total number of blocks
  776. os << "*** @@TotalNumberOfBlocks=" << lTotalNumberOfNodes << endl;
  777. };
  778. void FOutputMergedSectionHeader( ostream &os ) const
  779. {
  780. os << "*** @@MergedResults=" << endl;
  781. };
  782. bool bFHistoryOutputDisabled() const
  783. {
  784. return bGIsEnabledEnvVar( MAX_ENV_DISABLE_PROFILING_HISTORY );
  785. };
  786. public:
  787. static ostream& SStampCTime( ostream &os, const CTime &roTime )
  788. {
  789. os << roTime.GetHour() << ":" << roTime.GetMinute() << ":" << roTime.GetSecond();
  790. return os;
  791. };
  792. private:
  793. CMaxMiniProfiler_Base( const CMaxMiniProfiler_Base &o );
  794. const CMaxMiniProfiler_Base& operator =( const CMaxMiniProfiler_Base & );
  795. };
  796. /*********************************************************************************
  797. /* Functions:
  798. /* GOutputProfilings
  799. /* lGGetNumberOfProfilingSubNodes
  800. /* lGDetermineMaxLevelOfProfilings
  801. /* GRemoveInAndOutBiasFromProfilingNodes
  802. /* Comments:
  803. /* Done this way to avoid virtuals at node level (and to have common
  804. /* output code for Standard and NoHistory profilers)
  805. /********************************************************************************/
  806. template <class TVectorItem>
  807. void GOutputProfilings(
  808. ostream &os,
  809. std::vector<TVectorItem> &roProfilings,
  810. unsigned long lMaxLevel,
  811. double dPrecisionThreshold,
  812. bool bOutputAbsoluteTimeRange )
  813. {
  814. std::vector<TVectorItem>::iterator i;
  815. std::vector<TVectorItem>::size_type n;
  816. std::vector<std::vector<TVectorItem>::size_type> parents( 1 + lMaxLevel );
  817. parents[ 0 ] = 0;
  818. for ( i = roProfilings.begin(), n = 0; roProfilings.end() != i; i++, n++ )
  819. {
  820. // signal the validity of the node
  821. assert( 0 != ( *i ).nFGetCount() );
  822. os << ( ( ( ( *i ).dFGetDelta() / ( *i ).nFGetCount() ) < dPrecisionThreshold ) ? "X" : " " );
  823. // output the node
  824. ( *i ).FOutput( os );
  825. // register it as the last parent of its level
  826. long currentlevel = ( *i ).lFGetLevel();
  827. parents[ currentlevel ] = n;
  828. // output the % for all parents of the node
  829. os << " @@PERCENT=";
  830. double deltat = ( *i ).dFGetDelta();
  831. for ( long j = currentlevel - 1; j >= 0; j-- )
  832. os << 100.0 * deltat / roProfilings[ parents[ j ] ].dFGetDelta() << "% ";
  833. // output the time range in units
  834. if ( bOutputAbsoluteTimeRange )
  835. {
  836. os << " @@Range=";
  837. ( *i ).FStampAbsoluteRange( os );
  838. }
  839. // finish output for this node
  840. os << endl;
  841. }
  842. };
  843. template <class TVectorItem, class TVectorIterator>
  844. unsigned long lGGetNumberOfProfilingSubNodes(
  845. const std::vector<TVectorItem> &roProfilings,
  846. TVectorIterator &roOrg )
  847. {
  848. unsigned long level = ( *roOrg ).lFGetLevel();
  849. unsigned long n;
  850. TVectorIterator i = roOrg;
  851. i++;
  852. for ( n = 0; roProfilings.end() != i; i++, n++ )
  853. if ( ( *i ).lFGetLevel() <= level )
  854. break;
  855. return n;
  856. };
  857. template <class TVectorItem>
  858. unsigned long lGDetermineMaxLevelOfProfilings(
  859. const std::vector<TVectorItem> &roProfilings )
  860. {
  861. unsigned long l = 0;
  862. std::vector<TVectorItem>::const_iterator i;
  863. for ( i = roProfilings.begin(); roProfilings.end() != i; i++ )
  864. if ( ( *i ).lFGetLevel() > l )
  865. l = ( *i ).lFGetLevel();
  866. return l;
  867. };
  868. template <class TVectorItem>
  869. void GRemoveInAndOutBiasFromProfilingNodes(
  870. std::vector<TVectorItem> &roProfilings,
  871. const CMaxTimerAbstraction &roOutOfBraceBiasApproximation,
  872. const CMaxTimerAbstraction &roInOfBraceBiasApproximation )
  873. {
  874. std::vector<TVectorItem>::iterator i;
  875. unsigned long t, k;
  876. for ( i = roProfilings.begin(); roProfilings.end() != i; i++ )
  877. {
  878. CMaxTimerAbstraction &rtaDelta = ( *i ).roFGetDelta();
  879. t = ::lGGetNumberOfProfilingSubNodes( roProfilings, i );
  880. for ( k = 0; k < t; k++ )
  881. rtaDelta.FSubstract( roOutOfBraceBiasApproximation );
  882. for ( k = 0; k < t + 1; k++ )
  883. rtaDelta.FSubstract( roInOfBraceBiasApproximation );
  884. }
  885. };
  886. /*********************************************************************************
  887. /* Class:
  888. /* CMaxMiniProfiler_Standard
  889. /* Comments:
  890. /********************************************************************************/
  891. class CMaxMiniProfiler_Standard
  892. : public CMaxMiniProfiler_Base
  893. {
  894. protected:
  895. typedef std::vector<CMaxMiniProfiler_Node_Standard> MMPNodes;
  896. typedef MMPNodes::size_type MMPNodesRandomAccess;
  897. typedef std::vector<MMPNodesRandomAccess> MMPNodesReferences;
  898. typedef std::stack<MMPNodesRandomAccess, MMPNodesReferences> MMPStack;
  899. typedef MMPStack::size_type MMPStackSizeType;
  900. protected:
  901. // profiling nodes
  902. MMPNodes m_oProfilings;
  903. MMPNodesRandomAccess m_oLastNode;
  904. // stack for nested blocks
  905. MMPStack m_oStack;
  906. // heap acquisition timings
  907. MMPNodes m_oHeapAcquisitionTimings;
  908. public:
  909. // constructor / destructor
  910. // ------------------------
  911. CMaxMiniProfiler_Standard(
  912. const TCHAR *pszSpecificEnabler = NULL )
  913. : CMaxMiniProfiler_Base( pszSpecificEnabler )
  914. , m_oProfilings( 0 )
  915. , m_oLastNode( 0 )
  916. , m_oHeapAcquisitionTimings( 0 )
  917. {
  918. FInitDumpingSession();
  919. };
  920. ~CMaxMiniProfiler_Standard()
  921. {
  922. FDumpSession();
  923. FTermDumpingSession();
  924. };
  925. // dumping results - public interface
  926. // ----------------------------------
  927. void FDumpResults( bool bForced = false, bool = true )
  928. {
  929. if ( !bForced )
  930. {
  931. // can dump results only when all profiling nodes are closed
  932. // (except the main one); we don't want to artificially close the nodes
  933. // here at this point
  934. if ( 1 != m_oStack.size() )
  935. {
  936. assert( false );
  937. return;
  938. }
  939. }
  940. // dump
  941. FDumpSession();
  942. FTermDumpingSession();
  943. // prepare for next dump
  944. FInitDumpingSession();
  945. };
  946. // profiling nodes generation
  947. // --------------------------
  948. // Note: FCreateNewNode and FCloseCurrentNode are meant to be as fast as possible;
  949. // also, the bracket between FStart and FStop is as small as possible
  950. void FCreateNewNode( const char *pszTitle )
  951. {
  952. assert( ( 0 == m_oStack.size() ) || ( ::GetCurrentThreadId() == m_dwThreadId ) );
  953. if ( m_oProfilings.size() == m_oLastNode )
  954. FReserveMoreHeap();
  955. // Note: this is time constant
  956. m_oStack.push( m_oLastNode );
  957. CMaxMiniProfiler_Node_Standard &roNode = m_oProfilings[ m_oLastNode++ ];
  958. roNode.FInitialize( static_cast<ULONG>(m_oStack.size()) - 1, pszTitle );
  959. roNode.FStart();
  960. };
  961. void FCloseCurrentNode()
  962. {
  963. assert( ( 1 == m_oStack.size() ) || ( ::GetCurrentThreadId() == m_dwThreadId ) );
  964. // Note: this is time constant
  965. if ( m_oStack.size() > 0 )
  966. {
  967. m_oProfilings[ m_oStack.top() ].FStop();
  968. m_oStack.pop();
  969. }
  970. else
  971. assert( false );
  972. };
  973. // bias approximation
  974. // ------------------
  975. // Note: the result of this operation is used at output time uniquely
  976. void FSetBiasApproximationFrom( unsigned long lBiasSample )
  977. {
  978. unsigned int i;
  979. assert( !s_bBiasIsKnown );
  980. // Note: this function should be called immediately after having created
  981. // 1 BIAS (b) node
  982. // and x NOTHINGNESS (N) subnodes (n1 ... nx),
  983. // where x = lBiasSample
  984. assert( m_oLastNode > 1 + lBiasSample );
  985. // our out of brace bias is equal to (b - (n1 + n2 + ... + nx)) / x
  986. s_oOutOfBraceBiasApproximation = m_oProfilings[ m_oLastNode - ( 1 + lBiasSample ) ].roFGetDelta();
  987. for ( i = lBiasSample; i > 0; i-- )
  988. s_oOutOfBraceBiasApproximation.FSubstract( m_oProfilings[ m_oLastNode - i ].roFGetDelta() );
  989. s_oOutOfBraceBiasApproximation.FDivide( lBiasSample );
  990. // our in of brace bias is equal to ((n1 + n2 + ... + nx) - N.x) / x
  991. // Note: on purpose, we re-evaluate N as many times as there are samples
  992. s_oInOfBraceBiasApproximation = CMaxTimerAbstraction();
  993. CMaxTimerAbstraction delta;
  994. for ( i = lBiasSample; i > 0; i-- )
  995. {
  996. CMaxTimerAbstraction origin( 1 ), destination( 1 );
  997. delta.FAdd( CMaxTimerAbstraction::oSDifference( destination, origin ) );
  998. s_oInOfBraceBiasApproximation.FAdd( m_oProfilings[ m_oLastNode - i ].roFGetDelta() );
  999. }
  1000. s_oInOfBraceBiasApproximation.FSubstract( delta );
  1001. s_oInOfBraceBiasApproximation.FDivide( lBiasSample );
  1002. #if 1
  1003. // remove those BIAS and NOTHINGNESS nodes from the profiler's output nodes
  1004. MMPNodes::iterator iter;
  1005. MMPNodesRandomAccess n;
  1006. for ( iter = m_oProfilings.begin(), n = 0; ( m_oProfilings.end() != iter ) && ( n < m_oLastNode - ( 1 + lBiasSample ) ); iter++, n++ );
  1007. std::fill( iter, m_oProfilings.end(), CMaxMiniProfiler_Node_Standard() );
  1008. m_oLastNode -= ( 1 + lBiasSample );
  1009. #endif
  1010. s_bBiasIsKnown = true;
  1011. };
  1012. protected:
  1013. // dumping session management
  1014. // --------------------------
  1015. void FInitDumpingSession()
  1016. {
  1017. // prepare some heap
  1018. FReserveMoreHeap();
  1019. // put a main node
  1020. FCreateNewNode( MAX_PROFTAGNODE_TOP );
  1021. // verify that we start cleanly
  1022. assert( 1 == m_oStack.size() );
  1023. assert( 0 == m_oStack.top() );
  1024. };
  1025. void FDumpSession()
  1026. {
  1027. MMPStackSizeType lNumberOfOpenNodes;
  1028. // terminate our main node
  1029. FCloseCurrentNode();
  1030. // make sure all nodes are closed
  1031. lNumberOfOpenNodes = m_oStack.size();
  1032. while ( !m_oStack.empty() )
  1033. FCloseCurrentNode();
  1034. if ( m_oLastNode > 1 )
  1035. {
  1036. unsigned long lMaxLevel;
  1037. // final trimming and initializations
  1038. FTrimProfilings();
  1039. FIndexProfilings();
  1040. lMaxLevel = ::lGDetermineMaxLevelOfProfilings( m_oProfilings );
  1041. FComputeFullTitles( lMaxLevel );
  1042. // open the output file
  1043. ofstream os( m_poFileName, ios::out | ios::ate );
  1044. // output the raw profilings
  1045. FOutputHeader( os, lNumberOfOpenNodes );
  1046. if ( !bFHistoryOutputDisabled() )
  1047. FOutputProfilings( os, true, lMaxLevel );
  1048. // merge nodes and output merged results
  1049. FMergeProfilings();
  1050. FOutputMergedSectionHeader( os );
  1051. FOutputProfilings( os, false, lMaxLevel );
  1052. }
  1053. else
  1054. FOutputEmptySession();
  1055. };
  1056. void FTermDumpingSession()
  1057. {
  1058. while ( !m_oStack.empty() )
  1059. m_oStack.pop();
  1060. m_oLastNode = 0;
  1061. m_oProfilings.erase( m_oProfilings.begin(), m_oProfilings.end() );
  1062. m_oHeapAcquisitionTimings.erase( m_oHeapAcquisitionTimings.begin(), m_oHeapAcquisitionTimings.end() );
  1063. };
  1064. protected:
  1065. // for final output
  1066. // ----------------
  1067. void FOutputHeader( ostream &os, MMPStackSizeType lNumberOfOpenNodes )
  1068. {
  1069. FOutputHeaderCore( os, static_cast<ULONG>(lNumberOfOpenNodes), m_oProfilings[ 0 ], static_cast<ULONG>(m_oLastNode) );
  1070. // output the total number of heap allocations
  1071. double dTotalTimeInAllocations = m_oHeapAcquisitionTimings[ 0 ].dFGetDelta();
  1072. for ( MMPNodes::iterator i = m_oHeapAcquisitionTimings.begin(); m_oHeapAcquisitionTimings.end() != i; i++ )
  1073. dTotalTimeInAllocations += ( *i ).dFGetDelta();
  1074. os << "*** @@TotalNumberOfHeapAllocations=" << static_cast<ULONG>(m_oHeapAcquisitionTimings.size()) << "=";
  1075. CMaxMiniProfiler_Node_Base::SStampDeltaInSeconds( os, dTotalTimeInAllocations ) << endl;
  1076. // output the total profiling overhead
  1077. double dTotalOverhead =
  1078. ( ( double )( m_oLastNode - 1.0 ) * s_oOutOfBraceBiasApproximation.dFInSeconds() ) +
  1079. ( ( double )m_oLastNode * s_oInOfBraceBiasApproximation.dFInSeconds() );
  1080. double dTotalOverheadPercent =
  1081. 100.0 * ( dTotalOverhead / ( dTotalOverhead + m_oProfilings[ 0 ].dFGetDelta() ) );
  1082. os << "*** @@TotalProfilerOverhead=" << dTotalOverheadPercent << "%=";
  1083. CMaxMiniProfiler_Node_Base::SStampDeltaInSeconds( os, dTotalOverhead ) << endl;
  1084. // that's it
  1085. os << "***************************" << endl;
  1086. os << "*** @@History=" << endl;
  1087. };
  1088. void FOutputProfilings( ostream &os, bool bOutputAbsoluteTimeRange, unsigned long lMaxLevel )
  1089. {
  1090. double dPrecisionThreshold = 2.0 * ( s_oOutOfBraceBiasApproximation.dFInSeconds() + s_oInOfBraceBiasApproximation.dFInSeconds() );
  1091. ::GOutputProfilings( os, m_oProfilings, lMaxLevel, dPrecisionThreshold, bOutputAbsoluteTimeRange );
  1092. };
  1093. // final management of profiling nodes
  1094. // -----------------------------------
  1095. void FTrimProfilings()
  1096. {
  1097. MMPNodes::iterator i, j;
  1098. MMPNodesRandomAccess n;
  1099. // find the iterator that corresponds to the last node
  1100. for ( i = m_oProfilings.begin(), n = 0; ( m_oProfilings.end() != i ) && ( n < m_oLastNode ); i++, n++ );
  1101. // remove uninitialized nodes
  1102. m_oProfilings.erase( i, m_oProfilings.end() );
  1103. // remove heap allocation timings from affected nodes
  1104. for ( i = m_oHeapAcquisitionTimings.begin(); m_oHeapAcquisitionTimings.end() != i; i++ )
  1105. for ( j = m_oProfilings.begin(); m_oProfilings.end() != j; j++ )
  1106. ( *j ).FConditionalRemove( *i, s_oOutOfBraceBiasApproximation );
  1107. // remove from nodes the profiling bias
  1108. ::GRemoveInAndOutBiasFromProfilingNodes(
  1109. m_oProfilings, s_oOutOfBraceBiasApproximation, s_oInOfBraceBiasApproximation );
  1110. };
  1111. void FIndexProfilings()
  1112. {
  1113. MMPNodes::iterator i;
  1114. unsigned long n;
  1115. for ( i = m_oProfilings.begin(), n = 1; m_oProfilings.end() != i; i++, n++ )
  1116. ( *i ).FIndex( n );
  1117. };
  1118. void FComputeFullTitles( unsigned long lMaxLevel )
  1119. {
  1120. MMPNodes::iterator i;
  1121. MMPNodesRandomAccess j, n;
  1122. MMPNodesReferences parents( 1 + lMaxLevel );
  1123. parents[ 0 ] = 0;
  1124. for ( i = m_oProfilings.begin(), n = 0; m_oProfilings.end() != i; i++, n++ )
  1125. {
  1126. // register the node as the last parent of its level
  1127. unsigned long currentlevel = ( *i ).lFGetLevel();
  1128. parents[ currentlevel ] = n;
  1129. // compute the iterated node's full title
  1130. CMaxMiniProfiler_Node_Base::MMPNBString fulltitle;
  1131. for ( j = 0; j <= currentlevel; j++ )
  1132. fulltitle += CMaxMiniProfiler_Node_Base::MMPNBString( m_oProfilings[ parents[ j ] ].pszFGetTitle() );
  1133. ( *i ).FSetFullTitle( fulltitle );
  1134. }
  1135. };
  1136. void FMergeProfilings()
  1137. {
  1138. MMPNodes::iterator i;
  1139. // sort by level/name/index
  1140. std::sort( m_oProfilings.begin(), m_oProfilings.end(), CMaxMiniProfiler_Node_Standard::CCompareForNodeMerging() );
  1141. // merge the nodes that have same level/name
  1142. i = std::unique( m_oProfilings.begin(), m_oProfilings.end(), CMaxMiniProfiler_Node_Standard::CMergeSimilarNodes() );
  1143. m_oProfilings.erase( i, m_oProfilings.end() );
  1144. // sort by full name
  1145. std::sort( m_oProfilings.begin(), m_oProfilings.end(), CMaxMiniProfiler_Node_Standard::CCompareFullTitles() );
  1146. };
  1147. protected:
  1148. // heap management
  1149. // ---------------
  1150. void FReserveMoreHeap()
  1151. {
  1152. CMaxMiniProfiler_Node_Standard node;
  1153. // log the time we used to generate new heap
  1154. node.FStart();
  1155. node.FInitialize( 0, MAX_PROFTAGNODE_HEAPALLOCATION );
  1156. // reserve a new chunk of nodes
  1157. m_oProfilings.reserve( m_oProfilings.size() + s_lHeapBlockSize );
  1158. m_oProfilings.insert( m_oProfilings.end(),
  1159. m_oProfilings.capacity() - m_oProfilings.size(),
  1160. CMaxMiniProfiler_Node_Standard() );
  1161. // that's it
  1162. m_oHeapAcquisitionTimings.push_back( node );
  1163. m_oHeapAcquisitionTimings.back().FStop();
  1164. };
  1165. private:
  1166. CMaxMiniProfiler_Standard( const CMaxMiniProfiler_Standard &o );
  1167. const CMaxMiniProfiler_Standard& operator =( const CMaxMiniProfiler_Standard & );
  1168. };
  1169. /*********************************************************************************
  1170. /* Class:
  1171. /* CMaxMiniProfiler_NoHistory
  1172. /* Comments:
  1173. /* This implementation is targetted for massive amounts of nodes
  1174. /********************************************************************************/
  1175. class CMaxMiniProfiler_NoHistory
  1176. : public CMaxMiniProfiler_Base
  1177. {
  1178. protected:
  1179. typedef CMaxMiniProfiler_Node_NoHistory::CKey MMPNHKey;
  1180. typedef CMaxMiniProfiler_Node_NoHistory::CCompareKeys MMPNHKeyCompare;
  1181. typedef std::map<MMPNHKey, CMaxMiniProfiler_Node_NoHistory, MMPNHKeyCompare> MMPNHNodes;
  1182. typedef MMPNHNodes::iterator MMPNHNodesIterator;
  1183. typedef std::vector<MMPNHNodesIterator> MMPNHNodesReferences;
  1184. typedef std::stack<MMPNHNodesIterator, MMPNHNodesReferences> MMPNHStack;
  1185. typedef MMPNHStack::size_type MMPNHStackSizeType;
  1186. protected:
  1187. typedef std::vector<CMaxMiniProfiler_Node_NoHistory> MMPNHFinalNodes;
  1188. typedef MMPNHFinalNodes::iterator MMPNHFinalNodesIterator;
  1189. protected:
  1190. // profiling nodes
  1191. MMPNHNodes m_oProfilings;
  1192. unsigned long m_lLastNode;
  1193. // stack for nested blocks
  1194. MMPNHStack m_oStack;
  1195. public:
  1196. // constructor / destructor
  1197. // ------------------------
  1198. CMaxMiniProfiler_NoHistory(
  1199. const TCHAR *pszSpecificEnabler = NULL )
  1200. : CMaxMiniProfiler_Base( pszSpecificEnabler )
  1201. , m_lLastNode( 0 )
  1202. {
  1203. FInitDumpingSession();
  1204. };
  1205. ~CMaxMiniProfiler_NoHistory()
  1206. {
  1207. FDumpSession();
  1208. FTermDumpingSession();
  1209. };
  1210. // dumping results - public interface
  1211. // ----------------------------------
  1212. void FDumpResults( bool bForced = false, bool = true )
  1213. {
  1214. if ( !bForced )
  1215. {
  1216. // can dump results only when all profiling nodes are closed
  1217. // (except the main one); we don't want to artificially close the nodes
  1218. // here at this point
  1219. if ( 1 != m_oStack.size() )
  1220. {
  1221. assert( false );
  1222. return;
  1223. }
  1224. }
  1225. // dump
  1226. FDumpSession();
  1227. FTermDumpingSession();
  1228. // prepare for next dump
  1229. FInitDumpingSession();
  1230. };
  1231. // profiling nodes generation
  1232. // --------------------------
  1233. // Note: FCreateNewNode and FCloseCurrentNode are meant to be as fast as possible;
  1234. // also, the bracket between FStart and FStop is as small as possible
  1235. void FCreateNewNode( const char *pszTitle )
  1236. {
  1237. MMPNHNodesIterator i;
  1238. assert( ( 0 == m_oStack.size() ) || ( ::GetCurrentThreadId() == m_dwThreadId ) );
  1239. // A) this is not time constant
  1240. // ----------------------------
  1241. // Note: therefore we measure how much time we spend here
  1242. CMaxTimerAbstraction before( 1 );
  1243. {
  1244. // compute the checksum
  1245. ULONG_PTR lCheckSum = ( ULONG_PTR )pszTitle;
  1246. if ( !m_oStack.empty() )
  1247. lCheckSum += ( *m_oStack.top() ).first.m_lCheckSum;
  1248. // compute the key
  1249. MMPNHKey oKey( static_cast<unsigned long>(m_oStack.size()), pszTitle, lCheckSum );
  1250. // get the corresponding node, if any
  1251. i = m_oProfilings.find( oKey );
  1252. // otherwise, create a new node
  1253. if ( m_oProfilings.end() == i )
  1254. i = m_oProfilings.insert( MMPNHNodes::value_type( oKey, CMaxMiniProfiler_Node_NoHistory() ) ).first;
  1255. }
  1256. CMaxTimerAbstraction after( 1 );
  1257. // B) this is time constant
  1258. // ------------------------
  1259. // Note: therefore taken care of by bias computation
  1260. CMaxTimerAbstraction oInternalOverhead( CMaxTimerAbstraction::oSDifference( after, before ) );
  1261. m_lLastNode++;
  1262. ( *i ).second.FInitialize( static_cast<unsigned long>(m_oStack.size()), pszTitle, m_lLastNode, oInternalOverhead );
  1263. m_oStack.push( i );
  1264. ( *i ).second.FStart();
  1265. };
  1266. void FCloseCurrentNode()
  1267. {
  1268. assert( ( 1 == m_oStack.size() ) || ( ::GetCurrentThreadId() == m_dwThreadId ) );
  1269. // Note: this is time constant
  1270. if ( m_oStack.size() > 0 )
  1271. {
  1272. ( *m_oStack.top() ).second.FStop();
  1273. m_oStack.pop();
  1274. }
  1275. else
  1276. assert( false );
  1277. };
  1278. // bias approximation
  1279. // ------------------
  1280. // Note: the result of this operation is used at output time uniquely
  1281. void FSetBiasApproximationFrom( unsigned long lBiasSample )
  1282. {
  1283. unsigned int i;
  1284. MMPNHNodes::iterator j, j1, j2;
  1285. CMaxTimerAbstraction b, n, ib;
  1286. assert( !s_bBiasIsKnown );
  1287. // Note: this function should be called immediately after having created
  1288. // 1 BIAS (b) node
  1289. // and x NOTHINGNESS (N) subnodes (n1 ... nx),
  1290. // where x = lBiasSample
  1291. assert( m_lLastNode > 1 + lBiasSample );
  1292. // find bias and nothingness nodes
  1293. // Note: here we search by name, because it's not time critical and
  1294. // we don't know the checksum
  1295. CMaxMiniProfiler_Node_Base::MMPNBString id_bias( MAX_PROFTAGNODE_BIAS );
  1296. CMaxMiniProfiler_Node_Base::MMPNBString id_nothingness( MAX_PROFTAGNODE_NOTHINGNESS );
  1297. char cDone = 0;
  1298. for ( j = m_oProfilings.begin(); ( m_oProfilings.end() != j ) && ( ( 1 | 2 ) != cDone ); j++ )
  1299. {
  1300. CMaxMiniProfiler_Node_Base::MMPNBString id_iterated( ( *j ).second.pszFGetTitle() );
  1301. if ( id_iterated == id_bias )
  1302. {
  1303. assert( !( cDone & 1 ) );
  1304. b = ( *j ).second.roFGetDelta();
  1305. j1 = j;
  1306. cDone |= 1;
  1307. }
  1308. else if ( id_iterated == id_nothingness )
  1309. {
  1310. assert( !( cDone & 2 ) );
  1311. n = ( *j ).second.roFGetDelta();
  1312. ib = ( *j ).second.roFGetInternalOverhead();
  1313. j2 = j;
  1314. cDone |= 2;
  1315. }
  1316. }
  1317. assert( ( 1 | 2 ) == cDone );
  1318. if ( cDone & 1 )
  1319. m_oProfilings.erase( j1 );
  1320. if ( cDone & 2 )
  1321. m_oProfilings.erase( j2 );
  1322. // our out of brace bias is equal to (b - (n1 + n2 + ... + nx) - (ib1 + ib2 + ... + ibx)) / x
  1323. // Note: ib is the internal bias (or overhead), and is taken care of separately
  1324. s_oOutOfBraceBiasApproximation = b;
  1325. s_oOutOfBraceBiasApproximation.FSubstract( n );
  1326. s_oOutOfBraceBiasApproximation.FSubstract( ib );
  1327. s_oOutOfBraceBiasApproximation.FDivide( lBiasSample );
  1328. // our in of brace bias is equal to ((n1 + n2 + ... + nx) - N.x) / x
  1329. // Note: on purpose, we re-evaluate N as many times as there are samples
  1330. CMaxTimerAbstraction delta;
  1331. for ( i = lBiasSample; i > 0; i-- )
  1332. {
  1333. CMaxTimerAbstraction origin( 1 ), destination( 1 );
  1334. delta.FAdd( CMaxTimerAbstraction::oSDifference( destination, origin ) );
  1335. }
  1336. s_oInOfBraceBiasApproximation = n;
  1337. s_oInOfBraceBiasApproximation.FSubstract( delta );
  1338. s_oInOfBraceBiasApproximation.FDivide( lBiasSample );
  1339. s_bBiasIsKnown = true;
  1340. };
  1341. protected:
  1342. // dumping session management
  1343. // --------------------------
  1344. void FInitDumpingSession()
  1345. {
  1346. // put a main node
  1347. FCreateNewNode( MAX_PROFTAGNODE_TOP );
  1348. // verify that we start cleanly
  1349. assert( 1 == m_oStack.size() );
  1350. };
  1351. void FDumpSession()
  1352. {
  1353. MMPNHStackSizeType lNumberOfOpenNodes;
  1354. MMPNHFinalNodes oFinalNodes;
  1355. unsigned long lMaxLevel;
  1356. // terminate our main node
  1357. FCloseCurrentNode();
  1358. // make sure all nodes are closed
  1359. lNumberOfOpenNodes = m_oStack.size();
  1360. while ( !m_oStack.empty() )
  1361. FCloseCurrentNode();
  1362. // get the final list of nodes, sorted by index
  1363. FGetFinalNodes( oFinalNodes );
  1364. if ( oFinalNodes.size() > 1 )
  1365. {
  1366. // final trimming and initializations
  1367. ::GRemoveInAndOutBiasFromProfilingNodes(
  1368. oFinalNodes, s_oOutOfBraceBiasApproximation, s_oInOfBraceBiasApproximation );
  1369. lMaxLevel = ::lGDetermineMaxLevelOfProfilings( oFinalNodes );
  1370. CMaxTimerAbstraction oTotalInternalOverhead = oFRemoveInternalOverheadFromFinalNodes( oFinalNodes );
  1371. // open the output file
  1372. ofstream os( m_poFileName, ios::out | ios::ate );
  1373. // output the raw profilings
  1374. FOutputHeader( oFinalNodes, os, lNumberOfOpenNodes, oTotalInternalOverhead );
  1375. FOutputFinalNodes( oFinalNodes, os, lMaxLevel );
  1376. }
  1377. else
  1378. FOutputEmptySession();
  1379. };
  1380. void FTermDumpingSession()
  1381. {
  1382. while ( !m_oStack.empty() )
  1383. m_oStack.pop();
  1384. m_oProfilings.erase( m_oProfilings.begin(), m_oProfilings.end() );
  1385. };
  1386. protected:
  1387. // for final output
  1388. // ----------------
  1389. void FOutputHeader(
  1390. MMPNHFinalNodes &roFinalNodes,
  1391. ostream &os,
  1392. MMPNHStackSizeType lNumberOfOpenNodes,
  1393. const CMaxTimerAbstraction &roTotalInternalOverhead )
  1394. {
  1395. FOutputHeaderCore( os, static_cast<unsigned long>(lNumberOfOpenNodes), roFinalNodes[ 0 ], m_lLastNode );
  1396. // output the total profiling overhead
  1397. double dTotalOverhead =
  1398. ( ( double )( m_lLastNode - 1.0 ) * s_oOutOfBraceBiasApproximation.dFInSeconds() ) +
  1399. ( ( double )m_lLastNode * s_oInOfBraceBiasApproximation.dFInSeconds() ) +
  1400. roTotalInternalOverhead.dFInSeconds();
  1401. double dTotalOverheadPercent =
  1402. 100.0 * ( dTotalOverhead / ( dTotalOverhead + roFinalNodes[ 0 ].dFGetDelta() ) );
  1403. os << "*** @@TotalProfilerOverhead=" << dTotalOverheadPercent << "%=";
  1404. CMaxMiniProfiler_Node_Base::SStampDeltaInSeconds( os, dTotalOverhead ) << endl;
  1405. // that's it
  1406. os << "***************************" << endl;
  1407. FOutputMergedSectionHeader( os );
  1408. };
  1409. // final management of profiling nodes
  1410. // -----------------------------------
  1411. void FGetFinalNodes( MMPNHFinalNodes &roFinalNodes )
  1412. {
  1413. assert( !m_oProfilings.empty() );
  1414. // copy the map of profiling nodes into a simple vector
  1415. for ( MMPNHNodes::iterator i = m_oProfilings.begin(); m_oProfilings.end() != i; i++ )
  1416. {
  1417. ( *i ).second.FSetCheckSum( ( *i ).first.m_lCheckSum );
  1418. roFinalNodes.push_back( ( *i ).second );
  1419. }
  1420. // sort the vector by nodes indexes
  1421. std::sort( roFinalNodes.begin(), roFinalNodes.end(), CMaxMiniProfiler_Node_Base::CCompareIndexes() );
  1422. // reparent the lost nodes
  1423. // Note: sorting by nodes indexes is not good enough when the profiled code has some
  1424. // conditional branches; suppose a new node appears in a branch, its index might
  1425. // be greater than nodes that don't belong to that branch; therefore reparenting
  1426. // those lost nodes is necessary
  1427. // Note: top node doesn't have a parent, so skip it
  1428. // Note: this algorithm is O(n2) right now, and could be improved, but since it is
  1429. // executed at output time only, I don't care
  1430. MMPNHFinalNodesIterator j = roFinalNodes.begin();
  1431. j++;
  1432. while ( roFinalNodes.end() != j )
  1433. {
  1434. const MMPNHFinalNodesIterator oldj = j;
  1435. bool bWrongParent = false;
  1436. unsigned long lTargetLevel = ( *j ).lFGetLevel() - 1;
  1437. ULONG_PTR lTargetCheckSum = ( *j ).lFGetCheckSum() - ( ULONG_PTR )( *j ).pszFGetTitle();
  1438. // find the real parent of j (must appear before j in the sorted vector)
  1439. for ( MMPNHFinalNodesIterator k = j; roFinalNodes.end() != k; k-- )
  1440. {
  1441. unsigned long lIteratedLevel = ( *k ).lFGetLevel();
  1442. // the real parent must have a level equal to lTargetLevel
  1443. if ( lIteratedLevel != lTargetLevel )
  1444. {
  1445. // maybe j didn't even have an immediate wrong parent
  1446. if ( lIteratedLevel < lTargetLevel )
  1447. bWrongParent = true;
  1448. continue;
  1449. }
  1450. // the parent must have a checksum equal to lTargetCheckSum,
  1451. // otherwise it is a wrong parent
  1452. if ( ( *k ).lFGetCheckSum() != lTargetCheckSum )
  1453. bWrongParent = true;
  1454. // we found the real parent
  1455. else
  1456. {
  1457. // if no wrong parent was encountered, nothing to do
  1458. if ( !bWrongParent )
  1459. {
  1460. j++;
  1461. break;
  1462. }
  1463. // otherwise, we must move the node below its real parent
  1464. else
  1465. {
  1466. CMaxMiniProfiler_Node_NoHistory nodecopy = *j;
  1467. j++;
  1468. k++;
  1469. roFinalNodes.erase( oldj );
  1470. roFinalNodes.insert( k, nodecopy );
  1471. bWrongParent = false;
  1472. break;
  1473. }
  1474. }
  1475. }
  1476. assert( !bWrongParent );
  1477. assert( oldj != j );
  1478. }
  1479. }
  1480. CMaxTimerAbstraction oFRemoveInternalOverheadFromFinalNodes( MMPNHFinalNodes &roFinalNodes )
  1481. {
  1482. CMaxTimerAbstraction oTotalOverhead;
  1483. MMPNHFinalNodes::iterator i;
  1484. std::vector<MMPNHFinalNodesIterator> parents;
  1485. std::vector<MMPNHFinalNodesIterator>::iterator j;
  1486. unsigned long l, s;
  1487. for ( i = roFinalNodes.begin(); roFinalNodes.end() != i; i++ )
  1488. {
  1489. // get the current node level (l) and stack of parents size (s)
  1490. l = ( *i ).lFGetLevel();
  1491. s = static_cast<unsigned long>(parents.size());
  1492. // get the iterated node's internal overhead
  1493. const CMaxTimerAbstraction &roOverhead = ( *i ).roFGetInternalOverhead();
  1494. oTotalOverhead.FAdd( roOverhead );
  1495. // update the stack of parents
  1496. if ( s > 0 )
  1497. {
  1498. while ( s > l )
  1499. {
  1500. parents.pop_back();
  1501. s--;
  1502. }
  1503. }
  1504. assert( l == s );
  1505. // remove internal overhead from all parents
  1506. for ( j = parents.begin(); parents.end() != j; j++ )
  1507. {
  1508. assert( ( *j ) != i );
  1509. CMaxTimerAbstraction &rtaDelta = ( *( *j ) ).roFGetDelta();
  1510. rtaDelta.FSubstract( roOverhead );
  1511. }
  1512. // insert the current node in the stack of parents
  1513. parents.push_back( i );
  1514. }
  1515. return oTotalOverhead;
  1516. };
  1517. void FOutputFinalNodes( MMPNHFinalNodes &roFinalNodes, ostream &os, unsigned long lMaxLevel )
  1518. {
  1519. double dPrecisionThreshold = 2.0 * ( s_oOutOfBraceBiasApproximation.dFInSeconds() + s_oInOfBraceBiasApproximation.dFInSeconds() );
  1520. ::GOutputProfilings( os, roFinalNodes, lMaxLevel, dPrecisionThreshold, false );
  1521. };
  1522. private:
  1523. CMaxMiniProfiler_NoHistory( const CMaxMiniProfiler_NoHistory &o );
  1524. const CMaxMiniProfiler_NoHistory& operator =( const CMaxMiniProfiler_NoHistory & );
  1525. };
  1526. /*********************************************************************************
  1527. /* Class:
  1528. /* CMaxMultithreadProfiler
  1529. /* Comments:
  1530. /* Instantiates and manages one CMaxMiniProfiler per calling thread
  1531. /********************************************************************************/
  1532. template <class TMiniProfiler>
  1533. class CMaxMultithreadProfiler
  1534. {
  1535. protected:
  1536. typedef std::less<DWORD> MTPThreadIdsCompare;
  1537. typedef std::map<DWORD, TMiniProfiler*, MTPThreadIdsCompare> MTPMap;
  1538. protected:
  1539. class __CMaxCriticalSection
  1540. {
  1541. protected:
  1542. CRITICAL_SECTION m_oNTCriticalSection;
  1543. public:
  1544. __CMaxCriticalSection(){ ::InitializeCriticalSection( &m_oNTCriticalSection ); };
  1545. ~__CMaxCriticalSection(){ ::DeleteCriticalSection( &m_oNTCriticalSection ); };
  1546. bool Lock() const { ::EnterCriticalSection( &( ( __CMaxCriticalSection * )this )->m_oNTCriticalSection ); return true; };
  1547. bool Unlock() const { ::LeaveCriticalSection( &( ( __CMaxCriticalSection * )this )->m_oNTCriticalSection ); return true; };
  1548. operator CRITICAL_SECTION*() const { return ( CRITICAL_SECTION* )&m_oNTCriticalSection; };
  1549. };
  1550. protected:
  1551. MTPMap m_oProfilers;
  1552. __CMaxCriticalSection m_oLockProfilers;
  1553. public:
  1554. CMaxMultithreadProfiler(
  1555. const TCHAR * = NULL )
  1556. {
  1557. m_oProfilers[ ::GetCurrentThreadId() ] = new TMiniProfiler();
  1558. };
  1559. ~CMaxMultithreadProfiler()
  1560. {
  1561. if ( !m_oProfilers.empty() )
  1562. FFlushProfilers();
  1563. };
  1564. void FLockProfiler()
  1565. {
  1566. m_oLockProfilers.Lock();
  1567. };
  1568. void FUnlockProfiler()
  1569. {
  1570. m_oLockProfilers.Unlock();
  1571. };
  1572. void FDumpResults( bool bForced = false, bool bCurrentThreadOnly = true )
  1573. {
  1574. m_oLockProfilers.Lock();
  1575. {
  1576. DWORD id = ::GetCurrentThreadId();
  1577. MTPMap::iterator i;
  1578. if ( m_oProfilers.empty() )
  1579. {
  1580. m_oLockProfilers.Unlock();
  1581. return;
  1582. }
  1583. for ( i = m_oProfilers.begin(); m_oProfilers.end() != i; i++ )
  1584. if ( !bCurrentThreadOnly || ( ( *i ).first == id ) )
  1585. ( *i ).second->FDumpResults( bForced );
  1586. if ( bForced )
  1587. FFlushProfilers();
  1588. }
  1589. m_oLockProfilers.Unlock();
  1590. };
  1591. void FCreateNewNode( const char *pszTitle )
  1592. {
  1593. m_oLockProfilers.Lock();
  1594. {
  1595. DWORD id = ::GetCurrentThreadId();
  1596. MTPMap::iterator i = m_oProfilers.find( id );
  1597. if ( m_oProfilers.end() != i )
  1598. ( *i ).second->FCreateNewNode( pszTitle );
  1599. else
  1600. {
  1601. TMiniProfiler *pNewProfiler = new TMiniProfiler();
  1602. m_oProfilers[ id ] = pNewProfiler;
  1603. pNewProfiler->FCreateNewNode( pszTitle );
  1604. }
  1605. }
  1606. m_oLockProfilers.Unlock();
  1607. };
  1608. void FCloseCurrentNode()
  1609. {
  1610. m_oLockProfilers.Lock();
  1611. {
  1612. DWORD id = ::GetCurrentThreadId();
  1613. MTPMap::iterator i = m_oProfilers.find( id );
  1614. assert( m_oProfilers.end() != i );
  1615. ( *i ).second->FCloseCurrentNode();
  1616. }
  1617. m_oLockProfilers.Unlock();
  1618. };
  1619. bool bFIsBiasKnown() const
  1620. {
  1621. bool b;
  1622. m_oLockProfilers.Lock();
  1623. assert( !m_oProfilers.empty() );
  1624. b = ( *m_oProfilers.begin() ).second->bFIsBiasKnown();
  1625. m_oLockProfilers.Unlock();
  1626. return b;
  1627. };
  1628. void FSetBiasApproximationFrom( unsigned long lBiasSample )
  1629. {
  1630. m_oLockProfilers.Lock();
  1631. assert( !m_oProfilers.empty() );
  1632. ( *m_oProfilers.begin() ).second->FSetBiasApproximationFrom( lBiasSample );
  1633. m_oLockProfilers.Unlock();
  1634. };
  1635. protected:
  1636. void FFlushProfilers()
  1637. {
  1638. m_oLockProfilers.Lock();
  1639. assert( !m_oProfilers.empty() );
  1640. for ( MTPMap::iterator i = m_oProfilers.begin(); m_oProfilers.end() != i; i++ )
  1641. delete ( *i ).second;
  1642. m_oProfilers.erase( m_oProfilers.begin(), m_oProfilers.end() );
  1643. m_oLockProfilers.Unlock();
  1644. };
  1645. private:
  1646. CMaxMultithreadProfiler( const CMaxMultithreadProfiler &o );
  1647. const CMaxMultithreadProfiler& operator =( const CMaxMultithreadProfiler & );
  1648. };
  1649. #endif // }
  1650. #ifdef MAX_PROFILING_ENABLED_DLL // {
  1651. /*********************************************************************************
  1652. /* Class:
  1653. /* CMaxProfilingDLLWrapper
  1654. /* Comments:
  1655. /* For simplified use through the macros defined above
  1656. /********************************************************************************/
  1657. class CMaxProfilingDLLWrapper
  1658. {
  1659. protected:
  1660. class __CMaxLoadLibrary
  1661. {
  1662. protected:
  1663. HINSTANCE m_hLibrary;
  1664. public:
  1665. __CMaxLoadLibrary( LPCTSTR pszLibraryFileName )
  1666. : m_hLibrary( NULL )
  1667. {
  1668. if ( NULL != pszLibraryFileName )
  1669. m_hLibrary = ::LoadLibrary( pszLibraryFileName );
  1670. };
  1671. ~__CMaxLoadLibrary()
  1672. {
  1673. if ( NULL != m_hLibrary )
  1674. ::FreeLibrary( m_hLibrary );
  1675. };
  1676. operator HINSTANCE() const
  1677. {
  1678. return m_hLibrary;
  1679. };
  1680. };
  1681. protected:
  1682. __CMaxLoadLibrary m_oLibrary;
  1683. protected:
  1684. void ( *m_pfn_LockProfiler )();
  1685. void ( *m_pfn_UnlockProfiler )();
  1686. void ( *m_pfn_DumpResults )( bool, bool );
  1687. void ( *m_pfn_CreateNewNode )( const char * );
  1688. void ( *m_pfn_CloseCurrentNode )();
  1689. bool ( *m_pfn_IsBiasKnown )();
  1690. void ( *m_pfn_SetBiasApproximationFrom )( unsigned long );
  1691. protected:
  1692. static void SLockProfiler_Bogus(){};
  1693. static void SUnlockProfiler_Bogus(){};
  1694. static void SDumpResults_Bogus( bool, bool ){};
  1695. static void SCreateNewNode_Bogus( const char * ){};
  1696. static void SCloseCurrentNode_Bogus(){};
  1697. static bool bSIsBiasKnown_Bogus(){ return true; };
  1698. static void SSetBiasApproximationFrom_Bogus( unsigned long ){};
  1699. public:
  1700. CMaxProfilingDLLWrapper(
  1701. const TCHAR *pszSpecificEnabler = NULL )
  1702. #ifndef unix
  1703. : m_oLibrary( _T( "s:\\ds\\util\\maxprof.dll" ) )
  1704. #else
  1705. : m_oLibrary( NULL )
  1706. #endif
  1707. , m_pfn_LockProfiler( NULL )
  1708. , m_pfn_UnlockProfiler( NULL )
  1709. , m_pfn_DumpResults( NULL )
  1710. , m_pfn_CreateNewNode( NULL )
  1711. , m_pfn_CloseCurrentNode( NULL )
  1712. , m_pfn_IsBiasKnown( NULL )
  1713. , m_pfn_SetBiasApproximationFrom( NULL )
  1714. {
  1715. // if the profiler is enabled, get the dll's entry points for profiling
  1716. // (if possible)
  1717. if ( bFProfilerEnabled( pszSpecificEnabler ) &&
  1718. ( NULL != ( HINSTANCE )m_oLibrary ) )
  1719. {
  1720. m_pfn_LockProfiler = ( void ( * )() )::GetProcAddress( m_oLibrary, "LockProfiler" );
  1721. assert( NULL != m_pfn_LockProfiler );
  1722. m_pfn_UnlockProfiler = ( void ( * )() )::GetProcAddress( m_oLibrary, "UnlockProfiler" );
  1723. assert( NULL != m_pfn_UnlockProfiler );
  1724. m_pfn_DumpResults = ( void ( * )( bool, bool ) )::GetProcAddress( m_oLibrary, "DumpResults" );
  1725. assert( NULL != m_pfn_DumpResults );
  1726. m_pfn_CreateNewNode = ( void ( * )( const char * ) )::GetProcAddress( m_oLibrary, "CreateNewNode" );
  1727. assert( NULL != m_pfn_CreateNewNode );
  1728. m_pfn_CloseCurrentNode = ( void ( * )() )::GetProcAddress( m_oLibrary, "CloseCurrentNode" );
  1729. assert( NULL != m_pfn_CloseCurrentNode );
  1730. m_pfn_IsBiasKnown = ( bool ( * )() )::GetProcAddress( m_oLibrary, "IsBiasKnown" );
  1731. assert( NULL != m_pfn_IsBiasKnown );
  1732. m_pfn_SetBiasApproximationFrom = ( void ( * )( unsigned long ) )::GetProcAddress( m_oLibrary, "SetBiasApproximationFrom" );
  1733. assert( NULL != m_pfn_SetBiasApproximationFrom );
  1734. }
  1735. // otherwise, create bogus entry points
  1736. // Note: this technique is preferred to using "if"s on each call, so this
  1737. // switch does not affect the profiling mode at all
  1738. else
  1739. {
  1740. m_pfn_LockProfiler = SLockProfiler_Bogus;
  1741. m_pfn_UnlockProfiler = SUnlockProfiler_Bogus;
  1742. m_pfn_DumpResults = SDumpResults_Bogus;
  1743. m_pfn_CreateNewNode = SCreateNewNode_Bogus;
  1744. m_pfn_CloseCurrentNode = SCloseCurrentNode_Bogus;
  1745. m_pfn_IsBiasKnown = bSIsBiasKnown_Bogus;
  1746. m_pfn_SetBiasApproximationFrom = SSetBiasApproximationFrom_Bogus;
  1747. }
  1748. };
  1749. // Note: this is the easiest way to avoid a severe bug in the DLL version of the
  1750. // profiler; basically, if a client DLL detaches the profiled process, the
  1751. // "titles" maintained by address by the profiling nodes become invalid, and
  1752. // can no longer be dereferenced; therefore, to avoid this problem, I make
  1753. // sure all profiling nodes are dumped when a client DLL detaches. This
  1754. // may affect results in some circumstances, but should be OK in most cases
  1755. ~CMaxProfilingDLLWrapper()
  1756. { FDumpResults( true, false ); };
  1757. void FLockProfiler()
  1758. { ( *m_pfn_LockProfiler )(); };
  1759. void FUnlockProfiler()
  1760. { ( *m_pfn_UnlockProfiler )(); };
  1761. void FDumpResults( bool bForced = false, bool bCurrentThreadOnly = true )
  1762. { ( *m_pfn_DumpResults )( bForced, bCurrentThreadOnly ); };
  1763. void FCreateNewNode( const char *pszTitle )
  1764. { ( *m_pfn_CreateNewNode )( pszTitle ); };
  1765. void FCloseCurrentNode()
  1766. { ( *m_pfn_CloseCurrentNode )(); };
  1767. bool bFIsBiasKnown() const
  1768. { return ( *m_pfn_IsBiasKnown )(); };
  1769. void FSetBiasApproximationFrom( unsigned long lBiasSample )
  1770. { ( *m_pfn_SetBiasApproximationFrom )( lBiasSample ); };
  1771. protected:
  1772. #ifdef MAX_PROFILING_CONDITIONAL
  1773. bool bFProfilerEnabled(
  1774. const TCHAR *pszSpecificEnabler ) const
  1775. {
  1776. // Note: the global enabler allows you to enable/disable
  1777. // all "subsystems" at once
  1778. // Note: the specific enabler allows you to enable/disable
  1779. // specific "subsystems", given that the global
  1780. // enabler is set
  1781. return ( bGIsEnabledEnvVar( MAX_ENV_ENABLE_PROFILING, MAX_ENV_ALL ) ||
  1782. ( bGIsEnabledEnvVar( pszSpecificEnabler ) && bGIsEnabledEnvVar( MAX_ENV_ENABLE_PROFILING ) ) );
  1783. }
  1784. #else
  1785. bool bFProfilerEnabled( const TCHAR * ) const
  1786. {
  1787. return true;
  1788. }
  1789. #endif
  1790. };
  1791. #endif // }
  1792. #if defined MAX_PROFILING_ENABLED || defined MAX_PROFILING_ENABLED_DLL // {
  1793. //#ifdef _DEBUG
  1794. // #pragma message( "MAXPROFILER Warning: beware of profilings generated with a DEBUG build." )
  1795. //#endif
  1796. /*********************************************************************************
  1797. /* Class:
  1798. /* CMaxProfilingObject
  1799. /* Comments:
  1800. /* For simplified use through the macros defined above. The typedef
  1801. /* allows easy substitution between multi-threaded (default) and
  1802. /* single-threaded profilers
  1803. /********************************************************************************/
  1804. class CMaxProfilingObject
  1805. {
  1806. public:
  1807. #ifdef MAX_PROFILING_ENABLED_DLL
  1808. typedef CMaxProfilingDLLWrapper MPOProfiler;
  1809. #else
  1810. typedef CMaxMultithreadProfiler<CMaxMiniProfiler_Standard> MPOProfiler;
  1811. #endif
  1812. protected:
  1813. static MPOProfiler s_oProfiler;
  1814. protected:
  1815. class __CBiasApproximation
  1816. {
  1817. public:
  1818. __CBiasApproximation()
  1819. {
  1820. const unsigned long lBiasSample = 20;
  1821. // if bias has already been computed once, do nothing
  1822. if ( CMaxProfilingObject::s_oProfiler.bFIsBiasKnown() )
  1823. return;
  1824. // compute bias through the used profiler
  1825. CMaxProfilingObject::s_oProfiler.FLockProfiler();
  1826. {
  1827. CMaxProfilingObject::SCreateNewNode( MAX_PROFTAGNODE_BIAS );
  1828. for ( int i = 0; i < lBiasSample; i++ )
  1829. {
  1830. CMaxProfilingObject::SCreateNewNode( MAX_PROFTAGNODE_NOTHINGNESS );
  1831. CMaxProfilingObject::SCloseCurrentNode();
  1832. }
  1833. CMaxProfilingObject::SCloseCurrentNode();
  1834. CMaxProfilingObject::s_oProfiler.FSetBiasApproximationFrom( lBiasSample );
  1835. }
  1836. CMaxProfilingObject::s_oProfiler.FUnlockProfiler();
  1837. };
  1838. };
  1839. friend __CBiasApproximation;
  1840. protected:
  1841. static __CBiasApproximation s_oBiasApproximation;
  1842. public:
  1843. static void SDumpResults( bool bForced = false, bool bCurrentThreadOnly = true )
  1844. { s_oProfiler.FDumpResults( bForced, bCurrentThreadOnly ); };
  1845. static void SCreateNewNode( const char *pszTitle )
  1846. { s_oProfiler.FCreateNewNode( pszTitle ); };
  1847. static void SCloseCurrentNode()
  1848. { s_oProfiler.FCloseCurrentNode(); };
  1849. };
  1850. #endif // }
  1851. #ifndef MAX_PROFILING_DLL_IMPLEMENTATION // {
  1852. /*********************************************************************************
  1853. /* Class:
  1854. /* CMaxProfilingBlockWrapper
  1855. /* Comments:
  1856. /* As a substitute to the macros (Ray's request). Hoping that those inlines
  1857. /* disappear completely when profiling is turned off. The alternative
  1858. /* would have been to define a new set of macros taking an additional
  1859. /* parameter (a unique name for each instance of the wrapper within the
  1860. /* same scope)
  1861. /* Note:
  1862. /* I use nothrow here, but the current implementations don't guaranty
  1863. /* that no exception will be thrown
  1864. /********************************************************************************/
  1865. class CMaxProfilingBlockWrapper
  1866. {
  1867. public:
  1868. MAXPROFNOTHROW CMaxProfilingBlockWrapper( const char *pszTitle )
  1869. { BEGIN_PROFILING_BLOCK( pszTitle ); };
  1870. MAXPROFNOTHROW ~CMaxProfilingBlockWrapper()
  1871. { END_PROFILING_BLOCK; };
  1872. public:
  1873. MAXPROFNOTHROW static void SDump()
  1874. { DUMP_PROFILING_RESULTS; };
  1875. };
  1876. #endif // }
  1877. /*********************************************************************************
  1878. /* Comments:
  1879. /* Here is the code used to generate maxprof.dll
  1880. /*********************************************************************************
  1881. #define MAX_PROFILING_DLL_IMPLEMENTATION
  1882. #include <iomanip.h>
  1883. #include <profile.h>
  1884. #pragma warning( disable : 4786 )
  1885. __MAX_MINIPROFILER_IMPLEMENTATION
  1886. typedef CMaxMultithreadProfiler<CMaxMiniProfiler_NoHistory> GDllProfiler_Type1;
  1887. typedef CMaxMultithreadProfiler<CMaxMiniProfiler_Standard> GDllProfiler_Type2;
  1888. GDllProfiler_Type1 g_oProfiler;
  1889. __declspec(dllexport) void LockProfiler()
  1890. { g_oProfiler.FLockProfiler(); }
  1891. __declspec(dllexport) void UnlockProfiler()
  1892. { g_oProfiler.FUnlockProfiler(); }
  1893. __declspec(dllexport) void DumpResults( bool bForced, bool bCurrentThreadOnly )
  1894. { g_oProfiler.FDumpResults( bForced, bCurrentThreadOnly ); }
  1895. __declspec(dllexport) void CreateNewNode( const char *pszTitle )
  1896. { g_oProfiler.FCreateNewNode( pszTitle ); }
  1897. __declspec(dllexport) void CloseCurrentNode()
  1898. { g_oProfiler.FCloseCurrentNode(); }
  1899. __declspec(dllexport) bool IsBiasKnown()
  1900. { return g_oProfiler.bFIsBiasKnown(); }
  1901. __declspec(dllexport) void SetBiasApproximationFrom( unsigned long lBiasSample )
  1902. { g_oProfiler.FSetBiasApproximationFrom( lBiasSample ); }
  1903. #pragma warning( default : 4786 )
  1904. /*********************************************************************************
  1905. /* Comments:
  1906. /* Here is the DEF file used to generate maxprof.dll
  1907. /*********************************************************************************
  1908. EXPORTS
  1909. LockProfiler @1
  1910. UnlockProfiler @2
  1911. DumpResults @3
  1912. CreateNewNode @4
  1913. CloseCurrentNode @5
  1914. IsBiasKnown @6
  1915. SetBiasApproximationFrom @7
  1916. /********************************************************************************/
  1917. // Note: I don't reenable C4786, and I know it...
  1918. #endif // }