source-engine/utils/xbox/vxconsole/timestamp_log.cpp
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

624 lines
17 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// TIMESTAMP_LOG.CPP
//
// TimeStamp Log Display.
//=====================================================================================//
#include "vxconsole.h"
#define ID_TIMESTAMPLOG_LISTVIEW 100
// column id
#define ID_TSL_TIME 0
#define ID_TSL_DELTATIME 1
#define ID_TSL_MEMORY 2
#define ID_TSL_DELTAMEMORY 3
#define ID_TSL_MESSAGE 4
typedef struct
{ const CHAR* name;
int width;
int subItemIndex;
CHAR nameBuff[32];
} label_t;
struct timeStampLogNode_t
{
int index;
float time;
float deltaTime;
int memory;
int deltaMemory;
char timeBuff[32];
char deltaTimeBuff[32];
char memoryBuff[32];
char deltaMemoryBuff[32];
char *pMessage;
timeStampLogNode_t *pNext;
};
HWND g_timeStampLog_hWnd;
HWND g_timeStampLog_hWndListView;
RECT g_timeStampLog_windowRect;
timeStampLogNode_t *g_timeStampLog_pNodes;
int g_timeStampLog_sortColumn;
int g_timeStampLog_sortDescending;
label_t g_timeStampLog_Labels[] =
{
{"Time", 100, ID_TSL_TIME},
{"Delta Time", 100, ID_TSL_DELTATIME},
{"Memory", 100, ID_TSL_MEMORY},
{"Delta Memory", 100, ID_TSL_DELTAMEMORY},
{"Message", 400, ID_TSL_MESSAGE},
};
//-----------------------------------------------------------------------------
// TimeStampLog_CompareFunc
//
//-----------------------------------------------------------------------------
int CALLBACK TimeStampLog_CompareFunc( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort )
{
timeStampLogNode_t* pNodeA = ( timeStampLogNode_t* )lParam1;
timeStampLogNode_t* pNodeB = ( timeStampLogNode_t* )lParam2;
int sort = 0;
switch ( g_timeStampLog_sortColumn )
{
case ID_TSL_TIME:
sort = ( int )( 1000.0f*pNodeA->time - 1000.0f*pNodeB->time );
break;
case ID_TSL_DELTATIME:
sort = ( int )( 1000.0f*pNodeA->deltaTime - 1000.0f*pNodeB->deltaTime );
break;
case ID_TSL_MEMORY:
sort = pNodeA->memory - pNodeB->memory;
break;
case ID_TSL_DELTAMEMORY:
sort = pNodeA->deltaMemory - pNodeB->deltaMemory;
break;
case ID_TSL_MESSAGE:
sort = stricmp( pNodeA->pMessage, pNodeB->pMessage );
break;
}
// flip the sort order
if ( g_timeStampLog_sortDescending )
sort *= -1;
return ( sort );
}
//-----------------------------------------------------------------------------
// TimeStampLog_SortItems
//
//-----------------------------------------------------------------------------
void TimeStampLog_SortItems()
{
LVITEM lvitem;
timeStampLogNode_t *pNode;
int i;
if ( !g_timeStampLog_hWnd )
{
// only sort if window is visible
return;
}
ListView_SortItems( g_timeStampLog_hWndListView, TimeStampLog_CompareFunc, 0 );
memset( &lvitem, 0, sizeof( lvitem ) );
lvitem.mask = LVIF_PARAM;
// get each item and reset its list index
int itemCount = ListView_GetItemCount( g_timeStampLog_hWndListView );
for ( i=0; i<itemCount; i++ )
{
lvitem.iItem = i;
ListView_GetItem( g_timeStampLog_hWndListView, &lvitem );
pNode = ( timeStampLogNode_t* )lvitem.lParam;
pNode->index = i;
}
// update list view columns with sort key
for ( i=0; i<sizeof( g_timeStampLog_Labels )/sizeof( g_timeStampLog_Labels[0] ); i++ )
{
char symbol;
LVCOLUMN lvc;
if ( i == g_timeStampLog_sortColumn )
symbol = g_timeStampLog_sortDescending ? '<' : '>';
else
symbol = ' ';
sprintf( g_timeStampLog_Labels[i].nameBuff, "%s %c", g_timeStampLog_Labels[i].name, symbol );
memset( &lvc, 0, sizeof( lvc ) );
lvc.mask = LVCF_TEXT;
lvc.pszText = ( LPSTR )g_timeStampLog_Labels[i].nameBuff;
ListView_SetColumn( g_timeStampLog_hWndListView, i, &lvc );
}
}
//-----------------------------------------------------------------------------
// TimeStampLog_AddViewItem
//
//-----------------------------------------------------------------------------
void TimeStampLog_AddViewItem( timeStampLogNode_t* pNode )
{
LVITEM lvi;
if ( !g_timeStampLog_hWnd )
{
// only valid if log window is visible
return;
}
// update the text callback buffers
sprintf( pNode->timeBuff, "%2.2d:%2.2d:%2.2d:%3.3d", ( int )pNode->time/3600, ( ( int )pNode->time/60 )%60, ( int )pNode->time%60, ( int )( 1000*( pNode->time-( int )pNode->time ) )%1000 );
sprintf( pNode->deltaTimeBuff, "%.3f", pNode->deltaTime );
sprintf( pNode->memoryBuff, "%.2f MB", pNode->memory/( 1024.0f*1024.0f ) );
sprintf( pNode->deltaMemoryBuff, "%d", pNode->deltaMemory );
int itemCount = ListView_GetItemCount( g_timeStampLog_hWndListView );
// setup and insert at end of list
memset( &lvi, 0, sizeof( lvi ) );
lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
lvi.iItem = itemCount;
lvi.iSubItem = 0;
lvi.state = 0;
lvi.stateMask = 0;
lvi.pszText = LPSTR_TEXTCALLBACK;
lvi.lParam = ( LPARAM )pNode;
// insert
pNode->index = ListView_InsertItem( g_timeStampLog_hWndListView, &lvi );
}
//-----------------------------------------------------------------------------
// TimeStampLog_AddItem
//
//-----------------------------------------------------------------------------
void TimeStampLog_AddItem( float time, float deltaTime, int memory, int deltaMemory, const char* pMessage )
{
timeStampLogNode_t* pNode;
// create and init
pNode = new timeStampLogNode_t;
memset( pNode, 0, sizeof( timeStampLogNode_t ) );
pNode->time = time;
pNode->deltaTime = deltaTime;
pNode->memory = memory;
pNode->deltaMemory = deltaMemory;
pNode->pMessage = Sys_CopyString( pMessage ? pMessage : "" );
// link in
pNode->pNext = g_timeStampLog_pNodes;
g_timeStampLog_pNodes = pNode;
TimeStampLog_AddViewItem( pNode );
}
//-----------------------------------------------------------------------------
// TimeStampLog_Clear
//
//-----------------------------------------------------------------------------
void TimeStampLog_Clear()
{
timeStampLogNode_t* pNode;
timeStampLogNode_t* pNextNode;
// delete all the list view entries
if ( g_timeStampLog_hWnd )
ListView_DeleteAllItems( g_timeStampLog_hWndListView );
// delete nodes in chain
pNode = g_timeStampLog_pNodes;
while ( pNode )
{
pNextNode = pNode->pNext;
Sys_Free( pNode->pMessage );
delete pNode;
pNode = pNextNode;
}
g_timeStampLog_pNodes = NULL;
}
//-----------------------------------------------------------------------------
// TimeStampLog_SaveConfig
//
//-----------------------------------------------------------------------------
void TimeStampLog_SaveConfig()
{
char buff[256];
Sys_SetRegistryInteger( "timeStampLogSortColumn", g_timeStampLog_sortColumn );
Sys_SetRegistryInteger( "timeStampLogSortDescending", g_timeStampLog_sortDescending );
WINDOWPLACEMENT wp;
memset( &wp, 0, sizeof( wp ) );
wp.length = sizeof( WINDOWPLACEMENT );
GetWindowPlacement( g_timeStampLog_hWnd, &wp );
g_timeStampLog_windowRect = wp.rcNormalPosition;
sprintf( buff, "%d %d %d %d", g_timeStampLog_windowRect.left, g_timeStampLog_windowRect.top, g_timeStampLog_windowRect.right, g_timeStampLog_windowRect.bottom );
Sys_SetRegistryString( "timeStampLogWindowRect", buff );
}
//-----------------------------------------------------------------------------
// TimeStampLog_LoadConfig
//
//-----------------------------------------------------------------------------
void TimeStampLog_LoadConfig()
{
int numArgs;
char buff[256];
Sys_GetRegistryInteger( "timeStampLogSortColumn", ID_TSL_TIME, g_timeStampLog_sortColumn );
Sys_GetRegistryInteger( "timeStampLogSortDescending", false, g_timeStampLog_sortDescending );
Sys_GetRegistryString( "timeStampLogWindowRect", buff, "", sizeof( buff ) );
numArgs = sscanf( buff, "%d %d %d %d", &g_timeStampLog_windowRect.left, &g_timeStampLog_windowRect.top, &g_timeStampLog_windowRect.right, &g_timeStampLog_windowRect.bottom );
if ( numArgs != 4 || g_timeStampLog_windowRect.left < 0 || g_timeStampLog_windowRect.top < 0 || g_timeStampLog_windowRect.right < 0 || g_timeStampLog_windowRect.bottom < 0 )
memset( &g_timeStampLog_windowRect, 0, sizeof( g_timeStampLog_windowRect ) );
}
//-----------------------------------------------------------------------------
// TimeStampLog_SizeWindow
//
//-----------------------------------------------------------------------------
void TimeStampLog_SizeWindow( HWND hwnd, int width, int height )
{
if ( width==0 || height==0 )
{
RECT rcClient;
GetClientRect( hwnd, &rcClient );
width = rcClient.right;
height = rcClient.bottom;
}
// position the ListView
SetWindowPos( g_timeStampLog_hWndListView, NULL, 0, 0, width, height, SWP_NOZORDER );
}
//-----------------------------------------------------------------------------
// TimeStampLog_Export
//
//-----------------------------------------------------------------------------
void TimeStampLog_Export()
{
OPENFILENAME ofn;
char logFilename[MAX_PATH];
int retval;
FILE* fp;
int i;
int count;
memset( &ofn, 0, sizeof( ofn ) );
ofn.lStructSize = sizeof( ofn );
ofn.hwndOwner = g_timeStampLog_hWnd;
ofn.lpstrFile = logFilename;
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof( logFilename );
ofn.lpstrFilter = "Excel CSV\0*.CSV\0All Files\0*.*\0";
ofn.nFilterIndex = 1;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = "c:\\";
ofn.Flags = OFN_PATHMUSTEXIST;
// display the open dialog box
retval = GetOpenFileName( &ofn );
if ( !retval )
return;
Sys_AddExtension( ".csv", logFilename, sizeof( logFilename ) );
fp = fopen( logFilename, "wt+" );
if ( !fp )
return;
// labels
count = ARRAYSIZE( g_timeStampLog_Labels );
for ( i=0; i<count; i++ )
{
fprintf( fp, "\"%s\"", g_timeStampLog_Labels[i].name );
if ( i != count-1 )
fprintf( fp, "," );
}
fprintf( fp, "\n" );
// build a list for easy reverse traversal
CUtlVector< timeStampLogNode_t* > nodeList;
timeStampLogNode_t *pNode;
pNode = g_timeStampLog_pNodes;
while ( pNode )
{
nodeList.AddToTail( pNode );
pNode = pNode->pNext;
}
// dump to the log
for ( int i=nodeList.Count()-1; i>=0; i-- )
{
pNode = nodeList[i];
fprintf( fp, "\"%s\"", pNode->timeBuff );
fprintf( fp, ",\"%s\"", pNode->deltaTimeBuff );
fprintf( fp, ",\"%s\"", pNode->memoryBuff );
fprintf( fp, ",\"%s\"", pNode->deltaMemoryBuff );
fprintf( fp, ",\"%s\"", pNode->pMessage );
fprintf( fp, "\n" );
}
fclose( fp );
}
//-----------------------------------------------------------------------------
// TimeStampLog_WndProc
//
//-----------------------------------------------------------------------------
LRESULT CALLBACK TimeStampLog_WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
WORD wID = LOWORD( wParam );
timeStampLogNode_t *pNode;
switch ( message )
{
case WM_CREATE:
return 0L;
case WM_DESTROY:
TimeStampLog_SaveConfig();
g_timeStampLog_hWnd = NULL;
return 0L;
case WM_SIZE:
TimeStampLog_SizeWindow( hwnd, LOWORD( lParam ), HIWORD( lParam ) );
return 0L;
case WM_NOTIFY:
switch ( ( ( LPNMHDR )lParam )->code )
{
case LVN_COLUMNCLICK:
NMLISTVIEW* pnmlv;
pnmlv = ( NMLISTVIEW* )lParam;
if ( g_timeStampLog_sortColumn == pnmlv->iSubItem )
{
// user has clicked on same column - flip the sort
g_timeStampLog_sortDescending ^= 1;
}
else
{
// sort by new column
g_timeStampLog_sortColumn = pnmlv->iSubItem;
}
TimeStampLog_SortItems();
return 0L;
case LVN_GETDISPINFO:
NMLVDISPINFO* plvdi;
plvdi = ( NMLVDISPINFO* )lParam;
pNode = ( timeStampLogNode_t * )plvdi->item.lParam;
switch ( plvdi->item.iSubItem )
{
case ID_TSL_TIME:
plvdi->item.pszText = pNode->timeBuff;
return 0L;
case ID_TSL_DELTATIME:
plvdi->item.pszText = pNode->deltaTimeBuff;
return 0L;
case ID_TSL_MEMORY:
plvdi->item.pszText = pNode->memoryBuff;
return 0L;
case ID_TSL_DELTAMEMORY:
plvdi->item.pszText = pNode->deltaMemoryBuff;
return 0L;
case ID_TSL_MESSAGE:
plvdi->item.pszText = pNode->pMessage;
return 0L;
default:
break;
}
break;
}
break;
case WM_COMMAND:
switch ( wID )
{
case IDM_OPTIONS_CLEAR:
TimeStampLog_Clear();
return 0L;
case IDM_OPTIONS_EXPORT:
TimeStampLog_Export();
return 0L;
}
break;
}
return ( DefWindowProc( hwnd, message, wParam, lParam ) );
}
//-----------------------------------------------------------------------------
// TimeStampLog_Init
//
//-----------------------------------------------------------------------------
bool TimeStampLog_Init()
{
// set up our window class
WNDCLASS wndclass;
memset( &wndclass, 0, sizeof( wndclass ) );
wndclass.style = 0;
wndclass.lpfnWndProc = TimeStampLog_WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = g_hInstance;
wndclass.hIcon = g_hIcons[ICON_APPLICATION];
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
wndclass.hbrBackground = g_hBackgroundBrush;
wndclass.lpszMenuName = MAKEINTRESOURCE( MENU_TIMESTAMPLOG );
wndclass.lpszClassName = "TIMESTAMPLOGCLASS";
if ( !RegisterClass( &wndclass ) )
return false;
TimeStampLog_LoadConfig();
return true;
}
//-----------------------------------------------------------------------------
// TimeStampLog_Open
//
//-----------------------------------------------------------------------------
void TimeStampLog_Open()
{
RECT clientRect;
HWND hWnd;
int i;
timeStampLogNode_t *pNode;
if ( g_timeStampLog_hWnd )
{
// only one file log instance
if ( IsIconic( g_timeStampLog_hWnd ) )
ShowWindow( g_timeStampLog_hWnd, SW_RESTORE );
SetForegroundWindow( g_timeStampLog_hWnd );
return;
}
hWnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
"TIMESTAMPLOGCLASS",
"TimeStamp Log",
WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_SIZEBOX|WS_MINIMIZEBOX|WS_MAXIMIZEBOX,
0,
0,
600,
300,
g_hDlgMain,
NULL,
g_hInstance,
NULL );
g_timeStampLog_hWnd = hWnd;
GetClientRect( g_timeStampLog_hWnd, &clientRect );
hWnd = CreateWindow(
WC_LISTVIEW,
"",
WS_VISIBLE|WS_CHILD|LVS_REPORT,
0,
0,
clientRect.right-clientRect.left,
clientRect.bottom-clientRect.top,
g_timeStampLog_hWnd,
( HMENU )ID_TIMESTAMPLOG_LISTVIEW,
g_hInstance,
NULL );
g_timeStampLog_hWndListView = hWnd;
// init list view columns
for ( i=0; i<sizeof( g_timeStampLog_Labels )/sizeof( g_timeStampLog_Labels[0] ); i++ )
{
LVCOLUMN lvc;
memset( &lvc, 0, sizeof( lvc ) );
lvc.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;
lvc.iSubItem = 0;
lvc.cx = g_timeStampLog_Labels[i].width;
lvc.fmt = LVCFMT_LEFT;
lvc.pszText = ( LPSTR )g_timeStampLog_Labels[i].name;
ListView_InsertColumn( g_timeStampLog_hWndListView, i, &lvc );
}
ListView_SetBkColor( g_timeStampLog_hWndListView, g_backgroundColor );
ListView_SetTextBkColor( g_timeStampLog_hWndListView, g_backgroundColor );
DWORD style = LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_HEADERDRAGDROP;
ListView_SetExtendedListViewStyleEx( g_timeStampLog_hWndListView, style, style );
// populate list view
pNode = g_timeStampLog_pNodes;
while ( pNode )
{
TimeStampLog_AddViewItem( pNode );
pNode = pNode->pNext;
}
TimeStampLog_SortItems();
if ( g_timeStampLog_windowRect.right && g_timeStampLog_windowRect.bottom )
MoveWindow( g_timeStampLog_hWnd, g_timeStampLog_windowRect.left, g_timeStampLog_windowRect.top, g_timeStampLog_windowRect.right-g_timeStampLog_windowRect.left, g_timeStampLog_windowRect.bottom-g_timeStampLog_windowRect.top, FALSE );
ShowWindow( g_timeStampLog_hWnd, SHOW_OPENWINDOW );
}
//-----------------------------------------------------------------------------
// rc_TimeStampLog
//
// Sent from application with time stamp log
//-----------------------------------------------------------------------------
int rc_TimeStampLog( char* commandPtr )
{
char *cmdToken;
int timeStampAddr;
int retAddr;
int retVal;
int errCode = -1;
xrTimeStamp_t timeStamp;
// get data
cmdToken = GetToken( &commandPtr );
if ( !cmdToken[0] )
goto cleanUp;
sscanf( cmdToken, "%x", &timeStampAddr );
// get retAddr
cmdToken = GetToken( &commandPtr );
if ( !cmdToken[0] )
goto cleanUp;
sscanf( cmdToken, "%x", &retAddr );
// get the caller's data
DmGetMemory( ( void* )timeStampAddr, sizeof( xrTimeStamp_t ), &timeStamp, NULL );
// swap the structure
BigFloat( &timeStamp.time, &timeStamp.time );
BigFloat( &timeStamp.deltaTime, &timeStamp.deltaTime );
timeStamp.memory = BigDWord( timeStamp.memory );
timeStamp.deltaMemory = BigDWord( timeStamp.deltaMemory );
// add to log
TimeStampLog_AddItem( timeStamp.time, timeStamp.deltaTime, timeStamp.memory, timeStamp.deltaMemory, timeStamp.messageString );
TimeStampLog_SortItems();
// return the result
retVal = 0;
int xboxRetVal = BigDWord( retVal );
DmSetMemory( ( void* )retAddr, sizeof( int ), &xboxRetVal, NULL );
DebugCommand( "0x%8.8x = TimeStampLog( 0x%8.8x )\n", retVal, timeStampAddr );
// success
errCode = 0;
cleanUp:
return ( errCode );
}