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.

623 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // TIMESTAMP_LOG.CPP
  4. //
  5. // TimeStamp Log Display.
  6. //=====================================================================================//
  7. #include "vxconsole.h"
  8. #define ID_TIMESTAMPLOG_LISTVIEW 100
  9. // column id
  10. #define ID_TSL_TIME 0
  11. #define ID_TSL_DELTATIME 1
  12. #define ID_TSL_MEMORY 2
  13. #define ID_TSL_DELTAMEMORY 3
  14. #define ID_TSL_MESSAGE 4
  15. typedef struct
  16. { const CHAR* name;
  17. int width;
  18. int subItemIndex;
  19. CHAR nameBuff[32];
  20. } label_t;
  21. struct timeStampLogNode_t
  22. {
  23. int index;
  24. float time;
  25. float deltaTime;
  26. int memory;
  27. int deltaMemory;
  28. char timeBuff[32];
  29. char deltaTimeBuff[32];
  30. char memoryBuff[32];
  31. char deltaMemoryBuff[32];
  32. char *pMessage;
  33. timeStampLogNode_t *pNext;
  34. };
  35. HWND g_timeStampLog_hWnd;
  36. HWND g_timeStampLog_hWndListView;
  37. RECT g_timeStampLog_windowRect;
  38. timeStampLogNode_t *g_timeStampLog_pNodes;
  39. int g_timeStampLog_sortColumn;
  40. int g_timeStampLog_sortDescending;
  41. label_t g_timeStampLog_Labels[] =
  42. {
  43. {"Time", 100, ID_TSL_TIME},
  44. {"Delta Time", 100, ID_TSL_DELTATIME},
  45. {"Memory", 100, ID_TSL_MEMORY},
  46. {"Delta Memory", 100, ID_TSL_DELTAMEMORY},
  47. {"Message", 400, ID_TSL_MESSAGE},
  48. };
  49. //-----------------------------------------------------------------------------
  50. // TimeStampLog_CompareFunc
  51. //
  52. //-----------------------------------------------------------------------------
  53. int CALLBACK TimeStampLog_CompareFunc( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort )
  54. {
  55. timeStampLogNode_t* pNodeA = ( timeStampLogNode_t* )lParam1;
  56. timeStampLogNode_t* pNodeB = ( timeStampLogNode_t* )lParam2;
  57. int sort = 0;
  58. switch ( g_timeStampLog_sortColumn )
  59. {
  60. case ID_TSL_TIME:
  61. sort = ( int )( 1000.0f*pNodeA->time - 1000.0f*pNodeB->time );
  62. break;
  63. case ID_TSL_DELTATIME:
  64. sort = ( int )( 1000.0f*pNodeA->deltaTime - 1000.0f*pNodeB->deltaTime );
  65. break;
  66. case ID_TSL_MEMORY:
  67. sort = pNodeA->memory - pNodeB->memory;
  68. break;
  69. case ID_TSL_DELTAMEMORY:
  70. sort = pNodeA->deltaMemory - pNodeB->deltaMemory;
  71. break;
  72. case ID_TSL_MESSAGE:
  73. sort = stricmp( pNodeA->pMessage, pNodeB->pMessage );
  74. break;
  75. }
  76. // flip the sort order
  77. if ( g_timeStampLog_sortDescending )
  78. sort *= -1;
  79. return ( sort );
  80. }
  81. //-----------------------------------------------------------------------------
  82. // TimeStampLog_SortItems
  83. //
  84. //-----------------------------------------------------------------------------
  85. void TimeStampLog_SortItems()
  86. {
  87. LVITEM lvitem;
  88. timeStampLogNode_t *pNode;
  89. int i;
  90. if ( !g_timeStampLog_hWnd )
  91. {
  92. // only sort if window is visible
  93. return;
  94. }
  95. ListView_SortItems( g_timeStampLog_hWndListView, TimeStampLog_CompareFunc, 0 );
  96. memset( &lvitem, 0, sizeof( lvitem ) );
  97. lvitem.mask = LVIF_PARAM;
  98. // get each item and reset its list index
  99. int itemCount = ListView_GetItemCount( g_timeStampLog_hWndListView );
  100. for ( i=0; i<itemCount; i++ )
  101. {
  102. lvitem.iItem = i;
  103. ListView_GetItem( g_timeStampLog_hWndListView, &lvitem );
  104. pNode = ( timeStampLogNode_t* )lvitem.lParam;
  105. pNode->index = i;
  106. }
  107. // update list view columns with sort key
  108. for ( i=0; i<sizeof( g_timeStampLog_Labels )/sizeof( g_timeStampLog_Labels[0] ); i++ )
  109. {
  110. char symbol;
  111. LVCOLUMN lvc;
  112. if ( i == g_timeStampLog_sortColumn )
  113. symbol = g_timeStampLog_sortDescending ? '<' : '>';
  114. else
  115. symbol = ' ';
  116. sprintf( g_timeStampLog_Labels[i].nameBuff, "%s %c", g_timeStampLog_Labels[i].name, symbol );
  117. memset( &lvc, 0, sizeof( lvc ) );
  118. lvc.mask = LVCF_TEXT;
  119. lvc.pszText = ( LPSTR )g_timeStampLog_Labels[i].nameBuff;
  120. ListView_SetColumn( g_timeStampLog_hWndListView, i, &lvc );
  121. }
  122. }
  123. //-----------------------------------------------------------------------------
  124. // TimeStampLog_AddViewItem
  125. //
  126. //-----------------------------------------------------------------------------
  127. void TimeStampLog_AddViewItem( timeStampLogNode_t* pNode )
  128. {
  129. LVITEM lvi;
  130. if ( !g_timeStampLog_hWnd )
  131. {
  132. // only valid if log window is visible
  133. return;
  134. }
  135. // update the text callback buffers
  136. 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 );
  137. sprintf( pNode->deltaTimeBuff, "%.3f", pNode->deltaTime );
  138. sprintf( pNode->memoryBuff, "%.2f MB", pNode->memory/( 1024.0f*1024.0f ) );
  139. sprintf( pNode->deltaMemoryBuff, "%d", pNode->deltaMemory );
  140. int itemCount = ListView_GetItemCount( g_timeStampLog_hWndListView );
  141. // setup and insert at end of list
  142. memset( &lvi, 0, sizeof( lvi ) );
  143. lvi.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
  144. lvi.iItem = itemCount;
  145. lvi.iSubItem = 0;
  146. lvi.state = 0;
  147. lvi.stateMask = 0;
  148. lvi.pszText = LPSTR_TEXTCALLBACK;
  149. lvi.lParam = ( LPARAM )pNode;
  150. // insert
  151. pNode->index = ListView_InsertItem( g_timeStampLog_hWndListView, &lvi );
  152. }
  153. //-----------------------------------------------------------------------------
  154. // TimeStampLog_AddItem
  155. //
  156. //-----------------------------------------------------------------------------
  157. void TimeStampLog_AddItem( float time, float deltaTime, int memory, int deltaMemory, const char* pMessage )
  158. {
  159. timeStampLogNode_t* pNode;
  160. // create and init
  161. pNode = new timeStampLogNode_t;
  162. memset( pNode, 0, sizeof( timeStampLogNode_t ) );
  163. pNode->time = time;
  164. pNode->deltaTime = deltaTime;
  165. pNode->memory = memory;
  166. pNode->deltaMemory = deltaMemory;
  167. pNode->pMessage = Sys_CopyString( pMessage ? pMessage : "" );
  168. // link in
  169. pNode->pNext = g_timeStampLog_pNodes;
  170. g_timeStampLog_pNodes = pNode;
  171. TimeStampLog_AddViewItem( pNode );
  172. }
  173. //-----------------------------------------------------------------------------
  174. // TimeStampLog_Clear
  175. //
  176. //-----------------------------------------------------------------------------
  177. void TimeStampLog_Clear()
  178. {
  179. timeStampLogNode_t* pNode;
  180. timeStampLogNode_t* pNextNode;
  181. // delete all the list view entries
  182. if ( g_timeStampLog_hWnd )
  183. ListView_DeleteAllItems( g_timeStampLog_hWndListView );
  184. // delete nodes in chain
  185. pNode = g_timeStampLog_pNodes;
  186. while ( pNode )
  187. {
  188. pNextNode = pNode->pNext;
  189. Sys_Free( pNode->pMessage );
  190. delete pNode;
  191. pNode = pNextNode;
  192. }
  193. g_timeStampLog_pNodes = NULL;
  194. }
  195. //-----------------------------------------------------------------------------
  196. // TimeStampLog_SaveConfig
  197. //
  198. //-----------------------------------------------------------------------------
  199. void TimeStampLog_SaveConfig()
  200. {
  201. char buff[256];
  202. Sys_SetRegistryInteger( "timeStampLogSortColumn", g_timeStampLog_sortColumn );
  203. Sys_SetRegistryInteger( "timeStampLogSortDescending", g_timeStampLog_sortDescending );
  204. WINDOWPLACEMENT wp;
  205. memset( &wp, 0, sizeof( wp ) );
  206. wp.length = sizeof( WINDOWPLACEMENT );
  207. GetWindowPlacement( g_timeStampLog_hWnd, &wp );
  208. g_timeStampLog_windowRect = wp.rcNormalPosition;
  209. sprintf( buff, "%d %d %d %d", g_timeStampLog_windowRect.left, g_timeStampLog_windowRect.top, g_timeStampLog_windowRect.right, g_timeStampLog_windowRect.bottom );
  210. Sys_SetRegistryString( "timeStampLogWindowRect", buff );
  211. }
  212. //-----------------------------------------------------------------------------
  213. // TimeStampLog_LoadConfig
  214. //
  215. //-----------------------------------------------------------------------------
  216. void TimeStampLog_LoadConfig()
  217. {
  218. int numArgs;
  219. char buff[256];
  220. Sys_GetRegistryInteger( "timeStampLogSortColumn", ID_TSL_TIME, g_timeStampLog_sortColumn );
  221. Sys_GetRegistryInteger( "timeStampLogSortDescending", false, g_timeStampLog_sortDescending );
  222. Sys_GetRegistryString( "timeStampLogWindowRect", buff, "", sizeof( buff ) );
  223. numArgs = sscanf( buff, "%d %d %d %d", &g_timeStampLog_windowRect.left, &g_timeStampLog_windowRect.top, &g_timeStampLog_windowRect.right, &g_timeStampLog_windowRect.bottom );
  224. if ( numArgs != 4 || g_timeStampLog_windowRect.left < 0 || g_timeStampLog_windowRect.top < 0 || g_timeStampLog_windowRect.right < 0 || g_timeStampLog_windowRect.bottom < 0 )
  225. memset( &g_timeStampLog_windowRect, 0, sizeof( g_timeStampLog_windowRect ) );
  226. }
  227. //-----------------------------------------------------------------------------
  228. // TimeStampLog_SizeWindow
  229. //
  230. //-----------------------------------------------------------------------------
  231. void TimeStampLog_SizeWindow( HWND hwnd, int width, int height )
  232. {
  233. if ( width==0 || height==0 )
  234. {
  235. RECT rcClient;
  236. GetClientRect( hwnd, &rcClient );
  237. width = rcClient.right;
  238. height = rcClient.bottom;
  239. }
  240. // position the ListView
  241. SetWindowPos( g_timeStampLog_hWndListView, NULL, 0, 0, width, height, SWP_NOZORDER );
  242. }
  243. //-----------------------------------------------------------------------------
  244. // TimeStampLog_Export
  245. //
  246. //-----------------------------------------------------------------------------
  247. void TimeStampLog_Export()
  248. {
  249. OPENFILENAME ofn;
  250. char logFilename[MAX_PATH];
  251. int retval;
  252. FILE* fp;
  253. int i;
  254. int count;
  255. memset( &ofn, 0, sizeof( ofn ) );
  256. ofn.lStructSize = sizeof( ofn );
  257. ofn.hwndOwner = g_timeStampLog_hWnd;
  258. ofn.lpstrFile = logFilename;
  259. ofn.lpstrFile[0] = '\0';
  260. ofn.nMaxFile = sizeof( logFilename );
  261. ofn.lpstrFilter = "Excel CSV\0*.CSV\0All Files\0*.*\0";
  262. ofn.nFilterIndex = 1;
  263. ofn.lpstrFileTitle = NULL;
  264. ofn.nMaxFileTitle = 0;
  265. ofn.lpstrInitialDir = "c:\\";
  266. ofn.Flags = OFN_PATHMUSTEXIST;
  267. // display the open dialog box
  268. retval = GetOpenFileName( &ofn );
  269. if ( !retval )
  270. return;
  271. Sys_AddExtension( ".csv", logFilename, sizeof( logFilename ) );
  272. fp = fopen( logFilename, "wt+" );
  273. if ( !fp )
  274. return;
  275. // labels
  276. count = ARRAYSIZE( g_timeStampLog_Labels );
  277. for ( i=0; i<count; i++ )
  278. {
  279. fprintf( fp, "\"%s\"", g_timeStampLog_Labels[i].name );
  280. if ( i != count-1 )
  281. fprintf( fp, "," );
  282. }
  283. fprintf( fp, "\n" );
  284. // build a list for easy reverse traversal
  285. CUtlVector< timeStampLogNode_t* > nodeList;
  286. timeStampLogNode_t *pNode;
  287. pNode = g_timeStampLog_pNodes;
  288. while ( pNode )
  289. {
  290. nodeList.AddToTail( pNode );
  291. pNode = pNode->pNext;
  292. }
  293. // dump to the log
  294. for ( int i=nodeList.Count()-1; i>=0; i-- )
  295. {
  296. pNode = nodeList[i];
  297. fprintf( fp, "\"%s\"", pNode->timeBuff );
  298. fprintf( fp, ",\"%s\"", pNode->deltaTimeBuff );
  299. fprintf( fp, ",\"%s\"", pNode->memoryBuff );
  300. fprintf( fp, ",\"%s\"", pNode->deltaMemoryBuff );
  301. fprintf( fp, ",\"%s\"", pNode->pMessage );
  302. fprintf( fp, "\n" );
  303. }
  304. fclose( fp );
  305. }
  306. //-----------------------------------------------------------------------------
  307. // TimeStampLog_WndProc
  308. //
  309. //-----------------------------------------------------------------------------
  310. LRESULT CALLBACK TimeStampLog_WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
  311. {
  312. WORD wID = LOWORD( wParam );
  313. timeStampLogNode_t *pNode;
  314. switch ( message )
  315. {
  316. case WM_CREATE:
  317. return 0L;
  318. case WM_DESTROY:
  319. TimeStampLog_SaveConfig();
  320. g_timeStampLog_hWnd = NULL;
  321. return 0L;
  322. case WM_SIZE:
  323. TimeStampLog_SizeWindow( hwnd, LOWORD( lParam ), HIWORD( lParam ) );
  324. return 0L;
  325. case WM_NOTIFY:
  326. switch ( ( ( LPNMHDR )lParam )->code )
  327. {
  328. case LVN_COLUMNCLICK:
  329. NMLISTVIEW* pnmlv;
  330. pnmlv = ( NMLISTVIEW* )lParam;
  331. if ( g_timeStampLog_sortColumn == pnmlv->iSubItem )
  332. {
  333. // user has clicked on same column - flip the sort
  334. g_timeStampLog_sortDescending ^= 1;
  335. }
  336. else
  337. {
  338. // sort by new column
  339. g_timeStampLog_sortColumn = pnmlv->iSubItem;
  340. }
  341. TimeStampLog_SortItems();
  342. return 0L;
  343. case LVN_GETDISPINFO:
  344. NMLVDISPINFO* plvdi;
  345. plvdi = ( NMLVDISPINFO* )lParam;
  346. pNode = ( timeStampLogNode_t * )plvdi->item.lParam;
  347. switch ( plvdi->item.iSubItem )
  348. {
  349. case ID_TSL_TIME:
  350. plvdi->item.pszText = pNode->timeBuff;
  351. return 0L;
  352. case ID_TSL_DELTATIME:
  353. plvdi->item.pszText = pNode->deltaTimeBuff;
  354. return 0L;
  355. case ID_TSL_MEMORY:
  356. plvdi->item.pszText = pNode->memoryBuff;
  357. return 0L;
  358. case ID_TSL_DELTAMEMORY:
  359. plvdi->item.pszText = pNode->deltaMemoryBuff;
  360. return 0L;
  361. case ID_TSL_MESSAGE:
  362. plvdi->item.pszText = pNode->pMessage;
  363. return 0L;
  364. default:
  365. break;
  366. }
  367. break;
  368. }
  369. break;
  370. case WM_COMMAND:
  371. switch ( wID )
  372. {
  373. case IDM_OPTIONS_CLEAR:
  374. TimeStampLog_Clear();
  375. return 0L;
  376. case IDM_OPTIONS_EXPORT:
  377. TimeStampLog_Export();
  378. return 0L;
  379. }
  380. break;
  381. }
  382. return ( DefWindowProc( hwnd, message, wParam, lParam ) );
  383. }
  384. //-----------------------------------------------------------------------------
  385. // TimeStampLog_Init
  386. //
  387. //-----------------------------------------------------------------------------
  388. bool TimeStampLog_Init()
  389. {
  390. // set up our window class
  391. WNDCLASS wndclass;
  392. memset( &wndclass, 0, sizeof( wndclass ) );
  393. wndclass.style = 0;
  394. wndclass.lpfnWndProc = TimeStampLog_WndProc;
  395. wndclass.cbClsExtra = 0;
  396. wndclass.cbWndExtra = 0;
  397. wndclass.hInstance = g_hInstance;
  398. wndclass.hIcon = g_hIcons[ICON_APPLICATION];
  399. wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
  400. wndclass.hbrBackground = g_hBackgroundBrush;
  401. wndclass.lpszMenuName = MAKEINTRESOURCE( MENU_TIMESTAMPLOG );
  402. wndclass.lpszClassName = "TIMESTAMPLOGCLASS";
  403. if ( !RegisterClass( &wndclass ) )
  404. return false;
  405. TimeStampLog_LoadConfig();
  406. return true;
  407. }
  408. //-----------------------------------------------------------------------------
  409. // TimeStampLog_Open
  410. //
  411. //-----------------------------------------------------------------------------
  412. void TimeStampLog_Open()
  413. {
  414. RECT clientRect;
  415. HWND hWnd;
  416. int i;
  417. timeStampLogNode_t *pNode;
  418. if ( g_timeStampLog_hWnd )
  419. {
  420. // only one file log instance
  421. if ( IsIconic( g_timeStampLog_hWnd ) )
  422. ShowWindow( g_timeStampLog_hWnd, SW_RESTORE );
  423. SetForegroundWindow( g_timeStampLog_hWnd );
  424. return;
  425. }
  426. hWnd = CreateWindowEx(
  427. WS_EX_CLIENTEDGE,
  428. "TIMESTAMPLOGCLASS",
  429. "TimeStamp Log",
  430. WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_SIZEBOX|WS_MINIMIZEBOX|WS_MAXIMIZEBOX,
  431. 0,
  432. 0,
  433. 600,
  434. 300,
  435. g_hDlgMain,
  436. NULL,
  437. g_hInstance,
  438. NULL );
  439. g_timeStampLog_hWnd = hWnd;
  440. GetClientRect( g_timeStampLog_hWnd, &clientRect );
  441. hWnd = CreateWindow(
  442. WC_LISTVIEW,
  443. "",
  444. WS_VISIBLE|WS_CHILD|LVS_REPORT,
  445. 0,
  446. 0,
  447. clientRect.right-clientRect.left,
  448. clientRect.bottom-clientRect.top,
  449. g_timeStampLog_hWnd,
  450. ( HMENU )ID_TIMESTAMPLOG_LISTVIEW,
  451. g_hInstance,
  452. NULL );
  453. g_timeStampLog_hWndListView = hWnd;
  454. // init list view columns
  455. for ( i=0; i<sizeof( g_timeStampLog_Labels )/sizeof( g_timeStampLog_Labels[0] ); i++ )
  456. {
  457. LVCOLUMN lvc;
  458. memset( &lvc, 0, sizeof( lvc ) );
  459. lvc.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;
  460. lvc.iSubItem = 0;
  461. lvc.cx = g_timeStampLog_Labels[i].width;
  462. lvc.fmt = LVCFMT_LEFT;
  463. lvc.pszText = ( LPSTR )g_timeStampLog_Labels[i].name;
  464. ListView_InsertColumn( g_timeStampLog_hWndListView, i, &lvc );
  465. }
  466. ListView_SetBkColor( g_timeStampLog_hWndListView, g_backgroundColor );
  467. ListView_SetTextBkColor( g_timeStampLog_hWndListView, g_backgroundColor );
  468. DWORD style = LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_EX_HEADERDRAGDROP;
  469. ListView_SetExtendedListViewStyleEx( g_timeStampLog_hWndListView, style, style );
  470. // populate list view
  471. pNode = g_timeStampLog_pNodes;
  472. while ( pNode )
  473. {
  474. TimeStampLog_AddViewItem( pNode );
  475. pNode = pNode->pNext;
  476. }
  477. TimeStampLog_SortItems();
  478. if ( g_timeStampLog_windowRect.right && g_timeStampLog_windowRect.bottom )
  479. 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 );
  480. ShowWindow( g_timeStampLog_hWnd, SHOW_OPENWINDOW );
  481. }
  482. //-----------------------------------------------------------------------------
  483. // rc_TimeStampLog
  484. //
  485. // Sent from application with time stamp log
  486. //-----------------------------------------------------------------------------
  487. int rc_TimeStampLog( char* commandPtr )
  488. {
  489. char *cmdToken;
  490. int timeStampAddr;
  491. int retAddr;
  492. int retVal;
  493. int errCode = -1;
  494. xrTimeStamp_t timeStamp;
  495. // get data
  496. cmdToken = GetToken( &commandPtr );
  497. if ( !cmdToken[0] )
  498. goto cleanUp;
  499. sscanf( cmdToken, "%x", &timeStampAddr );
  500. // get retAddr
  501. cmdToken = GetToken( &commandPtr );
  502. if ( !cmdToken[0] )
  503. goto cleanUp;
  504. sscanf( cmdToken, "%x", &retAddr );
  505. // get the caller's data
  506. DmGetMemory( ( void* )timeStampAddr, sizeof( xrTimeStamp_t ), &timeStamp, NULL );
  507. // swap the structure
  508. BigFloat( &timeStamp.time, &timeStamp.time );
  509. BigFloat( &timeStamp.deltaTime, &timeStamp.deltaTime );
  510. timeStamp.memory = BigDWord( timeStamp.memory );
  511. timeStamp.deltaMemory = BigDWord( timeStamp.deltaMemory );
  512. // add to log
  513. TimeStampLog_AddItem( timeStamp.time, timeStamp.deltaTime, timeStamp.memory, timeStamp.deltaMemory, timeStamp.messageString );
  514. TimeStampLog_SortItems();
  515. // return the result
  516. retVal = 0;
  517. int xboxRetVal = BigDWord( retVal );
  518. DmSetMemory( ( void* )retAddr, sizeof( int ), &xboxRetVal, NULL );
  519. DebugCommand( "0x%8.8x = TimeStampLog( 0x%8.8x )\n", retVal, timeStampAddr );
  520. // success
  521. errCode = 0;
  522. cleanUp:
  523. return ( errCode );
  524. }