691 lines
22 KiB
C++
691 lines
22 KiB
C++
|
//========= Copyright (c), Valve LLC, All rights reserved. ============
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//=============================================================================
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "gcsystemaccess.h"
|
||
|
#include "gcjob.h"
|
||
|
#include "gcsdk/gcreportprinter.h"
|
||
|
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
namespace GCSDK
|
||
|
{
|
||
|
|
||
|
|
||
|
static GCConVar gcaccess_enable( "gcaccess_enable", "1", "Kill switch that disables all gcaccess tracking and systems" );
|
||
|
|
||
|
DECLARE_GC_EMIT_GROUP( g_EGAccess, access );
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||
|
GCAccessSystem_t GenerateUniqueGCAccessID()
|
||
|
{
|
||
|
static GCAccessSystem_t s_nID = 0;
|
||
|
return s_nID++;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||
|
// CGCAccessSystem
|
||
|
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
//a GCAccess system that has been registered
|
||
|
class CGCAccessSystem
|
||
|
{
|
||
|
public:
|
||
|
//the name of the system for display
|
||
|
CUtlString m_sName;
|
||
|
//the unique ID for this system
|
||
|
GCAccessSystem_t m_nID;
|
||
|
//which actions should be enabled or disabled for this system in particular
|
||
|
uint32 m_nActions;
|
||
|
uint32 m_nSuppressActions;
|
||
|
|
||
|
struct AccessStats_t
|
||
|
{
|
||
|
AccessStats_t();
|
||
|
void Add( const AccessStats_t& rhs );
|
||
|
//how many raw accesses have we had?
|
||
|
uint32 m_nNumValid;
|
||
|
//how many failed because the context didn't have rights?
|
||
|
uint32 m_nNumFail;
|
||
|
//how many failed because the associated ID wasn't valid?
|
||
|
uint32 m_nNumFailID;
|
||
|
};
|
||
|
|
||
|
struct TrackedJob_t
|
||
|
{
|
||
|
CUtlString m_sContext;
|
||
|
AccessStats_t m_Stats;
|
||
|
};
|
||
|
|
||
|
//which jobs have violated this system access rights
|
||
|
CUtlHashMapLarge< uintp, TrackedJob_t > m_Jobs;
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||
|
// CGCAccessContext
|
||
|
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
CGCAccessContext::CGCAccessContext() :
|
||
|
m_nActions( 0 ),
|
||
|
m_nSuppressActions( 0 )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void CGCAccessContext::Init( const char* pszName, uint32 nActions, uint32 nSuppressActions )
|
||
|
{
|
||
|
m_sName = pszName;
|
||
|
m_nActions = nActions;
|
||
|
m_nSuppressActions = nSuppressActions;
|
||
|
}
|
||
|
|
||
|
void CGCAccessContext::AddSystem( GCAccessSystem_t nSystem )
|
||
|
{
|
||
|
m_nSystems.InsertIfNotFound( nSystem );
|
||
|
}
|
||
|
|
||
|
bool CGCAccessContext::HasSystem( GCAccessSystem_t system ) const
|
||
|
{
|
||
|
return ( m_nSystems.Find( system ) != m_nSystems.InvalidIndex() );
|
||
|
}
|
||
|
|
||
|
bool CGCAccessContext::IsSubsetOf( const CGCAccessContext& context ) const
|
||
|
{
|
||
|
FOR_EACH_VEC( m_nSystems, nCurrSystem )
|
||
|
{
|
||
|
if( !context.HasSystem( m_nSystems[ nCurrSystem ] ) )
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CGCAccessContext::AddSystemsFrom( const CGCAccessContext& context )
|
||
|
{
|
||
|
FOR_EACH_VEC( context.m_nSystems, nCurrSystem )
|
||
|
{
|
||
|
AddSystem( context.m_nSystems[ nCurrSystem ] );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CGCAccessContext::SetAction( EGCAccessAction eAction, bool bSet )
|
||
|
{
|
||
|
if( bSet )
|
||
|
m_nActions |= eAction;
|
||
|
else
|
||
|
m_nActions &= ~( uint32 )eAction;
|
||
|
}
|
||
|
|
||
|
void CGCAccessContext::SetSuppressAction( EGCAccessAction eAction, bool bSet )
|
||
|
{
|
||
|
if( bSet )
|
||
|
m_nSuppressActions |= eAction;
|
||
|
else
|
||
|
m_nSuppressActions &= ~( uint32 )eAction;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||
|
// CGCAccess
|
||
|
//-----------------------------------------------------------------------------------------------------------------------------------------
|
||
|
|
||
|
static CGCAccess g_GCAccess;
|
||
|
CGCAccess& GGCAccess()
|
||
|
{
|
||
|
return g_GCAccess;
|
||
|
}
|
||
|
|
||
|
CGCAccessSystem::AccessStats_t::AccessStats_t()
|
||
|
: m_nNumFail( 0 )
|
||
|
, m_nNumFailID( 0 )
|
||
|
, m_nNumValid( 0 )
|
||
|
{}
|
||
|
|
||
|
void CGCAccessSystem::AccessStats_t::Add( const AccessStats_t& rhs )
|
||
|
{
|
||
|
m_nNumFail += rhs.m_nNumFail;
|
||
|
m_nNumFailID += rhs.m_nNumFailID;
|
||
|
m_nNumValid += rhs.m_nNumValid;
|
||
|
}
|
||
|
|
||
|
CGCAccess::CGCAccess() :
|
||
|
m_nActions( GCAccessAction_TrackSuccess | GCAccessAction_TrackFail| GCAccessAction_ReturnFail ),
|
||
|
m_nSuppressActions( 0 )
|
||
|
{
|
||
|
m_GlobalContext.Init( "Global" );
|
||
|
}
|
||
|
|
||
|
void CGCAccess::RegisterSystem( const char* pszName, GCAccessSystem_t nID, uint32 nActions, uint32 nSupressActions )
|
||
|
{
|
||
|
//make sure that we don't have a conflict
|
||
|
int nIndex = m_Systems.Find( nID );
|
||
|
if( nIndex != m_Systems.InvalidIndex() )
|
||
|
{
|
||
|
// !FIXME!
|
||
|
// DOTAMERGE AssertMsg varargs
|
||
|
AssertMsg3( false, "Multiple systems conflciting in Register System: ID %d, original: %s, new %s", nID, m_Systems[ nIndex ]->m_sName.String(), pszName );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CGCAccessSystem* pSystem = new CGCAccessSystem;
|
||
|
pSystem->m_sName = pszName;
|
||
|
pSystem->m_nID = nID;
|
||
|
pSystem->m_nActions = nActions;
|
||
|
pSystem->m_nSuppressActions = nSupressActions;
|
||
|
m_Systems.Insert( nID, pSystem );
|
||
|
}
|
||
|
|
||
|
bool CGCAccess::ValidateAccess( GCAccessSystem_t nSystem )
|
||
|
{
|
||
|
//global kill switch
|
||
|
if( !gcaccess_enable.GetBool() )
|
||
|
return true;
|
||
|
|
||
|
return InternalValidateAccess( nSystem, CSteamID(), CSteamID() );
|
||
|
}
|
||
|
|
||
|
bool CGCAccess::ValidateSteamIDAccess( GCAccessSystem_t nSystem, CSteamID steamID )
|
||
|
{
|
||
|
//global kill switch
|
||
|
if( !gcaccess_enable.GetBool() )
|
||
|
return true;
|
||
|
|
||
|
CSteamID expectedID = ( g_pJobCur ) ? g_pJobCur->SOVALIDATE_GetSteamID() : steamID;
|
||
|
return InternalValidateAccess( nSystem, steamID, expectedID );
|
||
|
}
|
||
|
|
||
|
bool CGCAccess::InternalValidateAccess( GCAccessSystem_t nSystem, CSteamID steamID, CSteamID expectedID )
|
||
|
{
|
||
|
//see if tracking for this system is disabled. This list is almost always empty, so just use a linear search for now.
|
||
|
FOR_EACH_VEC( m_SuppressAccess, nAction )
|
||
|
{
|
||
|
if( m_SuppressAccess[ nAction ].m_nSystem == nSystem )
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//assume the global context
|
||
|
const CGCAccessContext* pContext = &m_GlobalContext;
|
||
|
const char* pszJobName = "[global]";
|
||
|
|
||
|
//if we have a job, use it's context
|
||
|
if( g_pJobCur )
|
||
|
{
|
||
|
const CGCJob* pJob = static_cast< const CGCJob* >( g_pJobCur );
|
||
|
pszJobName = pJob->GetName();
|
||
|
if( pJob->GetContext() )
|
||
|
pContext = pJob->GetContext();
|
||
|
}
|
||
|
|
||
|
//is this a valid system to access
|
||
|
bool bValidSteamID = ( steamID == expectedID );
|
||
|
bool bValidSystem = pContext->HasSystem( nSystem );
|
||
|
bool bValidAccess = ( bValidSystem && bValidSteamID );
|
||
|
|
||
|
//determine the actions we want to do for tracking
|
||
|
uint32 nActions = pContext->GetActions() | m_nActions;
|
||
|
uint32 nSuppressActions = pContext->GetSuppressActions() | m_nSuppressActions;
|
||
|
|
||
|
//look up the system that we are accessing
|
||
|
CGCAccessSystem* pSystem = NULL;
|
||
|
int nSystemIndex = m_Systems.Find( nSystem );
|
||
|
if( nSystemIndex == m_Systems.InvalidIndex() )
|
||
|
{
|
||
|
// !FIXME! DOTAMERGE
|
||
|
// AssertMsg varargs
|
||
|
AssertMsg1( false, "Error: Tracking a system that has not been registered. Make sure to register system %u", nSystem );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//make sure that we have a stat entry for this job
|
||
|
pSystem = m_Systems[ nSystemIndex ];
|
||
|
nActions |= pSystem->m_nActions;
|
||
|
nSuppressActions |= pSystem->m_nSuppressActions;
|
||
|
}
|
||
|
|
||
|
//remove any suppressed actions
|
||
|
nActions &= ~nSuppressActions;
|
||
|
|
||
|
//see if we need to track this access
|
||
|
bool bTrackSuccess = ( nActions & GCAccessAction_TrackSuccess ) && bValidAccess;
|
||
|
bool bTrackFail = ( nActions & GCAccessAction_TrackFail ) && !bValidAccess;
|
||
|
if( ( bTrackSuccess || bTrackFail ) && pSystem )
|
||
|
{
|
||
|
//make sure that we have a stat entry for this job
|
||
|
int nJobIndex = pSystem->m_Jobs.Find( ( uintp )pszJobName );
|
||
|
if( nJobIndex == pSystem->m_Jobs.InvalidIndex() )
|
||
|
{
|
||
|
nJobIndex = pSystem->m_Jobs.Insert( ( uintp )pszJobName );
|
||
|
pSystem->m_Jobs[ nJobIndex ].m_sContext = pContext->GetName();
|
||
|
}
|
||
|
|
||
|
//update our stats based upon what was valid and what wasn't
|
||
|
CGCAccessSystem::AccessStats_t& stats = pSystem->m_Jobs[ nJobIndex ].m_Stats;
|
||
|
if( bTrackSuccess )
|
||
|
stats.m_nNumValid++;
|
||
|
|
||
|
if( bTrackFail )
|
||
|
{
|
||
|
if( !bValidSteamID )
|
||
|
stats.m_nNumFailID++;
|
||
|
if( !bValidSystem )
|
||
|
stats.m_nNumFail++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//see if this is a single access we want to try and track
|
||
|
FOR_EACH_VEC( m_SingleAsserts, nCurrAssert )
|
||
|
{
|
||
|
SingleAssert_t* pAssert = m_SingleAsserts[ nCurrAssert ];
|
||
|
if( nSystem == pAssert->m_System )
|
||
|
{
|
||
|
if( V_stricmp( ( pAssert->m_bContext ) ? pContext->GetName() : pszJobName, pAssert->m_sContextOrJob ) == 0 )
|
||
|
{
|
||
|
//log this assert
|
||
|
{
|
||
|
// !FIXME! DOTAMERGE
|
||
|
//CGCInterface::CDisableAssertRateLimit disableAssertLimit;
|
||
|
|
||
|
// !FIXME! DOTAMERGE
|
||
|
// AssertMsg varargs
|
||
|
AssertMsg3( false, "GCSystemAccess Caught %s (context %s) calling into %s", pszJobName, pContext->GetName(), pSystem ? pSystem->m_sName.String() : "unknown" );
|
||
|
}
|
||
|
|
||
|
//and clear it
|
||
|
delete pAssert;
|
||
|
m_SingleAsserts.FastRemove( nCurrAssert );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//at this point, if it is a valid access, just bail
|
||
|
if( bValidAccess )
|
||
|
return true;
|
||
|
|
||
|
//otherwise, handle the failure case
|
||
|
if( nActions & ( GCAccessAction_Msg | GCAccessAction_Assert ) )
|
||
|
{
|
||
|
int nSystemIndex = m_Systems.Find( nSystem );
|
||
|
const char* pszSystem = ( nSystemIndex != m_Systems.InvalidIndex() ) ? m_Systems[ nSystemIndex ]->m_sName.String() : "<unregistered>";
|
||
|
|
||
|
//display a message based upon if it was a system or ID violation
|
||
|
CFmtStr1024 szMsg;
|
||
|
if( !bValidSystem )
|
||
|
{
|
||
|
szMsg.sprintf( "Job %s Accessed invalid system %s (%u) while in context %s\n", pszJobName, pszSystem, nSystem, pContext->GetName() );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
szMsg.sprintf( "Job %s Accessed invalid steam ID %s but expected %s in system %s (%u) while in context %s\n", pszJobName, steamID.RenderLink(), expectedID.RenderLink(), pszSystem, nSystem, pContext->GetName() );
|
||
|
}
|
||
|
|
||
|
if( nActions & GCAccessAction_Msg )
|
||
|
EG_MSG( g_EGAccess, "%s", szMsg.String() );
|
||
|
if( nActions & GCAccessAction_Assert )
|
||
|
{
|
||
|
// !FIXME! DOTAMERGE
|
||
|
// AssertMsg varargs
|
||
|
AssertMsg1( false, "%s", szMsg.String() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return !( nActions & GCAccessAction_ReturnFail );
|
||
|
}
|
||
|
|
||
|
void CGCAccess::SuppressAccess( GCAccessSystem_t nSystem, bool bEnable )
|
||
|
{
|
||
|
//see if it is an existing item already suppressed that we need to modify the ref count of
|
||
|
FOR_EACH_VEC( m_SuppressAccess, nAccess )
|
||
|
{
|
||
|
if( m_SuppressAccess[ nAccess ].m_nSystem == nSystem )
|
||
|
{
|
||
|
if( bEnable )
|
||
|
{
|
||
|
//to enable, we want to decrease the disable count, or remove if we are fully re-enabled
|
||
|
if( m_SuppressAccess[ nAccess ].m_nCount <= 1 )
|
||
|
m_SuppressAccess.FastRemove( nAccess );
|
||
|
else
|
||
|
m_SuppressAccess[ nAccess ].m_nCount--;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_SuppressAccess[ nAccess ].m_nCount++;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//only add it to the list if we are disabling
|
||
|
if( !bEnable )
|
||
|
{
|
||
|
SuppressAccess_t access;
|
||
|
access.m_nSystem = nSystem;
|
||
|
access.m_nCount = 1;
|
||
|
m_SuppressAccess.AddToTail( access );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CGCAccess::ClearSystemStats()
|
||
|
{
|
||
|
FOR_EACH_MAP_FAST( m_Systems, nSystem )
|
||
|
{
|
||
|
m_Systems[ nSystem ]->m_Jobs.Purge();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool CGCAccess::CatchSingleAssert( const char* pszSystem, bool bContext, const char* pszContextOrJob )
|
||
|
{
|
||
|
//find the system we are referencing so we don't add invalid breakpoints if we can avoid it
|
||
|
GCAccessSystem_t nSystemID = ( GCAccessSystem_t )-1;
|
||
|
FOR_EACH_MAP_FAST( m_Systems, nSystem )
|
||
|
{
|
||
|
if( V_stricmp( m_Systems[ nSystem ]->m_sName, pszSystem ) == 0 )
|
||
|
{
|
||
|
nSystemID = m_Systems.Key( nSystem );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( nSystemID == ( GCAccessSystem_t )-1 )
|
||
|
return false;
|
||
|
|
||
|
SingleAssert_t* pAssert = new SingleAssert_t;
|
||
|
pAssert->m_System = nSystemID;
|
||
|
pAssert->m_bContext = bContext;
|
||
|
pAssert->m_sContextOrJob = pszContextOrJob;
|
||
|
m_SingleAsserts.AddToTail( pAssert );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CGCAccess::ClearSingleAsserts()
|
||
|
{
|
||
|
m_SingleAsserts.PurgeAndDeleteElements();
|
||
|
}
|
||
|
|
||
|
|
||
|
//given a display type and a stats object, determines if it meets the criteria
|
||
|
static bool ShouldDisplayStats( CGCAccess::EDisplay eDisplay, const CGCAccessSystem::AccessStats_t& stats )
|
||
|
{
|
||
|
if( eDisplay == CGCAccess::eDisplay_Referenced )
|
||
|
return ( stats.m_nNumFail + stats.m_nNumFailID + stats.m_nNumValid ) > 0;
|
||
|
if( eDisplay == CGCAccess::eDisplay_Violations )
|
||
|
return ( stats.m_nNumFail + stats.m_nNumFailID ) > 0;
|
||
|
if( eDisplay == CGCAccess::eDisplay_IDViolations )
|
||
|
return stats.m_nNumFailID > 0;
|
||
|
|
||
|
//unknown, so just assume all
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CGCAccess::ReportJobs( const char* pszContext, EDisplay eDisplay ) const
|
||
|
{
|
||
|
//collect all of the job stats into a single summary table
|
||
|
CUtlHashMapLarge< uintp, CGCAccessSystem::TrackedJob_t > jobs;
|
||
|
FOR_EACH_MAP_FAST( m_Systems, nSystem )
|
||
|
{
|
||
|
const CGCAccessSystem* pSystem = m_Systems[ nSystem ];
|
||
|
FOR_EACH_MAP_FAST( pSystem->m_Jobs, nJob )
|
||
|
{
|
||
|
const CGCAccessSystem::TrackedJob_t& job = pSystem->m_Jobs[ nJob ];
|
||
|
//skip any contexts we don't care about
|
||
|
if( pszContext && ( V_stricmp( pszContext, job.m_sContext ) != 0 ) )
|
||
|
continue;
|
||
|
if( !ShouldDisplayStats( eDisplay, job.m_Stats ) )
|
||
|
continue;
|
||
|
|
||
|
int nIndex = jobs.Find( pSystem->m_Jobs.Key( nJob ) );
|
||
|
if( nIndex == jobs.InvalidIndex() )
|
||
|
{
|
||
|
nIndex = jobs.Insert( pSystem->m_Jobs.Key( nJob ) );
|
||
|
jobs[ nIndex ].m_sContext = job.m_sContext;
|
||
|
}
|
||
|
|
||
|
jobs[ nIndex ].m_Stats.Add( job.m_Stats );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CGCReportPrinter rp;
|
||
|
rp.AddStringColumn( "Job" );
|
||
|
rp.AddStringColumn( "Context" );
|
||
|
rp.AddIntColumn( "Valid", CGCReportPrinter::eSummary_Total );
|
||
|
rp.AddIntColumn( "Invalid", CGCReportPrinter::eSummary_Total );
|
||
|
rp.AddIntColumn( "InvalidID", CGCReportPrinter::eSummary_Total );
|
||
|
|
||
|
FOR_EACH_MAP_FAST( jobs, nJob )
|
||
|
{
|
||
|
rp.StrValue( ( const char* )jobs.Key( nJob ), CFmtStr( "gcaccess_dump_job \"%s\" %d", ( const char* )jobs.Key( nJob ), eDisplay ).String() );
|
||
|
rp.StrValue( jobs[ nJob ].m_sContext, CFmtStr( "gcaccess_dump_context \"%s\" %d", jobs[ nJob ].m_sContext.String(), eDisplay ).String() );
|
||
|
rp.IntValue( jobs[ nJob ].m_Stats.m_nNumValid );
|
||
|
rp.IntValue( jobs[ nJob ].m_Stats.m_nNumFail );
|
||
|
rp.IntValue( jobs[ nJob ].m_Stats.m_nNumFailID );
|
||
|
rp.CommitRow();
|
||
|
}
|
||
|
|
||
|
rp.SortReport( "Job", false );
|
||
|
rp.PrintReport( SPEW_CONSOLE );
|
||
|
}
|
||
|
|
||
|
void CGCAccess::ReportSystems( const char* pszContext, EDisplay eDisplay ) const
|
||
|
{
|
||
|
CGCReportPrinter rp;
|
||
|
rp.AddStringColumn( "System" );
|
||
|
rp.AddIntColumn( "ID", CGCReportPrinter::eSummary_None );
|
||
|
rp.AddIntColumn( "Jobs", CGCReportPrinter::eSummary_None );
|
||
|
rp.AddIntColumn( "Valid", CGCReportPrinter::eSummary_Total );
|
||
|
rp.AddIntColumn( "Invalid", CGCReportPrinter::eSummary_Total );
|
||
|
rp.AddIntColumn( "InvalidID", CGCReportPrinter::eSummary_Total );
|
||
|
|
||
|
FOR_EACH_MAP_FAST( m_Systems, nSystem )
|
||
|
{
|
||
|
const CGCAccessSystem* pSystem = m_Systems[ nSystem ];
|
||
|
CGCAccessSystem::AccessStats_t stats;
|
||
|
FOR_EACH_MAP_FAST( pSystem->m_Jobs, nJob )
|
||
|
{
|
||
|
const CGCAccessSystem::TrackedJob_t& job = pSystem->m_Jobs[ nJob ];
|
||
|
//skip any contexts we don't care about
|
||
|
if( pszContext && ( V_stricmp( pszContext, job.m_sContext ) != 0 ) )
|
||
|
continue;
|
||
|
|
||
|
stats.Add( job.m_Stats );
|
||
|
}
|
||
|
|
||
|
if( !ShouldDisplayStats( eDisplay, stats ) )
|
||
|
continue;
|
||
|
|
||
|
rp.StrValue( pSystem->m_sName, CFmtStr( "gcaccess_dump_system \"%s\" %d", pSystem->m_sName.String(), eDisplay ).String() );
|
||
|
rp.IntValue( pSystem->m_nID );
|
||
|
rp.IntValue( pSystem->m_Jobs.Count() );
|
||
|
rp.IntValue( stats.m_nNumValid );
|
||
|
rp.IntValue( stats.m_nNumFail );
|
||
|
rp.IntValue( stats.m_nNumFailID );
|
||
|
rp.CommitRow();
|
||
|
}
|
||
|
|
||
|
rp.SortReport( "System", false );
|
||
|
rp.PrintReport( SPEW_CONSOLE );
|
||
|
}
|
||
|
|
||
|
void CGCAccess::FullReport( const char* pszSystemFilter, const char* pszContextFilter, const char* pszJobFilter, EDisplay eDisplay ) const
|
||
|
{
|
||
|
CGCReportPrinter rp;
|
||
|
rp.AddStringColumn( "Job" );
|
||
|
rp.AddStringColumn( "Context" );
|
||
|
rp.AddStringColumn( "System" );
|
||
|
rp.AddIntColumn( "Valid", CGCReportPrinter::eSummary_Total );
|
||
|
rp.AddIntColumn( "Invalid", CGCReportPrinter::eSummary_Total );
|
||
|
rp.AddIntColumn( "InvalidID", CGCReportPrinter::eSummary_Total );
|
||
|
|
||
|
FOR_EACH_MAP_FAST( m_Systems, nSystem )
|
||
|
{
|
||
|
const CGCAccessSystem* pSystem = m_Systems[ nSystem ];
|
||
|
if( pszSystemFilter && V_stricmp( pszSystemFilter, pSystem->m_sName ) )
|
||
|
continue;
|
||
|
|
||
|
FOR_EACH_MAP_FAST( pSystem->m_Jobs, nJob )
|
||
|
{
|
||
|
const CGCAccessSystem::AccessStats_t& stats = pSystem->m_Jobs[ nJob ].m_Stats;
|
||
|
const char* pszJob = ( const char* )pSystem->m_Jobs.Key( nJob );
|
||
|
const char* pszContext = ( const char* )pSystem->m_Jobs[ nJob ].m_sContext;
|
||
|
|
||
|
if( !ShouldDisplayStats( eDisplay, stats ) )
|
||
|
continue;
|
||
|
if( pszJobFilter && V_stricmp( pszJobFilter, pszJob ) )
|
||
|
continue;
|
||
|
if( pszContextFilter && V_stricmp( pszContextFilter, pszContext ) )
|
||
|
continue;
|
||
|
|
||
|
rp.StrValue( pszJob, CFmtStr( "gcaccess_dump_job \"%s\" %d", pszJob, eDisplay ).String() );
|
||
|
rp.StrValue( pszContext, CFmtStr( "gcaccess_dump_context \"%s\" %d", pszContext, eDisplay ).String() );
|
||
|
rp.StrValue( pSystem->m_sName, CFmtStr( "gcaccess_dump_system \"%s\" %d", pSystem->m_sName.String(), eDisplay ).String() );
|
||
|
rp.IntValue( stats.m_nNumValid );
|
||
|
rp.IntValue( stats.m_nNumFail );
|
||
|
rp.IntValue( stats.m_nNumFailID );
|
||
|
rp.CommitRow();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rp.SortReport( "Context", false );
|
||
|
rp.PrintReport( SPEW_CONSOLE );
|
||
|
}
|
||
|
|
||
|
void CGCAccess::DependencyReport( const char* pszSystem, EDisplay eDisplay ) const
|
||
|
{
|
||
|
//first off, we need to find the system we are scanning for dependencies on
|
||
|
const CGCAccessSystem* pMatchSystem = NULL;
|
||
|
FOR_EACH_MAP_FAST( m_Systems, nSystem )
|
||
|
{
|
||
|
const CGCAccessSystem* pSystem = m_Systems[ nSystem ];
|
||
|
if( V_stricmp( pszSystem, pSystem->m_sName ) == 0 )
|
||
|
{
|
||
|
pMatchSystem = pSystem;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( !pMatchSystem )
|
||
|
{
|
||
|
EG_MSG( SPEW_CONSOLE, "Unable to find system %s for dependency report\n", pszSystem );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//now generate our report
|
||
|
CGCReportPrinter rp;
|
||
|
rp.AddStringColumn( "Job" );
|
||
|
rp.AddStringColumn( "Context" );
|
||
|
rp.AddStringColumn( "System" );
|
||
|
rp.AddIntColumn( "Valid", CGCReportPrinter::eSummary_Total );
|
||
|
rp.AddIntColumn( "Invalid", CGCReportPrinter::eSummary_Total );
|
||
|
rp.AddIntColumn( "InvalidID", CGCReportPrinter::eSummary_Total );
|
||
|
|
||
|
FOR_EACH_MAP_FAST( m_Systems, nSystem )
|
||
|
{
|
||
|
const CGCAccessSystem* pSystem = m_Systems[ nSystem ];
|
||
|
//skip ourself
|
||
|
if( pSystem == pMatchSystem )
|
||
|
continue;
|
||
|
|
||
|
FOR_EACH_MAP_FAST( pSystem->m_Jobs, nJob )
|
||
|
{
|
||
|
const CGCAccessSystem::AccessStats_t& stats = pSystem->m_Jobs[ nJob ].m_Stats;
|
||
|
const char* pszJob = ( const char* )pSystem->m_Jobs.Key( nJob );
|
||
|
const char* pszContext = ( const char* )pSystem->m_Jobs[ nJob ].m_sContext;
|
||
|
|
||
|
//skip any job that isn't using our match system
|
||
|
if( !pMatchSystem->m_Jobs.HasElement( ( uintp )pszJob ) )
|
||
|
continue;
|
||
|
if( !ShouldDisplayStats( eDisplay, stats ) )
|
||
|
continue;
|
||
|
|
||
|
rp.StrValue( pszJob, CFmtStr( "gcaccess_dump_job \"%s\" %d", pszJob, eDisplay ).String() );
|
||
|
rp.StrValue( pszContext, CFmtStr( "gcaccess_dump_context \"%s\" %d", pszContext, eDisplay ).String() );
|
||
|
rp.StrValue( pSystem->m_sName, CFmtStr( "gcaccess_dump_system \"%s\" %d", pSystem->m_sName.String(), eDisplay ).String() );
|
||
|
rp.IntValue( stats.m_nNumValid );
|
||
|
rp.IntValue( stats.m_nNumFail );
|
||
|
rp.IntValue( stats.m_nNumFailID );
|
||
|
rp.CommitRow();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rp.SortReport( "System", false );
|
||
|
rp.PrintReport( SPEW_CONSOLE );
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
GC_CON_COMMAND_PARAMS( gcaccess_dump_job, 1, "<Job Name> Dumps all of the accesses associated with the specified job" )
|
||
|
{
|
||
|
CGCAccess::EDisplay eDisplay = ( args.ArgC() >= 3 ) ? ( CGCAccess::EDisplay )V_atoi( args[ 2 ] ) : CGCAccess::eDisplay_Referenced;
|
||
|
GGCAccess().FullReport( NULL, NULL, args[ 1 ], eDisplay );
|
||
|
}
|
||
|
|
||
|
GC_CON_COMMAND_PARAMS( gcaccess_dump_context, 1, "<Job Name> Dumps all of the accesses associated with the specified context" )
|
||
|
{
|
||
|
CGCAccess::EDisplay eDisplay = ( args.ArgC() >= 3 ) ? ( CGCAccess::EDisplay )V_atoi( args[ 2 ] ) : CGCAccess::eDisplay_Referenced;
|
||
|
GGCAccess().FullReport( NULL, args[ 1 ], NULL, eDisplay );
|
||
|
}
|
||
|
|
||
|
GC_CON_COMMAND_PARAMS( gcaccess_dump_system, 1, "<Job Name> Dumps all of the accesses associated with the specified system" )
|
||
|
{
|
||
|
CGCAccess::EDisplay eDisplay = ( args.ArgC() >= 3 ) ? ( CGCAccess::EDisplay )V_atoi( args[ 2 ] ) : CGCAccess::eDisplay_Referenced;
|
||
|
GGCAccess().FullReport( args[ 1 ], NULL, NULL, eDisplay );
|
||
|
}
|
||
|
|
||
|
GC_CON_COMMAND_PARAMS( gcaccess_dump_context_jobs, 1, "<Context Name> Dumps all of the accesses associated with the specified context" )
|
||
|
{
|
||
|
CGCAccess::EDisplay eDisplay = ( args.ArgC() >= 3 ) ? ( CGCAccess::EDisplay )V_atoi( args[ 2 ] ) : CGCAccess::eDisplay_Referenced;
|
||
|
GGCAccess().ReportJobs( args[ 1 ], eDisplay );
|
||
|
}
|
||
|
|
||
|
GC_CON_COMMAND_PARAMS( gcaccess_dump_context_systems, 1, "<Context Name> Dumps all of the accesses associated with the specified context" )
|
||
|
{
|
||
|
CGCAccess::EDisplay eDisplay = ( args.ArgC() >= 3 ) ? ( CGCAccess::EDisplay )V_atoi( args[ 2 ] ) : CGCAccess::eDisplay_Referenced;
|
||
|
GGCAccess().ReportSystems( args[ 1 ], eDisplay );
|
||
|
}
|
||
|
|
||
|
GC_CON_COMMAND_PARAMS( gcaccess_dump_system_dependencies, 1, "<System Name> This will dump out for all jobs that reference the specified system, what other systems they reference" )
|
||
|
{
|
||
|
CGCAccess::EDisplay eDisplay = ( args.ArgC() >= 3 ) ? ( CGCAccess::EDisplay )V_atoi( args[ 2 ] ) : CGCAccess::eDisplay_Referenced;
|
||
|
GGCAccess().DependencyReport( args[ 1 ], eDisplay );
|
||
|
}
|
||
|
|
||
|
GC_CON_COMMAND( gcaccess_dump_jobs, "Dumps all the jobs that have been tracked with the gcaccess system as well as count of violations" )
|
||
|
{
|
||
|
CGCAccess::EDisplay eDisplay = ( args.ArgC() >= 2 ) ? ( CGCAccess::EDisplay )V_atoi( args[ 1 ] ) : CGCAccess::eDisplay_Referenced;
|
||
|
GGCAccess().ReportJobs( NULL, eDisplay);
|
||
|
}
|
||
|
|
||
|
GC_CON_COMMAND( gcaccess_dump_systems, "Dumps all the systems that have been tracked with the gcaccess system as well as count of violations" )
|
||
|
{
|
||
|
CGCAccess::EDisplay eDisplay = ( args.ArgC() >= 2 ) ? ( CGCAccess::EDisplay )V_atoi( args[ 1 ] ) : CGCAccess::eDisplay_Referenced;
|
||
|
GGCAccess().ReportSystems( NULL, eDisplay );
|
||
|
}
|
||
|
|
||
|
GC_CON_COMMAND( gcaccess_dump_full, "Dumps all the information tracked by the gcaccess" )
|
||
|
{
|
||
|
CGCAccess::EDisplay eDisplay = ( args.ArgC() >= 2 ) ? ( CGCAccess::EDisplay )V_atoi( args[ 1 ] ) : CGCAccess::eDisplay_Referenced;
|
||
|
GGCAccess().FullReport( NULL, NULL, NULL, eDisplay );
|
||
|
}
|
||
|
|
||
|
GC_CON_COMMAND_PARAMS( gcaccess_catch_context, 2, "<system> <context> - Catches and generates an assert when the specified context calls into the provided system. This will only generate a single assert" )
|
||
|
{
|
||
|
if( !GGCAccess().CatchSingleAssert( args[ 1 ], true, args[ 2 ] ) )
|
||
|
EG_MSG( SPEW_CONSOLE, "Unable to register catch, likely \'%s\' is not a valid system.", args[ 1 ] );
|
||
|
}
|
||
|
|
||
|
GC_CON_COMMAND_PARAMS( gcaccess_catch_job, 2, "<system> <job> - Catches and generates an assert when the specified job calls into the provided system. This will only generate a single assert" )
|
||
|
{
|
||
|
if( !GGCAccess().CatchSingleAssert( args[ 1 ], false, args[ 2 ] ) )
|
||
|
EG_MSG( SPEW_CONSOLE, "Unable to register catch, likely \'%s\' is not a valid system.", args[ 1 ] );
|
||
|
}
|
||
|
|
||
|
GC_CON_COMMAND( gcaccess_catch_clear, "Clears all previously specified catches" )
|
||
|
{
|
||
|
GGCAccess().ClearSingleAsserts();
|
||
|
}
|
||
|
|
||
|
} //namespace GCSDK
|