Team Fortress 2 Source Code as on 22/4/2020
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.

255 lines
10 KiB

  1. //========= Copyright (c), Valve LLC, All rights reserved. ============
  2. //
  3. // Purpose: A utility for tracking access to various systems
  4. //
  5. //=============================================================================
  6. #ifndef GCSYSTEMACCESS_H
  7. #define GCSYSTEMACCESS_H
  8. #pragma once
  9. namespace GCSDK
  10. {
  11. //behaviors that can be triggered for system access violation. Each aspect of this system has
  12. //actions that can be enabled or suppressed. The algorithm for this is to combine all the enabled
  13. //actions, then remove all the suppressed and perform those operations when a violation is found
  14. enum EGCAccessAction
  15. {
  16. //returns failure to the verifying code so it can handle it accordingly
  17. GCAccessAction_ReturnFail = ( 1<<0 ),
  18. //track this into a stats overview for number of offenses
  19. GCAccessAction_TrackFail = ( 1<<1 ),
  20. //track this into a stats overview for number of accesses
  21. GCAccessAction_TrackSuccess = ( 1<<2 ),
  22. //report this violation to the console/log
  23. GCAccessAction_Msg = ( 1<<3 ),
  24. //generate an assert at the point this is violated
  25. GCAccessAction_Assert = ( 1<<4 ),
  26. //activate a breakpoint
  27. GCAccessAction_Breakpoint = ( 1<<5 )
  28. };
  29. //how we identify an actual system
  30. typedef uint32 GCAccessSystem_t;
  31. //a structure that is inserted into a context object and holds onto GCAccessSystem index that the context can then initialize itself from. Classes can derive
  32. //from this to initialize itself, but derived class may NOT have any members as that would change the binary layout of the object
  33. class CGCAContextSystemRef
  34. {
  35. public:
  36. CGCAContextSystemRef( GCAccessSystem_t id ) : m_SystemID( id ) {}
  37. const GCAccessSystem_t m_SystemID;
  38. };
  39. //this will generate a unique system access ID that is guaranteed to be unique within a single run of the GC
  40. GCAccessSystem_t GenerateUniqueGCAccessID();
  41. //defines a system access object class which can be used to validate access to systems. This class can be registered with the
  42. //GCA_REGISTER_SYSTEM macro using the same name.
  43. #define GCA_SYSTEM( Name ) \
  44. class CGCA##Name : public CGCAContextSystemRef { \
  45. public: \
  46. static const char* GetName() { return #Name; } \
  47. static GCAccessSystem_t GetID() { static GCAccessSystem_t s_ID = GenerateUniqueGCAccessID(); return s_ID; } \
  48. CGCA##Name() : CGCAContextSystemRef( GetID() ) {} \
  49. };
  50. //similar to the above, but does not auto assign an ID and instead uses the fixed specified ID
  51. #define GCA_SYSTEM_ID( Name, ID ) \
  52. class CGCA##Name : public CGCAContextSystemRef { \
  53. public: \
  54. static const char* GetName() { return #Name; } \
  55. static GCAccessSystem_t GetID() { return (ID); } \
  56. CGCA##Name() : CGCAContextSystemRef( GetID() ) {} \
  57. };
  58. //called to register the specified system with the GC access system
  59. #define GCA_REGISTER_SYSTEM( Name ) GGCAccess().RegisterSystem( CGCA##Name::GetName(), CGCA##Name::GetID() )
  60. //called to begin a context class which derives from (has the same rights as) a parent context. The proper setup for this is:
  61. // GCA_BEGIN_CONTEXT_PARENT( MyContext, ParentContext )
  62. // GCA_CONTEXT_SYSTEM( System )
  63. // GCA_END_CONTEXT( MyContext )
  64. #define GCA_BEGIN_CONTEXT_PARENT( ClassName, Parent ) \
  65. class ClassName : public Parent { \
  66. public: \
  67. ClassName() : m_nFirstSystemMarker_##ClassName( 0 ), m_nLastSystemMarker_##ClassName( 0 ) { \
  68. Init( #ClassName ); \
  69. for( const GCAccessSystem_t* pSystem = &m_nFirstSystemMarker_##ClassName + 1; pSystem != &m_nLastSystemMarker_##ClassName; pSystem++ ) \
  70. AddSystem( *pSystem ); \
  71. } \
  72. static ClassName& Singleton() { static ClassName s_Singleton; return s_Singleton; } \
  73. const GCAccessSystem_t m_nFirstSystemMarker_##ClassName;
  74. //this is the same as the above but where you don't specify a parent context
  75. #define GCA_BEGIN_CONTEXT( ClassName ) GCA_BEGIN_CONTEXT_PARENT( ClassName, CGCAccessContext )
  76. //called to add a context to a system. This must come between a BEGIN/END_CONTEXT block
  77. #define GCA_CONTEXT_SYSTEM( SystemName ) \
  78. const CGCA##SystemName SystemName;
  79. //called to add a generic system reference to a context. The reference will use the provided class and value name and the specified ID
  80. #define GCA_CONTEXT_SYSTEM_ID( ClassName, VarName, SystemID ) \
  81. class ClassName : public CGCAContextSystemRef { \
  82. public: \
  83. ClassName() : CGCAContextSystemRef( SystemID ) {} \
  84. } VarName;
  85. //closes out the definition of a context
  86. #define GCA_END_CONTEXT( ClassName ) \
  87. const GCAccessSystem_t m_nLastSystemMarker_##ClassName; \
  88. };
  89. //creates a context that is basically the same as another context but aliased. Useful if you want a derived context that doesn't have any additional systems
  90. #define GCA_ALIAS_CONTEXT( NewName, ParentName ) \
  91. GCA_BEGIN_CONTEXT_PARENT( NewName, ParentName ) \
  92. GCA_END_CONTEXT( NewName )
  93. //a simple way to define an empty context
  94. #define GCA_EMPTY_CONTEXT( Context ) GCA_BEGIN_CONTEXT( Context ) GCA_END_CONTEXT( Context )
  95. //called to obtain the ID of a system
  96. #define GCA_GET_SYSTEM_ID( SystemName ) ( CGCA##SystemName::GetID() )
  97. //called to validate access to a system, either with an ID or without
  98. #define GCA_VALIDATE_ACCESS( SystemName ) GGCAccess().ValidateAccess( GCA_GET_SYSTEM_ID( SystemName ) )
  99. #define GCA_VALIDATE_ACCESS_STEAMID( SystemName, SteamID ) GGCAccess().ValidateSteamIDAccess( GCA_GET_SYSTEM_ID( SystemName ), ( SteamID ) )
  100. //a context, which is a collection of systems that are valid to be accessed. Each job and GC has a context to validate
  101. //system access
  102. class CGCAccessContext
  103. {
  104. public:
  105. CGCAccessContext();
  106. void Init( const char* pszName, uint32 nActions = 0, uint32 nSuppressActions = 0 );
  107. void AddSystem( GCAccessSystem_t nSystem );
  108. const char* GetName() const { return m_sName.String(); }
  109. uint32 GetActions() const { return m_nActions; }
  110. uint32 GetSuppressActions() const { return m_nSuppressActions; }
  111. void SetAction( EGCAccessAction eAction, bool bSet );
  112. void SetSuppressAction( EGCAccessAction eAction, bool bSet );
  113. //determines if this system has the
  114. bool HasSystem( GCAccessSystem_t system ) const;
  115. //given a context, this will verify that it is a proper subset of the provided context
  116. bool IsSubsetOf( const CGCAccessContext& context ) const;
  117. //given another context, this will add all of its systems to this context
  118. void AddSystemsFrom( const CGCAccessContext& context );
  119. private:
  120. //the textual name of this context
  121. CUtlString m_sName;
  122. //which actions to enable or disable in particular based upon this context
  123. uint32 m_nActions;
  124. uint32 m_nSuppressActions;
  125. //which systems that the context has enabled, in sorted order for fast lookup
  126. CUtlSortVector< GCAccessSystem_t > m_nSystems;
  127. };
  128. class CGCAccessSystem;
  129. //the global GC access tracker which has the systems registered and handles accumulating stats, presenting reports, etc
  130. class CGCAccess
  131. {
  132. public:
  133. CGCAccess();
  134. //-----------------------------------------
  135. // Setup
  136. //called to register a system information
  137. void RegisterSystem( const char* pszName, GCAccessSystem_t nID, uint32 nActions = 0, uint32 nSupressActions = 0 );
  138. //-----------------------------------------
  139. // Validation
  140. //called to verify access to a system. The return of this will be false if it is an invalid access and the return fail action is specified
  141. bool ValidateAccess( GCAccessSystem_t nSystem );
  142. //verify access to a system that also requires that the active SteamID of the current job matches the provided Steam ID, and count it as a fail
  143. bool ValidateSteamIDAccess( GCAccessSystem_t nSystem, CSteamID steamID );
  144. //called to suppress tracking of access for the specified access type. This should typically only be accessed via the access supress utility object
  145. void SuppressAccess( GCAccessSystem_t nSystem, bool bEnable );
  146. //-----------------------------------------
  147. // Report and console operations
  148. //when displaying a report, this will determine how stats should be filtered
  149. enum EDisplay
  150. {
  151. eDisplay_Referenced,
  152. eDisplay_Violations,
  153. eDisplay_IDViolations,
  154. eDisplay_All
  155. };
  156. //called to reset all accumulated stats for all the systems
  157. void ClearSystemStats();
  158. //called to generate a report of every access for a specific job
  159. void ReportJobs( const char* pszContext, EDisplay eDisplay ) const;
  160. //called to generate a report of each system in summary
  161. void ReportSystems( const char* pszContext, EDisplay eDisplay ) const;
  162. //dumps all the collected stats
  163. void FullReport( const char* pszSystemFilter, const char* pszContextFilter, const char* pszJobFilter, EDisplay eDisplay ) const;
  164. //dumps a dependency report for a given system. Essentially for every job that depends upon the named system, what are the other
  165. //systems that it also relies upon
  166. void DependencyReport( const char* pszSystem, EDisplay eDisplay ) const;
  167. //called to register a one time assert that will fire the next time the job/context/system pair is hit
  168. bool CatchSingleAssert( const char* pszSystem, bool bContext, const char* pszContextOrJob );
  169. //clears all registered single asserts that have not yet fired
  170. void ClearSingleAsserts();
  171. //if there is not a specific job in flight, this global context is what will be checked for access
  172. CGCAccessContext m_GlobalContext;
  173. //global options that are set for typical system violation
  174. uint32 m_nActions;
  175. uint32 m_nSuppressActions;
  176. private:
  177. //handles internally validating the system. Takes an optional parameter indicating if the steam ID check failed
  178. bool InternalValidateAccess( GCAccessSystem_t nSystem, CSteamID steamID, CSteamID expectedID );
  179. //the list of single fire asserts that we want to catch and report
  180. struct SingleAssert_t
  181. {
  182. bool m_bContext;
  183. GCAccessSystem_t m_System;
  184. CUtlString m_sContextOrJob;
  185. };
  186. CUtlVector< SingleAssert_t* > m_SingleAsserts;
  187. //systems that have their access suppressed
  188. struct SuppressAccess_t
  189. {
  190. GCAccessSystem_t m_nSystem;
  191. uint32 m_nCount;
  192. };
  193. CUtlVector< SuppressAccess_t > m_SuppressAccess;
  194. //the registered systems
  195. CUtlHashMapLarge< GCAccessSystem_t, CGCAccessSystem* > m_Systems;
  196. //steam ID
  197. };
  198. //global singleton accessor
  199. CGCAccess& GGCAccess();
  200. //utility class to temporarily disable access tracking for a system while within the scope of this object
  201. class CGCAccessSupressTracking
  202. {
  203. public:
  204. CGCAccessSupressTracking( GCAccessSystem_t nSystem ) : m_nSystem( nSystem ) { GGCAccess().SuppressAccess( m_nSystem, false ); }
  205. ~CGCAccessSupressTracking() { GGCAccess().SuppressAccess( m_nSystem, true ); }
  206. private:
  207. GCAccessSystem_t m_nSystem;
  208. };
  209. } //namespace GCSDK
  210. #endif