csgo-2018-source/engine/status.cpp
2021-07-24 21:11:47 -07:00

272 lines
8.0 KiB
C++

//===== Copyright 1996-2013, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#ifdef _WIN32
#if !defined(_X360)
#include "winlite.h"
#endif
#include "tier0/memdbgon.h" // needed because in release builds crtdbg.h is handled specially if USE_MEM_DEBUG is defined
#include "tier0/memdbgoff.h"
#include <crtdbg.h> // For getting at current heap size
#endif
#include "tier0/vprof.h"
#include "tier0/etwprof.h"
#include "tier0/icommandline.h"
#include "tier0/systeminformation.h"
#include "tier1/utlbuffer.h"
#include "tier1/keyvalues.h"
#include "matchmaking/imatchframework.h"
#include "edict.h"
#include "cdll_engine_int.h"
#include "igame.h"
#include "host_cmd.h"
#include "status.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
class CKeyValuesDumpForStatusReport : public IKeyValuesDumpContextAsText
{
public:
CKeyValuesDumpForStatusReport( CUtlBuffer &buffer ) : m_buffer( buffer ) {}
public:
virtual bool KvWriteText( char const *szText ) { m_buffer.Printf( "%s", szText ); return true; }
protected:
CUtlBuffer &m_buffer;
};
//-----------------------------------------------------------------------------
static char g_szCombinedStatusBuffer[10240] = {0};
static char g_szMinidumpInfoBuffer[4094] = {0}; // 4094 because ETW marks are limited to this.
static char g_szHostStatusBuffer[1024] = {0};
static char g_szMemoryStatusBuffer[1024] = {0};
static char g_szMatchmakingStatusBuffer[4094] = {0}; // 4094 because ETW marks are limited to this.
static char* g_szAudioStatusBuffer; // Returned by audio code
extern CGlobalVars g_ServerGlobalVariables;
extern int gHostSpawnCount;
extern int g_nMapLoadCount;
#ifdef DEDICATED
PAGED_POOL_INFO_t g_pagedpoolinfo;
char g_minidumpinfo[ 4094 ] = {0};
#else
extern PAGED_POOL_INFO_t g_pagedpoolinfo;
extern char g_minidumpinfo[ 4094 ];
#endif
extern bool g_bUpdateMinidumpComment;
static PAGED_POOL_INFO_t final;
//-----------------------------------------------------------------------------
static void Status_UpdateMemoryStatus( bool bIncludeFullMemoryInfo )
{
g_szMemoryStatusBuffer[0] = 0;
CUtlBuffer buf( g_szMemoryStatusBuffer, sizeof(g_szMemoryStatusBuffer), CUtlBuffer::TEXT_BUFFER );
if ( bIncludeFullMemoryInfo )
{
#ifdef _WIN32
MEMORYSTATUSEX memStat;
ZeroMemory(&memStat, sizeof(MEMORYSTATUSEX));
memStat.dwLength = sizeof(MEMORYSTATUSEX);
if ( GlobalMemoryStatusEx( &memStat ) )
{
double MbDiv = 1024.0 * 1024.0;
buf.Printf( "Windows OS memory status:\nmemusage( %d %% )\ntotalPhysical Mb(%.2f)\nfreePhysical Mb(%.2f)\ntotalPaging Mb(%.2f)\nfreePaging Mb(%.2f)\ntotalVirtualMem Mb(%.2f)\nfreeVirtualMem Mb(%.2f)\nextendedVirtualFree Mb(%.2f)\n\n",
memStat.dwMemoryLoad,
(double)memStat.ullTotalPhys/MbDiv,
(double)memStat.ullAvailPhys/MbDiv,
(double)memStat.ullTotalPageFile/MbDiv,
(double)memStat.ullAvailPageFile/MbDiv,
(double)memStat.ullTotalVirtual/MbDiv,
(double)memStat.ullAvailVirtual/MbDiv,
(double)memStat.ullAvailExtendedVirtual/MbDiv);
}
#endif
}
if ( g_pMemAlloc->MemoryAllocFailed() )
{
buf.Printf( "*** Memory allocation failed for %d bytes! ***\n", g_pMemAlloc->MemoryAllocFailed() );
}
GenericMemoryStat_t *pMemoryStats = NULL;
int nMemoryStatCount = g_pMemAlloc->GetGenericMemoryStats( &pMemoryStats );
if ( nMemoryStatCount > 0 )
{
buf.Printf( "g_pMemAlloc->GetGenericMemoryStats(): %d\n", nMemoryStatCount );
for ( int i = 0; i < nMemoryStatCount; i++ )
{
buf.Printf( "%d. %s : %d \n", i + 1, pMemoryStats[i].name, pMemoryStats[i].value );
}
}
buf.Printf( "\n" );
}
static void Status_UpdateMinidumpCommentBuffer()
{
g_szMinidumpInfoBuffer[0] = 0;
CUtlBuffer minidumpInfoBuffer( g_szMinidumpInfoBuffer, sizeof(g_szMinidumpInfoBuffer), CUtlBuffer::TEXT_BUFFER );
minidumpInfoBuffer.Printf( "Uptime: %f\n", Plat_FloatTime() );
if ( g_minidumpinfo[0] != 0 )
{
minidumpInfoBuffer.Printf( "%s", g_minidumpinfo );
}
minidumpInfoBuffer.Printf( "Active: %s\nSpawnCount %d\nMapLoad Count %d\n", ( game && game->IsActiveApp() ) ? "active" : "inactive", gHostSpawnCount, g_nMapLoadCount );
}
static CUtlBuffer *s_pStatusBuffer = NULL;
static void Status_PrintCallback( const char *fmt, ... )
{
if ( s_pStatusBuffer )
{
va_list argptr;
va_start (argptr,fmt);
s_pStatusBuffer->VaPrintf( fmt, argptr );
va_end (argptr);
}
}
static void Status_UpdateHostStatusBuffer()
{
g_szHostStatusBuffer[0] = 0;
CUtlBuffer hostStatusBuffer( g_szHostStatusBuffer, sizeof(g_szHostStatusBuffer), CUtlBuffer::TEXT_BUFFER );
s_pStatusBuffer = &hostStatusBuffer;
Host_PrintStatus( kCommandSrcCode, Status_PrintCallback, false );
s_pStatusBuffer = NULL;
}
static void Status_UpdateMatchmakingBuffer()
{
// matchmaking status text
g_szMatchmakingStatusBuffer[0] = 0;
if ( g_pMatchFramework )
{
if ( IMatchSession *pMatchSession = g_pMatchFramework->GetMatchSession() )
{
CUtlBuffer matchmakingStatusBuffer( g_szMatchmakingStatusBuffer, sizeof(g_szMatchmakingStatusBuffer), CUtlBuffer::TEXT_BUFFER );
matchmakingStatusBuffer.Printf( "match session %p\n", pMatchSession );
CKeyValuesDumpForStatusReport kvDumpContext( matchmakingStatusBuffer );
matchmakingStatusBuffer.Printf( "session system data:\n" );
pMatchSession->GetSessionSystemData()->Dump( &kvDumpContext );
matchmakingStatusBuffer.Printf( "session settings:\n" );
pMatchSession->GetSessionSettings()->Dump( &kvDumpContext );
}
}
if ( !g_szMatchmakingStatusBuffer[0] )
{
V_sprintf_safe( g_szMatchmakingStatusBuffer, "No matchmaking.\n" );
}
}
static void ConvertNewlinesToTabsinPlace( char *pBuffer )
{
while( pBuffer && *pBuffer != 0 )
{
pBuffer = V_strstr(pBuffer, "\n");
if ( pBuffer )
{
*pBuffer = '\t';
}
}
}
static void Status_UpdateBuffers( bool bForETW )
{
Status_UpdateMinidumpCommentBuffer();
Status_UpdateMemoryStatus( !bForETW );
Status_UpdateHostStatusBuffer();
Status_UpdateMatchmakingBuffer();
#ifndef DEDICATED
extern char* Status_UpdateAudioBuffer();
g_szAudioStatusBuffer = Status_UpdateAudioBuffer();
#else
static char chNoAudio[8] = { 'n', '/', 'a', 0 };
g_szAudioStatusBuffer = chNoAudio;
#endif
if ( bForETW )
{
// walk through buffers converting \n to \t
ConvertNewlinesToTabsinPlace( g_szMinidumpInfoBuffer );
ConvertNewlinesToTabsinPlace( g_szMemoryStatusBuffer );
ConvertNewlinesToTabsinPlace( g_szHostStatusBuffer );
ConvertNewlinesToTabsinPlace( g_szMatchmakingStatusBuffer );
ConvertNewlinesToTabsinPlace( g_szAudioStatusBuffer );
}
}
static void Status_UpdateCombinedBuffer()
{
Status_UpdateBuffers( false );
g_szCombinedStatusBuffer[0] = 0;
CUtlBuffer combinedStatusBuffer( g_szCombinedStatusBuffer, sizeof(g_szCombinedStatusBuffer), CUtlBuffer::TEXT_BUFFER );
combinedStatusBuffer.Printf( "\nGame Info:\n%s", g_szMinidumpInfoBuffer );
combinedStatusBuffer.Printf( "\nMemory Info:\n%s", g_szMemoryStatusBuffer );
combinedStatusBuffer.Printf( "\nHost Info:\n%s", g_szHostStatusBuffer );
combinedStatusBuffer.Printf( "\nMatchmaking Info:\n%s", g_szMatchmakingStatusBuffer );
combinedStatusBuffer.Printf( "\nAudio Info:\n%s", g_szAudioStatusBuffer );
}
// public functions
const char *Status_GetBuffer()
{
return g_szCombinedStatusBuffer;
}
void Status_Update()
{
Status_UpdateCombinedBuffer();
}
void Status_CheckSendETWMark()
{
// If ETW (Event Tracing for Windows) is available,
// collect status text every 5 seconds
#ifdef ETW_MARKS_ENABLED
// Don't emit status events if nobody is listening.
if ( ETWIsTracingEnabled() )
{
static double fLastETWUpdateTime = -5.0;
double fCurrentTime = Plat_FloatTime();
// update status buffer every 5 seconds and write to vtrace/xperf mark
if ( fCurrentTime - fLastETWUpdateTime > 5.0 )
{
fLastETWUpdateTime = fCurrentTime;
Status_UpdateBuffers( true );
ETWMark( g_szMinidumpInfoBuffer );
ETWMark( g_szMemoryStatusBuffer );
ETWMark( g_szHostStatusBuffer );
ETWMark( g_szMatchmakingStatusBuffer );
ETWMark( g_szAudioStatusBuffer );
}
}
#endif
}