source-engine/utils/vmpi/vmpi_distribute_tracker.cpp

580 lines
13 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= 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" );
}
}
}