Team Fortress 2 Source Code as on 22/4/2020
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.

579 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include <windows.h>
  7. #include <conio.h>
  8. #include <io.h>
  9. #include "vmpi.h"
  10. #include "vmpi_distribute_work.h"
  11. #include "tier0/platform.h"
  12. #include "tier0/dbg.h"
  13. #include "utlvector.h"
  14. #include "utllinkedlist.h"
  15. #define EVENT_TYPE_SEND_WORK_UNIT 0
  16. #define EVENT_TYPE_WU_STARTED 1
  17. #define EVENT_TYPE_WU_COMPLETED 2
  18. class CWorkUnitEvent
  19. {
  20. public:
  21. int m_iEventType; // EVENT_TYPE_ define.
  22. int m_iWorker;
  23. double m_flTime;
  24. };
  25. class CWorkUnit
  26. {
  27. public:
  28. CWorkUnit()
  29. {
  30. m_iWorkerCompleted = -1;
  31. }
  32. int m_iWorkerCompleted; // Which worker completed this work unit (-1 if not done yet).
  33. CUtlVector<CWorkUnitEvent> m_Events;
  34. };
  35. static CUtlVector<CWorkUnit> g_WorkUnits;
  36. static double g_flJobStartTime;
  37. static bool g_bTrackWorkUnitEvents = false;
  38. static int CountActiveWorkUnits()
  39. {
  40. int nActive = 0;
  41. for ( int i=0; i < g_WorkUnits.Count(); i++ )
  42. {
  43. if ( g_WorkUnits[i].m_iWorkerCompleted == -1 )
  44. ++nActive;
  45. }
  46. return nActive;
  47. }
  48. // ------------------------------------------------------------------------ //
  49. // Graphical functions.
  50. // ------------------------------------------------------------------------ //
  51. static bool g_bUseGraphics = false;
  52. static HWND g_hWnd = 0;
  53. static int g_LastSizeX = 600, g_LastSizeY = 600;
  54. static COLORREF g_StateColors[] =
  55. {
  56. RGB(50,50,50),
  57. RGB(100,0,0),
  58. RGB(150,0,0),
  59. RGB(0,155,0),
  60. RGB(0,255,0),
  61. RGB(0,0,150),
  62. RGB(0,0,250)
  63. };
  64. static HANDLE g_hCreateEvent = 0;
  65. static HANDLE g_hDestroyWindowEvent = 0;
  66. static HANDLE g_hDestroyWindowCompletedEvent = 0;
  67. static CRITICAL_SECTION g_CS;
  68. class CWUStatus
  69. {
  70. public:
  71. CWUStatus()
  72. {
  73. m_iState = 0;
  74. memset( &m_Rect, 0, sizeof( m_Rect ) );
  75. }
  76. RECT m_Rect;
  77. int m_iState; // 0 = not sent yet
  78. // 1 = sent, 2 = sent recently
  79. // 3 = done, 4 = done recently
  80. // 5 = started, 6 = started recently
  81. float m_flTransitionTime;
  82. };
  83. CUtlVector<CWUStatus> g_WUStatus;
  84. int g_nChanges = 0;
  85. int g_nLastDrawnChanges = -1;
  86. static LRESULT CALLBACK TrackerWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  87. {
  88. switch( uMsg )
  89. {
  90. case WM_PAINT:
  91. {
  92. // Do one pass for each color..
  93. HBRUSH hStateColors[ARRAYSIZE( g_StateColors )];
  94. for ( int i=0; i < ARRAYSIZE( hStateColors ); i++ )
  95. hStateColors[i] = CreateSolidBrush( g_StateColors[i] );
  96. // Copy the WU statuses.
  97. CUtlVector<CWUStatus> wuStatus;
  98. EnterCriticalSection( &g_CS );
  99. g_nLastDrawnChanges = g_nChanges;
  100. wuStatus.SetSize( g_WUStatus.Count() );
  101. memcpy( wuStatus.Base(), g_WUStatus.Base(), wuStatus.Count() * sizeof( wuStatus[0] ) );
  102. LeaveCriticalSection( &g_CS );
  103. PAINTSTRUCT ps;
  104. HDC hDC = BeginPaint( hwnd, &ps );
  105. for ( int iState=0; iState < ARRAYSIZE( hStateColors ); iState++ )
  106. {
  107. HGDIOBJ hOldObj = SelectObject( hDC, hStateColors[iState] );
  108. for ( int iWU=0; iWU < wuStatus.Count(); iWU++ )
  109. {
  110. if ( wuStatus[iWU].m_iState != iState )
  111. continue;
  112. RECT &rc = wuStatus[iWU].m_Rect;
  113. Rectangle( hDC, rc.left, rc.top, rc.right, rc.bottom );
  114. }
  115. SelectObject( hDC, hOldObj );
  116. DeleteObject( hStateColors[iState] );
  117. }
  118. EndPaint( hwnd, &ps );
  119. }
  120. break;
  121. case WM_SIZE:
  122. {
  123. int width = LOWORD( lParam );
  124. int height = HIWORD( lParam );
  125. g_LastSizeX = width;
  126. g_LastSizeY = height;
  127. // Figure out the rectangles for everything.
  128. int nWorkUnits = g_WUStatus.Count();
  129. // What is the max width of the grid elements so they will fit in the width and height.
  130. int testSize;
  131. for ( testSize=20; testSize > 1; testSize-- )
  132. {
  133. int nX = width / testSize;
  134. int nY = height / testSize;
  135. if ( nX * nY >= nWorkUnits )
  136. break;
  137. }
  138. static int minTestSize = 3;
  139. testSize = max( testSize, minTestSize );
  140. int xPos=0, yPos=0;
  141. for ( int i=0; i < nWorkUnits; i++ )
  142. {
  143. g_WUStatus[i].m_Rect.left = xPos;
  144. g_WUStatus[i].m_Rect.top = yPos;
  145. g_WUStatus[i].m_Rect.right = xPos + testSize;
  146. g_WUStatus[i].m_Rect.bottom = yPos + testSize;
  147. xPos += testSize;
  148. if ( (xPos+testSize) > width )
  149. {
  150. yPos += testSize;
  151. xPos = 0;
  152. }
  153. }
  154. }
  155. break;
  156. }
  157. return DefWindowProc( hwnd, uMsg, wParam, lParam );
  158. }
  159. static void CheckFlashTimers()
  160. {
  161. double flCurTime = Plat_FloatTime();
  162. EnterCriticalSection( &g_CS );
  163. // Check timers for the events that just happened (we show them in a brighter color if they just occurred).
  164. for ( int iWU=0; iWU < g_WUStatus.Count(); iWU++ )
  165. {
  166. CWUStatus &s = g_WUStatus[iWU];
  167. if ( s.m_iState == 2 || s.m_iState == 4 || s.m_iState == 6 )
  168. {
  169. if ( flCurTime > s.m_flTransitionTime )
  170. {
  171. s.m_iState -= 1;
  172. ++g_nChanges;
  173. }
  174. }
  175. }
  176. LeaveCriticalSection( &g_CS );
  177. }
  178. static DWORD WINAPI ThreadProc( LPVOID lpParameter )
  179. {
  180. // Create the window.
  181. const char *pClassName = "VMPI_Tracker";
  182. // Register the application
  183. WNDCLASSEX WndClsEx;
  184. WndClsEx.cbSize = sizeof(WNDCLASSEX);
  185. WndClsEx.style = CS_HREDRAW | CS_VREDRAW;
  186. WndClsEx.lpfnWndProc = TrackerWindowProc;
  187. WndClsEx.cbClsExtra = 0;
  188. WndClsEx.cbWndExtra = 0;
  189. WndClsEx.hIcon = NULL;
  190. WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW);
  191. WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  192. WndClsEx.lpszMenuName = NULL;
  193. WndClsEx.lpszClassName = pClassName;
  194. WndClsEx.hInstance = (HINSTANCE)GetCurrentProcess();
  195. WndClsEx.hIconSm = NULL;
  196. RegisterClassEx(&WndClsEx);
  197. // Create the window.
  198. g_hWnd = CreateWindow(
  199. pClassName,
  200. "VMPI Tracker",
  201. WS_OVERLAPPEDWINDOW,
  202. 0, 0, g_LastSizeX, g_LastSizeY,
  203. NULL, NULL,
  204. (HINSTANCE)GetCurrentProcess(),
  205. NULL );
  206. ShowWindow( g_hWnd, SW_SHOW );
  207. // Tell the main thread we're ready.
  208. SetEvent( g_hCreateEvent );
  209. // Run our main loop.
  210. while ( WaitForSingleObject( g_hDestroyWindowEvent, 200 ) != WAIT_OBJECT_0 )
  211. {
  212. MSG msg;
  213. while ( PeekMessage( &msg, g_hWnd, 0, 0, PM_REMOVE ) )
  214. {
  215. TranslateMessage(&msg);
  216. DispatchMessage(&msg);
  217. }
  218. CheckFlashTimers();
  219. if ( g_nChanges != g_nLastDrawnChanges )
  220. InvalidateRect( g_hWnd, NULL, FALSE );
  221. }
  222. // Tell the main thread we're done.
  223. SetEvent( g_hDestroyWindowCompletedEvent );
  224. return 0;
  225. }
  226. static void Graphical_Start()
  227. {
  228. g_bUseGraphics = VMPI_IsParamUsed( mpi_Graphics );
  229. if ( !g_bUseGraphics )
  230. return;
  231. // Setup an event so we'll wait until the window is ready.
  232. if ( !g_hCreateEvent )
  233. {
  234. g_hCreateEvent = CreateEvent( 0, 0, 0, 0 );
  235. g_hDestroyWindowEvent = CreateEvent( 0, 0, 0, 0 );
  236. g_hDestroyWindowCompletedEvent = CreateEvent( 0, 0, 0, 0 );
  237. InitializeCriticalSection( &g_CS );
  238. }
  239. ResetEvent( g_hCreateEvent );
  240. ResetEvent( g_hDestroyWindowCompletedEvent );
  241. g_WUStatus.SetSize( g_WorkUnits.Count() );
  242. for ( int i=0; i < g_WUStatus.Count(); i++ )
  243. g_WUStatus[i].m_iState = 0;
  244. // Setup our thread.
  245. CreateThread( NULL, 0, ThreadProc, NULL, 0, NULL );
  246. // Wait until the event is signaled.
  247. WaitForSingleObject( g_hCreateEvent, INFINITE );
  248. }
  249. static void Graphical_WorkUnitSentToWorker( int iWorkUnit )
  250. {
  251. if ( !g_bUseGraphics )
  252. return;
  253. EnterCriticalSection( &g_CS );
  254. CWUStatus &s = g_WUStatus[iWorkUnit];
  255. if ( s.m_iState != 3 && s.m_iState != 4 && s.m_iState != 5 && s.m_iState != 6 )
  256. {
  257. s.m_iState = 2;
  258. s.m_flTransitionTime = Plat_FloatTime() + 0.1f;
  259. ++g_nChanges;
  260. }
  261. LeaveCriticalSection( &g_CS );
  262. }
  263. static void Graphical_WorkUnitStarted( int iWorkUnit )
  264. {
  265. if ( !g_bUseGraphics )
  266. return;
  267. EnterCriticalSection( &g_CS );
  268. if ( g_WUStatus[iWorkUnit].m_iState != 3 && g_WUStatus[iWorkUnit].m_iState != 4 )
  269. {
  270. g_WUStatus[iWorkUnit].m_iState = 6;
  271. g_WUStatus[iWorkUnit].m_flTransitionTime = Plat_FloatTime() + 0.1f;
  272. ++g_nChanges;
  273. }
  274. LeaveCriticalSection( &g_CS );
  275. }
  276. static void Graphical_WorkUnitCompleted( int iWorkUnit )
  277. {
  278. if ( !g_bUseGraphics )
  279. return;
  280. EnterCriticalSection( &g_CS );
  281. g_WUStatus[iWorkUnit].m_iState = 4;
  282. g_WUStatus[iWorkUnit].m_flTransitionTime = Plat_FloatTime() + 0.1f;
  283. ++g_nChanges;
  284. LeaveCriticalSection( &g_CS );
  285. }
  286. static void Graphical_End()
  287. {
  288. if ( !g_bUseGraphics )
  289. return;
  290. SetEvent( g_hDestroyWindowEvent );
  291. WaitForSingleObject( g_hDestroyWindowCompletedEvent, INFINITE );
  292. }
  293. // ------------------------------------------------------------------------ //
  294. // Interface functions.
  295. // ------------------------------------------------------------------------ //
  296. void VMPITracker_Start( int nWorkUnits )
  297. {
  298. g_bTrackWorkUnitEvents = (VMPI_IsParamUsed( mpi_TrackEvents ) || VMPI_IsParamUsed( mpi_Graphics ));
  299. g_flJobStartTime = Plat_FloatTime();
  300. g_WorkUnits.Purge();
  301. if ( g_bTrackWorkUnitEvents )
  302. {
  303. g_WorkUnits.SetSize( nWorkUnits );
  304. }
  305. Graphical_Start();
  306. }
  307. void VMPITracker_WorkUnitSentToWorker( int iWorkUnit, int iWorker )
  308. {
  309. if ( g_bTrackWorkUnitEvents )
  310. {
  311. CWorkUnitEvent event;
  312. event.m_iEventType = EVENT_TYPE_SEND_WORK_UNIT;
  313. event.m_iWorker = iWorker;
  314. event.m_flTime = Plat_FloatTime();
  315. g_WorkUnits[iWorkUnit].m_Events.AddToTail( event );
  316. }
  317. Graphical_WorkUnitSentToWorker( iWorkUnit );
  318. }
  319. void VMPITracker_WorkUnitStarted( int iWorkUnit, int iWorker )
  320. {
  321. if ( g_bTrackWorkUnitEvents )
  322. {
  323. CWorkUnitEvent event;
  324. event.m_iEventType = EVENT_TYPE_WU_STARTED;
  325. event.m_iWorker = iWorker;
  326. event.m_flTime = Plat_FloatTime();
  327. g_WorkUnits[iWorkUnit].m_Events.AddToTail( event );
  328. }
  329. Graphical_WorkUnitStarted( iWorkUnit );
  330. }
  331. void VMPITracker_WorkUnitCompleted( int iWorkUnit, int iWorker )
  332. {
  333. if ( g_bTrackWorkUnitEvents )
  334. {
  335. CWorkUnitEvent event;
  336. event.m_iEventType = EVENT_TYPE_WU_COMPLETED;
  337. event.m_iWorker = iWorker;
  338. event.m_flTime = Plat_FloatTime();
  339. g_WorkUnits[iWorkUnit].m_Events.AddToTail( event );
  340. g_WorkUnits[iWorkUnit].m_iWorkerCompleted = iWorker;
  341. }
  342. Graphical_WorkUnitCompleted( iWorkUnit );
  343. }
  344. void VMPITracker_End()
  345. {
  346. g_WorkUnits.Purge();
  347. Graphical_End();
  348. }
  349. bool VMPITracker_WriteDebugFile( const char *pFilename )
  350. {
  351. FILE *fp = fopen( pFilename, "wt" );
  352. if ( fp )
  353. {
  354. fprintf( fp, "# work units: %d\n", g_WorkUnits.Count() );
  355. fprintf( fp, "# active work units: %d\n", CountActiveWorkUnits() );
  356. fprintf( fp, "\n" );
  357. fprintf( fp, "--- Events ---" );
  358. fprintf( fp, "\n" );
  359. fprintf( fp, "\n" );
  360. for ( int i=0; i < g_WorkUnits.Count(); i++ )
  361. {
  362. CWorkUnit *wu = &g_WorkUnits[i];
  363. if ( wu->m_iWorkerCompleted != -1 )
  364. continue;
  365. fprintf( fp, " work unit %d\n", i );
  366. fprintf( fp, "\n" );
  367. if ( wu->m_Events.Count() == 0 )
  368. {
  369. fprintf( fp, " *no events*\n" );
  370. }
  371. else
  372. {
  373. for ( int iEvent=0; iEvent < wu->m_Events.Count(); iEvent++ )
  374. {
  375. CWorkUnitEvent *pEvent = &wu->m_Events[iEvent];
  376. if ( pEvent->m_iEventType == EVENT_TYPE_WU_STARTED )
  377. {
  378. fprintf( fp, " started (by worker %s) %.1f seconds ago\n",
  379. VMPI_GetMachineName( wu->m_Events[iEvent].m_iWorker ),
  380. Plat_FloatTime() - wu->m_Events[iEvent].m_flTime );
  381. }
  382. else if ( pEvent->m_iEventType == EVENT_TYPE_SEND_WORK_UNIT )
  383. {
  384. fprintf( fp, " sent (to worker %s) %.1f seconds ago\n",
  385. VMPI_GetMachineName( wu->m_Events[iEvent].m_iWorker ),
  386. Plat_FloatTime() - wu->m_Events[iEvent].m_flTime );
  387. }
  388. else if ( pEvent->m_iEventType == EVENT_TYPE_WU_COMPLETED )
  389. {
  390. fprintf( fp, " completed (by worker %s) %.1f seconds ago\n",
  391. VMPI_GetMachineName( wu->m_Events[iEvent].m_iWorker ),
  392. Plat_FloatTime() - wu->m_Events[iEvent].m_flTime );
  393. }
  394. }
  395. }
  396. fprintf( fp, "\n" );
  397. }
  398. fclose( fp );
  399. return true;
  400. }
  401. else
  402. {
  403. return false;
  404. }
  405. }
  406. void VMPITracker_HandleDebugKeypresses()
  407. {
  408. if ( !g_bTrackWorkUnitEvents )
  409. return;
  410. if ( !kbhit() )
  411. return;
  412. static int iState = 0;
  413. int key = toupper( getch() );
  414. if ( iState == 0 )
  415. {
  416. if ( key == 'D' )
  417. {
  418. iState = 1;
  419. Warning("\n\n"
  420. "----------------------\n"
  421. "1. Write debug file (ascending filenames).\n"
  422. "2. Write debug file (c:\\vmpi_tracker_0.txt).\n"
  423. "3. Invite debug workers (password: 'debugworker').\n"
  424. "\n"
  425. "0. Exit menu.\n"
  426. "----------------------\n"
  427. "\n"
  428. );
  429. }
  430. }
  431. else if ( iState == 1 )
  432. {
  433. if ( key == '1' )
  434. {
  435. iState = 0;
  436. int nMaxTries = 128;
  437. char filename[512];
  438. int iFile = 1;
  439. for ( iFile; iFile < nMaxTries; iFile++ )
  440. {
  441. Q_snprintf( filename, sizeof( filename ), "c:\\vmpi_tracker_%d.txt", iFile );
  442. if ( _access( filename, 0 ) != 0 )
  443. break;
  444. }
  445. if ( iFile == nMaxTries )
  446. {
  447. Warning( "** Please delete c:\\vmpi_tracker_*.txt and try again.\n" );
  448. }
  449. else
  450. {
  451. if ( VMPITracker_WriteDebugFile( filename ) )
  452. Warning( "Wrote %s successfully.\n", filename );
  453. else
  454. Warning( "Failed to write %s successfully.\n", filename );
  455. }
  456. }
  457. else if ( key == '2' )
  458. {
  459. iState = 0;
  460. const char *filename = "c:\\vmpi_tracker_0.txt";
  461. if ( VMPITracker_WriteDebugFile( filename ) )
  462. Warning( "Wrote %s successfully.\n", filename );
  463. else
  464. Warning( "Failed to write %s successfully.\n", filename );
  465. }
  466. else if ( key == '3' )
  467. {
  468. iState = 0;
  469. Warning( "\nInviting debug workers with password 'debugworker'...\nGo ahead and connect them.\n" );
  470. VMPI_InviteDebugWorkers();
  471. }
  472. else if ( key == '0' )
  473. {
  474. iState = 0;
  475. Warning( "\n\nExited menu.\n\n" );
  476. }
  477. }
  478. }