diff options
Diffstat (limited to 'utils/vmpi/vmpi_distribute_tracker.cpp')
| -rw-r--r-- | utils/vmpi/vmpi_distribute_tracker.cpp | 579 |
1 files changed, 579 insertions, 0 deletions
diff --git a/utils/vmpi/vmpi_distribute_tracker.cpp b/utils/vmpi/vmpi_distribute_tracker.cpp new file mode 100644 index 0000000..9a112fd --- /dev/null +++ b/utils/vmpi/vmpi_distribute_tracker.cpp @@ -0,0 +1,579 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include <windows.h> +#include <conio.h> +#include <io.h> +#include "vmpi.h" +#include "vmpi_distribute_work.h" +#include "tier0/platform.h" +#include "tier0/dbg.h" +#include "utlvector.h" +#include "utllinkedlist.h" + + +#define EVENT_TYPE_SEND_WORK_UNIT 0 +#define EVENT_TYPE_WU_STARTED 1 +#define EVENT_TYPE_WU_COMPLETED 2 + +class CWorkUnitEvent +{ +public: + int m_iEventType; // EVENT_TYPE_ define. + int m_iWorker; + double m_flTime; +}; + + +class CWorkUnit +{ +public: + CWorkUnit() + { + m_iWorkerCompleted = -1; + } + + int m_iWorkerCompleted; // Which worker completed this work unit (-1 if not done yet). + + CUtlVector<CWorkUnitEvent> m_Events; +}; + + +static CUtlVector<CWorkUnit> g_WorkUnits; +static double g_flJobStartTime; +static bool g_bTrackWorkUnitEvents = false; + + +static int CountActiveWorkUnits() +{ + int nActive = 0; + for ( int i=0; i < g_WorkUnits.Count(); i++ ) + { + if ( g_WorkUnits[i].m_iWorkerCompleted == -1 ) + ++nActive; + } + return nActive; +} + + +// ------------------------------------------------------------------------ // +// Graphical functions. +// ------------------------------------------------------------------------ // + +static bool g_bUseGraphics = false; +static HWND g_hWnd = 0; + +static int g_LastSizeX = 600, g_LastSizeY = 600; + +static COLORREF g_StateColors[] = +{ + RGB(50,50,50), + RGB(100,0,0), + RGB(150,0,0), + RGB(0,155,0), + RGB(0,255,0), + RGB(0,0,150), + RGB(0,0,250) +}; + +static HANDLE g_hCreateEvent = 0; +static HANDLE g_hDestroyWindowEvent = 0; +static HANDLE g_hDestroyWindowCompletedEvent = 0; + +static CRITICAL_SECTION g_CS; + +class CWUStatus +{ +public: + CWUStatus() + { + m_iState = 0; + memset( &m_Rect, 0, sizeof( m_Rect ) ); + } + + RECT m_Rect; + int m_iState; // 0 = not sent yet + // 1 = sent, 2 = sent recently + // 3 = done, 4 = done recently + // 5 = started, 6 = started recently + float m_flTransitionTime; +}; +CUtlVector<CWUStatus> g_WUStatus; +int g_nChanges = 0; +int g_nLastDrawnChanges = -1; + +static LRESULT CALLBACK TrackerWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + switch( uMsg ) + { + case WM_PAINT: + { + // Do one pass for each color.. + HBRUSH hStateColors[ARRAYSIZE( g_StateColors )]; + for ( int i=0; i < ARRAYSIZE( hStateColors ); i++ ) + hStateColors[i] = CreateSolidBrush( g_StateColors[i] ); + + // Copy the WU statuses. + CUtlVector<CWUStatus> wuStatus; + EnterCriticalSection( &g_CS ); + + g_nLastDrawnChanges = g_nChanges; + wuStatus.SetSize( g_WUStatus.Count() ); + memcpy( wuStatus.Base(), g_WUStatus.Base(), wuStatus.Count() * sizeof( wuStatus[0] ) ); + + LeaveCriticalSection( &g_CS ); + + PAINTSTRUCT ps; + HDC hDC = BeginPaint( hwnd, &ps ); + for ( int iState=0; iState < ARRAYSIZE( hStateColors ); iState++ ) + { + HGDIOBJ hOldObj = SelectObject( hDC, hStateColors[iState] ); + + for ( int iWU=0; iWU < wuStatus.Count(); iWU++ ) + { + if ( wuStatus[iWU].m_iState != iState ) + continue; + + RECT &rc = wuStatus[iWU].m_Rect; + Rectangle( hDC, rc.left, rc.top, rc.right, rc.bottom ); + } + + SelectObject( hDC, hOldObj ); + DeleteObject( hStateColors[iState] ); + } + EndPaint( hwnd, &ps ); + } + break; + + case WM_SIZE: + { + int width = LOWORD( lParam ); + int height = HIWORD( lParam ); + + g_LastSizeX = width; + g_LastSizeY = height; + + // Figure out the rectangles for everything. + int nWorkUnits = g_WUStatus.Count(); + + // What is the max width of the grid elements so they will fit in the width and height. + int testSize; + for ( testSize=20; testSize > 1; testSize-- ) + { + int nX = width / testSize; + int nY = height / testSize; + if ( nX * nY >= nWorkUnits ) + break; + } + static int minTestSize = 3; + testSize = max( testSize, minTestSize ); + + int xPos=0, yPos=0; + for ( int i=0; i < nWorkUnits; i++ ) + { + g_WUStatus[i].m_Rect.left = xPos; + g_WUStatus[i].m_Rect.top = yPos; + g_WUStatus[i].m_Rect.right = xPos + testSize; + g_WUStatus[i].m_Rect.bottom = yPos + testSize; + + xPos += testSize; + if ( (xPos+testSize) > width ) + { + yPos += testSize; + xPos = 0; + } + } + } + break; + } + + return DefWindowProc( hwnd, uMsg, wParam, lParam ); +} + +static void CheckFlashTimers() +{ + double flCurTime = Plat_FloatTime(); + + EnterCriticalSection( &g_CS ); + + // Check timers for the events that just happened (we show them in a brighter color if they just occurred). + for ( int iWU=0; iWU < g_WUStatus.Count(); iWU++ ) + { + CWUStatus &s = g_WUStatus[iWU]; + + if ( s.m_iState == 2 || s.m_iState == 4 || s.m_iState == 6 ) + { + if ( flCurTime > s.m_flTransitionTime ) + { + s.m_iState -= 1; + ++g_nChanges; + } + } + } + + LeaveCriticalSection( &g_CS ); +} + +static DWORD WINAPI ThreadProc( LPVOID lpParameter ) +{ + // Create the window. + const char *pClassName = "VMPI_Tracker"; + + // Register the application + WNDCLASSEX WndClsEx; + WndClsEx.cbSize = sizeof(WNDCLASSEX); + WndClsEx.style = CS_HREDRAW | CS_VREDRAW; + WndClsEx.lpfnWndProc = TrackerWindowProc; + WndClsEx.cbClsExtra = 0; + WndClsEx.cbWndExtra = 0; + WndClsEx.hIcon = NULL; + WndClsEx.hCursor = LoadCursor(NULL, IDC_ARROW); + WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + WndClsEx.lpszMenuName = NULL; + WndClsEx.lpszClassName = pClassName; + WndClsEx.hInstance = (HINSTANCE)GetCurrentProcess(); + WndClsEx.hIconSm = NULL; + RegisterClassEx(&WndClsEx); + + // Create the window. + g_hWnd = CreateWindow( + pClassName, + "VMPI Tracker", + WS_OVERLAPPEDWINDOW, + 0, 0, g_LastSizeX, g_LastSizeY, + NULL, NULL, + (HINSTANCE)GetCurrentProcess(), + NULL ); + + ShowWindow( g_hWnd, SW_SHOW ); + + // Tell the main thread we're ready. + SetEvent( g_hCreateEvent ); + + // Run our main loop. + while ( WaitForSingleObject( g_hDestroyWindowEvent, 200 ) != WAIT_OBJECT_0 ) + { + MSG msg; + while ( PeekMessage( &msg, g_hWnd, 0, 0, PM_REMOVE ) ) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + CheckFlashTimers(); + + if ( g_nChanges != g_nLastDrawnChanges ) + InvalidateRect( g_hWnd, NULL, FALSE ); + } + + // Tell the main thread we're done. + SetEvent( g_hDestroyWindowCompletedEvent ); + return 0; +} + +static void Graphical_Start() +{ + g_bUseGraphics = VMPI_IsParamUsed( mpi_Graphics ); + if ( !g_bUseGraphics ) + return; + + // Setup an event so we'll wait until the window is ready. + if ( !g_hCreateEvent ) + { + g_hCreateEvent = CreateEvent( 0, 0, 0, 0 ); + g_hDestroyWindowEvent = CreateEvent( 0, 0, 0, 0 ); + g_hDestroyWindowCompletedEvent = CreateEvent( 0, 0, 0, 0 ); + InitializeCriticalSection( &g_CS ); + } + ResetEvent( g_hCreateEvent ); + ResetEvent( g_hDestroyWindowCompletedEvent ); + + g_WUStatus.SetSize( g_WorkUnits.Count() ); + for ( int i=0; i < g_WUStatus.Count(); i++ ) + g_WUStatus[i].m_iState = 0; + + // Setup our thread. + CreateThread( NULL, 0, ThreadProc, NULL, 0, NULL ); + + // Wait until the event is signaled. + WaitForSingleObject( g_hCreateEvent, INFINITE ); +} + +static void Graphical_WorkUnitSentToWorker( int iWorkUnit ) +{ + if ( !g_bUseGraphics ) + return; + + EnterCriticalSection( &g_CS ); + + CWUStatus &s = g_WUStatus[iWorkUnit]; + if ( s.m_iState != 3 && s.m_iState != 4 && s.m_iState != 5 && s.m_iState != 6 ) + { + s.m_iState = 2; + s.m_flTransitionTime = Plat_FloatTime() + 0.1f; + ++g_nChanges; + } + + LeaveCriticalSection( &g_CS ); +} + +static void Graphical_WorkUnitStarted( int iWorkUnit ) +{ + if ( !g_bUseGraphics ) + return; + + EnterCriticalSection( &g_CS ); + + if ( g_WUStatus[iWorkUnit].m_iState != 3 && g_WUStatus[iWorkUnit].m_iState != 4 ) + { + g_WUStatus[iWorkUnit].m_iState = 6; + g_WUStatus[iWorkUnit].m_flTransitionTime = Plat_FloatTime() + 0.1f; + ++g_nChanges; + } + + LeaveCriticalSection( &g_CS ); +} + +static void Graphical_WorkUnitCompleted( int iWorkUnit ) +{ + if ( !g_bUseGraphics ) + return; + + EnterCriticalSection( &g_CS ); + g_WUStatus[iWorkUnit].m_iState = 4; + g_WUStatus[iWorkUnit].m_flTransitionTime = Plat_FloatTime() + 0.1f; + ++g_nChanges; + LeaveCriticalSection( &g_CS ); +} + +static void Graphical_End() +{ + if ( !g_bUseGraphics ) + return; + + SetEvent( g_hDestroyWindowEvent ); + WaitForSingleObject( g_hDestroyWindowCompletedEvent, INFINITE ); +} + + +// ------------------------------------------------------------------------ // +// Interface functions. +// ------------------------------------------------------------------------ // + +void VMPITracker_Start( int nWorkUnits ) +{ + g_bTrackWorkUnitEvents = (VMPI_IsParamUsed( mpi_TrackEvents ) || VMPI_IsParamUsed( mpi_Graphics )); + g_flJobStartTime = Plat_FloatTime(); + g_WorkUnits.Purge(); + + if ( g_bTrackWorkUnitEvents ) + { + g_WorkUnits.SetSize( nWorkUnits ); + } + + Graphical_Start(); +} + + +void VMPITracker_WorkUnitSentToWorker( int iWorkUnit, int iWorker ) +{ + if ( g_bTrackWorkUnitEvents ) + { + CWorkUnitEvent event; + event.m_iEventType = EVENT_TYPE_SEND_WORK_UNIT; + event.m_iWorker = iWorker; + event.m_flTime = Plat_FloatTime(); + g_WorkUnits[iWorkUnit].m_Events.AddToTail( event ); + } + + Graphical_WorkUnitSentToWorker( iWorkUnit ); +} + + +void VMPITracker_WorkUnitStarted( int iWorkUnit, int iWorker ) +{ + if ( g_bTrackWorkUnitEvents ) + { + CWorkUnitEvent event; + event.m_iEventType = EVENT_TYPE_WU_STARTED; + event.m_iWorker = iWorker; + event.m_flTime = Plat_FloatTime(); + g_WorkUnits[iWorkUnit].m_Events.AddToTail( event ); + } + + Graphical_WorkUnitStarted( iWorkUnit ); +} + + +void VMPITracker_WorkUnitCompleted( int iWorkUnit, int iWorker ) +{ + if ( g_bTrackWorkUnitEvents ) + { + CWorkUnitEvent event; + event.m_iEventType = EVENT_TYPE_WU_COMPLETED; + event.m_iWorker = iWorker; + event.m_flTime = Plat_FloatTime(); + g_WorkUnits[iWorkUnit].m_Events.AddToTail( event ); + g_WorkUnits[iWorkUnit].m_iWorkerCompleted = iWorker; + } + + Graphical_WorkUnitCompleted( iWorkUnit ); +} + + +void VMPITracker_End() +{ + g_WorkUnits.Purge(); + Graphical_End(); +} + + +bool VMPITracker_WriteDebugFile( const char *pFilename ) +{ + FILE *fp = fopen( pFilename, "wt" ); + if ( fp ) + { + fprintf( fp, "# work units: %d\n", g_WorkUnits.Count() ); + fprintf( fp, "# active work units: %d\n", CountActiveWorkUnits() ); + + fprintf( fp, "\n" ); + fprintf( fp, "--- Events ---" ); + fprintf( fp, "\n" ); + fprintf( fp, "\n" ); + + for ( int i=0; i < g_WorkUnits.Count(); i++ ) + { + CWorkUnit *wu = &g_WorkUnits[i]; + + if ( wu->m_iWorkerCompleted != -1 ) + continue; + + fprintf( fp, " work unit %d\n", i ); + fprintf( fp, "\n" ); + + if ( wu->m_Events.Count() == 0 ) + { + fprintf( fp, " *no events*\n" ); + } + else + { + for ( int iEvent=0; iEvent < wu->m_Events.Count(); iEvent++ ) + { + CWorkUnitEvent *pEvent = &wu->m_Events[iEvent]; + + if ( pEvent->m_iEventType == EVENT_TYPE_WU_STARTED ) + { + fprintf( fp, " started (by worker %s) %.1f seconds ago\n", + VMPI_GetMachineName( wu->m_Events[iEvent].m_iWorker ), + Plat_FloatTime() - wu->m_Events[iEvent].m_flTime ); + } + else if ( pEvent->m_iEventType == EVENT_TYPE_SEND_WORK_UNIT ) + { + fprintf( fp, " sent (to worker %s) %.1f seconds ago\n", + VMPI_GetMachineName( wu->m_Events[iEvent].m_iWorker ), + Plat_FloatTime() - wu->m_Events[iEvent].m_flTime ); + } + else if ( pEvent->m_iEventType == EVENT_TYPE_WU_COMPLETED ) + { + fprintf( fp, " completed (by worker %s) %.1f seconds ago\n", + VMPI_GetMachineName( wu->m_Events[iEvent].m_iWorker ), + Plat_FloatTime() - wu->m_Events[iEvent].m_flTime ); + } + } + } + fprintf( fp, "\n" ); + } + + fclose( fp ); + return true; + } + else + { + return false; + } +} + + +void VMPITracker_HandleDebugKeypresses() +{ + if ( !g_bTrackWorkUnitEvents ) + return; + + if ( !kbhit() ) + return; + + static int iState = 0; + + int key = toupper( getch() ); + if ( iState == 0 ) + { + if ( key == 'D' ) + { + iState = 1; + Warning("\n\n" + "----------------------\n" + "1. Write debug file (ascending filenames).\n" + "2. Write debug file (c:\\vmpi_tracker_0.txt).\n" + "3. Invite debug workers (password: 'debugworker').\n" + "\n" + "0. Exit menu.\n" + "----------------------\n" + "\n" + ); + } + } + else if ( iState == 1 ) + { + if ( key == '1' ) + { + iState = 0; + + int nMaxTries = 128; + char filename[512]; + int iFile = 1; + for ( iFile; iFile < nMaxTries; iFile++ ) + { + Q_snprintf( filename, sizeof( filename ), "c:\\vmpi_tracker_%d.txt", iFile ); + if ( _access( filename, 0 ) != 0 ) + break; + } + if ( iFile == nMaxTries ) + { + Warning( "** Please delete c:\\vmpi_tracker_*.txt and try again.\n" ); + } + else + { + if ( VMPITracker_WriteDebugFile( filename ) ) + Warning( "Wrote %s successfully.\n", filename ); + else + Warning( "Failed to write %s successfully.\n", filename ); + } + } + else if ( key == '2' ) + { + iState = 0; + + const char *filename = "c:\\vmpi_tracker_0.txt"; + if ( VMPITracker_WriteDebugFile( filename ) ) + Warning( "Wrote %s successfully.\n", filename ); + else + Warning( "Failed to write %s successfully.\n", filename ); + } + else if ( key == '3' ) + { + iState = 0; + Warning( "\nInviting debug workers with password 'debugworker'...\nGo ahead and connect them.\n" ); + VMPI_InviteDebugWorkers(); + } + else if ( key == '0' ) + { + iState = 0; + Warning( "\n\nExited menu.\n\n" ); + } + } +} + + |