summaryrefslogtreecommitdiff
path: root/external/vpc/public/tier0/stackstats.h
diff options
context:
space:
mode:
Diffstat (limited to 'external/vpc/public/tier0/stackstats.h')
-rw-r--r--external/vpc/public/tier0/stackstats.h966
1 files changed, 966 insertions, 0 deletions
diff --git a/external/vpc/public/tier0/stackstats.h b/external/vpc/public/tier0/stackstats.h
new file mode 100644
index 0000000..a14d64b
--- /dev/null
+++ b/external/vpc/public/tier0/stackstats.h
@@ -0,0 +1,966 @@
+//========= Copyright � 1996-2008, Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Tools for grabbing/dumping the stack at runtime
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TIER0_STACKSTATS_H
+#define TIER0_STACKSTATS_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tier0/stacktools.h"
+#include "tier0/threadtools.h"
+
+#if defined ENABLE_RUNTIME_STACK_TRANSLATION
+#define ENABLE_STACK_STATS_GATHERING //uncomment to enable the gathering class
+#endif
+
+#if defined ENABLE_STACK_STATS_GATHERING
+#include "tier0/valve_off.h"
+# include <map> //needed for CCallStackStatsGatherer
+# include <vector>
+#include "tier0/valve_on.h"
+#define CDefaultStatsGathererAllocator std::allocator
+#else
+template<class _Ty> class CNullStatsGathererAllocator { CNullStatsGathererAllocator( void ) { } };
+#define CDefaultStatsGathererAllocator CNullStatsGathererAllocator
+#endif
+
+
+typedef size_t (*FN_DescribeStruct)( uint8 *, size_t );
+
+class CCallStackStatsGatherer_Standardized_t;
+struct CCallStackStatsGatherer_FunctionTable_t
+{
+ void (*pfn_GetDumpInfo)( void *, const char *&, size_t &, size_t &, void *&, size_t &, CCallStackStatsGatherer_Standardized_t *&, size_t & );
+ void (*pfn_PushSubTree)( void *, const CCallStackStatsGatherer_Standardized_t &, const CCallStackStorage & );
+ void (*pfn_PopSubTree)( void * );
+ size_t (*pfn_DescribeCallStackStatStruct)( uint8 *, size_t );
+ void (*pfn_SyncMutexes)( void *, bool );
+ void *(*pfn_GetEntry)( void *, uint32 );
+ void (*pfn_ApplyTreeAccessLock)( void *, bool );
+ void (*pfn_LockEntry)( void *, uint32, bool );
+};
+
+//templatized classes are fun, can't really use a base pointer effectively, so we'll have a translator struct and store pointers to instances of the translator function
+class CCallStackStatsGatherer_Standardized_t
+{
+public:
+ CCallStackStatsGatherer_Standardized_t( void ) {};
+ CCallStackStatsGatherer_Standardized_t( void *pThis, const CCallStackStatsGatherer_FunctionTable_t &FnTable ) : pGatherer( pThis ), pFunctionTable( &FnTable ) {};
+
+ //go ahead and create some helper functions that are likely to be used by helper code
+ void PushSubTree( const CCallStackStatsGatherer_Standardized_t &SubTree, const CCallStackStorage &PushStack = CCallStackStorage() ) const
+ {
+ pFunctionTable->pfn_PushSubTree( pGatherer, SubTree, PushStack );
+ }
+
+ inline void PopSubTree( void ) const
+ {
+ pFunctionTable->pfn_PopSubTree( pGatherer );
+ }
+
+ void *pGatherer;
+ const CCallStackStatsGatherer_FunctionTable_t *pFunctionTable;
+};
+
+//Designed to be called by an instance of CCallStackStatsGatherer to dump it's data
+PLATFORM_INTERFACE bool _CCallStackStatsGatherer_Internal_DumpStatsToFile( const char *szFileName, const CCallStackStatsGatherer_Standardized_t &StatsGatherer, bool bAllowMemoryAllocations );
+
+
+
+
+
+
+template <class STATSTRUCT>
+class CCallStackStatsGatherer_StructAccessor_Base
+{
+public:
+ CCallStackStatsGatherer_StructAccessor_Base( const CCallStackStatsGatherer_Standardized_t &Gatherer, int iEntryIndex ) : m_Gatherer( Gatherer ), m_iEntryIndex( iEntryIndex ) {};
+protected:
+ uint32 m_iEntryIndex; //index of the stat entry we want in the vector. Stored as index as the vector base address can change.
+ CCallStackStatsGatherer_Standardized_t m_Gatherer; //so we can lock the vector memory in place while manipulating the values
+};
+
+
+class CCallStackStatsGatherer_StatMutexBase
+{
+public:
+ void LockEntry( uint32 iEntryIndex, bool bLock ) {} //true to increase lock refcount, false to decrease
+};
+
+template <uint32 SHAREDENTRYMUTEXES> //must be a power of 2
+class CCallStackStatsGatherer_StatMutexPool
+{
+public:
+ void LockEntry( uint32 iEntryIndex, bool bLock )
+ {
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ COMPILE_TIME_ASSERT( (SHAREDENTRYMUTEXES & (SHAREDENTRYMUTEXES - 1)) == 0 ); //must be a power of 2
+
+ if( bLock )
+ {
+ m_IndividualEntryMutexes[iEntryIndex & (SHAREDENTRYMUTEXES - 1)].Lock();
+ }
+ else
+ {
+ m_IndividualEntryMutexes[iEntryIndex & (SHAREDENTRYMUTEXES - 1)].Unlock();
+ }
+#endif
+ }
+protected:
+ CThreadFastMutex m_IndividualEntryMutexes[SHAREDENTRYMUTEXES];
+};
+
+
+
+
+//STATSTRUCT - The structure you'll use to track whatever it is you're tracking.
+//CAPTUREDCALLSTACKLENGTH - The maximum length of your stack trace that we'll use to distinguish entries
+//STACKACQUISITIONFUNCTION - The function to use to gather the current call stack. GetCallStack() is safe, GetCallStack_Fast() is faster, but has special requirements
+//STATMUTEXHANDLER - If you want automatic mutex management for your individual entries, supply a handler here.
+// You'll need to not only call GetEntry(), but lock/unlock the entry while accessing it.
+//TEMPLATIZEDMEMORYALLOCATOR - We'll need to allocate memory, supply an allocator if you want to manage that
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION = GetCallStack, typename STATMUTEXHANDLER = CCallStackStatsGatherer_StatMutexPool<4>, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR = CDefaultStatsGathererAllocator>
+class CCallStackStatsGatherer : public STATMUTEXHANDLER
+{
+public:
+#if !defined( ENABLE_STACK_STATS_GATHERING )
+ CCallStackStatsGatherer( void )
+ {
+ for( size_t i = 0; i != CAPTUREDCALLSTACKLENGTH; ++i )
+ m_SingleCallStack[i] = NULL;
+ }
+#endif
+
+ CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT> GetEntry( void * const CallStack[CAPTUREDCALLSTACKLENGTH] ); //get the entry using some callstack grabbed a while ago. Assumes ALL invalid entries have been nullified
+ CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT> GetEntry( void * const CallStack[CAPTUREDCALLSTACKLENGTH], uint32 iValidEntries ); //same as above, but does the work of nullifying invalid entries
+ CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT> GetEntry( const CCallStackStorage &PushStack = CCallStackStorage( STACKACQUISITIONFUNCTION ) );
+ CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT> GetEntry( uint32 iEntryIndex );
+
+ //get the entry index for the caller's current call stack. Pre-nullified entries count as valid entries (and save re-nullification work)
+ uint32 GetEntryIndex( void * const CallStack[CAPTUREDCALLSTACKLENGTH], uint32 iValidEntries ); //index is unchanging, safe to keep and use later (designed for exactly that purpose)
+ uint32 GetEntryIndex( const CCallStackStorage &PushStack = CCallStackStorage( STACKACQUISITIONFUNCTION ) );
+
+
+
+ typedef void *(&StackReference)[CAPTUREDCALLSTACKLENGTH];
+ StackReference GetCallStackForIndex( uint32 iEntryIndex )
+ {
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ return m_StatEntries[iEntryIndex].m_CallStack;
+#else
+ return m_SingleCallStack;
+#endif
+ }
+
+ void GetCallStackForIndex( uint32 iEntryIndex, void *CallStackOut[CAPTUREDCALLSTACKLENGTH] );
+
+ size_t NumEntries( void ) const;
+
+ bool DumpToFile( const char *szFileName, bool bAllowMemoryAllocations = true );
+
+ static const CCallStackStatsGatherer_FunctionTable_t &GetFunctionTable( void );
+
+ static void GetDumpInfo( void *pThis, const char *&szStructName, size_t &iCapturedStackLength, size_t &iEntrySizeWithStack, void *&pEntries, size_t &iEntryCount, CCallStackStatsGatherer_Standardized_t *&pSubTrees, size_t &iSubTreeCount );
+ static void PushSubTree( void *pParent, const CCallStackStatsGatherer_Standardized_t &SubTree, const CCallStackStorage &PushStack );
+
+ void PushSubTree( CCallStackStatsGatherer_Standardized_t &Parent, const CCallStackStorage &PushStack = CCallStackStorage( STACKACQUISITIONFUNCTION ) );
+
+ static void PopSubTree( void *pParent );
+ static void SyncMutexes( void *pParent, bool bLock ); //true for lock, false for unlock
+ static void *GetEntry( void *pParent, uint32 iEntryIndex );
+ static void ApplyTreeAccessLock( void *pParent, bool bLock );
+ static void LockEntry( void *pParent, uint32 iEntryIndex, bool bLock );
+
+ void Reset( void );
+
+
+ CCallStackStatsGatherer_Standardized_t Standardized( void );
+ operator CCallStackStatsGatherer_Standardized_t( void );
+
+ const static FN_GetCallStack StackFunction; //publish the requested acquisition function so you can easily key all your stack gathering to the class instance
+ const static size_t CapturedCallStackLength;
+private:
+
+#pragma pack(push)
+#pragma pack(1)
+ struct StackAndStats_t
+ {
+ void *m_CallStack[CAPTUREDCALLSTACKLENGTH];
+ STATSTRUCT m_Stats;
+ };
+#pragma pack(pop)
+
+ typedef CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR> ThisCast;
+
+
+#if defined( ENABLE_STACK_STATS_GATHERING )
+
+ struct IndexMapKey_t
+ {
+ IndexMapKey_t( void * const CallStack[CAPTUREDCALLSTACKLENGTH] )
+ {
+ m_Hash = 0;
+ for( int i = 0; i < CAPTUREDCALLSTACKLENGTH; ++i )
+ {
+ m_CallStack[i] = CallStack[i];
+ m_Hash += (uintp)CallStack[i];
+ }
+ }
+ bool operator<( const IndexMapKey_t &key ) const
+ {
+ if( m_Hash != key.m_Hash )
+ return m_Hash < key.m_Hash;
+
+ //only here if there's a hash match. Do a full check. Extremely likely to be the exact same call stack. But not 100% guaranteed.
+ for( int i = 0; i < CAPTUREDCALLSTACKLENGTH; ++i )
+ {
+ if( m_CallStack[i] == key.m_CallStack[i] )
+ {
+ continue;
+ }
+
+ return m_CallStack[i] < key.m_CallStack[i];
+ }
+
+ return false; //exact same call stack
+ }
+
+ uintp m_Hash;
+ void *m_CallStack[CAPTUREDCALLSTACKLENGTH];
+ };
+
+ void KeepSubTree( CCallStackStatsGatherer_Standardized_t &SubTree )
+ {
+ AUTO_LOCK_FM( m_SubTreeMutex );
+ for( StoredSubTreeVector_t::iterator treeIter = m_StoredSubTrees.begin(); treeIter != m_StoredSubTrees.end(); ++treeIter )
+ {
+ if( SubTree.pGatherer == treeIter->pGatherer )
+ return;
+ }
+
+ //Warning( "Storing subtree\n" );
+
+ m_StoredSubTrees.push_back( SubTree );
+ }
+
+ uint32 PatchInSubTrees( void * const CallStackIn[CAPTUREDCALLSTACKLENGTH], void *CallStackOut[CAPTUREDCALLSTACKLENGTH], uint32 iValidEntries )
+ {
+ if( iValidEntries > CAPTUREDCALLSTACKLENGTH )
+ {
+ iValidEntries = CAPTUREDCALLSTACKLENGTH;
+ }
+
+ AUTO_LOCK_FM( m_SubTreeMutex );
+ if( m_PushedSubTrees.size() == 0 )
+ {
+ memcpy( CallStackOut, CallStackIn, sizeof( void * ) * iValidEntries );
+ return iValidEntries;
+ }
+
+ unsigned long iThreadID = ThreadGetCurrentId();
+ PushedSubTreeVector_t::reverse_iterator treeIter;
+ for( treeIter = m_PushedSubTrees.rbegin(); treeIter != m_PushedSubTrees.rend(); ++treeIter )
+ {
+ if( treeIter->iThreadID == iThreadID )
+ {
+ break;
+ }
+ }
+
+ if( treeIter == m_PushedSubTrees.rend() )
+ {
+ memcpy( CallStackOut, CallStackIn, sizeof( void * ) * iValidEntries );
+ return iValidEntries;
+ }
+
+ //char szTemp[4096];
+ //TranslateStackInfo( CallStackIn, CAPTUREDCALLSTACKLENGTH, szTemp, sizeof( szTemp ), "\n\t" );
+
+ //Warning( "Attempting to link trees:\n=======================ONE=======================\n\t%s\n", szTemp );
+ //TranslateStackInfo( treeIter->Stack, CAPTUREDCALLSTACKLENGTH, szTemp, sizeof( szTemp ), "\n\t" );
+ //Warning( "=======================TWO=======================\n\t%s\n", szTemp );
+
+ void *pMatchAddress = treeIter->Stack[1]; //while the first entry is where the actual push was made. The second entry is the first that is matchable in most cases
+
+ uint32 i;
+ for( i = 0; i < iValidEntries; ++i )
+ {
+ if( CallStackIn[i] == pMatchAddress )
+ {
+ //TranslateStackInfo( CallStackIn, i, szTemp, sizeof( szTemp ), "\n\t" );
+ //Warning( "======================MATCH======================\n\t%s\n", szTemp );
+
+ CallStackOut[i] = treeIter->tree.pGatherer; //tag this entry as leading into the sub-tree
+ KeepSubTree( treeIter->tree ); //store the sub-tree forever
+ return i + 1;
+ }
+ CallStackOut[i] = CallStackIn[i];
+ }
+
+ return iValidEntries;
+
+ //Warning( "=======================END=======================\n" );
+ }
+
+ struct StatIndex_t
+ {
+ StatIndex_t( void ) : m_Index((unsigned int)-1) {};
+ unsigned int m_Index;
+ };
+
+ typedef std::vector<StackAndStats_t, TEMPLATIZEDMEMORYALLOCATOR<StackAndStats_t> > StatVector_t;
+ typedef std::map< IndexMapKey_t, StatIndex_t, std::less<IndexMapKey_t>, TEMPLATIZEDMEMORYALLOCATOR<std::pair<const IndexMapKey_t, StatIndex_t> > > IndexMap_t;
+ typedef typename IndexMap_t::iterator IndexMapIter_t;
+ typedef typename IndexMap_t::value_type IndexMapEntry_t;
+
+ StatVector_t m_StatEntries;
+ IndexMap_t m_IndexMap;
+
+ struct PushedSubTree_t
+ {
+ unsigned long iThreadID;
+ CCallStackStatsGatherer_Standardized_t tree;
+ void *Stack[CAPTUREDCALLSTACKLENGTH];
+ };
+
+ typedef std::vector<PushedSubTree_t, TEMPLATIZEDMEMORYALLOCATOR<PushedSubTree_t> > PushedSubTreeVector_t;
+ PushedSubTreeVector_t m_PushedSubTrees;
+
+ typedef std::vector<CCallStackStatsGatherer_Standardized_t, TEMPLATIZEDMEMORYALLOCATOR<CCallStackStatsGatherer_Standardized_t> > StoredSubTreeVector_t;
+ StoredSubTreeVector_t m_StoredSubTrees;
+
+ CThreadFastMutex m_IndexMapMutex;
+ CThreadFastMutex m_SubTreeMutex;
+
+ //only for locking the memory in place, locked for write when the entry addresses might change.
+ //Locked for read when you've claimed you're manipulating a value.
+ //You're on your own for making sure two threads don't access the same index simultaneously
+ CThreadRWLock m_StatEntryLock;
+
+#else //#if defined( ENABLE_STACK_STATS_GATHERING )
+
+ STATSTRUCT m_SingleEntry; //the class is disabled, we'll always return this same struct
+ void *m_SingleCallStack[CAPTUREDCALLSTACKLENGTH];
+
+ static size_t NULL_DescribeCallStackStatStruct( uint8 *pDescribeWriteBuffer, size_t iDescribeMaxLength ) { return 0; }
+
+#endif //#if defined( ENABLE_STACK_STATS_GATHERING )
+};
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+const FN_GetCallStack CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::StackFunction = STACKACQUISITIONFUNCTION;
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+const size_t CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::CapturedCallStackLength = CAPTUREDCALLSTACKLENGTH;
+
+#if defined( ENABLE_STACK_STATS_GATHERING )
+
+class CallStackStatStructDescFuncs;
+PLATFORM_INTERFACE size_t _CCallStackStatsGatherer_Write_FieldDescriptions( CallStackStatStructDescFuncs *pFieldDescriptions, uint8 *pWriteBuffer, size_t iWriteBufferSize );
+//PLATFORM_INTERFACE size_t _CCallStackStatsGatherer_Write_FieldMergeScript( CallStackStatStructDescFuncs *pFieldDescriptions, CallStackStatStructDescFuncs::MergeScript_Language scriptMergeLanguage, uint8 *pWriteBuffer, size_t iWriteBufferSize );
+
+#define DECLARE_CALLSTACKSTATSTRUCT() static const char *STATSTRUCTSTRINGNAME;\
+ static size_t DescribeCallStackStatStruct( uint8 *pDescribeWriteBuffer, size_t iDescribeMaxLength );
+
+#define BEGIN_STATSTRUCTDESCRIPTION( className ) const char *className::STATSTRUCTSTRINGNAME = #className;\
+ size_t className::DescribeCallStackStatStruct( uint8 *pDescribeWriteBuffer, size_t iDescribeMaxLength ) {\
+ size_t iWroteBytes = 0;
+#define END_STATSTRUCTDESCRIPTION() return iWroteBytes; }
+
+
+#define DECLARE_CALLSTACKSTATSTRUCT_FIELDDESCRIPTION() static CallStackStatStructDescFuncs *GetStatStructFieldDescriptions( void );
+
+#define BEGIN_STATSTRUCTFIELDDESCRIPTION( className ) CallStackStatStructDescFuncs * className::GetStatStructFieldDescriptions( void ) {\
+ typedef className ThisStruct;\
+ CallStackStatStructDescFuncs *_pHeadLinkage = NULL;\
+ CallStackStatStructDescFuncs **_pLinkageHelperVar = &_pHeadLinkage;
+
+#define _DEFINE_STATSTRUCTFIELD_VARNAME( varName, fieldName, fieldStruct, fieldParmsInParentheses ) static fieldStruct varName##_desc##fieldParmsInParentheses;\
+ varName##_desc.m_szFieldName = #fieldName;\
+ varName##_desc.m_iFieldOffset = (size_t)(&((ThisStruct *)NULL)->fieldName);\
+ varName##_desc.m_pNext = NULL;\
+ *_pLinkageHelperVar = &varName##_desc;\
+ _pLinkageHelperVar = &varName##_desc.m_pNext;
+
+#define DEFINE_STATSTRUCTFIELD( fieldName, fieldStruct, fieldParmsInParentheses ) _DEFINE_STATSTRUCTFIELD_VARNAME( fieldName, fieldName, fieldStruct, fieldParmsInParentheses )
+#define DEFINE_STATSTRUCTFIELD_ARRAYENTRY( arrayName, arrayIndex, fieldStruct, fieldParmsInParentheses ) _DEFINE_STATSTRUCTFIELD_VARNAME( arrayName##_##arrayIndex, arrayName##[##arrayIndex##], fieldStruct, fieldParmsInParentheses )
+
+#define END_STATSTRUCTFIELDDESCRIPTION() static CallStackStatStructDescFuncs *s_pHeadStruct = _pHeadLinkage;\
+ return s_pHeadStruct; }
+
+#define WRITE_STATSTRUCT_FIELDDESCRIPTION() iWroteBytes += _CCallStackStatsGatherer_Write_FieldDescriptions( GetStatStructFieldDescriptions(), pDescribeWriteBuffer + iWroteBytes, iDescribeMaxLength - iWroteBytes );
+//#define WRITE_STATSTRUCT_FIELDMERGESCRIPT( scriptMergeLanguage ) iWroteBytes += _CCallStackStatsGatherer_Write_FieldMergeScript( GetStatStructFieldDescriptions(), CallStackStatStructDescFuncs::scriptMergeLanguage, pDescribeWriteBuffer + iWroteBytes, iDescribeMaxLength - iWroteBytes );
+
+
+#else //#if defined( ENABLE_STACK_STATS_GATHERING )
+
+#define DECLARE_CALLSTACKSTATSTRUCT()
+#define BEGIN_STATSTRUCTDESCRIPTION( className )
+#define END_STATSTRUCTDESCRIPTION()
+
+#define DECLARE_CALLSTACKSTATSTRUCT_FIELDDESCRIPTION()
+#define BEGIN_STATSTRUCTFIELDDESCRIPTION( className )
+#define DEFINE_STATSTRUCTFIELD( fieldName, fieldStruct, fieldParmsInParentheses )
+#define END_STATSTRUCTFIELDDESCRIPTION()
+
+#define WRITE_STATSTRUCT_FIELDDESCRIPTION()
+//#define WRITE_STATSTRUCT_FIELDMERGESCRIPT( scriptMergeLanguage )
+
+#endif //#if defined( ENABLE_STACK_STATS_GATHERING )
+
+
+
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT> CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::GetEntry( void * const CallStack[CAPTUREDCALLSTACKLENGTH] ) //get the entry using some callstack grabbed a while ago. Assumes ALL invalid entries have been nullified
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ return GetEntry( GetEntryIndex( CallStack ) );
+#else
+ return CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT>( Standardized(), 0 );
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT> CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::GetEntry( void * const CallStack[CAPTUREDCALLSTACKLENGTH], uint32 iValidEntries ) //same as above, but does the work of nullifying invalid entries
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ void *CleanedCallStack[CAPTUREDCALLSTACKLENGTH];
+ size_t i;
+ for( i = 0; i < CAPTUREDCALLSTACKLENGTH; ++i )
+ {
+ CleanedCallStack[i] = CallStack[i];
+ }
+
+ for( ; i < CAPTUREDCALLSTACKLENGTH; ++i )
+ {
+ CleanedCallStack[i] = NULL;
+ }
+ return GetEntry( GetEntryIndex( CleanedCallStack ) );
+#else
+ return CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT>( Standardized(), 0 );
+#endif
+}
+
+
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT> CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::GetEntry( const CCallStackStorage &PushStack )
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ COMPILE_TIME_ASSERT( CAPTUREDCALLSTACKLENGTH <= ARRAYSIZE( PushStack.pStack ) );
+ return GetEntry( GetEntryIndex( PushStack.pStack, PushStack.iValidEntries ) );
+#else
+ return CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT>( Standardized(), 0 );
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+uint32 CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::GetEntryIndex( const CCallStackStorage &PushStack )
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ COMPILE_TIME_ASSERT( CAPTUREDCALLSTACKLENGTH <= ARRAYSIZE( PushStack.pStack ) );
+ return GetEntryIndex( PushStack.pStack, PushStack.iValidEntries );
+#else
+ return 0;
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT> CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::GetEntry( uint32 iEntryIndex )
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ return CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT>( Standardized(), iEntryIndex );
+#else
+ return CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT>( Standardized(), 0 );
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+uint32 CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::GetEntryIndex( void * const CallStack[CAPTUREDCALLSTACKLENGTH], uint32 iValidEntries ) //index is unchanging, safe to keep and use later (designed for exactly that purpose)
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ AUTO_LOCK_FM( m_IndexMapMutex );
+ std::pair<IndexMapIter_t, bool> indexMapIter;
+ void *PatchedStack[CAPTUREDCALLSTACKLENGTH];
+
+ //if we have a sub-tree. We'll be splicing it into the original call stack as if it were a return address. Then patching that when we interpret the results later
+ //A stack with a sub-tree along the line is treated as distinctly different than one without a sub-tree
+ iValidEntries = PatchInSubTrees( CallStack, PatchedStack, iValidEntries );
+
+ Assert( iValidEntries <= CAPTUREDCALLSTACKLENGTH );
+
+ for( int i = iValidEntries; i < CAPTUREDCALLSTACKLENGTH; ++i )
+ {
+ PatchedStack[i] = NULL;
+ }
+
+ indexMapIter = m_IndexMap.insert( IndexMapEntry_t( IndexMapKey_t( PatchedStack ), StatIndex_t() ) );
+
+ if( indexMapIter.first->second.m_Index == -1 )
+ {
+ m_StatEntryLock.LockForWrite();
+ indexMapIter.first->second.m_Index = (unsigned int)m_StatEntries.size();
+
+ m_StatEntries.push_back( StackAndStats_t() );
+ memcpy( m_StatEntries[indexMapIter.first->second.m_Index].m_CallStack, PatchedStack, sizeof( void * ) * CAPTUREDCALLSTACKLENGTH );
+ m_StatEntryLock.UnlockWrite();
+ }
+
+ return indexMapIter.first->second.m_Index;
+#else
+ return 0;
+#endif
+}
+
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+void CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::GetCallStackForIndex( uint32 iEntryIndex, void *CallStackOut[CAPTUREDCALLSTACKLENGTH] )
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ m_StatEntryLock.LockForRead();
+ for( size_t i = 0; i != CAPTUREDCALLSTACKLENGTH; ++i )
+ {
+ CallStackOut[i] = m_StatEntries[iEntryIndex].m_CallStack[i];
+ }
+ m_StatEntryLock.UnlockRead();
+#else
+ for( size_t i = 0; i != CAPTUREDCALLSTACKLENGTH; ++i )
+ CallStackOut[i] = NULL;
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+size_t CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::NumEntries( void ) const
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ return m_StatEntries.size();
+#else
+ return 0;
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+bool CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::DumpToFile( const char *szFileName, bool bAllowMemoryAllocations )
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ CCallStackStatsGatherer_Standardized_t StandardThis = Standardized();
+ SyncMutexes( this, true );
+ bool bRetVal = _CCallStackStatsGatherer_Internal_DumpStatsToFile( szFileName, StandardThis, bAllowMemoryAllocations );
+ SyncMutexes( this, false );
+ return bRetVal;
+#else
+ return false;
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+const CCallStackStatsGatherer_FunctionTable_t &CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::GetFunctionTable( void )
+{
+ static CCallStackStatsGatherer_FunctionTable_t retVal =
+ { GetDumpInfo,
+ PushSubTree,
+ PopSubTree,
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ STATSTRUCT::DescribeCallStackStatStruct,
+#else
+ NULL_DescribeCallStackStatStruct,
+#endif
+ SyncMutexes,
+ GetEntry,
+ ApplyTreeAccessLock,
+ LockEntry };
+
+ return retVal;
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+void CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::GetDumpInfo( void *pThis, const char *&szStructName, size_t &iCapturedStackLength, size_t &iEntrySizeWithStack, void *&pEntries, size_t &iEntryCount, CCallStackStatsGatherer_Standardized_t *&pSubTrees, size_t &iSubTreeCount )
+{
+ ThisCast *pThisCast = (ThisCast *)pThis;
+ iCapturedStackLength = CAPTUREDCALLSTACKLENGTH;
+ iEntrySizeWithStack = sizeof( CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::StackAndStats_t );
+
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ szStructName = STATSTRUCT::STATSTRUCTSTRINGNAME;
+ iEntryCount = pThisCast->m_StatEntries.size();
+ pEntries = iEntryCount > 0 ? &pThisCast->m_StatEntries[0] : NULL;
+ iSubTreeCount = pThisCast->m_StoredSubTrees.size();
+ pSubTrees = iSubTreeCount > 0 ? &pThisCast->m_StoredSubTrees[0] : NULL;
+#else
+ szStructName = "";
+ iEntryCount = 0;
+ pEntries = NULL;
+ iSubTreeCount = 0;
+ pSubTrees = NULL;
+#endif
+}
+
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+void CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::PushSubTree( void *pParent, const CCallStackStatsGatherer_Standardized_t &SubTree, const CCallStackStorage &PushStack )
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ ThisCast *pParentCast = (ThisCast *)pParent;
+ PushedSubTree_t pushVal;
+ pushVal.iThreadID = ThreadGetCurrentId();
+ pushVal.tree = SubTree;
+
+ memcpy( pushVal.Stack, PushStack.pStack, MIN( CAPTUREDCALLSTACKLENGTH, PushStack.iValidEntries ) * sizeof( void * ) );
+
+ for( int i = PushStack.iValidEntries; i < CAPTUREDCALLSTACKLENGTH; ++i )
+ {
+ pushVal.Stack[i] = NULL;
+ }
+
+ pParentCast->m_SubTreeMutex.Lock();
+ pParentCast->m_PushedSubTrees.push_back( pushVal );
+ pParentCast->m_SubTreeMutex.Unlock();
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+void CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::PushSubTree( CCallStackStatsGatherer_Standardized_t &Parent, const CCallStackStorage &PushStack )
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ CCallStackStatsGatherer_Standardized_t StandardThis = Standardized();
+ Parent.PushSubTree( StandardThis, PushStack );
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+void CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::PopSubTree( void *pParent )
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ ThisCast *pParentCast = (ThisCast *)pParent;
+ pParentCast->m_SubTreeMutex.Lock();
+ unsigned long iThreadID = ThreadGetCurrentId();
+
+ for( PushedSubTreeVector_t::reverse_iterator treeIter = pParentCast->m_PushedSubTrees.rbegin(); treeIter != pParentCast->m_PushedSubTrees.rend(); ++treeIter )
+ {
+ if( treeIter->iThreadID == iThreadID )
+ {
+ ++treeIter; //[24.4.1/1] &*(reverse_iterator(i)) == &*(i - 1)
+ PushedSubTreeVector_t::iterator eraseIter = treeIter.base();
+ pParentCast->m_PushedSubTrees.erase(eraseIter);
+ break;
+ }
+ }
+
+ pParentCast->m_SubTreeMutex.Unlock();
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+void CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::SyncMutexes( void *pParent, bool bLock ) //true for lock, false for unlock
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ ThisCast *pParentCast = (ThisCast *)pParent;
+ if( bLock )
+ {
+ pParentCast->m_IndexMapMutex.Lock();
+ pParentCast->m_SubTreeMutex.Lock();
+
+ for( StoredSubTreeVector_t::iterator treeIter = pParentCast->m_StoredSubTrees.begin(); treeIter != pParentCast->m_StoredSubTrees.end(); ++treeIter )
+ {
+ treeIter->pFunctionTable->pfn_SyncMutexes( treeIter->pGatherer, true );
+ }
+ }
+ else
+ {
+ for( StoredSubTreeVector_t::iterator treeIter = pParentCast->m_StoredSubTrees.begin(); treeIter != pParentCast->m_StoredSubTrees.end(); ++treeIter )
+ {
+ treeIter->pFunctionTable->pfn_SyncMutexes( treeIter->pGatherer, false );
+ }
+
+ pParentCast->m_IndexMapMutex.Unlock();
+ pParentCast->m_SubTreeMutex.Unlock();
+ }
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+void *CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::GetEntry( void *pParent, uint32 iEntryIndex )
+{
+ ThisCast *pParentCast = (ThisCast *)pParent;
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ return &pParentCast->m_StatEntries[iEntryIndex].m_Stats;
+#else
+ return &pParentCast->m_SingleEntry;
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+void CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::ApplyTreeAccessLock( void *pParent, bool bLock )
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ ThisCast *pParentCast = (ThisCast *)pParent;
+ if( bLock )
+ {
+ pParentCast->m_StatEntryLock.LockForRead();
+ }
+ else
+ {
+ pParentCast->m_StatEntryLock.UnlockRead();
+ }
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+void CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::LockEntry( void *pParent, uint32 iEntryIndex, bool bLock )
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ ThisCast *pParentCast = (ThisCast *)pParent;
+ pParentCast->STATMUTEXHANDLER::LockEntry( iEntryIndex, bLock );
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+void CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::Reset( void )
+{
+#if defined( ENABLE_STACK_STATS_GATHERING )
+ m_StatEntryLock.LockForWrite();
+ m_IndexMapMutex.Lock();
+ m_SubTreeMutex.Lock();
+
+ m_StatEntries.clear();
+ m_IndexMap.clear();
+ m_StoredSubTrees.clear();
+
+ m_SubTreeMutex.Unlock();
+ m_IndexMapMutex.Unlock();
+ m_StatEntryLock.UnlockWrite();
+#endif
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::operator CCallStackStatsGatherer_Standardized_t( void )
+{
+ return CCallStackStatsGatherer_Standardized_t( this, GetFunctionTable() );
+}
+
+template <class STATSTRUCT, size_t CAPTUREDCALLSTACKLENGTH, FN_GetCallStack STACKACQUISITIONFUNCTION, typename STATMUTEXHANDLER, template <typename T> class TEMPLATIZEDMEMORYALLOCATOR>
+CCallStackStatsGatherer_Standardized_t CCallStackStatsGatherer<STATSTRUCT, CAPTUREDCALLSTACKLENGTH, STACKACQUISITIONFUNCTION, STATMUTEXHANDLER, TEMPLATIZEDMEMORYALLOCATOR>::Standardized( void )
+{
+ return CCallStackStatsGatherer_Standardized_t( this, GetFunctionTable() );
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class PLATFORM_CLASS CallStackStatStructDescFuncs
+{
+public:
+ //description file format
+ //1 byte version per entry. Assuming that the field will get more descriptive over time, reserving this now.
+ virtual size_t DescribeField( uint8 *pDescribeWriteBuffer, size_t iDescribeMaxLength ) = 0;
+
+ enum MergeScript_Language
+ {
+ SSMSL_Squirrel, //Only support squirrel for now, theoretically expandable to any vscript supported language
+ };
+
+#if 0 //embedded script handling not ready yet
+ //this is expected to write a piece of script code into the body of a function that will merge two values of this type
+ //The function will have two parameters, "mergeTo", and "mergeFrom". Both are tables with your field defined by name
+ //So, an example to merge an integer count defined as "foo" in the stack struct would look like this "mergeTo.foo += mergeFrom.foo\n"
+ virtual size_t DescribeMergeOperation( MergeScript_Language scriptLanguage, uint8 *pDescribeWriteBuffer, size_t iDescribeMaxLength ) = 0;
+#endif
+
+ //reserve your description field versions here to avoid stomping others
+ enum DescribeFieldVersions_t
+ {
+ DFV_BasicStatStructFieldTypes_t, //Format: 1 byte BasicStatStructFieldTypes_t type, 4 byte field offset, null terminated string field name
+ };
+
+ const char *m_szFieldName;
+ size_t m_iFieldOffset;
+ CallStackStatStructDescFuncs *m_pNext; //needed for how the description macros are laid out. Couldn't figure out a way to store the static struct instances as a static array
+};
+
+enum StatStructDescription_LumpID
+{
+ SSDLID_UNKNOWN,
+ SSDLID_STATICINTERPRETER,
+ SSDLID_FIELDDESC,
+ SSDLID_EMBEDDEDSCRIPT,
+
+
+ SSDLID_COUNT,
+ SSDLID_FORCE_UINT32 = 0xFFFFFFFF,
+};
+
+enum BasicStatStructFieldTypes_t
+{
+ BSSFT_UNKNOWN,
+ BSSFT_BOOL,
+ BSSFT_INT8,
+ BSSFT_UINT8,
+ BSSFT_INT16,
+ BSSFT_UINT16,
+ BSSFT_INT32,
+ BSSFT_UINT32,
+ BSSFT_INT64,
+ BSSFT_UINT64,
+ BSSFT_FLOAT,
+ BSSFT_DOUBLE,
+ BSSFT_VECTOR2D,
+ BSSFT_VECTOR3D,
+ BSSFT_VECTOR4D,
+
+ BSSFT_COUNT,
+};
+
+#define BSSFT_INT (sizeof( int ) == sizeof( int32 ) ? BSSFT_INT32 : BSSFT_INT64)
+#define BSSFT_UINT (sizeof( unsigned int ) == sizeof( uint32 ) ? BSSFT_UINT32 : BSSFT_UINT64)
+#define BSSFT_SIZE_T (sizeof( size_t ) == sizeof( uint32 ) ? BSSFT_UINT32 : BSSFT_UINT64)
+
+enum BasicStatStructFieldCombineMethods_t
+{
+ BSSFCM_UNKNOWN,
+ BSSFCM_CUSTOM, //rely on some outside handler
+ BSSFCM_ADD, //add the values
+ //BSSFCM_SUBTRACT, //what would subtract even mean? which one from which?
+ BSSFCM_MAX, //keep max value
+ BSSFCM_MIN, //keep min value
+ BSSFCM_AND, // &= , Non-integer behavior undefined
+ BSSFCM_OR, // |= , Non-integer behavior undefined
+ BSSFCM_XOR, // ^= , Non-integer behavior undefined
+ /*BSSFCM_LIST, //keep a list of each value (probably complicated)*/
+
+ BSSFCM_COUNT,
+};
+
+class PLATFORM_CLASS BasicStatStructFieldDesc : public CallStackStatStructDescFuncs
+{
+public:
+ BasicStatStructFieldDesc( BasicStatStructFieldTypes_t type, BasicStatStructFieldCombineMethods_t combineMethod ) : m_Type(type), m_Combine(combineMethod) {};
+ size_t DescribeField( uint8 *pDescribeWriteBuffer, size_t iDescribeMaxLength );
+#if 0 //embedded script handling not ready yet
+ size_t DescribeMergeOperation( MergeScript_Language scriptLanguage, uint8 *pDescribeWriteBuffer, size_t iDescribeMaxLength );
+#endif
+
+ BasicStatStructFieldTypes_t m_Type;
+ BasicStatStructFieldCombineMethods_t m_Combine;
+};
+
+
+
+
+
+
+
+//struct is locked in place while you're holding onto one of these. Get a base, then create one of these from it
+template <class STATSTRUCT>
+class CCallStackStatsGatherer_StructAccessor_AutoLock : CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT>
+{
+public:
+ CCallStackStatsGatherer_StructAccessor_AutoLock( CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT> &copyFrom )
+ : CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT>( copyFrom )
+ {
+ this->m_Gatherer.pFunctionTable->pfn_ApplyTreeAccessLock( this->m_Gatherer.pGatherer, true );
+ this->m_Gatherer.pFunctionTable->pfn_LockEntry( this->m_Gatherer.pGatherer, this->m_iEntryIndex, true );
+ this->m_pStruct = (STATSTRUCT *)this->m_Gatherer.pFunctionTable->pfn_GetEntry( this->m_Gatherer.pGatherer, this->m_iEntryIndex );
+ }
+
+ ~CCallStackStatsGatherer_StructAccessor_AutoLock( void )
+ {
+ this->m_Gatherer.pFunctionTable->pfn_LockEntry( this->m_Gatherer.pGatherer, this->m_iEntryIndex, false );
+ this->m_Gatherer.pFunctionTable->pfn_ApplyTreeAccessLock( this->m_Gatherer.pGatherer, false );
+ }
+
+ STATSTRUCT *operator->()
+ {
+ return this->m_pStruct;
+ }
+
+ STATSTRUCT *GetStruct( void ) //do not hold this pointer outside the lock period
+ {
+ return this->m_pStruct;
+ }
+
+protected:
+ STATSTRUCT *m_pStruct;
+};
+
+
+//struct is locked in place only between Lock() and paired Unlock() calls. Get a base, then create one of these from it.
+//It's safe to hold onto this for an extended period of time. The entry index is unchanging in the gatherer tree.
+template <class STATSTRUCT>
+class CCallStackStatsGatherer_StructAccessor_Manual : CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT>
+{
+public:
+ CCallStackStatsGatherer_StructAccessor_Manual( CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT> &copyFrom )
+ : CCallStackStatsGatherer_StructAccessor_Base<STATSTRUCT>( copyFrom ), m_pStruct( NULL )
+ { }
+
+ STATSTRUCT *operator->()
+ {
+ return this->m_pStruct; //NULL while entry is not locked.
+ }
+
+ STATSTRUCT *GetStruct( void ) //do not hold this pointer outside the lock period
+ {
+ return this->m_pStruct; //NULL while entry is not locked.
+ }
+
+ void Lock( void )
+ {
+ this->m_Gatherer.pFunctionTable->pfn_ApplyTreeAccessLock( this->m_Gatherer.pGatherer, true );
+ this->m_Gatherer.pFunctionTable->pfn_LockEntry( this->m_Gatherer.pGatherer, this->m_iEntryIndex, true );
+ this->m_pStruct = (STATSTRUCT *)this->m_Gatherer.pFunctionTable->pfn_GetEntry( this->m_Gatherer.pGatherer, this->m_iEntryIndex );
+ }
+
+ void Unlock( void )
+ {
+ this->m_pStruct = NULL;
+ this->m_Gatherer.pFunctionTable->pfn_LockEntry( this->m_Gatherer.pGatherer, this->m_iEntryIndex, false );
+ this->m_Gatherer.pFunctionTable->pfn_ApplyTreeAccessLock( this->m_Gatherer.pGatherer, false );
+ }
+
+protected:
+ STATSTRUCT *m_pStruct;
+};
+
+
+
+
+
+class CCallStackStats_PushSubTree_AutoPop
+{
+public:
+ CCallStackStats_PushSubTree_AutoPop( const CCallStackStatsGatherer_Standardized_t &Parent, const CCallStackStatsGatherer_Standardized_t &Child, const CCallStackStorage &PushStack = CCallStackStorage() )
+ : m_PopFrom( Parent )
+ {
+ Parent.pFunctionTable->pfn_PushSubTree( Parent.pGatherer, Child, PushStack );
+ }
+ ~CCallStackStats_PushSubTree_AutoPop( void )
+ {
+ m_PopFrom.PopSubTree();
+ }
+
+ CCallStackStatsGatherer_Standardized_t m_PopFrom;
+};
+
+
+#endif //#ifndef TIER0_STACKTOOLS_H