Counter Strike : Global Offensive Source Code
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.

223 lines
7.9 KiB

  1. //========= Copyright � 1996-2008, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: See c_memorylog.h
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "inetchannelinfo.h"
  8. #include "shaderapi/gpumemorystats.h"
  9. #include "tier1/fmtstr.h"
  10. #include "c_memorylog.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. #ifdef C_MEMORYLOG_TICKING_ENABLED
  14. ConVar memorylog_tick( "memorylog_tick", "20", 0, "Set to N to spew free memory to the console every N seconds to be captured by logging (0 disables)." );
  15. ConVar memorylog_mem_dump( "memorylog_mem_dump", "1", 0 );
  16. ConVar memorylog_spewhunkusage( "memorylog_spewhunkusage", "0", 0, "Set to 1 to spew hunk memory usage info after map load" );
  17. // Memory log auto game system instantiation
  18. C_MemoryLog g_MemoryLog;
  19. const char *GetMapName( void )
  20. {
  21. static char mapName[32];
  22. mapName[0] = 0;
  23. if ( engine->GetLevelNameShort() )
  24. V_strncpy( mapName, engine->GetLevelNameShort(), sizeof( mapName ) );
  25. if ( !mapName[ 0 ] )
  26. V_strncpy( mapName, "none", sizeof( mapName ) );
  27. return mapName;
  28. }
  29. void Cert_DebugString( const char * psz )
  30. {
  31. // Defined so we can spew this in a CERT build (useful for double-checking memory near the end of the project)
  32. #if defined( _X360 )
  33. // TODO: Disabled since we saw a couple of hangs inside OutputDebugStringA inside here: XBX_OutputDebugString( psz );
  34. Msg( "%s", psz );
  35. #elif defined( _WIN32 )
  36. ::OutputDebugStringA( psz );
  37. #elif defined(_PS3)
  38. printf( "%s", psz );
  39. #endif
  40. }
  41. bool C_MemoryLog::Init( void )
  42. {
  43. // Spew the current date+time on startup, to help associate logs with crashdumps
  44. struct tm time;
  45. Plat_GetLocalTime( &time );
  46. Cert_DebugString( CFmtStr( "!MEMORYLOG! %4d/%02d/%02d %02d:%02d:%02d\n", time.tm_year+1900, time.tm_mon+1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec ) );
  47. // Spew on the first frame after init
  48. m_fLastSpewTime = -memorylog_tick.GetFloat();
  49. // Set up a string we can search for in full heap crashdumps:
  50. memset( m_nRecentFreeMem, 0, sizeof( m_nRecentFreeMem ) );
  51. memcpy( m_nRecentFreeMem, "maryhadatinylamb", 16 );
  52. return true;
  53. }
  54. void C_MemoryLog::Spew( void )
  55. {
  56. // Spew "time | free mem | GPU free | listen/not | map | bots | players",
  57. // so we can use console logs to correlate memory leaks with playtest patterns.
  58. if ( memorylog_tick.GetFloat() <= 0 )
  59. return; // Disabled
  60. int time = (int)( Plat_FloatTime() + 0.5f );
  61. // NOTE: freeMem can be negative on PS3 (you can use more memory on a devkit than a retail kit has)
  62. int usedMemory, freeMemory;
  63. g_pMemAlloc->GlobalMemoryStatus( (size_t *)&usedMemory, (size_t *)&freeMemory );
  64. // Determine if this machine is the server
  65. INetChannelInfo *pNetInfo = engine->GetNetChannelInfo();
  66. bool listen = ( pNetInfo && ( pNetInfo->IsLoopback() || V_strstr( pNetInfo->GetAddress(), "127.0.0.1" ) ) );
  67. const char *mapName = GetMapName();
  68. char playerNames[ 512 ] = "", memoryLogLine[768] = "";
  69. int numBots = 0;
  70. int numPlayers = 0;
  71. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  72. {
  73. player_info_s playerInfo;
  74. CBasePlayer *player = UTIL_PlayerByIndex( i );
  75. if ( player && player->IsPlayer() && engine->GetPlayerInfo( i, &playerInfo ) )
  76. {
  77. char playerName[64];
  78. V_snprintf( playerName, sizeof( playerName ), ", %s%s%s%s",
  79. playerInfo.name,
  80. player->IsObserver() ? "|SPEC" : "",
  81. C_BasePlayer::IsLocalPlayer( player ) ? "|LOCAL" : "",
  82. playerInfo.fakeplayer ? "|BOT" : "" );
  83. V_strcat( playerNames, playerName, sizeof( playerNames ) );
  84. if ( playerInfo.fakeplayer )
  85. numBots++;
  86. else
  87. numPlayers++;
  88. }
  89. }
  90. // Get GPU free memory (TODO: the current PS3 allocator's nGPUMemFree isn't very useful, it's a high watermark)
  91. GPUMemoryStats stats;
  92. materials->GetGPUMemoryStats( stats );
  93. unsigned int GPUFree = stats.nGPUMemFree;
  94. V_snprintf( memoryLogLine, ARRAYSIZE(memoryLogLine),
  95. "[MEMORYLOG] Time:%6d | Free:%6.2f | GPU:%6.2f | %s | Map: %-32s | Bots:%2d | Players: %2d%s\n",
  96. time,
  97. freeMemory / ( 1024.0f*1024.0f ),
  98. GPUFree / ( 1024.0f*1024.0f ),
  99. listen ? "Server" : "Client",
  100. mapName,
  101. numBots, numPlayers, playerNames );
  102. Cert_DebugString( memoryLogLine );
  103. // Spew generic memory stats into a freeform [MEMORYLOG2] line
  104. GenericMemoryStat_t *pMemStats = NULL, *pMemStats2 = NULL;
  105. int nMemStats = g_pMemAlloc->GetGenericMemoryStats( &pMemStats );
  106. int nMemStats2 = engine->GetGenericMemoryStats( &pMemStats2 );
  107. if ( ( pMemStats && nMemStats ) || ( pMemStats2 && nMemStats2 ) )
  108. {
  109. // Spew N items per line, so they don't get truncated:
  110. const int ITEMS_PER_LINE = 4;
  111. int nStart = 0;
  112. while( nStart < ( nMemStats + nMemStats2 ) )
  113. {
  114. char *pCursor = memoryLogLine;
  115. V_snprintf( pCursor, ARRAYSIZE(memoryLogLine), "[MEMORYLOG2][%6d](Map:%s)", time, mapName );
  116. pCursor += V_strlen( pCursor );
  117. int nItem = 0;
  118. for ( int i = 0; pMemStats && ( i < nMemStats ); i++, nItem++ )
  119. {
  120. if ( ( nItem < nStart ) || ( nItem >= ( nStart + ITEMS_PER_LINE ) ) ) continue;
  121. int nAvail = ( ARRAYSIZE(memoryLogLine) - 1 ) - ( pCursor - memoryLogLine );
  122. V_snprintf( pCursor, nAvail, " [%s:%d]", pMemStats[i].name, pMemStats[i].value );
  123. pCursor += V_strlen( pCursor );
  124. }
  125. for ( int i = 0; pMemStats2 && ( i < nMemStats2 ); i++, nItem++ )
  126. {
  127. if ( ( nItem < nStart ) || ( nItem >= ( nStart + ITEMS_PER_LINE ) ) ) continue;
  128. int nAvail = ( ARRAYSIZE(memoryLogLine) - 1 ) - ( pCursor - memoryLogLine );
  129. V_snprintf( pCursor, nAvail, " [%s:%d]", pMemStats2[i].name, pMemStats2[i].value );
  130. pCursor += V_strlen( pCursor );
  131. }
  132. pCursor[0] = '\n';
  133. pCursor[1] = 0;
  134. Cert_DebugString( memoryLogLine );
  135. nStart += ITEMS_PER_LINE;
  136. }
  137. }
  138. // For now, also add a line tracking the behaviour of the GPU VB/IB allocator:
  139. //engine->ClientCmd( "gpu_buffer_allocator_spew brief" );
  140. // Keep the last N free memory values in an array, for inspection in full heap crashdumps
  141. for ( int i = 255; i > 4; i-- ) m_nRecentFreeMem[ i ] = m_nRecentFreeMem[ i - 1 ];
  142. m_nRecentFreeMem[ 4 ] = freeMemory;
  143. if ( memorylog_mem_dump.GetBool() )
  144. {
  145. #if defined( _MEMTEST )
  146. g_pMemAlloc->SetStatsExtraInfo( mapName, CFmtStr( "%d %d %d %d %d %d %d",
  147. stats.nGPUMemSize, stats.nGPUMemFree, stats.nTextureSize, stats.nRTSize, stats.nVBSize, stats.nIBSize, stats.nUnknown ) );
  148. #else
  149. V_snprintf( memoryLogLine, ARRAYSIZE(memoryLogLine),
  150. "[MEMORYSTATUS] [%s] RSX memory: total %.1fkb, free %.1fkb, textures %.1fkb, render targets %.1fkb, vertex buffers %.1fkb, index buffers %.1fkb, unknown %.1fkb\n",
  151. mapName, stats.nGPUMemSize/1024.0f, stats.nGPUMemFree/1024.0f, stats.nTextureSize/1024.0f, stats.nRTSize/1024.0f, stats.nVBSize/1024.0f, stats.nIBSize/1024.0f, stats.nUnknown/1024.0f );
  152. Cert_DebugString( memoryLogLine );
  153. // TODO: it would be good to get at least per-module totals from the release allocator
  154. #endif
  155. g_pMemAlloc->DumpStatsFileBase( mapName );
  156. Msg( "\nDatacache reports:\n" );
  157. g_pDataCache->OutputReport( DC_SUMMARY_REPORT, NULL );
  158. }
  159. }
  160. void C_MemoryLog::Update( float frametime )
  161. {
  162. float curTime = Plat_FloatTime();
  163. if ( ( curTime - m_fLastSpewTime ) >= memorylog_tick.GetFloat() )
  164. {
  165. m_fLastSpewTime = curTime;
  166. Spew();
  167. float spewTime = Plat_FloatTime() - curTime;
  168. if ( spewTime > 0.15f*memorylog_tick.GetFloat() )
  169. {
  170. // If Spew() takes a long time, account for it (otherwise keep memorylog ticks 'aligned')
  171. m_fLastSpewTime += spewTime;
  172. }
  173. }
  174. }
  175. void C_MemoryLog::LevelInitPostEntity( void )
  176. {
  177. // Spew on the first frame after map load
  178. m_fLastSpewTime = -memorylog_tick.GetFloat();
  179. // Spew hunk memory usage after map load
  180. if ( memorylog_spewhunkusage.GetBool() )
  181. {
  182. engine->ClientCmd( "hunk_print_allocations" );
  183. }
  184. if ( memorylog_mem_dump.GetBool() )
  185. {
  186. // Do an autosave to generate savegame data size spew during batch runs of the game:
  187. engine->ClientCmd( "autosave" );
  188. }
  189. }
  190. void C_MemoryLog::LevelShutdownPreEntity( void )
  191. {
  192. // Spew in case we don't make it to the next map
  193. Spew();
  194. }
  195. #endif // C_MEMORYLOG_TICKING_ENABLED