aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/env_debughistory.cpp
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
committerJoe Ludwig <[email protected]>2013-06-26 15:22:04 -0700
commit39ed87570bdb2f86969d4be821c94b722dc71179 (patch)
treeabc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/server/env_debughistory.cpp
downloadsource-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz
source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/server/env_debughistory.cpp')
-rw-r--r--mp/src/game/server/env_debughistory.cpp367
1 files changed, 367 insertions, 0 deletions
diff --git a/mp/src/game/server/env_debughistory.cpp b/mp/src/game/server/env_debughistory.cpp
new file mode 100644
index 00000000..1ce2c663
--- /dev/null
+++ b/mp/src/game/server/env_debughistory.cpp
@@ -0,0 +1,367 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "cbase.h"
+#include "isaverestore.h"
+#include "env_debughistory.h"
+#include "tier0/vprof.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// Number of characters worth of debug to use per history category
+#define DEBUG_HISTORY_VERSION 6
+#define DEBUG_HISTORY_FIRST_VERSIONED 5
+#define MAX_DEBUG_HISTORY_LINE_LENGTH 256
+#define MAX_DEBUG_HISTORY_LENGTH (1000 * MAX_DEBUG_HISTORY_LINE_LENGTH)
+
+//-----------------------------------------------------------------------------
+// Purpose: Stores debug history in savegame files for debugging reference
+//-----------------------------------------------------------------------------
+class CDebugHistory : public CBaseEntity
+{
+ DECLARE_CLASS( CDebugHistory, CBaseEntity );
+public:
+ DECLARE_DATADESC();
+
+ void Spawn();
+ void AddDebugHistoryLine( int iCategory, const char *szLine );
+ void ClearHistories( void );
+ void DumpDebugHistory( int iCategory );
+
+ int Save( ISave &save );
+ int Restore( IRestore &restore );
+
+private:
+ char m_DebugLines[MAX_HISTORY_CATEGORIES][MAX_DEBUG_HISTORY_LENGTH];
+ char *m_DebugLineEnd[MAX_HISTORY_CATEGORIES];
+};
+
+BEGIN_DATADESC( CDebugHistory )
+ //DEFINE_FIELD( m_DebugLines, FIELD_CHARACTER ), // Not saved because we write it out manually
+ //DEFINE_FIELD( m_DebugLineEnd, FIELD_CHARACTER ),
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( env_debughistory, CDebugHistory );
+
+// The handle to the debug history singleton. Created on first access via GetDebugHistory.
+static CHandle< CDebugHistory > s_DebugHistory;
+
+
+//-----------------------------------------------------------------------------
+// Spawn
+//-----------------------------------------------------------------------------
+void CDebugHistory::Spawn()
+{
+ BaseClass::Spawn();
+
+#ifdef DISABLE_DEBUG_HISTORY
+ UTIL_Remove( this );
+#else
+ if ( g_pGameRules && g_pGameRules->IsMultiplayer() )
+ {
+ UTIL_Remove( this );
+ }
+ else
+ {
+ Warning( "DEBUG HISTORY IS ENABLED. Disable before release (in env_debughistory.h).\n" );
+ }
+#endif
+
+ ClearHistories();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDebugHistory::AddDebugHistoryLine( int iCategory, const char *szLine )
+{
+ if ( iCategory < 0 || iCategory >= MAX_HISTORY_CATEGORIES )
+ {
+ Warning("Attempted to add a debughistory line to category %d. Valid categories are %d to %d.\n", iCategory, 0, (MAX_HISTORY_CATEGORIES-1) );
+ return;
+ }
+
+ // Don't do debug history before the singleton is properly set up.
+ if ( !m_DebugLineEnd[iCategory] )
+ return;
+
+ const char *pszRemaining = szLine;
+ int iCharsToWrite = strlen( pszRemaining ) + 1; // Add 1 so that we copy the null terminator
+
+ // Clip the line if it's too long. Wasteful doing it this way, but keeps code below nice & simple.
+ char szTmpBuffer[MAX_DEBUG_HISTORY_LINE_LENGTH];
+ if ( iCharsToWrite > MAX_DEBUG_HISTORY_LINE_LENGTH)
+ {
+ memcpy( szTmpBuffer, szLine, sizeof(szTmpBuffer) );
+ szTmpBuffer[MAX_DEBUG_HISTORY_LINE_LENGTH-1] = '\0';
+ pszRemaining = szTmpBuffer;
+ iCharsToWrite = MAX_DEBUG_HISTORY_LINE_LENGTH;
+ }
+
+ while ( iCharsToWrite )
+ {
+ int iCharsLeftBeforeLoop = sizeof(m_DebugLines[iCategory]) - (m_DebugLineEnd[iCategory] - m_DebugLines[iCategory]);
+
+ // Write into the buffer
+ int iWrote = MIN( iCharsToWrite, iCharsLeftBeforeLoop );
+ memcpy( m_DebugLineEnd[iCategory], pszRemaining, iWrote );
+ m_DebugLineEnd[iCategory] += iWrote;
+ pszRemaining += iWrote;
+
+ // Did we loop?
+ if ( iWrote == iCharsLeftBeforeLoop )
+ {
+ m_DebugLineEnd[iCategory] = m_DebugLines[iCategory];
+ }
+
+ iCharsToWrite -= iWrote;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDebugHistory::DumpDebugHistory( int iCategory )
+{
+ if ( iCategory < 0 || iCategory >= MAX_HISTORY_CATEGORIES )
+ {
+ Warning("Attempted to dump a history for category %d. Valid categories are %d to %d.\n", iCategory, 0, (MAX_HISTORY_CATEGORIES-1) );
+ return;
+ }
+
+ // Find the start of the oldest whole debug line.
+ const char *pszLine = m_DebugLineEnd[iCategory] + 1;
+ if ( (pszLine - m_DebugLines[iCategory]) >= sizeof(m_DebugLines[iCategory]) )
+ {
+ pszLine = m_DebugLines[iCategory];
+ }
+
+ // Are we at the start of a line? If there's a null terminator before us, then we're good to go.
+ while ( (!( pszLine == m_DebugLines[iCategory] && *(m_DebugLines[iCategory]+sizeof(m_DebugLines[iCategory])-1) == '\0' ) &&
+ !( pszLine != m_DebugLines[iCategory] && *(pszLine-1) == '\0' ))
+ || *pszLine == '\0' )
+ {
+ pszLine++;
+
+ // Have we looped?
+ if ( (pszLine - m_DebugLines[iCategory]) >= sizeof(m_DebugLines[iCategory]) )
+ {
+ pszLine = m_DebugLines[iCategory];
+ }
+
+ if ( pszLine == m_DebugLineEnd[iCategory] )
+ {
+ // We looped through the entire history, and found nothing.
+ Msg( "Debug History of Category %d is EMPTY\n", iCategory );
+ return;
+ }
+ }
+
+ // Now print everything up till the end
+ char szMsgBuffer[MAX_DEBUG_HISTORY_LINE_LENGTH];
+ char *pszMsg = szMsgBuffer;
+ Msg( "Starting Debug History Dump of Category %d\n", iCategory );
+ while ( pszLine != m_DebugLineEnd[iCategory] )
+ {
+ *pszMsg = *pszLine;
+ if ( *pszLine == '\0' )
+ {
+ if ( szMsgBuffer[0] != '\0' )
+ {
+ // Found a full line, so print it
+ Msg( "%s", szMsgBuffer );
+ }
+
+ // Clear the buffer
+ pszMsg = szMsgBuffer;
+ *pszMsg = '\0';
+ }
+ else
+ {
+ pszMsg++;
+ }
+
+ pszLine++;
+
+ // Have we looped?
+ if ( (pszLine - m_DebugLines[iCategory]) >= sizeof(m_DebugLines[iCategory]) )
+ {
+ pszLine = m_DebugLines[iCategory];
+ }
+ }
+ Msg("Ended Debug History Dump of Category %d\n", iCategory );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDebugHistory::ClearHistories( void )
+{
+ for ( int i = 0; i < MAX_HISTORY_CATEGORIES; i++ )
+ {
+ memset( m_DebugLines[i], 0, sizeof(m_DebugLines[i]) );
+ m_DebugLineEnd[i] = m_DebugLines[i];
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CDebugHistory::Save( ISave &save )
+{
+ int iVersion = DEBUG_HISTORY_VERSION;
+ save.WriteInt( &iVersion );
+ int iMaxCategorys = MAX_HISTORY_CATEGORIES;
+ save.WriteInt( &iMaxCategorys );
+ for ( int iCategory = 0; iCategory < MAX_HISTORY_CATEGORIES; iCategory++ )
+ {
+ int iEnd = m_DebugLineEnd[iCategory] - m_DebugLines[iCategory];
+ save.WriteInt( &iEnd );
+ save.WriteData( m_DebugLines[iCategory], MAX_DEBUG_HISTORY_LENGTH );
+ }
+
+ return BaseClass::Save(save);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CDebugHistory::Restore( IRestore &restore )
+{
+ ClearHistories();
+
+ int iVersion = restore.ReadInt();
+
+ if ( iVersion >= DEBUG_HISTORY_FIRST_VERSIONED )
+ {
+ int iMaxCategorys = restore.ReadInt();
+ for ( int iCategory = 0; iCategory < MIN(iMaxCategorys,MAX_HISTORY_CATEGORIES); iCategory++ )
+ {
+ int iEnd = restore.ReadInt();
+ m_DebugLineEnd[iCategory] = m_DebugLines[iCategory] + iEnd;
+ restore.ReadData( m_DebugLines[iCategory], sizeof(m_DebugLines[iCategory]), 0 );
+ }
+ }
+ else
+ {
+ int iMaxCategorys = iVersion;
+ for ( int iCategory = 0; iCategory < MIN(iMaxCategorys,MAX_HISTORY_CATEGORIES); iCategory++ )
+ {
+ int iEnd = restore.ReadInt();
+ m_DebugLineEnd[iCategory] = m_DebugLines[iCategory] + iEnd;
+ restore.ReadData( m_DebugLines[iCategory], sizeof(m_DebugLines[iCategory]), 0 );
+ }
+ }
+
+ return BaseClass::Restore(restore);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Singleton debug history. Created by first usage.
+//-----------------------------------------------------------------------------
+CDebugHistory *GetDebugHistory()
+{
+#ifdef DISABLE_DEBUG_HISTORY
+ return NULL;
+#endif
+
+ if ( g_pGameRules && g_pGameRules->IsMultiplayer() )
+ return NULL;
+
+ if ( s_DebugHistory == NULL )
+ {
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "env_debughistory" );
+ if ( pEnt )
+ {
+ s_DebugHistory = dynamic_cast<CDebugHistory*>(pEnt);
+ }
+ else
+ {
+ s_DebugHistory = ( CDebugHistory * )CreateEntityByName( "env_debughistory" );
+ if ( s_DebugHistory )
+ {
+ s_DebugHistory->Spawn();
+ }
+ }
+ }
+
+ Assert( s_DebugHistory );
+ return s_DebugHistory;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void AddDebugHistoryLine( int iCategory, const char *pszLine )
+{
+#ifdef DISABLE_DEBUG_HISTORY
+ return;
+#else
+ if ( g_pGameRules && g_pGameRules->IsMultiplayer() )
+ return;
+
+ if ( !GetDebugHistory() )
+ {
+ Warning("Failed to find or create an env_debughistory.\n" );
+ return;
+ }
+
+ GetDebugHistory()->AddDebugHistoryLine( iCategory, pszLine );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CC_DebugHistory_AddLine( const CCommand &args )
+{
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ if ( args.ArgC() < 3 )
+ {
+ Warning("Incorrect parameters. Format: <category id> <line>\n");
+ return;
+ }
+
+ int iCategory = atoi(args[ 1 ]);
+ const char *pszLine = args[ 2 ];
+ AddDebugHistoryLine( iCategory, pszLine );
+}
+static ConCommand dbghist_addline( "dbghist_addline", CC_DebugHistory_AddLine, "Add a line to the debug history. Format: <category id> <line>", FCVAR_NONE );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CC_DebugHistory_Dump( const CCommand &args )
+{
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ if ( args.ArgC() < 2 )
+ {
+ Warning("Incorrect parameters. Format: <category id>\n");
+ return;
+ }
+
+ if ( GetDebugHistory() )
+ {
+ int iCategory = atoi(args[ 1 ]);
+ GetDebugHistory()->DumpDebugHistory( iCategory );
+ }
+}
+
+static ConCommand dbghist_dump("dbghist_dump", CC_DebugHistory_Dump,
+ "Dump the debug history to the console. Format: <category id>\n"
+ " Categories:\n"
+ " 0: Entity I/O\n"
+ " 1: AI Decisions\n"
+ " 2: Scene Print\n"
+ " 3: Alyx Blind\n"
+ " 4: Log of damage done to player",
+ FCVAR_NONE );
+