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.

376 lines
11 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include <windows.h>
  7. #include "vmpi.h"
  8. #include "cmdlib.h"
  9. #include "vmpi_tools_shared.h"
  10. #include "tier1/strtools.h"
  11. #include "mpi_stats.h"
  12. #include "iphelpers.h"
  13. #include "tier0/minidump.h"
  14. // ----------------------------------------------------------------------------- //
  15. // Globals.
  16. // ----------------------------------------------------------------------------- //
  17. static bool g_bReceivedDirectoryInfo = false; // Have we gotten the qdir info yet?
  18. static bool g_bReceivedDBInfo = false;
  19. static CDBInfo g_DBInfo;
  20. static unsigned long g_JobPrimaryID;
  21. static int g_nDisconnects = 0; // Tracks how many remote processes have disconnected ungracefully.
  22. // ----------------------------------------------------------------------------- //
  23. // Shared dispatch code.
  24. // ----------------------------------------------------------------------------- //
  25. bool SharedDispatch( MessageBuffer *pBuf, int iSource, int iPacketID )
  26. {
  27. char *pInPos = &pBuf->data[2];
  28. switch ( pBuf->data[1] )
  29. {
  30. case VMPI_SUBPACKETID_DIRECTORIES:
  31. {
  32. Q_strncpy( gamedir, pInPos, sizeof( gamedir ) );
  33. pInPos += strlen( pInPos ) + 1;
  34. Q_strncpy( qdir, pInPos, sizeof( qdir ) );
  35. g_bReceivedDirectoryInfo = true;
  36. }
  37. return true;
  38. case VMPI_SUBPACKETID_DBINFO:
  39. {
  40. g_DBInfo = *((CDBInfo*)pInPos);
  41. pInPos += sizeof( CDBInfo );
  42. g_JobPrimaryID = *((unsigned long*)pInPos);
  43. g_bReceivedDBInfo = true;
  44. }
  45. return true;
  46. case VMPI_SUBPACKETID_CRASH:
  47. {
  48. char const chCrashInfoType = *pInPos;
  49. pInPos += 2;
  50. switch ( chCrashInfoType )
  51. {
  52. case 't':
  53. Warning( "\nWorker '%s' dead: %s\n", VMPI_GetMachineName( iSource ), pInPos );
  54. break;
  55. case 'f':
  56. {
  57. int iFileSize = * reinterpret_cast< int const * >( pInPos );
  58. pInPos += sizeof( iFileSize );
  59. // Temp folder
  60. char const *szFolder = NULL;
  61. if ( !szFolder ) szFolder = getenv( "TEMP" );
  62. if ( !szFolder ) szFolder = getenv( "TMP" );
  63. if ( !szFolder ) szFolder = "c:";
  64. // Base module name
  65. char chModuleName[_MAX_PATH], *pModuleName = chModuleName;
  66. ::GetModuleFileName( NULL, chModuleName, sizeof( chModuleName ) / sizeof( chModuleName[0] ) );
  67. if ( char *pch = strrchr( chModuleName, '.' ) )
  68. *pch = 0;
  69. if ( char *pch = strrchr( chModuleName, '\\' ) )
  70. *pch = 0, pModuleName = pch + 1;
  71. // Current time
  72. struct tm curTime;
  73. Plat_GetLocalTime( &curTime );
  74. // Number of minidumps this run
  75. static int s_numMiniDumps = 0;
  76. ++ s_numMiniDumps;
  77. // Prepare the filename
  78. char chSaveFileName[ 2 * _MAX_PATH ] = { 0 };
  79. sprintf( chSaveFileName, "%s\\vmpi_%s_on_%s_%d%.2d%2d%.2d%.2d%.2d_%d.mdmp",
  80. szFolder,
  81. pModuleName,
  82. VMPI_GetMachineName( iSource ),
  83. curTime.tm_year + 1900, /* Year less 2000 */
  84. curTime.tm_mon + 1, /* month (0 - 11 : 0 = January) */
  85. curTime.tm_mday, /* day of month (1 - 31) */
  86. curTime.tm_hour, /* hour (0 - 23) */
  87. curTime.tm_min, /* minutes (0 - 59) */
  88. curTime.tm_sec, /* seconds (0 - 59) */
  89. s_numMiniDumps
  90. );
  91. if ( FILE *fDump = fopen( chSaveFileName, "wb" ) )
  92. {
  93. fwrite( pInPos, 1, iFileSize, fDump );
  94. fclose( fDump );
  95. Warning( "\nSaved worker crash minidump '%s', size %d byte(s).\n",
  96. chSaveFileName, iFileSize );
  97. }
  98. else
  99. {
  100. Warning( "\nReceived worker crash minidump size %d byte(s), failed to save.\n", iFileSize );
  101. }
  102. }
  103. break;
  104. }
  105. }
  106. return true;
  107. }
  108. return false;
  109. }
  110. CDispatchReg g_SharedDispatchReg( VMPI_SHARED_PACKET_ID, SharedDispatch );
  111. VMPI_REGISTER_PACKET_ID( VMPI_SHARED_PACKET_ID );
  112. VMPI_REGISTER_SUBPACKET_ID( VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_DIRECTORIES );
  113. VMPI_REGISTER_SUBPACKET_ID( VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_DBINFO );
  114. VMPI_REGISTER_SUBPACKET_ID( VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_CRASH );
  115. VMPI_REGISTER_SUBPACKET_ID( VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_MULTICAST_ADDR );
  116. // ----------------------------------------------------------------------------- //
  117. // Module interfaces.
  118. // ----------------------------------------------------------------------------- //
  119. void SendQDirInfo()
  120. {
  121. char cPacketID[2] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_DIRECTORIES };
  122. MessageBuffer mb;
  123. mb.write( cPacketID, 2 );
  124. mb.write( gamedir, strlen( gamedir ) + 1 );
  125. mb.write( qdir, strlen( qdir ) + 1 );
  126. VMPI_SendData( mb.data, mb.getLen(), VMPI_PERSISTENT );
  127. }
  128. void RecvQDirInfo()
  129. {
  130. while ( !g_bReceivedDirectoryInfo )
  131. VMPI_DispatchNextMessage();
  132. }
  133. void SendDBInfo( const CDBInfo *pInfo, unsigned long jobPrimaryID )
  134. {
  135. char cPacketInfo[2] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_DBINFO };
  136. const void *pChunks[] = { cPacketInfo, pInfo, &jobPrimaryID };
  137. int chunkLengths[] = { 2, sizeof( CDBInfo ), sizeof( jobPrimaryID ) };
  138. VMPI_SendChunks( pChunks, chunkLengths, ARRAYSIZE( pChunks ), VMPI_PERSISTENT );
  139. }
  140. void RecvDBInfo( CDBInfo *pInfo, unsigned long *pJobPrimaryID )
  141. {
  142. while ( !g_bReceivedDBInfo )
  143. VMPI_DispatchNextMessage();
  144. *pInfo = g_DBInfo;
  145. *pJobPrimaryID = g_JobPrimaryID;
  146. }
  147. // If the file is successfully opened, read and sent returns the size of the file in bytes
  148. // otherwise returns 0 and nothing is sent
  149. int VMPI_SendFileChunk( const void *pvChunkPrefix, int lenPrefix, tchar const *ptchFileName )
  150. {
  151. HANDLE hFile = NULL;
  152. HANDLE hMapping = NULL;
  153. void const *pvMappedData = NULL;
  154. int iResult = 0;
  155. hFile = ::CreateFile( ptchFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
  156. if ( !hFile || ( hFile == INVALID_HANDLE_VALUE ) )
  157. goto done;
  158. hMapping = ::CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
  159. if ( !hMapping || ( hMapping == INVALID_HANDLE_VALUE ) )
  160. goto done;
  161. pvMappedData = ::MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 );
  162. if ( !pvMappedData )
  163. goto done;
  164. int iMappedFileSize = ::GetFileSize( hFile, NULL );
  165. if ( INVALID_FILE_SIZE == iMappedFileSize )
  166. goto done;
  167. // Send the data over VMPI
  168. if ( VMPI_Send3Chunks(
  169. pvChunkPrefix, lenPrefix,
  170. &iMappedFileSize, sizeof( iMappedFileSize ),
  171. pvMappedData, iMappedFileSize,
  172. VMPI_MASTER_ID ) )
  173. iResult = iMappedFileSize;
  174. // Fall-through for cleanup code to execute
  175. done:
  176. if ( pvMappedData )
  177. ::UnmapViewOfFile( pvMappedData );
  178. if ( hMapping && ( hMapping != INVALID_HANDLE_VALUE ) )
  179. ::CloseHandle( hMapping );
  180. if ( hFile && ( hFile != INVALID_HANDLE_VALUE ) )
  181. ::CloseHandle( hFile );
  182. return iResult;
  183. }
  184. void VMPI_HandleCrash( const char *pMessage, uint uCode, void *pvExceptionInfo, bool bAssert )
  185. {
  186. static LONG crashHandlerCount = 0;
  187. if ( InterlockedIncrement( &crashHandlerCount ) == 1 )
  188. {
  189. Msg( "\nFAILURE: '%s' (assert: %d)\n", pMessage, bAssert );
  190. // Send a message to the master.
  191. char crashMsg[4] = { VMPI_SHARED_PACKET_ID, VMPI_SUBPACKETID_CRASH, 't', ':' };
  192. VMPI_Send2Chunks(
  193. crashMsg,
  194. sizeof( crashMsg ),
  195. pMessage,
  196. strlen( pMessage ) + 1,
  197. VMPI_MASTER_ID );
  198. // Now attempt to create a minidump with the given exception information
  199. if ( pvExceptionInfo )
  200. {
  201. tchar tchMinidumpFileName[_MAX_PATH] = { 0 };
  202. bool bSucceededWritingMinidump = WriteMiniDumpUsingExceptionInfo(
  203. uCode, (ExceptionInfo_t*)pvExceptionInfo,
  204. MINIDUMP_WithDataSegs | MINIDUMP_WithIndirectlyReferencedMemory | MINIDUMP_WithProcessThreadData,
  205. // MINIDUMP_WithDataSegs | MINIDUMP_WithFullMemory | MINIDUMP_WithHandleData | MINIDUMP_WithUnloadedModules | MINIDUMP_WithIndirectlyReferencedMemory | MINIDUMP_WithProcessThreadData | MINIDUMP_WithPrivateReadWriteMemory,
  206. // MINIDUMP_Normal,
  207. tchMinidumpFileName );
  208. if ( bSucceededWritingMinidump )
  209. {
  210. crashMsg[2] = 'f';
  211. VMPI_SendFileChunk( crashMsg, sizeof( crashMsg ), tchMinidumpFileName );
  212. ::DeleteFile( tchMinidumpFileName );
  213. }
  214. }
  215. // Let the messages go out.
  216. Sleep( 500 );
  217. }
  218. InterlockedDecrement( &crashHandlerCount );
  219. }
  220. // This is called if we crash inside our crash handler. It just terminates the process immediately.
  221. LONG __stdcall VMPI_SecondExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo )
  222. {
  223. TerminateProcess( GetCurrentProcess(), 2 );
  224. return EXCEPTION_EXECUTE_HANDLER; // (never gets here anyway)
  225. }
  226. void VMPI_ExceptionFilter( unsigned long uCode, void *pvExceptionInfo )
  227. {
  228. // This is called if we crash inside our crash handler. It just terminates the process immediately.
  229. SetUnhandledExceptionFilter( VMPI_SecondExceptionFilter );
  230. //DWORD code = ExceptionInfo->ExceptionRecord->ExceptionCode;
  231. #define ERR_RECORD( name ) { name, #name }
  232. struct
  233. {
  234. int code;
  235. char *pReason;
  236. } errors[] =
  237. {
  238. ERR_RECORD( EXCEPTION_ACCESS_VIOLATION ),
  239. ERR_RECORD( EXCEPTION_ARRAY_BOUNDS_EXCEEDED ),
  240. ERR_RECORD( EXCEPTION_BREAKPOINT ),
  241. ERR_RECORD( EXCEPTION_DATATYPE_MISALIGNMENT ),
  242. ERR_RECORD( EXCEPTION_FLT_DENORMAL_OPERAND ),
  243. ERR_RECORD( EXCEPTION_FLT_DIVIDE_BY_ZERO ),
  244. ERR_RECORD( EXCEPTION_FLT_INEXACT_RESULT ),
  245. ERR_RECORD( EXCEPTION_FLT_INVALID_OPERATION ),
  246. ERR_RECORD( EXCEPTION_FLT_OVERFLOW ),
  247. ERR_RECORD( EXCEPTION_FLT_STACK_CHECK ),
  248. ERR_RECORD( EXCEPTION_FLT_UNDERFLOW ),
  249. ERR_RECORD( EXCEPTION_ILLEGAL_INSTRUCTION ),
  250. ERR_RECORD( EXCEPTION_IN_PAGE_ERROR ),
  251. ERR_RECORD( EXCEPTION_INT_DIVIDE_BY_ZERO ),
  252. ERR_RECORD( EXCEPTION_INT_OVERFLOW ),
  253. ERR_RECORD( EXCEPTION_INVALID_DISPOSITION ),
  254. ERR_RECORD( EXCEPTION_NONCONTINUABLE_EXCEPTION ),
  255. ERR_RECORD( EXCEPTION_PRIV_INSTRUCTION ),
  256. ERR_RECORD( EXCEPTION_SINGLE_STEP ),
  257. ERR_RECORD( EXCEPTION_STACK_OVERFLOW ),
  258. ERR_RECORD( EXCEPTION_ACCESS_VIOLATION ),
  259. };
  260. int nErrors = sizeof( errors ) / sizeof( errors[0] );
  261. int i=0;
  262. char *pchReason = NULL;
  263. char chUnknownBuffer[32];
  264. for ( i; ( i < nErrors ) && !pchReason; i++ )
  265. {
  266. if ( errors[i].code == uCode )
  267. pchReason = errors[i].pReason;
  268. }
  269. if ( i == nErrors )
  270. {
  271. sprintf( chUnknownBuffer, "Error code 0x%08X", uCode );
  272. pchReason = chUnknownBuffer;
  273. }
  274. VMPI_HandleCrash( pchReason, uCode, pvExceptionInfo, true );
  275. TerminateProcess( GetCurrentProcess(), 1 );
  276. }
  277. void HandleMPIDisconnect( int procID, const char *pReason )
  278. {
  279. int nLiveWorkers = VMPI_GetCurrentNumberOfConnections() - g_nDisconnects - 1;
  280. // We ran into the size limit before and it wasn't readily apparent that the size limit had
  281. // been breached, so make sure to show errors about invalid packet sizes..
  282. bool bOldSuppress = g_bSuppressPrintfOutput;
  283. g_bSuppressPrintfOutput = ( Q_stristr( pReason, "invalid packet size" ) == 0 );
  284. Warning( "\n\n--- WARNING: lost connection to '%s' (%s).\n", VMPI_GetMachineName( procID ), pReason );
  285. if ( g_bMPIMaster )
  286. {
  287. Warning( "%d workers remain.\n\n", nLiveWorkers );
  288. ++g_nDisconnects;
  289. /*
  290. if ( VMPI_GetCurrentNumberOfConnections() - g_nDisconnects <= 1 )
  291. {
  292. Error( "All machines disconnected!" );
  293. }
  294. */
  295. }
  296. else
  297. {
  298. VMPI_HandleAutoRestart();
  299. Error( "Worker quitting." );
  300. }
  301. g_bSuppressPrintfOutput = bOldSuppress;
  302. }