272 lines
8.0 KiB
C++
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
|
|
}
|
|
|