summaryrefslogtreecommitdiff
path: root/utils/vmpi/vmpi_job_watch
diff options
context:
space:
mode:
Diffstat (limited to 'utils/vmpi/vmpi_job_watch')
-rw-r--r--utils/vmpi/vmpi_job_watch/GraphControl.cpp246
-rw-r--r--utils/vmpi/vmpi_job_watch/GraphControl.h86
-rw-r--r--utils/vmpi/vmpi_job_watch/JobWatchDlg.cpp648
-rw-r--r--utils/vmpi/vmpi_job_watch/JobWatchDlg.h134
-rw-r--r--utils/vmpi/vmpi_job_watch/StdAfx.cpp15
-rw-r--r--utils/vmpi/vmpi_job_watch/StdAfx.h36
-rw-r--r--utils/vmpi/vmpi_job_watch/res/vmpi_browser_job_watch.icobin0 -> 1078 bytes
-rw-r--r--utils/vmpi/vmpi_job_watch/res/vmpi_browser_job_watch.rc213
-rw-r--r--utils/vmpi/vmpi_job_watch/resource.h32
-rw-r--r--utils/vmpi/vmpi_job_watch/vmpi_browser_job_watch.cpp75
-rw-r--r--utils/vmpi/vmpi_job_watch/vmpi_browser_job_watch.h56
-rw-r--r--utils/vmpi/vmpi_job_watch/vmpi_browser_job_watch.rc183
-rw-r--r--utils/vmpi/vmpi_job_watch/vmpi_job_watch.vpc80
13 files changed, 1604 insertions, 0 deletions
diff --git a/utils/vmpi/vmpi_job_watch/GraphControl.cpp b/utils/vmpi/vmpi_job_watch/GraphControl.cpp
new file mode 100644
index 0000000..44d7e57
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/GraphControl.cpp
@@ -0,0 +1,246 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// GraphControl.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "vmpi_browser_job_watch.h"
+#include "GraphControl.h"
+#include "mathlib/mathlib.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CGraphControl
+
+CGraphControl::CGraphControl()
+{
+}
+
+CGraphControl::~CGraphControl()
+{
+}
+
+
+BEGIN_MESSAGE_MAP(CGraphControl, CWnd)
+ //{{AFX_MSG_MAP(CGraphControl)
+ ON_WM_PAINT()
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+void CGraphControl::Clear()
+{
+ CRect rcClient;
+ GetClientRect( rcClient );
+
+ CDC *pDC = GetDC();
+
+ CBrush brush( RGB( 0, 0, 0 ) );
+ CBrush *pOldBrush = pDC->SelectObject( &brush );
+ pDC->Rectangle( 0, 0, rcClient.Width(), rcClient.Height() );
+ pDC->SelectObject( pOldBrush );
+
+ ReleaseDC( pDC );
+}
+
+void CGraphControl::Render( CDC *pDC )
+{
+ // Clear the background.
+ CRect rcClient;
+ GetClientRect( rcClient );
+
+
+ CBrush brush( RGB( 0, 0, 0 ) );
+ CBrush *pOldBrush = pDC->SelectObject( &brush );
+ pDC->Rectangle( 0, 0, rcClient.Width(), rcClient.Height() );
+ pDC->SelectObject( pOldBrush );
+
+
+
+ // Work backwards from the right side to the left.
+ int nIntervals = rcClient.Width();
+ DWORD intervalMS = 500; // one interval per pixel
+ DWORD startTime = 0xFFFFFFFF, endTime = 0;
+
+ // First, find which order of magnitude to use on the vertical scale by finding the maximum value.
+ for ( int iEntry=0; iEntry < m_Entries.Count(); iEntry++ )
+ {
+ DWORD msTime = m_Entries[iEntry].m_msTime;
+ startTime = min( startTime, msTime );
+ endTime = max( endTime, msTime );
+ }
+
+ int curTime = (int)endTime - nIntervals*intervalMS;
+
+
+
+ CGraphEntry prevEntry, curEntry;
+ prevEntry.m_msTime = curEntry.m_msTime = -1;
+
+ CUtlVector<POINT> sentPoints;
+ CUtlVector<POINT> receivedPoints;
+
+ int iCurEntry = -1;
+ int nMaxBytesSent = -1, nMaxBytesReceived = -1;
+
+ for ( int x=0; x < nIntervals; x++ )
+ {
+ if ( curTime >= 0 )
+ {
+ // Now find the graph_entry for the time we're at.
+ while ( prevEntry.m_msTime == -1 || curTime > curEntry.m_msTime )
+ {
+ ++iCurEntry;
+ if ( iCurEntry >= m_Entries.Count() )
+ goto ENDLOOP;
+
+ prevEntry = curEntry;
+ curEntry = m_Entries[iCurEntry];
+ }
+
+ if ( curTime >= prevEntry.m_msTime && curTime <= curEntry.m_msTime )
+ {
+ // Interpolate the bytes sent.
+ int nBytesSent = (int)RemapVal(
+ curTime,
+ prevEntry.m_msTime, curEntry.m_msTime,
+ prevEntry.m_nBytesSent, curEntry.m_nBytesSent );
+
+ POINT sentPoint = { x, nBytesSent };
+ sentPoints.AddToTail( sentPoint );
+ nMaxBytesSent = max( nMaxBytesSent, nBytesSent );
+
+
+ int nBytesReceived = (int)RemapVal(
+ curTime,
+ prevEntry.m_msTime, curEntry.m_msTime,
+ prevEntry.m_nBytesReceived, curEntry.m_nBytesReceived );
+
+ POINT receivedPoint = { x, nBytesReceived };
+ receivedPoints.AddToTail( receivedPoint );
+ nMaxBytesReceived = max( nMaxBytesReceived, nBytesReceived );
+ }
+ }
+
+ curTime += intervalMS;
+ }
+
+ ENDLOOP:;
+
+
+ // Now normalize all the values.
+ int largest = max( nMaxBytesSent, nMaxBytesReceived );
+ int topValue = (largest*11) / 10;
+/*
+ DWORD nZeros;
+ for( nZeros = 1; nZeros < 20; nZeros++ )
+ {
+ if ( largest < pow( 10, nZeros ) )
+ break;
+ }
+
+ // Now find the value at the top of the graph. We choose the smallest enclosing tenth of the
+ // order of magnitude we're at (so if we were at 1,000,000, and our max value was 350,000, we'd choose 400,000).
+ int iTenth;
+ int topValue;
+ for ( iTenth=1; iTenth <= 10; iTenth++ )
+ {
+ topValue = (DWORD)( pow( 10, nZeros-1 ) * iTenth );
+ if ( topValue >= largest )
+ break;
+ }
+*/
+
+ for ( int iSample=0; iSample < sentPoints.Count(); iSample++ )
+ {
+ double flHeight;
+
+ flHeight = ((double)sentPoints[iSample].y / topValue) * (rcClient.Height() - 1);
+ sentPoints[iSample].y = (int)( rcClient.Height() - flHeight );
+
+ flHeight = ((double)receivedPoints[iSample].y / topValue) * (rcClient.Height() - 1);
+ receivedPoints[iSample].y = (int)( rcClient.Height() - flHeight );
+ }
+
+
+ // Draw some horizontal lines dividing the space.
+ int nLines = 10;
+ for ( int iLine=0; iLine <= nLines; iLine++ )
+ {
+ CPen penLine;
+ COLORREF color;
+ if ( iLine == 0 || iLine == nLines/2 )
+ color = RGB( 0, 220, 0 );
+ else
+ color = RGB( 0, 100, 0 );
+
+ penLine.CreatePen( PS_SOLID, 1, color );
+ CPen *pOldPen = pDC->SelectObject( &penLine );
+
+ int y = (iLine * rcClient.Height()) / nLines;
+ pDC->MoveTo( 0, y );
+ pDC->LineTo( rcClient.Width(), y );
+
+ pDC->SelectObject( pOldPen );
+ }
+
+
+ // Now draw the lines for the data.
+ CPen penSent( PS_SOLID, 1, RGB( 0, 255, 0 ) );
+ CPen *pOldPen = pDC->SelectObject( &penSent );
+ pDC->Polyline( sentPoints.Base(), sentPoints.Count() );
+ pDC->SelectObject( pOldPen );
+
+ CPen penReceived( PS_SOLID, 1, RGB( 255, 255, 0 ) );
+ pOldPen = pDC->SelectObject( &penReceived );
+ pDC->Polyline( receivedPoints.Base(), receivedPoints.Count() );
+ pDC->SelectObject( pOldPen );
+
+
+
+ // Draw text labels.
+ pDC->SetTextColor( RGB( 200, 200, 200 ) );
+ pDC->SetBkColor( 0 );
+
+ CString str;
+ str.Format( "%dk", (topValue+511) / 1024 );
+ pDC->ExtTextOut( 0, 1, 0, NULL, str, NULL );
+
+ str.Format( "%dk", (topValue+511) / 1024 / 2 );
+ pDC->ExtTextOut( 0, rcClient.Height()/2 + 1, 0, NULL, str, NULL );
+}
+
+
+void CGraphControl::Fill( CUtlVector<CGraphEntry> &entries )
+{
+ CDC *pDC = GetDC();
+ if ( !pDC )
+ return;
+
+ m_Entries = entries;
+
+ Render( pDC );
+
+ ReleaseDC( pDC );
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CGraphControl message handlers
+
+void CGraphControl::OnPaint()
+{
+ CPaintDC dc(this); // device context for painting
+
+ Render( &dc );
+}
diff --git a/utils/vmpi/vmpi_job_watch/GraphControl.h b/utils/vmpi/vmpi_job_watch/GraphControl.h
new file mode 100644
index 0000000..b8fcb88
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/GraphControl.h
@@ -0,0 +1,86 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#if !defined(AFX_GRAPHCONTROL_H__9B50B827_F24D_4C5A_BA6E_A591A64E404D__INCLUDED_)
+#define AFX_GRAPHCONTROL_H__9B50B827_F24D_4C5A_BA6E_A591A64E404D__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// GraphControl.h : header file
+//
+
+#include "utlvector.h"
+
+
+class CGraphEntry
+{
+public:
+ CGraphEntry() :
+ m_msTime( 0 ),
+ m_nBytesSent( 0 ),
+ m_nBytesReceived( 0 )
+ {
+ }
+
+ int m_msTime;
+ int m_nBytesSent;
+ int m_nBytesReceived;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CGraphControl window
+
+class CGraphControl : public CWnd
+{
+// Construction
+public:
+ CGraphControl();
+
+// Attributes
+public:
+
+// Operations
+public:
+
+ void Clear();
+
+ // This function assumes you've already run the query and the graph_entry's are selected in.
+ void Fill( CUtlVector<CGraphEntry> &entries );
+
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CGraphControl)
+ //}}AFX_VIRTUAL
+
+// Implementation
+public:
+ virtual ~CGraphControl();
+
+
+protected:
+
+ void Render( CDC *pDC );
+
+ CUtlVector<CGraphEntry> m_Entries;
+
+ // Generated message map functions
+protected:
+ //{{AFX_MSG(CGraphControl)
+ afx_msg void OnPaint();
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_GRAPHCONTROL_H__9B50B827_F24D_4C5A_BA6E_A591A64E404D__INCLUDED_)
diff --git a/utils/vmpi/vmpi_job_watch/JobWatchDlg.cpp b/utils/vmpi/vmpi_job_watch/JobWatchDlg.cpp
new file mode 100644
index 0000000..4c7909b
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/JobWatchDlg.cpp
@@ -0,0 +1,648 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// JobWatchDlg.cpp : implementation file
+//
+
+#include "stdafx.h"
+#include "JobWatchDlg.h"
+#include "tier1/strtools.h"
+#include "consolewnd.h"
+#include "vmpi_browser_helpers.h"
+
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+#define IMPLEMENT_SORT_NUMBER_FN( FnName, VarName ) \
+ static int CALLBACK FnName( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam ) \
+ { \
+ CWorkerInfo *pInfo1 = (CWorkerInfo*)iItem1; \
+ CWorkerInfo *pInfo2 = (CWorkerInfo*)iItem2; \
+ return pInfo1->VarName > pInfo2->VarName; \
+ }
+
+static int CALLBACK SortByName( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CWorkerInfo *pInfo1 = (CWorkerInfo*)iItem1;
+ CWorkerInfo *pInfo2 = (CWorkerInfo*)iItem2;
+
+ return strcmp( pInfo1->m_ComputerName, pInfo2->m_ComputerName );
+}
+
+static int CALLBACK SortByCurrentStage( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam )
+{
+ CWorkerInfo *pInfo1 = (CWorkerInfo*)iItem1;
+ CWorkerInfo *pInfo2 = (CWorkerInfo*)iItem2;
+
+ return strcmp( pInfo1->m_CurrentStage, pInfo2->m_CurrentStage );
+}
+
+IMPLEMENT_SORT_NUMBER_FN( SortByConnected, m_bConnected )
+IMPLEMENT_SORT_NUMBER_FN( SortByWorkUnitsDone, m_nWorkUnitsDone );
+IMPLEMENT_SORT_NUMBER_FN( SortByRunningTime, m_RunningTimeMS );
+IMPLEMENT_SORT_NUMBER_FN( SortByThread0WU, m_ThreadWUs[0] );
+IMPLEMENT_SORT_NUMBER_FN( SortByThread1WU, m_ThreadWUs[1] );
+IMPLEMENT_SORT_NUMBER_FN( SortByThread2WU, m_ThreadWUs[2] );
+IMPLEMENT_SORT_NUMBER_FN( SortByThread3WU, m_ThreadWUs[3] );
+
+typedef int (CALLBACK *ServicesSortFn)( LPARAM iItem1, LPARAM iItem2, LPARAM lpParam );
+
+struct
+{
+ char *pText;
+ int width;
+ ServicesSortFn sortFn;
+} g_ColumnInfos[] =
+{
+ {"Computer Name", 150, SortByName},
+ {"Connected", 70, SortByConnected},
+ {"Work Units Done", 100, SortByWorkUnitsDone},
+ {"Running Time", 80, SortByRunningTime},
+ {"Current Stage", 180, SortByCurrentStage},
+ {"Thread 0", 70, SortByThread0WU},
+ {"Thread 1", 70, SortByThread1WU},
+ {"Thread 2", 70, SortByThread2WU},
+ {"Thread 3", 70, SortByThread3WU}
+};
+
+#define COLUMN_COMPUTER_NAME 0
+#define COLUMN_CONNECTED 1
+#define COLUMN_WORK_UNITS_DONE 2
+#define COLUMN_RUNNING_TIME 3
+#define COLUMN_CURRENT_STAGE 4
+#define COLUMN_THREAD0_WU 5
+#define COLUMN_THREAD1_WU 6
+#define COLUMN_THREAD2_WU 7
+#define COLUMN_THREAD3_WU 8
+
+int g_iSortColumn = 0;
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CJobWatchDlg dialog
+
+
+CJobWatchDlg::CJobWatchDlg(CWnd* pParent /*=NULL*/)
+ : CIdleDialog(CJobWatchDlg::IDD, pParent)
+{
+ m_CurMessageIndex = 0;
+ m_CurGraphTime = 0;
+ m_pSQL = NULL;
+ m_hMySQLDLL = NULL;
+ m_CurWorkerTextToken = 0;
+ m_LastQueryTime = 0;
+
+ //{{AFX_DATA_INIT(CJobWatchDlg)
+ // NOTE: the ClassWizard will add member initialization here
+ //}}AFX_DATA_INIT
+}
+
+
+CJobWatchDlg::~CJobWatchDlg()
+{
+ if ( m_pSQL )
+ {
+ m_pSQL->Release();
+ }
+
+ if ( m_hMySQLDLL )
+ {
+ Sys_UnloadModule( m_hMySQLDLL );
+ }
+}
+
+
+void CJobWatchDlg::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(CJobWatchDlg)
+ DDX_Control(pDX, IDC_WORKERS, m_Workers);
+ DDX_Control(pDX, IDC_TEXTOUTPUT, m_TextOutput);
+ //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CJobWatchDlg, CIdleDialog)
+ //{{AFX_MSG_MAP(CJobWatchDlg)
+ ON_LBN_SELCHANGE(IDC_WORKERS, OnSelChangeWorkers)
+ ON_WM_SIZE()
+ ON_NOTIFY(LVN_ODSTATECHANGED, IDC_WORKERS, OnOdstatechangedWorkers)
+ ON_NOTIFY(LVN_ITEMCHANGED, IDC_WORKERS, OnItemchangedWorkers)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CJobWatchDlg message handlers
+
+const char* FindArg( const char *pArgName, const char *pDefault="" )
+{
+ for ( int i=1; i < __argc; i++ )
+ {
+ if ( Q_stricmp( pArgName, __argv[i] ) == 0 )
+ {
+ if ( (i+1) < __argc )
+ return __argv[i+1];
+ else
+ return pDefault;
+ }
+ }
+ return NULL;
+}
+
+
+bool ReadStringFromFile( FILE *fp, char *pStr, int strSize )
+{
+ int i=0;
+ for ( i; i < strSize-2; i++ )
+ {
+ if ( fread( &pStr[i], 1, 1, fp ) != 1 ||
+ pStr[i] == '\n' )
+ {
+ break;
+ }
+ }
+
+ pStr[i] = 0;
+ return i != 0;
+}
+
+
+BOOL CJobWatchDlg::OnInitDialog()
+{
+ CDialog::OnInitDialog();
+
+
+ m_Workers.SetExtendedStyle( LVS_EX_FULLROWSELECT );
+
+ // Setup the headers.
+ for ( int i=0; i < ARRAYSIZE( g_ColumnInfos ); i++ )
+ {
+ m_Workers.InsertColumn( i, g_ColumnInfos[i].pText, LVCFMT_LEFT, g_ColumnInfos[i].width, i );
+ }
+
+
+ m_GraphControl.SubclassDlgItem( IDC_GRAPH_AREA, this );
+
+
+ CString str;
+
+ // Get all our startup info from the command line.
+ const char *pJobID = FindArg( "-JobID", NULL );
+ const char *pDBName = FindArg( "-dbname", NULL );
+ const char *pHostName = FindArg( "-hostname", NULL );
+ const char *pUserName = FindArg( "-username", NULL );
+
+ if ( !pJobID )
+ {
+ str.Format( "Missing a command line parameter (-JobID or -dbname or -hostname or -username)" );
+ MessageBox( str, "Error", MB_OK );
+ EndDialog( 1 );
+ return FALSE;
+ }
+
+ char hostName[512], dbName[512], userName[512];
+ if ( !pDBName || !pHostName || !pUserName )
+ {
+ char errString[512];
+
+ // If they don't specify the DB info, get it from where
+ const char *pFilename = "dbinfo_job_search.txt";
+ FILE *fp = fopen( pFilename, "rt" );
+ if ( !fp )
+ {
+ Q_snprintf( errString, sizeof( errString ), "Can't open '%s' for database info.", pFilename );
+ MessageBox( errString, "Error", MB_OK );
+ EndDialog( 0 );
+ return FALSE;
+ }
+
+ if ( !ReadStringFromFile( fp, hostName, sizeof( hostName ) ) ||
+ !ReadStringFromFile( fp, dbName, sizeof( dbName ) ) ||
+ !ReadStringFromFile( fp, userName, sizeof( userName ) )
+ )
+ {
+ fclose( fp );
+ Q_snprintf( errString, sizeof( errString ), "'%s' has invalid format.", pFilename );
+ MessageBox( errString, "Error", MB_OK );
+ EndDialog( 0 );
+ return FALSE;
+ }
+
+ pDBName = dbName;
+ pHostName = hostName;
+ pUserName = userName;
+
+ fclose( fp );
+ }
+
+ m_JobID = atoi( pJobID );
+
+ // Get the mysql interface.
+ IMySQL *pSQL;
+ if ( !Sys_LoadInterface( "mysql_wrapper", MYSQL_WRAPPER_VERSION_NAME, &m_hMySQLDLL, (void**)&pSQL ) )
+ return false;
+
+ if ( !pSQL->InitMySQL( pDBName, pHostName, pUserName ) )
+ {
+ pSQL->Release();
+ str.Format( "Can't init MYSQL db (db = '%s', host = '%s', user = '%s')", pDBName, pHostName, pUserName );
+ MessageBox( str, "Error", MB_OK );
+ EndDialog( 0 );
+ return FALSE;
+ }
+
+ m_pSQL = CreateMySQLAsync( pSQL );
+ if ( !m_pSQL )
+ {
+ pSQL->Release();
+ str.Format( "Can't create IMySQLAsync" );
+ MessageBox( str, "Error", MB_OK );
+ EndDialog( 0 );
+ return FALSE;
+ }
+
+
+ memset( m_bQueriesInProgress, 0, sizeof( m_bQueriesInProgress ) );
+
+
+ // (Init the idle processor so we can update text and graphs).
+ StartIdleProcessing( 100 );
+
+ // Fill in the command line control.
+ char cmdLine[2048];
+ Q_snprintf( cmdLine, sizeof( cmdLine ), "vmpi_job_watch -JobID %s -hostname %s -dbname %s -username %s",
+ pJobID, pHostName, pDBName, pUserName );
+ SetDlgItemText( IDC_COMMAND_LINE, cmdLine );
+
+ // Setup anchors.
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_WORKERS_PANEL ), ANCHOR_LEFT, ANCHOR_TOP, ANCHOR_WIDTH_PERCENT, ANCHOR_HEIGHT_PERCENT );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_WORKERS ), ANCHOR_LEFT, ANCHOR_TOP, ANCHOR_WIDTH_PERCENT, ANCHOR_HEIGHT_PERCENT );
+
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_TEXT_OUTPUT_PANEL ), ANCHOR_LEFT, ANCHOR_HEIGHT_PERCENT, ANCHOR_WIDTH_PERCENT, ANCHOR_BOTTOM );
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_TEXTOUTPUT ), ANCHOR_LEFT, ANCHOR_HEIGHT_PERCENT, ANCHOR_WIDTH_PERCENT, ANCHOR_BOTTOM );
+
+ m_AnchorMgr.AddAnchor( this, GetDlgItem( IDC_GRAPHS_PANEL ), ANCHOR_WIDTH_PERCENT, ANCHOR_TOP, ANCHOR_RIGHT, ANCHOR_HEIGHT_PERCENT );
+ m_AnchorMgr.AddAnchor( this, &m_GraphControl, ANCHOR_WIDTH_PERCENT, ANCHOR_TOP, ANCHOR_RIGHT, ANCHOR_HEIGHT_PERCENT );
+
+ return TRUE; // return TRUE unless you set the focus to a control
+ // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+
+CWorkerInfo* CJobWatchDlg::FindWorkerByID( unsigned long jobWorkerID )
+{
+ int nIndex = -1;
+ while ( ( nIndex = m_Workers.GetNextItem( nIndex, LVNI_ALL ) ) != -1 )
+ {
+ CWorkerInfo *pInfo = (CWorkerInfo*)m_Workers.GetItemData( nIndex );
+ if ( pInfo->m_JobWorkerID == jobWorkerID )
+ return pInfo;
+ }
+
+ return NULL;
+}
+
+
+CWorkerInfo* CJobWatchDlg::FindWorkerByMachineName( const char *pMachineName )
+{
+ int nIndex = -1;
+ while ( ( nIndex = m_Workers.GetNextItem( nIndex, LVNI_ALL ) ) != -1 )
+ {
+ CWorkerInfo *pInfo = (CWorkerInfo*)m_Workers.GetItemData( nIndex );
+ if ( Q_stricmp( pInfo->m_ComputerName, pMachineName ) == 0 )
+ return pInfo;
+ }
+
+ return NULL;
+}
+
+
+void CJobWatchDlg::SetWorkerListItemInt( int nIndex, int iColumn, int value )
+{
+ char str[512];
+ Q_snprintf( str, sizeof( str ), "%d", value );
+ m_Workers.SetItemText( nIndex, iColumn, str );
+}
+
+
+void CJobWatchDlg::UpdateWorkersList()
+{
+ int nIndex = -1;
+ while ( ( nIndex = m_Workers.GetNextItem( nIndex, LVNI_ALL ) ) != -1 )
+ {
+ CWorkerInfo *pInfo = (CWorkerInfo*)m_Workers.GetItemData( nIndex );
+
+ char *pConnectedStr = pInfo->m_bConnected ? "yes" : "no";
+ m_Workers.SetItemText( nIndex, COLUMN_CONNECTED, pConnectedStr );
+
+ SetWorkerListItemInt( nIndex, COLUMN_WORK_UNITS_DONE, pInfo->m_nWorkUnitsDone );
+
+ char timeStr[1024];
+ FormatTimeString( pInfo->m_RunningTimeMS / 1000, timeStr, sizeof( timeStr ) );
+ m_Workers.SetItemText( nIndex, COLUMN_RUNNING_TIME, timeStr );
+
+ // Current stage.
+ SetWorkerListItemInt( nIndex, COLUMN_WORK_UNITS_DONE, pInfo->m_nWorkUnitsDone );
+
+ m_Workers.SetItemText( nIndex, COLUMN_CURRENT_STAGE, pInfo->m_CurrentStage );
+
+ SetWorkerListItemInt( nIndex, COLUMN_THREAD0_WU, pInfo->m_ThreadWUs[0] );
+ SetWorkerListItemInt( nIndex, COLUMN_THREAD1_WU, pInfo->m_ThreadWUs[1] );
+ SetWorkerListItemInt( nIndex, COLUMN_THREAD2_WU, pInfo->m_ThreadWUs[2] );
+ SetWorkerListItemInt( nIndex, COLUMN_THREAD3_WU, pInfo->m_ThreadWUs[3] );
+ }
+
+ ResortItems();
+}
+
+
+bool CJobWatchDlg::GetCurJobWorkerID( unsigned long &id )
+{
+ POSITION pos = m_Workers.GetFirstSelectedItemPosition();
+ if ( !pos )
+ return false;
+
+ int index = m_Workers.GetNextSelectedItem( pos );
+ CWorkerInfo *pInfo = (CWorkerInfo*)m_Workers.GetItemData( index );
+ id = pInfo->m_JobWorkerID;
+ return true;
+}
+
+
+void CJobWatchDlg::OnSelChangeWorkers()
+{
+ // Clear the text output and invalidate any old queries for text.
+ int nLen = m_TextOutput.SendMessage( EM_GETLIMITTEXT, 0, 0 );
+ m_TextOutput.SendMessage( EM_SETSEL, 0, nLen );
+ m_TextOutput.SendMessage( EM_REPLACESEL, FALSE, (LPARAM)"" );
+
+ m_CurMessageIndex = 0;
+ m_CurWorkerTextToken++;
+ m_LastQueryTime = 0; // force a query.
+
+ m_GraphControl.Clear();
+ m_CurGraphTime = -1;
+}
+
+
+void CJobWatchDlg::ResortItems()
+{
+ m_Workers.SortItems( g_ColumnInfos[g_iSortColumn].sortFn, (LPARAM)this );
+}
+
+
+
+void CJobWatchDlg::OnIdle()
+{
+ // Issue any queries that we need to.
+ DWORD curTime = GetTickCount();
+ if ( curTime - m_LastQueryTime >= 1000 )
+ {
+ m_LastQueryTime = curTime;
+ char query[2048];
+
+ unsigned long jobWorkerID;
+ bool bJobWorkerIDValid = GetCurJobWorkerID( jobWorkerID );
+
+ if ( !m_bQueriesInProgress[QUERY_TEXT] && bJobWorkerIDValid )
+ {
+ Q_snprintf( query, sizeof( query ), "select * from text_messages where JobWorkerID=%lu and MessageIndex >= %lu", jobWorkerID, m_CurMessageIndex );
+ m_pSQL->Execute( query, (void*)(QUERY_TEXT | (m_CurWorkerTextToken << 16)) );
+ m_bQueriesInProgress[QUERY_TEXT] = true;
+ }
+
+ if ( !m_bQueriesInProgress[QUERY_GRAPH] && bJobWorkerIDValid )
+ {
+ Q_snprintf( query, sizeof( query ), "select * from graph_entry where JobWorkerID=%lu", jobWorkerID );
+ m_pSQL->Execute( query, (void*)QUERY_GRAPH );
+ m_bQueriesInProgress[QUERY_GRAPH] = true;
+ }
+
+ if ( !m_bQueriesInProgress[QUERY_WORKER_STATS] )
+ {
+ Q_snprintf( query, sizeof( query ), "select JobWorkerID, WorkerState, NumWorkUnits, "
+ "RunningTimeMS, CurrentStage, Thread0WU, Thread1WU, Thread2WU, Thread3WU, IsMaster, MachineName "
+ " from job_worker_start where JobID=%lu", m_JobID );
+
+ m_pSQL->Execute( query, (void*)QUERY_WORKER_STATS );
+ m_bQueriesInProgress[QUERY_WORKER_STATS] = true;
+ }
+ }
+
+
+ // Pickup query results.
+ CQueryResults results;
+ while ( m_pSQL->GetNextResults( results ) && results.m_pResults )
+ {
+ int iQueryID = ((int)results.m_pUserData) & 0xFFFF;
+ int iExtraData = ((int)results.m_pUserData) >> 16;
+
+ if ( results.m_pResults )
+ {
+ if ( iQueryID == QUERY_TEXT )
+ {
+ if ( iExtraData == m_CurWorkerTextToken )
+ {
+ ProcessQueryResults_Text( results.m_pResults );
+ }
+ }
+ else if ( iQueryID == QUERY_GRAPH )
+ {
+ ProcessQueryResults_Graph( results.m_pResults );
+ }
+ else if ( iQueryID == QUERY_WORKER_STATS )
+ {
+ ProcessQueryResults_WorkerStats( results.m_pResults );
+ }
+
+ results.m_pResults->Release();
+ }
+
+ m_bQueriesInProgress[iQueryID] = false;
+ }
+}
+
+
+void CJobWatchDlg::ProcessQueryResults_WorkerStats( IMySQLRowSet *pSet )
+{
+ bool bChange = false;
+ while ( pSet->NextRow() )
+ {
+ int iColumn = 0;
+ int workerID = pSet->GetColumnValue_Int( iColumn++ );
+ int workerState = pSet->GetColumnValue_Int( iColumn++ );
+ int nWorkUnits = pSet->GetColumnValue_Int( iColumn++ );
+ unsigned long runningTimeMS = pSet->GetColumnValue_Int( iColumn++ );
+ const char *pCurrentStage = pSet->GetColumnValue_String( iColumn++ );
+ int iThread0WU = pSet->GetColumnValue_Int( iColumn++ );
+ int iThread1WU = pSet->GetColumnValue_Int( iColumn++ );
+ int iThread2WU = pSet->GetColumnValue_Int( iColumn++ );
+ int iThread3WU = pSet->GetColumnValue_Int( iColumn++ );
+ int bIsMaster = pSet->GetColumnValue_Int( iColumn++ );
+ const char *pMachineName = pSet->GetColumnValue_String( iColumn );
+
+ CWorkerInfo *pInfo = FindWorkerByID( workerID );
+ if ( pInfo )
+ {
+ if ( workerState != pInfo->m_bConnected ||
+ nWorkUnits != pInfo->m_nWorkUnitsDone ||
+ runningTimeMS != pInfo->m_RunningTimeMS ||
+ stricmp( pCurrentStage, pInfo->m_CurrentStage ) != 0 ||
+ iThread0WU != pInfo->m_ThreadWUs[0] ||
+ iThread1WU != pInfo->m_ThreadWUs[1] ||
+ iThread2WU != pInfo->m_ThreadWUs[2] ||
+ iThread3WU != pInfo->m_ThreadWUs[3]
+ )
+ {
+ bChange = true;
+ pInfo->m_bConnected = workerState;
+ pInfo->m_nWorkUnitsDone = nWorkUnits;
+ pInfo->m_RunningTimeMS = runningTimeMS;
+ pInfo->m_CurrentStage = pCurrentStage;
+ pInfo->m_ThreadWUs[0] = iThread0WU;
+ pInfo->m_ThreadWUs[1] = iThread1WU;
+ pInfo->m_ThreadWUs[2] = iThread2WU;
+ pInfo->m_ThreadWUs[3] = iThread3WU;
+ }
+ }
+ else
+ {
+ // Add a new entry.
+ CWorkerInfo *pInfo = new CWorkerInfo;
+ pInfo->m_ComputerName = pMachineName;
+ pInfo->m_bConnected = false;
+ pInfo->m_nWorkUnitsDone = 0;
+ pInfo->m_RunningTimeMS = 0;
+ pInfo->m_JobWorkerID = workerID;
+
+ int index;
+ if ( bIsMaster )
+ {
+ char tempStr[512];
+ Q_snprintf( tempStr, sizeof( tempStr ), "%s [master]", (const char*)pInfo->m_ComputerName );
+ index = m_Workers.InsertItem( COLUMN_COMPUTER_NAME, tempStr, NULL );
+ }
+ else
+ {
+ index = m_Workers.InsertItem( COLUMN_COMPUTER_NAME, pInfo->m_ComputerName, NULL );
+ }
+
+ m_Workers.SetItemData( index, (DWORD)pInfo );
+ bChange = true;
+ }
+ }
+
+ if ( bChange )
+ {
+ UpdateWorkersList();
+ }
+}
+
+
+void CJobWatchDlg::ProcessQueryResults_Text( IMySQLRowSet *pSet )
+{
+ CUtlVector<char> text;
+
+ while ( pSet->NextRow() )
+ {
+ const char *pTextStr = pSet->GetColumnValue( "text" ).String();
+ int len = strlen( pTextStr );
+ text.AddMultipleToTail( len, pTextStr );
+
+ m_CurMessageIndex = pSet->GetColumnValue( "MessageIndex" ).Int32() + 1;
+ }
+
+ text.AddToTail( 0 );
+ FormatAndSendToEditControl( m_TextOutput.GetSafeHwnd(), text.Base() );
+}
+
+
+void CJobWatchDlg::ProcessQueryResults_Graph( IMySQLRowSet *pSet )
+{
+ int iMSTime = pSet->GetColumnIndex( "MSSinceJobStart" );
+ int iBytesSent = pSet->GetColumnIndex( "BytesSent" );
+ int iBytesReceived = pSet->GetColumnIndex( "BytesReceived" );
+
+ // See if there's anything new.
+ CUtlVector<CGraphEntry> entries;
+
+ int highest = m_CurGraphTime;
+ while ( pSet->NextRow() )
+ {
+ CGraphEntry entry;
+ entry.m_msTime = pSet->GetColumnValue( iMSTime ).Int32();
+ entry.m_nBytesSent = pSet->GetColumnValue( iBytesSent ).Int32();
+ entry.m_nBytesReceived = pSet->GetColumnValue( iBytesReceived ).Int32();
+ entries.AddToTail( entry );
+
+ highest = max( highest, entry.m_msTime );
+ }
+
+ if ( highest > m_CurGraphTime )
+ {
+ m_CurGraphTime = highest;
+
+ m_GraphControl.Clear();
+ m_GraphControl.Fill( entries );
+ }
+}
+
+
+void CJobWatchDlg::OnSize(UINT nType, int cx, int cy)
+{
+ CIdleDialog::OnSize(nType, cx, cy);
+
+ m_AnchorMgr.UpdateAnchors( this );
+}
+
+
+BOOL CJobWatchDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
+{
+ NMHDR *pHdr = (NMHDR*)lParam;
+ if ( pHdr->idFrom == IDC_WORKERS )
+ {
+ if ( pHdr->code == LVN_COLUMNCLICK )
+ {
+ LPNMLISTVIEW pListView = (LPNMLISTVIEW)lParam;
+
+ // Now sort by this column.
+ g_iSortColumn = max( 0, min( pListView->iSubItem, (int)ARRAYSIZE( g_ColumnInfos ) - 1 ) );
+ ResortItems();
+ }
+ }
+
+ return CIdleDialog::OnNotify(wParam, lParam, pResult);
+}
+
+void CJobWatchDlg::OnOdstatechangedWorkers(NMHDR* pNMHDR, LRESULT* pResult)
+{
+ NMLVODSTATECHANGE* pStateChanged = (NMLVODSTATECHANGE*)pNMHDR;
+
+ if ( !( pStateChanged->uOldState & LVIS_SELECTED ) && ( pStateChanged->uNewState & LVIS_SELECTED ) )
+ {
+ OnSelChangeWorkers();
+ }
+
+ *pResult = 0;
+}
+
+void CJobWatchDlg::OnItemchangedWorkers(NMHDR* pNMHDR, LRESULT* pResult)
+{
+ NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
+
+ if ( !( pNMListView->uOldState & LVIS_SELECTED ) && ( pNMListView->uNewState & LVIS_SELECTED ) )
+ {
+ OnSelChangeWorkers();
+ }
+
+ *pResult = 0;
+}
diff --git a/utils/vmpi/vmpi_job_watch/JobWatchDlg.h b/utils/vmpi/vmpi_job_watch/JobWatchDlg.h
new file mode 100644
index 0000000..6bec50b
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/JobWatchDlg.h
@@ -0,0 +1,134 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#if !defined(AFX_JOBWATCHDLG_H__761BDEEF_D549_4F10_817C_1C1FAF9FCA47__INCLUDED_)
+#define AFX_JOBWATCHDLG_H__761BDEEF_D549_4F10_817C_1C1FAF9FCA47__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+// JobWatchDlg.h : header file
+//
+
+
+#include "idle_dialog.h"
+#include "resource.h"
+#include "utlvector.h"
+#include "imysqlwrapper.h"
+#include "GraphControl.h"
+#include "window_anchor_mgr.h"
+#include "mysql_async.h"
+
+
+class CWorkerInfo
+{
+public:
+ CWorkerInfo()
+ {
+ m_bConnected = false;
+ m_nWorkUnitsDone = 0;
+ m_JobWorkerID = 0xFFFFFFFF;
+ m_RunningTimeMS = 0;
+ m_ThreadWUs[0] = m_ThreadWUs[1] = m_ThreadWUs[2] = m_ThreadWUs[3] = -1;
+ }
+
+ CString m_ComputerName;
+ int m_bConnected;
+ int m_nWorkUnitsDone;
+ unsigned long m_JobWorkerID;
+ unsigned long m_RunningTimeMS;
+ CString m_CurrentStage;
+ int m_ThreadWUs[4];
+};
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// CJobWatchDlg dialog
+
+class CJobWatchDlg : public CIdleDialog
+{
+// Construction
+public:
+ CJobWatchDlg( CWnd* pParent = NULL); // standard constructor
+ virtual ~CJobWatchDlg();
+
+// Dialog Data
+ //{{AFX_DATA(CJobWatchDlg)
+ enum { IDD = IDD_JOB_WATCH };
+ CListCtrl m_Workers;
+ CEdit m_TextOutput;
+ //}}AFX_DATA
+
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CJobWatchDlg)
+ protected:
+ virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
+ virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
+ //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+ virtual void OnIdle();
+ void RefreshWorkerStats();
+ CWorkerInfo* FindWorkerByID( unsigned long jobWorkerID );
+ CWorkerInfo* FindWorkerByMachineName( const char *pMachineName );
+ void SetWorkerListItemInt( int nIndex, int iColumn, int value );
+ void UpdateWorkersList();
+ void ResortItems();
+
+ // Query IDs.
+ enum
+ {
+ QUERY_TEXT=0,
+ QUERY_GRAPH,
+ QUERY_WORKER_STATS,
+ NUM_QUERIES
+ };
+
+ void ProcessQueryResults_Graph( IMySQLRowSet *pSet );
+ void ProcessQueryResults_Text( IMySQLRowSet *pSet );
+ void ProcessQueryResults_WorkerStats( IMySQLRowSet *pSet );
+
+ bool m_bQueriesInProgress[NUM_QUERIES];
+
+ // This is our connection to the mysql database.
+ IMySQLAsync *m_pSQL;
+ CSysModule *m_hMySQLDLL;
+
+ CWindowAnchorMgr m_AnchorMgr;
+
+
+ bool GetCurJobWorkerID( unsigned long &id );
+
+ CGraphControl m_GraphControl;
+ unsigned long m_JobID;
+ int m_CurGraphTime;
+
+ int m_CurMessageIndex;
+ int m_CurWorkerTextToken; // used to let it ignore old text in the thread's queue
+
+ DWORD m_LastQueryTime; // Last time we made a query.
+
+ // Generated message map functions
+ //{{AFX_MSG(CJobWatchDlg)
+ virtual BOOL OnInitDialog();
+ afx_msg void OnSelChangeWorkers();
+ afx_msg void OnSize(UINT nType, int cx, int cy);
+ afx_msg void OnOdstatechangedWorkers(NMHDR* pNMHDR, LRESULT* pResult);
+ afx_msg void OnItemchangedWorkers(NMHDR* pNMHDR, LRESULT* pResult);
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_JOBWATCHDLG_H__761BDEEF_D549_4F10_817C_1C1FAF9FCA47__INCLUDED_)
diff --git a/utils/vmpi/vmpi_job_watch/StdAfx.cpp b/utils/vmpi/vmpi_job_watch/StdAfx.cpp
new file mode 100644
index 0000000..86ac49b
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/StdAfx.cpp
@@ -0,0 +1,15 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// stdafx.cpp : source file that includes just the standard includes
+// vmpi_browser_job_watch.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+
+
diff --git a/utils/vmpi/vmpi_job_watch/StdAfx.h b/utils/vmpi/vmpi_job_watch/StdAfx.h
new file mode 100644
index 0000000..3c1d23e
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/StdAfx.h
@@ -0,0 +1,36 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__E8FBDA6A_CE57_4416_8329_90155CD6CEC3__INCLUDED_)
+#define AFX_STDAFX_H__E8FBDA6A_CE57_4416_8329_90155CD6CEC3__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+
+#include "tier0/basetypes.h"
+
+#include <afxwin.h> // MFC core and standard components
+#include <afxext.h> // MFC extensions
+#include <afxdisp.h> // MFC Automation classes
+#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h> // MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__E8FBDA6A_CE57_4416_8329_90155CD6CEC3__INCLUDED_)
diff --git a/utils/vmpi/vmpi_job_watch/res/vmpi_browser_job_watch.ico b/utils/vmpi/vmpi_job_watch/res/vmpi_browser_job_watch.ico
new file mode 100644
index 0000000..7eef0bc
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/res/vmpi_browser_job_watch.ico
Binary files differ
diff --git a/utils/vmpi/vmpi_job_watch/res/vmpi_browser_job_watch.rc2 b/utils/vmpi/vmpi_job_watch/res/vmpi_browser_job_watch.rc2
new file mode 100644
index 0000000..6e93fd0
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/res/vmpi_browser_job_watch.rc2
@@ -0,0 +1,13 @@
+//
+// VMPI_BROWSER_JOB_WATCH.RC2 - resources Microsoft Visual C++ does not edit directly
+//
+
+#ifdef APSTUDIO_INVOKED
+ #error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Add manually edited resources here...
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/utils/vmpi/vmpi_job_watch/resource.h b/utils/vmpi/vmpi_job_watch/resource.h
new file mode 100644
index 0000000..c81a23e
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/resource.h
@@ -0,0 +1,32 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by vmpi_browser_job_watch.rc
+//
+#define IDD_VMPI_BROWSER_JOB_WATCH_DIALOG 102
+#define IDR_MAINFRAME 128
+#define IDD_JOB_WATCH 135
+#define IDC_WORKERS 1001
+#define IDC_GRAPH_AREA 1002
+#define IDC_TEXTOUTPUT 1005
+#define IDC_WORKERS_PANEL 1006
+#define IDC_TEXT_OUTPUT_PANEL 1007
+#define IDC_GRAPHS_PANEL 1008
+#define IDC_COMMAND_LINE 1009
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 129
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1010
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/utils/vmpi/vmpi_job_watch/vmpi_browser_job_watch.cpp b/utils/vmpi/vmpi_job_watch/vmpi_browser_job_watch.cpp
new file mode 100644
index 0000000..efdf515
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/vmpi_browser_job_watch.cpp
@@ -0,0 +1,75 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// vmpi_browser_job_watch.cpp : Defines the class behaviors for the application.
+//
+
+#include "stdafx.h"
+#include "vmpi_browser_job_watch.h"
+#include "JobWatchDlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CVMPIBrowserJobWatchApp
+
+BEGIN_MESSAGE_MAP(CVMPIBrowserJobWatchApp, CWinApp)
+ //{{AFX_MSG_MAP(CVMPIBrowserJobWatchApp)
+ // NOTE - the ClassWizard will add and remove mapping macros here.
+ // DO NOT EDIT what you see in these blocks of generated code!
+ //}}AFX_MSG
+ ON_COMMAND(ID_HELP, CWinApp::OnHelp)
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CVMPIBrowserJobWatchApp construction
+
+CVMPIBrowserJobWatchApp::CVMPIBrowserJobWatchApp()
+{
+ // TODO: add construction code here,
+ // Place all significant initialization in InitInstance
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// The one and only CVMPIBrowserJobWatchApp object
+
+CVMPIBrowserJobWatchApp theApp;
+
+/////////////////////////////////////////////////////////////////////////////
+// CVMPIBrowserJobWatchApp initialization
+
+BOOL CVMPIBrowserJobWatchApp::InitInstance()
+{
+ AfxEnableControlContainer();
+
+ // Standard initialization
+ // If you are not using these features and wish to reduce the size
+ // of your final executable, you should remove from the following
+ // the specific initialization routines you do not need.
+
+ CJobWatchDlg dlg;
+ m_pMainWnd = &dlg;
+ int nResponse = dlg.DoModal();
+ if (nResponse == IDOK)
+ {
+ // TODO: Place code here to handle when the dialog is
+ // dismissed with OK
+ }
+ else if (nResponse == IDCANCEL)
+ {
+ // TODO: Place code here to handle when the dialog is
+ // dismissed with Cancel
+ }
+
+ // Since the dialog has been closed, return FALSE so that we exit the
+ // application, rather than start the application's message pump.
+ return FALSE;
+}
diff --git a/utils/vmpi/vmpi_job_watch/vmpi_browser_job_watch.h b/utils/vmpi/vmpi_job_watch/vmpi_browser_job_watch.h
new file mode 100644
index 0000000..bd55384
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/vmpi_browser_job_watch.h
@@ -0,0 +1,56 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// vmpi_browser_job_watch.h : main header file for the VMPI_BROWSER_JOB_WATCH application
+//
+
+#if !defined(AFX_VMPI_BROWSER_JOB_WATCH_H__1DF22047_F615_4799_913A_222E3701BE5E__INCLUDED_)
+#define AFX_VMPI_BROWSER_JOB_WATCH_H__1DF22047_F615_4799_913A_222E3701BE5E__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#ifndef __AFXWIN_H__
+ #error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include "resource.h" // main symbols
+
+/////////////////////////////////////////////////////////////////////////////
+// CVMPIBrowserJobWatchApp:
+// See vmpi_browser_job_watch.cpp for the implementation of this class
+//
+
+class CVMPIBrowserJobWatchApp : public CWinApp
+{
+public:
+ CVMPIBrowserJobWatchApp();
+
+// Overrides
+ // ClassWizard generated virtual function overrides
+ //{{AFX_VIRTUAL(CVMPIBrowserJobWatchApp)
+ public:
+ virtual BOOL InitInstance();
+ //}}AFX_VIRTUAL
+
+// Implementation
+
+ //{{AFX_MSG(CVMPIBrowserJobWatchApp)
+ // NOTE - the ClassWizard will add and remove member functions here.
+ // DO NOT EDIT what you see in these blocks of generated code !
+ //}}AFX_MSG
+ DECLARE_MESSAGE_MAP()
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_VMPI_BROWSER_JOB_WATCH_H__1DF22047_F615_4799_913A_222E3701BE5E__INCLUDED_)
diff --git a/utils/vmpi/vmpi_job_watch/vmpi_browser_job_watch.rc b/utils/vmpi/vmpi_job_watch/vmpi_browser_job_watch.rc
new file mode 100644
index 0000000..56c3fc7
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/vmpi_browser_job_watch.rc
@@ -0,0 +1,183 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
+ "#define _AFX_NO_OLE_RESOURCES\r\n"
+ "#define _AFX_NO_TRACKER_RESOURCES\r\n"
+ "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
+ "\r\n"
+ "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
+ "#ifdef _WIN32\r\n"
+ "LANGUAGE 9, 1\r\n"
+ "#pragma code_page(1252)\r\n"
+ "#endif //_WIN32\r\n"
+ "#include ""res\\vmpi_browser_job_watch.rc2"" // non-Microsoft Visual C++ edited resources\r\n"
+ "#include ""afxres.rc"" // Standard components\r\n"
+ "#endif\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDR_MAINFRAME ICON DISCARDABLE "res\\vmpi_browser_job_watch.ico"
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904B0"
+ BEGIN
+ VALUE "CompanyName", "\0"
+ VALUE "FileDescription", "vmpi_browser_job_watch MFC Application\0"
+ VALUE "FileVersion", "1, 0, 0, 1\0"
+ VALUE "InternalName", "vmpi_browser_job_watch\0"
+ VALUE "LegalCopyright", "Copyright (C) 2003\0"
+ VALUE "LegalTrademarks", "\0"
+ VALUE "OriginalFilename", "vmpi_browser_job_watch.EXE\0"
+ VALUE "ProductName", "vmpi_browser_job_watch Application\0"
+ VALUE "ProductVersion", "1, 0, 0, 1\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_JOB_WATCH DIALOG DISCARDABLE 0, 0, 592, 366
+STYLE WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION |
+ WS_SYSMENU | WS_THICKFRAME
+CAPTION "Job Watch"
+FONT 8, "MS Sans Serif"
+BEGIN
+ EDITTEXT IDC_TEXTOUTPUT,13,177,291,179,ES_MULTILINE | ES_READONLY |
+ WS_VSCROLL | WS_HSCROLL
+ GROUPBOX "Workers",IDC_WORKERS_PANEL,7,32,578,132
+ GROUPBOX "Text Output",IDC_TEXT_OUTPUT_PANEL,7,164,303,195
+ GROUPBOX "Graphs",IDC_GRAPHS_PANEL,318,164,267,195
+ CONTROL "",IDC_GRAPH_AREA,"Static",SS_BLACKFRAME,323,176,255,179
+ CONTROL "List1",IDC_WORKERS,"SysListView32",LVS_REPORT |
+ LVS_SORTASCENDING | WS_BORDER | WS_TABSTOP,13,41,564,118
+ LTEXT "Command Line:",IDC_STATIC,7,9,50,8
+ EDITTEXT IDC_COMMAND_LINE,60,7,364,14,ES_AUTOHSCROLL |
+ ES_READONLY
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_JOB_WATCH, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 585
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 359
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#define _AFX_NO_SPLITTER_RESOURCES
+#define _AFX_NO_OLE_RESOURCES
+#define _AFX_NO_TRACKER_RESOURCES
+#define _AFX_NO_PROPERTY_RESOURCES
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#endif //_WIN32
+#include "res\vmpi_browser_job_watch.rc2" // non-Microsoft Visual C++ edited resources
+#include "afxres.rc" // Standard components
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/utils/vmpi/vmpi_job_watch/vmpi_job_watch.vpc b/utils/vmpi/vmpi_job_watch/vmpi_job_watch.vpc
new file mode 100644
index 0000000..9d8f372
--- /dev/null
+++ b/utils/vmpi/vmpi_job_watch/vmpi_job_watch.vpc
@@ -0,0 +1,80 @@
+//-----------------------------------------------------------------------------
+// vmpi_job_watch.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+$Macro OUTBINNAME "vmpi_job_watch"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE,.\,..\,..\..\common,..\mysql\include"
+ $PreprocessorDefinitions "$BASE;PROTECTED_THINGS_DISABLE;WINVER=0x501;NO_WARN_MBCS_MFC_DEPRECATION"
+ $EnableC++Exceptions "Yes (/EHsc)"
+ }
+}
+
+$Configuration "Debug"
+{
+ $Linker
+ {
+ // Deprecated MBCS MFC libraries for VS 2013 (nafxcw.lib and nafxcwd.lib) can be downloaded from http://go.microsoft.com/?linkid=9832071
+ $AdditionalDependencies "$BASE nafxcwd.lib"
+ $IgnoreSpecificLibrary "nafxcw.lib libcmt.lib"
+ }
+}
+
+$Configuration "Release"
+{
+ $Linker
+ {
+ // Deprecated MBCS MFC libraries for VS 2013 (nafxcw.lib and nafxcwd.lib) can be downloaded from http://go.microsoft.com/?linkid=9832071
+ $AdditionalDependencies "$BASE nafxcw.lib libcmt.lib"
+ $IgnoreSpecificLibrary "nafxcwd.lib libcmtd.lib"
+ }
+}
+
+$Project "vmpi_job_watch"
+{
+ $Folder "Source Files"
+ {
+ -$File "$SRCDIR\public\tier0\memoverride.cpp"
+
+ $File "..\..\common\consolewnd.cpp"
+ $File "GraphControl.cpp"
+ $File "..\idle_dialog.cpp"
+ $File "JobWatchDlg.cpp"
+ $File "..\mysql_async.cpp"
+ $File "..\vmpi_browser_helpers.cpp"
+ $File "vmpi_browser_job_watch.cpp"
+ $File "vmpi_browser_job_watch.rc"
+ $File "..\win_idle.cpp"
+ $File "..\window_anchor_mgr.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "..\..\common\consolewnd.h"
+ $File "GraphControl.h"
+ $File "..\idle_dialog.h"
+ $File "JobWatchDlg.h"
+ $File "..\mysql_async.h"
+ $File "..\mysql_wrapper.h"
+ $File "Resource.h"
+ $File "StdAfx.h"
+ $File "vmpi_browser_job_watch.h"
+ $File "..\win_idle.h"
+ }
+
+ $Folder "Resource Files"
+ {
+ $File "res\vmpi_browser_job_watch.ico"
+ $File "res\vmpi_browser_job_watch.rc2"
+ }
+}