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.

2094 lines
55 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Real-Time Hierarchical Profiling
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "pch_tier0.h"
  8. #include "tier0/memalloc.h"
  9. #include "tier0/valve_off.h"
  10. #if defined(_WIN32) && !defined(_X360)
  11. #define WIN_32_LEAN_AND_MEAN
  12. #include <windows.h>
  13. #endif
  14. #include <assert.h>
  15. #ifdef _WIN32
  16. #pragma warning(disable:4073)
  17. #pragma init_seg( lib )
  18. #endif
  19. #pragma warning(push, 1)
  20. #pragma warning(disable:4786)
  21. #pragma warning(disable:4530)
  22. #include <map>
  23. #include <vector>
  24. #include <algorithm>
  25. #pragma warning(pop)
  26. #include "tier0/valve_on.h"
  27. #include "tier0/vprof.h"
  28. #include "tier0/l2cache.h"
  29. #include "tier0/tslist.h"
  30. #include "tier0/dynfunction.h"
  31. #ifdef _X360
  32. #include "xbox/xbox_console.h"
  33. #else // NOT _X360:
  34. #include "tier0/memdbgon.h"
  35. #endif
  36. // NOTE: Explicitly and intentionally using STL in here to not generate any
  37. // cyclical dependencies between the low-level debug library and the higher
  38. // level data structures (toml 01-27-03)
  39. using namespace std;
  40. #ifdef VPROF_ENABLED
  41. #if defined(_X360) && !defined(_CERT) // enable PIX CPU trace:
  42. #include "tracerecording.h"
  43. #pragma comment( lib, "tracerecording.lib" )
  44. #pragma comment( lib, "xbdm.lib" )
  45. #endif
  46. //-----------------------------------------------------------------------------
  47. bool g_VProfSignalSpike;
  48. //-----------------------------------------------------------------------------
  49. CVProfile g_VProfCurrentProfile;
  50. int CVProfNode::s_iCurrentUniqueNodeID = 0;
  51. CVProfNode::~CVProfNode()
  52. {
  53. #if !defined( _WIN32 ) && !defined( POSIX )
  54. delete m_pChild;
  55. delete m_pSibling;
  56. #endif
  57. }
  58. CVProfNode *CVProfNode::GetSubNode( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName, int budgetFlags )
  59. {
  60. // Try to find this sub node
  61. CVProfNode * child = m_pChild;
  62. while ( child )
  63. {
  64. if ( child->m_pszName == pszName )
  65. {
  66. return child;
  67. }
  68. child = child->m_pSibling;
  69. }
  70. // We didn't find it, so add it
  71. CVProfNode * node = new CVProfNode( pszName, detailLevel, this, pBudgetGroupName, budgetFlags );
  72. node->m_pSibling = m_pChild;
  73. m_pChild = node;
  74. return node;
  75. }
  76. CVProfNode *CVProfNode::GetSubNode( const tchar *pszName, int detailLevel, const tchar *pBudgetGroupName )
  77. {
  78. return GetSubNode( pszName, detailLevel, pBudgetGroupName, BUDGETFLAG_OTHER );
  79. }
  80. //-------------------------------------
  81. void CVProfNode::EnterScope()
  82. {
  83. m_nCurFrameCalls++;
  84. if ( m_nRecursions++ == 0 )
  85. {
  86. m_Timer.Start();
  87. #ifndef _X360
  88. if ( g_VProfCurrentProfile.UsePME() )
  89. {
  90. m_L2Cache.Start();
  91. }
  92. #else // 360 code:
  93. if ( g_VProfCurrentProfile.UsePME() || ((m_iBitFlags & kRecordL2) != 0) )
  94. {
  95. m_PMCData.Start();
  96. }
  97. if ( (m_iBitFlags & kCPUTrace) != 0)
  98. {
  99. // this node is to be recorded. Which recording mode are we in?
  100. switch ( g_VProfCurrentProfile.GetCPUTraceMode() )
  101. {
  102. case CVProfile::kFirstHitNode:
  103. case CVProfile::kAllNodesInFrame_Recording:
  104. case CVProfile::kAllNodesInFrame_RecordingMultiFrame:
  105. // we are presently recording.
  106. if ( !XTraceStartRecording( g_VProfCurrentProfile.GetCPUTraceFilename() ) )
  107. {
  108. Msg( "XTraceStartRecording failed, error code %d\n", GetLastError() );
  109. }
  110. default:
  111. // no default.
  112. break;
  113. }
  114. }
  115. #endif
  116. #ifdef VPROF_VTUNE_GROUP
  117. g_VProfCurrentProfile.PushGroup( m_BudgetGroupID );
  118. #endif
  119. }
  120. }
  121. //-------------------------------------
  122. bool CVProfNode::ExitScope()
  123. {
  124. if ( --m_nRecursions == 0 && m_nCurFrameCalls != 0 )
  125. {
  126. m_Timer.End();
  127. m_CurFrameTime += m_Timer.GetDuration();
  128. #ifndef _X360
  129. if ( g_VProfCurrentProfile.UsePME() )
  130. {
  131. m_L2Cache.End();
  132. m_iCurL2CacheMiss += m_L2Cache.GetL2CacheMisses();
  133. }
  134. #else // 360 code:
  135. if ( g_VProfCurrentProfile.UsePME() || ((m_iBitFlags & kRecordL2) != 0) )
  136. {
  137. m_PMCData.End();
  138. m_iCurL2CacheMiss += m_PMCData.GetL2CacheMisses();
  139. m_iCurLoadHitStores += m_PMCData.GetLHS();
  140. }
  141. if ( (m_iBitFlags & kCPUTrace) != 0 )
  142. {
  143. // this node is enabled to be recorded. What mode are we in?
  144. switch ( g_VProfCurrentProfile.GetCPUTraceMode() )
  145. {
  146. case CVProfile::kFirstHitNode:
  147. {
  148. // one-off recording. stop now.
  149. if ( XTraceStopRecording() )
  150. {
  151. Msg( "CPU trace finished.\n" );
  152. if ( g_VProfCurrentProfile.TraceCompleteEvent() )
  153. {
  154. // signal VXConsole that trace is completed
  155. XBX_rTraceComplete();
  156. }
  157. }
  158. // don't trace again next frame, overwriting the file.
  159. g_VProfCurrentProfile.SetCPUTraceEnabled( CVProfile::kDisabled );
  160. break;
  161. }
  162. case CVProfile::kAllNodesInFrame_Recording:
  163. case CVProfile::kAllNodesInFrame_RecordingMultiFrame:
  164. {
  165. // one-off recording. stop now.
  166. if ( XTraceStopRecording() )
  167. {
  168. if ( g_VProfCurrentProfile.GetCPUTraceMode() == CVProfile::kAllNodesInFrame_RecordingMultiFrame )
  169. {
  170. Msg( "%.3f msec in %s\n", m_CurFrameTime.GetMillisecondsF(), g_VProfCurrentProfile.GetCPUTraceFilename() );
  171. }
  172. else
  173. {
  174. Msg( "CPU trace finished.\n" );
  175. }
  176. }
  177. // Spew time info for file to allow figuring it out later
  178. g_VProfCurrentProfile.LatchMultiFrame( m_CurFrameTime.GetLongCycles() );
  179. #if 0 // This doesn't want to work on the xbox360-- MoveFile not available or file still being put down to disk?
  180. char suffix[ 32 ];
  181. _snprintf( suffix, sizeof( suffix ), "_%.3f_msecs", flMsecs );
  182. char fn[ 512 ];
  183. strncpy( fn, g_VProfCurrentProfile.GetCPUTraceFilename(), sizeof( fn ) );
  184. char *p = strrchr( fn, '.' );
  185. if ( *p )
  186. {
  187. *p = 0;
  188. }
  189. strncat( fn, suffix, sizeof( fn ) );
  190. strncat( fn, ".pix2", sizeof( fn ) );
  191. BOOL bSuccess = MoveFile( g_VProfCurrentProfile.GetCPUTraceFilename(), fn );
  192. if ( !bSuccess )
  193. {
  194. DWORD eCode = GetLastError();
  195. Msg( "Error %d\n", eCode );
  196. }
  197. #endif
  198. // we're still recording until the frame is done.
  199. // but, increment the index.
  200. g_VProfCurrentProfile.IncrementMultiTraceIndex();
  201. break;
  202. }
  203. }
  204. // g_VProfCurrentProfile.IsCPUTraceEnabled() &&
  205. }
  206. #endif
  207. #ifdef VPROF_VTUNE_GROUP
  208. g_VProfCurrentProfile.PopGroup();
  209. #endif
  210. }
  211. return ( m_nRecursions == 0 );
  212. }
  213. //-------------------------------------
  214. void CVProfNode::Pause()
  215. {
  216. if ( m_nRecursions > 0 )
  217. {
  218. m_Timer.End();
  219. m_CurFrameTime += m_Timer.GetDuration();
  220. #ifndef _X360
  221. if ( g_VProfCurrentProfile.UsePME() )
  222. {
  223. m_L2Cache.End();
  224. m_iCurL2CacheMiss += m_L2Cache.GetL2CacheMisses();
  225. }
  226. #else // 360 code:
  227. if ( g_VProfCurrentProfile.UsePME() || ((m_iBitFlags & kRecordL2) != 0) )
  228. {
  229. m_PMCData.End();
  230. m_iCurL2CacheMiss += m_PMCData.GetL2CacheMisses();
  231. m_iCurLoadHitStores += m_PMCData.GetLHS();
  232. }
  233. #endif
  234. }
  235. if ( m_pChild )
  236. {
  237. m_pChild->Pause();
  238. }
  239. if ( m_pSibling )
  240. {
  241. m_pSibling->Pause();
  242. }
  243. }
  244. //-------------------------------------
  245. void CVProfNode::Resume()
  246. {
  247. if ( m_nRecursions > 0 )
  248. {
  249. m_Timer.Start();
  250. #ifndef _X360
  251. if ( g_VProfCurrentProfile.UsePME() )
  252. {
  253. m_L2Cache.Start();
  254. }
  255. #else
  256. if ( g_VProfCurrentProfile.UsePME() || ((m_iBitFlags & kRecordL2) != 0) )
  257. {
  258. m_PMCData.Start();
  259. }
  260. #endif
  261. }
  262. if ( m_pChild )
  263. {
  264. m_pChild->Resume();
  265. }
  266. if ( m_pSibling )
  267. {
  268. m_pSibling->Resume();
  269. }
  270. }
  271. //-------------------------------------
  272. void CVProfNode::Reset()
  273. {
  274. m_nPrevFrameCalls = 0;
  275. m_PrevFrameTime.Init();
  276. m_nCurFrameCalls = 0;
  277. m_CurFrameTime.Init();
  278. m_nTotalCalls = 0;
  279. m_TotalTime.Init();
  280. m_PeakTime.Init();
  281. m_iPrevL2CacheMiss = 0;
  282. m_iCurL2CacheMiss = 0;
  283. m_iTotalL2CacheMiss = 0;
  284. #ifdef _X360
  285. m_iPrevLoadHitStores = 0;
  286. m_iCurLoadHitStores = 0;
  287. m_iTotalLoadHitStores = 0;
  288. #endif
  289. if ( m_pChild )
  290. {
  291. m_pChild->Reset();
  292. }
  293. if ( m_pSibling )
  294. {
  295. m_pSibling->Reset();
  296. }
  297. }
  298. //-------------------------------------
  299. void CVProfNode::MarkFrame()
  300. {
  301. m_nPrevFrameCalls = m_nCurFrameCalls;
  302. m_PrevFrameTime = m_CurFrameTime;
  303. m_iPrevL2CacheMiss = m_iCurL2CacheMiss;
  304. #ifdef _X360
  305. m_iPrevLoadHitStores = m_iCurLoadHitStores;
  306. #endif
  307. m_nTotalCalls += m_nCurFrameCalls;
  308. m_TotalTime += m_CurFrameTime;
  309. if ( m_PeakTime.IsLessThan( m_CurFrameTime ) )
  310. {
  311. m_PeakTime = m_CurFrameTime;
  312. }
  313. m_CurFrameTime.Init();
  314. m_nCurFrameCalls = 0;
  315. m_iTotalL2CacheMiss += m_iCurL2CacheMiss;
  316. m_iCurL2CacheMiss = 0;
  317. #ifdef _X360
  318. m_iTotalLoadHitStores += m_iCurLoadHitStores;
  319. m_iCurLoadHitStores = 0;
  320. #endif
  321. if ( m_pChild )
  322. {
  323. m_pChild->MarkFrame();
  324. }
  325. if ( m_pSibling )
  326. {
  327. m_pSibling->MarkFrame();
  328. }
  329. }
  330. //-------------------------------------
  331. void CVProfNode::ResetPeak()
  332. {
  333. m_PeakTime.Init();
  334. if ( m_pChild )
  335. {
  336. m_pChild->ResetPeak();
  337. }
  338. if ( m_pSibling )
  339. {
  340. m_pSibling->ResetPeak();
  341. }
  342. }
  343. void CVProfNode::SetCurFrameTime( unsigned long milliseconds )
  344. {
  345. m_CurFrameTime.Init( (float)milliseconds );
  346. }
  347. #ifdef DBGFLAG_VALIDATE
  348. //-----------------------------------------------------------------------------
  349. // Purpose: Ensure that all of our internal structures are consistent, and
  350. // account for all memory that we've allocated.
  351. // Input: validator - Our global validator object
  352. // pchName - Our name (typically a member var in our container)
  353. //-----------------------------------------------------------------------------
  354. void CVProfNode::Validate( CValidator &validator, tchar *pchName )
  355. {
  356. validator.Push( _T("CVProfNode"), this, pchName );
  357. m_L2Cache.Validate( validator, _T("m_L2Cache") );
  358. if ( m_pSibling )
  359. m_pSibling->Validate( validator, _T("m_pSibling") );
  360. if ( m_pChild )
  361. m_pChild->Validate( validator, _T("m_pChild") );
  362. validator.Pop( );
  363. }
  364. #endif // DBGFLAG_VALIDATE
  365. //-----------------------------------------------------------------------------
  366. struct TimeSums_t
  367. {
  368. const tchar *pszProfileScope;
  369. int calls;
  370. double time;
  371. double timeLessChildren;
  372. double peak;
  373. };
  374. static bool TimeCompare( const TimeSums_t &lhs, const TimeSums_t &rhs )
  375. {
  376. return ( lhs.time > rhs.time );
  377. }
  378. static bool TimeLessChildrenCompare( const TimeSums_t &lhs, const TimeSums_t &rhs )
  379. {
  380. return ( lhs.timeLessChildren > rhs.timeLessChildren );
  381. }
  382. static bool PeakCompare( const TimeSums_t &lhs, const TimeSums_t &rhs )
  383. {
  384. return ( lhs.peak > rhs.peak );
  385. }
  386. static bool AverageTimeCompare( const TimeSums_t &lhs, const TimeSums_t &rhs )
  387. {
  388. double avgLhs = ( lhs.calls ) ? lhs.time / (double)lhs.calls : 0.0;
  389. double avgRhs = ( rhs.calls ) ? rhs.time / (double)rhs.calls : 0.0;
  390. return ( avgLhs > avgRhs );
  391. }
  392. static bool AverageTimeLessChildrenCompare( const TimeSums_t &lhs, const TimeSums_t &rhs )
  393. {
  394. double avgLhs = ( lhs.calls ) ? lhs.timeLessChildren / (double)lhs.calls : 0.0;
  395. double avgRhs = ( rhs.calls ) ? rhs.timeLessChildren / (double)rhs.calls : 0.0;
  396. return ( avgLhs > avgRhs );
  397. }
  398. static bool PeakOverAverageCompare( const TimeSums_t &lhs, const TimeSums_t &rhs )
  399. {
  400. double avgLhs = ( lhs.calls ) ? lhs.time / (double)lhs.calls : 0.0;
  401. double avgRhs = ( rhs.calls ) ? rhs.time / (double)rhs.calls : 0.0;
  402. double lhsPoA = ( avgLhs != 0 ) ? lhs.peak / avgLhs : 0.0;
  403. double rhsPoA = ( avgRhs != 0 ) ? rhs.peak / avgRhs : 0.0;
  404. return ( lhsPoA > rhsPoA );
  405. }
  406. map<CVProfNode *, double> g_TimesLessChildren;
  407. int g_TotalFrames;
  408. map<const tchar *, size_t> g_TimeSumsMap;
  409. vector<TimeSums_t> g_TimeSums;
  410. CVProfNode * g_pStartNode;
  411. const tchar * g_pszSumNode;
  412. //-------------------------------------
  413. void CVProfile::SumTimes( CVProfNode *pNode, int budgetGroupID )
  414. {
  415. if ( !pNode )
  416. return; // this generally only happens on a failed FindNode()
  417. bool bSetStartNode;
  418. if ( !g_pStartNode && _tcscmp( pNode->GetName(), g_pszSumNode ) == 0 )
  419. {
  420. g_pStartNode = pNode;
  421. bSetStartNode = true;
  422. }
  423. else
  424. bSetStartNode = false;
  425. if ( GetRoot() != pNode )
  426. {
  427. if ( g_pStartNode && pNode->GetTotalCalls() > 0 && ( budgetGroupID == -1 || pNode->GetBudgetGroupID() == budgetGroupID ) )
  428. {
  429. double timeLessChildren = pNode->GetTotalTimeLessChildren();
  430. g_TimesLessChildren.insert( make_pair( pNode, timeLessChildren ) );
  431. map<const tchar *, size_t>::iterator iter;
  432. iter = g_TimeSumsMap.find( pNode->GetName() ); // intenionally using address of string rather than string compare (toml 01-27-03)
  433. if ( iter == g_TimeSumsMap.end() )
  434. {
  435. TimeSums_t timeSums = { pNode->GetName(), pNode->GetTotalCalls(), pNode->GetTotalTime(), timeLessChildren, pNode->GetPeakTime() };
  436. g_TimeSumsMap.insert( make_pair( pNode->GetName(), g_TimeSums.size() ) );
  437. g_TimeSums.push_back( timeSums );
  438. }
  439. else
  440. {
  441. TimeSums_t &timeSums = g_TimeSums[iter->second];
  442. timeSums.calls += pNode->GetTotalCalls();
  443. timeSums.time += pNode->GetTotalTime();
  444. timeSums.timeLessChildren += timeLessChildren;
  445. if ( pNode->GetPeakTime() > timeSums.peak )
  446. timeSums.peak = pNode->GetPeakTime();
  447. }
  448. }
  449. if( ( !g_pStartNode || pNode != g_pStartNode ) && pNode->GetSibling() )
  450. {
  451. SumTimes( pNode->GetSibling(), budgetGroupID );
  452. }
  453. }
  454. if( pNode->GetChild() )
  455. {
  456. SumTimes( pNode->GetChild(), budgetGroupID );
  457. }
  458. if ( bSetStartNode )
  459. g_pStartNode = NULL;
  460. }
  461. //-------------------------------------
  462. CVProfNode *CVProfile::FindNode( CVProfNode *pStartNode, const tchar *pszNode )
  463. {
  464. if ( _tcscmp( pStartNode->GetName(), pszNode ) != 0 )
  465. {
  466. CVProfNode *pFoundNode = NULL;
  467. if ( pStartNode->GetSibling() )
  468. {
  469. pFoundNode = FindNode( pStartNode->GetSibling(), pszNode );
  470. }
  471. if ( !pFoundNode && pStartNode->GetChild() )
  472. {
  473. pFoundNode = FindNode( pStartNode->GetChild(), pszNode );
  474. }
  475. return pFoundNode;
  476. }
  477. return pStartNode;
  478. }
  479. //-------------------------------------
  480. #ifdef _X360
  481. void CVProfile::PMCDisableAllNodes(CVProfNode *pStartNode)
  482. {
  483. if (pStartNode == NULL)
  484. {
  485. pStartNode = GetRoot();
  486. }
  487. pStartNode->EnableL2andLHS(false);
  488. if ( pStartNode->GetSibling() )
  489. {
  490. PMCDisableAllNodes(pStartNode->GetSibling());
  491. }
  492. if ( pStartNode->GetChild() )
  493. {
  494. PMCDisableAllNodes(pStartNode->GetChild());
  495. }
  496. }
  497. // recursively set l2/lhs recording state for a node and all children AND SIBLINGS
  498. static void PMCRecursiveL2Set(CVProfNode *pNode, bool enableState)
  499. {
  500. if ( pNode )
  501. {
  502. pNode->EnableL2andLHS(enableState);
  503. if ( pNode->GetSibling() )
  504. {
  505. PMCRecursiveL2Set( pNode->GetSibling(), enableState );
  506. }
  507. if ( pNode->GetChild() )
  508. {
  509. PMCRecursiveL2Set( pNode->GetChild(), enableState );
  510. }
  511. }
  512. }
  513. bool CVProfile::PMCEnableL2Upon(const tchar *pszNodeName, bool bRecursive)
  514. {
  515. // PMCDisableAllNodes();
  516. CVProfNode *pNode = FindNode( GetRoot(), pszNodeName );
  517. if (pNode)
  518. {
  519. pNode->EnableL2andLHS(true);
  520. if (bRecursive)
  521. {
  522. PMCRecursiveL2Set(pNode->GetChild(), true);
  523. }
  524. return true;
  525. }
  526. else
  527. {
  528. return false;
  529. }
  530. }
  531. bool CVProfile::PMCDisableL2Upon(const tchar *pszNodeName, bool bRecursive)
  532. {
  533. // PMCDisableAllNodes();
  534. CVProfNode *pNode = FindNode( GetRoot(), pszNodeName );
  535. if ( pNode )
  536. {
  537. pNode->EnableL2andLHS( false );
  538. if ( bRecursive )
  539. {
  540. PMCRecursiveL2Set( pNode->GetChild(), false );
  541. }
  542. return true;
  543. }
  544. else
  545. {
  546. return false;
  547. }
  548. }
  549. static void DumpEnabledPMCNodesInner(CVProfNode* pNode)
  550. {
  551. if (!pNode)
  552. return;
  553. if (pNode->IsL2andLHSEnabled())
  554. {
  555. Msg( _T("\t%s\n"), pNode->GetName() );
  556. }
  557. // depth first printing clearer
  558. if ( pNode->GetChild() )
  559. {
  560. DumpEnabledPMCNodesInner(pNode->GetChild());
  561. }
  562. if ( pNode->GetSibling() )
  563. {
  564. DumpEnabledPMCNodesInner(pNode->GetChild());
  565. }
  566. }
  567. void CVProfile::DumpEnabledPMCNodes( void )
  568. {
  569. Msg( _T("Nodes enabled for PMC counters:\n") );
  570. CVProfNode *pNode = GetRoot();
  571. DumpEnabledPMCNodesInner( pNode );
  572. Msg( _T("(end)\n") );
  573. }
  574. CVProfNode *CVProfile::CPUTraceGetEnabledNode(CVProfNode *pStartNode)
  575. {
  576. if (!pStartNode)
  577. {
  578. pStartNode = GetRoot();
  579. }
  580. if ( (pStartNode->m_iBitFlags & CVProfNode::kCPUTrace) != 0 )
  581. {
  582. return pStartNode;
  583. }
  584. if (pStartNode->GetSibling())
  585. {
  586. CVProfNode *retval = CPUTraceGetEnabledNode(pStartNode->GetSibling());
  587. if (retval)
  588. return retval;
  589. }
  590. if (pStartNode->GetChild())
  591. {
  592. CVProfNode *retval = CPUTraceGetEnabledNode(pStartNode->GetChild());
  593. if (retval)
  594. return retval;
  595. }
  596. return NULL;
  597. }
  598. const char *CVProfile::SetCPUTraceFilename( const char *filename )
  599. {
  600. strncpy( m_CPUTraceFilename, filename, sizeof( m_CPUTraceFilename ) );
  601. return GetCPUTraceFilename();
  602. }
  603. /// Returns a pointer to an internal static, so you don't need to
  604. /// make temporary char buffers for this to write into. What of it?
  605. /// You're not hanging on to that pointer. That would be foolish.
  606. const char *CVProfile::GetCPUTraceFilename()
  607. {
  608. static char retBuf[256];
  609. switch ( m_iCPUTraceEnabled )
  610. {
  611. case kAllNodesInFrame_WaitingForMark:
  612. case kAllNodesInFrame_Recording:
  613. _snprintf( retBuf, sizeof( retBuf ), "e:\\%.128s%.4d.pix2", m_CPUTraceFilename, m_iSuccessiveTraceIndex );
  614. break;
  615. case kAllNodesInFrame_WaitingForMarkMultiFrame:
  616. case kAllNodesInFrame_RecordingMultiFrame:
  617. _snprintf( retBuf, sizeof( retBuf ), "e:\\%.128s_%.4d_%.4d.pix2", m_CPUTraceFilename, m_nFrameCount, m_iSuccessiveTraceIndex );
  618. break;
  619. default:
  620. _snprintf( retBuf, sizeof( retBuf ), "e:\\%.128s.pix2", m_CPUTraceFilename );
  621. }
  622. return retBuf;
  623. }
  624. bool CVProfile::TraceCompleteEvent( void )
  625. {
  626. return m_bTraceCompleteEvent;
  627. }
  628. CVProfNode *CVProfile::CPUTraceEnableForNode(const tchar *pszNodeName)
  629. {
  630. // disable whatever may be enabled already (we can only trace one node at a time)
  631. CPUTraceDisableAllNodes();
  632. CVProfNode *which = FindNode(GetRoot(), pszNodeName);
  633. if (which)
  634. {
  635. which->m_iBitFlags |= CVProfNode::kCPUTrace;
  636. return which;
  637. }
  638. else
  639. return NULL;
  640. }
  641. void CVProfile::CPUTraceDisableAllNodes(CVProfNode *pStartNode)
  642. {
  643. if (!pStartNode)
  644. {
  645. pStartNode = GetRoot();
  646. }
  647. pStartNode->m_iBitFlags &= ~CVProfNode::kCPUTrace;
  648. if (pStartNode->GetSibling())
  649. {
  650. CPUTraceDisableAllNodes(pStartNode->GetSibling());
  651. }
  652. if (pStartNode->GetChild())
  653. {
  654. CPUTraceDisableAllNodes(pStartNode->GetChild());
  655. }
  656. }
  657. #endif
  658. //-------------------------------------
  659. void CVProfile::SumTimes( const tchar *pszStartNode, int budgetGroupID )
  660. {
  661. if ( GetRoot()->GetChild() )
  662. {
  663. if ( pszStartNode == NULL )
  664. g_pStartNode = GetRoot();
  665. else
  666. g_pStartNode = NULL;
  667. g_pszSumNode = pszStartNode;
  668. SumTimes( GetRoot(), budgetGroupID );
  669. g_pStartNode = NULL;
  670. }
  671. }
  672. //-------------------------------------
  673. // This array lets us generate the commonly used indent levels
  674. // without looping. That then lets us print our vprof nodes
  675. // in a single call, which is more efficient and works better
  676. // with output streams like ETW where each call represents a
  677. // 'line' of text. Indent levels beyond what is represented
  678. // in this array are, regretfully, clamped, however the highest
  679. // indent level seen in testing was 10.
  680. static const char* s_indentText[] =
  681. {
  682. "", // 0
  683. "", // 1
  684. "| ", // 2
  685. "| | ", // 3
  686. "| | | ", // 4
  687. "| | | | ", // 5
  688. "| | | | | ", // 6
  689. "| | | | | | ", // 7
  690. "| | | | | | | ", // 8
  691. "| | | | | | | | ", // 9
  692. "| | | | | | | | | ", // 10
  693. "| | | | | | | | | | ", // 11
  694. "| | | | | | | | | | | ", // 12
  695. "| | | | | | | | | | | | ", // 13
  696. "| | | | | | | | | | | | | ", // 14
  697. };
  698. void CVProfile::DumpNodes( CVProfNode *pNode, int indent, bool bAverageAndCountOnly )
  699. {
  700. if ( !pNode )
  701. return; // this generally only happens on a failed FindNode()
  702. bool fIsRoot = ( pNode == GetRoot() );
  703. if ( fIsRoot || pNode == g_pStartNode )
  704. {
  705. if( bAverageAndCountOnly )
  706. {
  707. m_pOutputStream( _T(" Avg Time/Frame (ms)\n") );
  708. m_pOutputStream( _T("[ func+child func ] Count\n") );
  709. m_pOutputStream( _T(" ---------- ------ ------\n") );
  710. }
  711. else
  712. {
  713. m_pOutputStream( _T(" Sum (ms) Avg Time/Frame (ms) Avg Time/Call (ms)\n") );
  714. m_pOutputStream( _T("[ func+child func ] [ func+child func ] [ func+child func ] Count Peak\n") );
  715. m_pOutputStream( _T(" ---------- ------ ---------- ------ ---------- ------ ------ ------\n") );
  716. }
  717. }
  718. if ( !fIsRoot )
  719. {
  720. map<CVProfNode *, double>::iterator iterTimeLessChildren = g_TimesLessChildren.find( pNode );
  721. indent = Max( indent, 0 );
  722. indent = Min( indent, (int)ARRAYSIZE( s_indentText ) - 1 );
  723. const char* indentText = s_indentText[ indent ];
  724. double dNodeTime = 0;
  725. if(iterTimeLessChildren != g_TimesLessChildren.end())
  726. dNodeTime = iterTimeLessChildren->second;
  727. if( bAverageAndCountOnly )
  728. {
  729. m_pOutputStream( _T(" %10.3f %6.2f %6d %s%s\n"),
  730. ( pNode->GetTotalCalls() > 0 ) ? pNode->GetTotalTime() / (double)NumFramesSampled() : 0,
  731. ( pNode->GetTotalCalls() > 0 ) ? dNodeTime / (double)NumFramesSampled() : 0,
  732. pNode->GetTotalCalls(), indentText, pNode->GetName() );
  733. }
  734. else
  735. {
  736. m_pOutputStream( _T(" %10.3f %6.2f %10.3f %6.2f %10.3f %6.2f %6d %6.2f %s%s\n"),
  737. pNode->GetTotalTime(), dNodeTime,
  738. ( pNode->GetTotalCalls() > 0 ) ? pNode->GetTotalTime() / (double)NumFramesSampled() : 0,
  739. ( pNode->GetTotalCalls() > 0 ) ? dNodeTime / (double)NumFramesSampled() : 0,
  740. ( pNode->GetTotalCalls() > 0 ) ? pNode->GetTotalTime() / (double)pNode->GetTotalCalls() : 0,
  741. ( pNode->GetTotalCalls() > 0 ) ? dNodeTime / (double)pNode->GetTotalCalls() : 0,
  742. pNode->GetTotalCalls(), pNode->GetPeakTime(), indentText, pNode->GetName() );
  743. }
  744. }
  745. if( pNode->GetChild() )
  746. {
  747. DumpNodes( pNode->GetChild(), indent + 1, bAverageAndCountOnly );
  748. }
  749. if( !( fIsRoot || pNode == g_pStartNode ) && pNode->GetSibling() )
  750. {
  751. DumpNodes( pNode->GetSibling(), indent, bAverageAndCountOnly );
  752. }
  753. }
  754. //-------------------------------------
  755. #if defined( _X360 )
  756. static void CalcBudgetGroupTimes_Recursive( CVProfNode *pNode, unsigned int *groupTimes, int numGroups, float flScale )
  757. {
  758. int groupID;
  759. CVProfNode *nodePtr;
  760. groupID = pNode->GetBudgetGroupID();
  761. if ( groupID >= numGroups )
  762. {
  763. return;
  764. }
  765. groupTimes[groupID] += flScale*pNode->GetPrevTimeLessChildren();
  766. nodePtr = pNode->GetSibling();
  767. if ( nodePtr )
  768. {
  769. CalcBudgetGroupTimes_Recursive( nodePtr, groupTimes, numGroups, flScale );
  770. }
  771. nodePtr = pNode->GetChild();
  772. if ( nodePtr )
  773. {
  774. CalcBudgetGroupTimes_Recursive( nodePtr, groupTimes, numGroups, flScale );
  775. }
  776. }
  777. static void CalcBudgetGroupL2CacheMisses_Recursive( CVProfNode *pNode, unsigned int *groupTimes, int numGroups, float flScale )
  778. {
  779. int groupID;
  780. CVProfNode *nodePtr;
  781. groupID = pNode->GetBudgetGroupID();
  782. if ( groupID >= numGroups )
  783. {
  784. return;
  785. }
  786. groupTimes[groupID] += flScale*pNode->GetPrevL2CacheMissLessChildren();
  787. nodePtr = pNode->GetSibling();
  788. if ( nodePtr )
  789. {
  790. CalcBudgetGroupL2CacheMisses_Recursive( nodePtr, groupTimes, numGroups, flScale );
  791. }
  792. nodePtr = pNode->GetChild();
  793. if ( nodePtr )
  794. {
  795. CalcBudgetGroupL2CacheMisses_Recursive( nodePtr, groupTimes, numGroups, flScale );
  796. }
  797. }
  798. static void CalcBudgetGroupLHS_Recursive( CVProfNode *pNode, unsigned int *groupTimes, int numGroups, float flScale )
  799. {
  800. int groupID;
  801. CVProfNode *nodePtr;
  802. groupID = pNode->GetBudgetGroupID();
  803. if ( groupID >= numGroups )
  804. {
  805. return;
  806. }
  807. groupTimes[groupID] += flScale*pNode->GetPrevLoadHitStoreLessChildren();
  808. nodePtr = pNode->GetSibling();
  809. if ( nodePtr )
  810. {
  811. CalcBudgetGroupLHS_Recursive( nodePtr, groupTimes, numGroups, flScale );
  812. }
  813. nodePtr = pNode->GetChild();
  814. if ( nodePtr )
  815. {
  816. CalcBudgetGroupLHS_Recursive( nodePtr, groupTimes, numGroups, flScale );
  817. }
  818. }
  819. void CVProfile::VXConsoleReportMode( VXConsoleReportMode_t mode )
  820. {
  821. m_ReportMode = mode;
  822. }
  823. void CVProfile::VXConsoleReportScale( VXConsoleReportMode_t mode, float flScale )
  824. {
  825. m_pReportScale[mode] = flScale;
  826. }
  827. //-----------------------------------------------------------------------------
  828. // Send the all the counter attributes once to VXConsole at profiling start
  829. //-----------------------------------------------------------------------------
  830. void CVProfile::VXProfileStart()
  831. {
  832. const char *names[XBX_MAX_PROFILE_COUNTERS];
  833. unsigned int colors[XBX_MAX_PROFILE_COUNTERS];
  834. int numGroups;
  835. int counterGroup;
  836. const char *pGroupName;
  837. int i;
  838. int r,g,b,a;
  839. // vprof system must be running
  840. if ( m_enabled <= 0 || !m_UpdateMode )
  841. {
  842. return;
  843. }
  844. if ( m_UpdateMode & VPROF_UPDATE_BUDGET )
  845. {
  846. // update budget profiling
  847. numGroups = g_VProfCurrentProfile.GetNumBudgetGroups();
  848. if ( numGroups > XBX_MAX_PROFILE_COUNTERS )
  849. {
  850. numGroups = XBX_MAX_PROFILE_COUNTERS;
  851. }
  852. for ( i=0; i<numGroups; i++ )
  853. {
  854. names[i] = g_VProfCurrentProfile.GetBudgetGroupName( i );
  855. g_VProfCurrentProfile.GetBudgetGroupColor( i, r, g, b, a );
  856. colors[i] = XMAKECOLOR( r, g, b );
  857. }
  858. // send all the profile attributes
  859. XBX_rSetProfileAttributes( "cpu", numGroups, names, colors );
  860. }
  861. if ( m_UpdateMode & (VPROF_UPDATE_TEXTURE_GLOBAL|VPROF_UPDATE_TEXTURE_PERFRAME) )
  862. {
  863. // update texture profiling
  864. numGroups = 0;
  865. counterGroup = (m_UpdateMode & VPROF_UPDATE_TEXTURE_GLOBAL) ? COUNTER_GROUP_TEXTURE_GLOBAL : COUNTER_GROUP_TEXTURE_PER_FRAME;
  866. for ( i=0; i<g_VProfCurrentProfile.GetNumCounters(); i++ )
  867. {
  868. if ( g_VProfCurrentProfile.GetCounterGroup( i ) == counterGroup )
  869. {
  870. // strip undesired prefix
  871. pGroupName = g_VProfCurrentProfile.GetCounterName( i );
  872. if ( !strnicmp( pGroupName, "texgroup_frame_", 15 ) )
  873. {
  874. pGroupName += 15;
  875. }
  876. else if ( !strnicmp( pGroupName, "texgroup_global_", 16 ) )
  877. {
  878. pGroupName += 16;
  879. }
  880. names[numGroups] = pGroupName;
  881. g_VProfCurrentProfile.GetBudgetGroupColor( numGroups, r, g, b, a );
  882. colors[numGroups] = XMAKECOLOR( r, g, b );
  883. numGroups++;
  884. if ( numGroups == XBX_MAX_PROFILE_COUNTERS )
  885. {
  886. break;
  887. }
  888. }
  889. }
  890. // send all the profile attributes
  891. XBX_rSetProfileAttributes( "texture", numGroups, names, colors );
  892. }
  893. }
  894. //-----------------------------------------------------------------------------
  895. // Send the counters to VXConsole
  896. //-----------------------------------------------------------------------------
  897. void CVProfile::VXProfileUpdate()
  898. {
  899. int i;
  900. int counterGroup;
  901. int numGroups;
  902. unsigned int groupData[XBX_MAX_PROFILE_COUNTERS];
  903. // vprof system must be running
  904. if ( m_enabled <= 0 || !m_UpdateMode )
  905. {
  906. return;
  907. }
  908. if ( m_UpdateMode & VPROF_UPDATE_BUDGET )
  909. {
  910. // send the cpu counters
  911. numGroups = g_VProfCurrentProfile.GetNumBudgetGroups();
  912. if ( numGroups > XBX_MAX_PROFILE_COUNTERS )
  913. {
  914. numGroups = XBX_MAX_PROFILE_COUNTERS;
  915. }
  916. memset( groupData, 0, numGroups * sizeof( unsigned int ) );
  917. CVProfNode *pNode = g_VProfCurrentProfile.GetRoot();
  918. if ( pNode && pNode->GetChild() )
  919. {
  920. switch ( m_ReportMode )
  921. {
  922. default:
  923. case VXCONSOLE_REPORT_TIME:
  924. CalcBudgetGroupTimes_Recursive( pNode->GetChild(), groupData, numGroups, m_pReportScale[VXCONSOLE_REPORT_TIME] );
  925. break;
  926. case VXCONSOLE_REPORT_L2CACHE_MISSES:
  927. CalcBudgetGroupL2CacheMisses_Recursive( pNode->GetChild(), groupData, numGroups, m_pReportScale[VXCONSOLE_REPORT_L2CACHE_MISSES] );
  928. break;
  929. case VXCONSOLE_REPORT_LOAD_HIT_STORE:
  930. CalcBudgetGroupLHS_Recursive( pNode->GetChild(), groupData, numGroups, m_pReportScale[VXCONSOLE_REPORT_LOAD_HIT_STORE] );
  931. break;
  932. }
  933. }
  934. XBX_rSetProfileData( "cpu", numGroups, groupData );
  935. }
  936. if ( m_UpdateMode & ( VPROF_UPDATE_TEXTURE_GLOBAL|VPROF_UPDATE_TEXTURE_PERFRAME ) )
  937. {
  938. // send the texture counters
  939. numGroups = 0;
  940. counterGroup = ( m_UpdateMode & VPROF_UPDATE_TEXTURE_GLOBAL ) ? COUNTER_GROUP_TEXTURE_GLOBAL : COUNTER_GROUP_TEXTURE_PER_FRAME;
  941. for ( i = 0; i < g_VProfCurrentProfile.GetNumCounters(); i++ )
  942. {
  943. if ( g_VProfCurrentProfile.GetCounterGroup( i ) == counterGroup )
  944. {
  945. // get the size in bytes
  946. groupData[numGroups++] = g_VProfCurrentProfile.GetCounterValue( i );
  947. if ( numGroups == XBX_MAX_PROFILE_COUNTERS )
  948. {
  949. break;
  950. }
  951. }
  952. }
  953. XBX_rSetProfileData( "texture", numGroups, groupData );
  954. }
  955. }
  956. void CVProfile::VXEnableUpdateMode( int event, bool bEnable )
  957. {
  958. // enable or disable the updating of specified events
  959. if ( bEnable )
  960. {
  961. m_UpdateMode |= event;
  962. }
  963. else
  964. {
  965. m_UpdateMode &= ~event;
  966. }
  967. // force a resend of possibly affected attributes
  968. VXProfileStart();
  969. }
  970. #define MAX_VPROF_NODES_IN_LIST 4096
  971. static void VXBuildNodeList_r( CVProfNode *pNode, xVProfNodeItem_t *pNodeList, int *pNumNodes )
  972. {
  973. if ( !pNode )
  974. {
  975. return;
  976. }
  977. if ( *pNumNodes >= MAX_VPROF_NODES_IN_LIST )
  978. {
  979. // list full
  980. return;
  981. }
  982. // add to list
  983. pNodeList[*pNumNodes].pName = (const char *)pNode->GetName();
  984. pNodeList[*pNumNodes].pBudgetGroupName = g_VProfCurrentProfile.GetBudgetGroupName( pNode->GetBudgetGroupID() );
  985. int r, g, b, a;
  986. g_VProfCurrentProfile.GetBudgetGroupColor( pNode->GetBudgetGroupID(), r, g, b, a );
  987. pNodeList[*pNumNodes].budgetGroupColor = XMAKECOLOR( r, g, b );
  988. pNodeList[*pNumNodes].totalCalls = pNode->GetTotalCalls();
  989. pNodeList[*pNumNodes].inclusiveTime = pNode->GetTotalTime();
  990. pNodeList[*pNumNodes].exclusiveTime = pNode->GetTotalTimeLessChildren();
  991. (*pNumNodes)++;
  992. CVProfNode *nodePtr = pNode->GetSibling();
  993. if ( nodePtr )
  994. {
  995. VXBuildNodeList_r( nodePtr, pNodeList, pNumNodes );
  996. }
  997. nodePtr = pNode->GetChild();
  998. if ( nodePtr )
  999. {
  1000. VXBuildNodeList_r( nodePtr, pNodeList, pNumNodes );
  1001. }
  1002. }
  1003. void CVProfile::VXSendNodes( void )
  1004. {
  1005. Pause();
  1006. xVProfNodeItem_t *pNodeList = (xVProfNodeItem_t *)stackalloc( MAX_VPROF_NODES_IN_LIST * sizeof(xVProfNodeItem_t) );
  1007. int numNodes = 0;
  1008. VXBuildNodeList_r( GetRoot(), pNodeList, &numNodes );
  1009. // send to vxconsole
  1010. XBX_rVProfNodeList( numNodes, pNodeList );
  1011. Resume();
  1012. }
  1013. #endif
  1014. //-------------------------------------
  1015. static void DumpSorted( CVProfile::StreamOut_t outputStream, const tchar *pszHeading, double totalTime, bool (*pfnSort)( const TimeSums_t &, const TimeSums_t & ), int maxLen = 999999 )
  1016. {
  1017. unsigned i;
  1018. vector<TimeSums_t> sortedSums;
  1019. sortedSums = g_TimeSums;
  1020. sort( sortedSums.begin(), sortedSums.end(), pfnSort );
  1021. outputStream( _T("%s\n"), pszHeading);
  1022. outputStream( _T(" Scope Calls Calls/Frame Time+Child Pct Time Pct Avg/Frame Avg/Call Avg-NoChild Peak\n"));
  1023. outputStream( _T(" ---------------------------------------------------- ----------- ----------- ----------- ------ ----------- ------ ----------- ----------- ----------- -----------\n"));
  1024. for ( i = 0; i < sortedSums.size() && i < (unsigned)maxLen; i++ )
  1025. {
  1026. double avg = ( sortedSums[i].calls ) ? sortedSums[i].time / (double)sortedSums[i].calls : 0.0;
  1027. double avgLessChildren = ( sortedSums[i].calls ) ? sortedSums[i].timeLessChildren / (double)sortedSums[i].calls : 0.0;
  1028. outputStream( _T(" %52.52s%12d%12.3f%12.3f%7.2f%12.3f%7.2f%12.3f%12.3f%12.3f%12.3f\n"),
  1029. sortedSums[i].pszProfileScope,
  1030. sortedSums[i].calls,
  1031. (float)sortedSums[i].calls / (float)g_TotalFrames,
  1032. sortedSums[i].time,
  1033. min( ( sortedSums[i].time / totalTime ) * 100.0, 100.0 ),
  1034. sortedSums[i].timeLessChildren,
  1035. min( ( sortedSums[i].timeLessChildren / totalTime ) * 100.0, 100.0 ),
  1036. sortedSums[i].time / (float)g_TotalFrames,
  1037. avg,
  1038. avgLessChildren,
  1039. sortedSums[i].peak );
  1040. }
  1041. }
  1042. #if _X360
  1043. // Dump information on all nodes with PMC recording
  1044. static void DumpPMC( CVProfNode *pNode, bool &bPrintHeader, uint64 L2thresh = 1, uint64 LHSthresh = 1 )
  1045. {
  1046. if (!pNode) return;
  1047. uint64 l2 = pNode->GetL2CacheMisses();
  1048. uint64 lhs = pNode->GetLoadHitStores();
  1049. if ( l2 > L2thresh &&
  1050. lhs > LHSthresh )
  1051. {
  1052. // met threshold.
  1053. if (bPrintHeader)
  1054. {
  1055. // print header
  1056. Msg( _T("-- 360 PMC information --\n") );
  1057. Msg( _T("Scope L2/call L2/frame LHS/call LHS/frame\n") );
  1058. Msg( _T("---------------------------------------------------- --------- --------- --------- ---------\n") );
  1059. bPrintHeader = false;
  1060. }
  1061. // print
  1062. float calls = pNode->GetTotalCalls();
  1063. float frames = g_TotalFrames;
  1064. Msg( _T("%52.52s %9.2f %9.2f %9.2f %9.2f\n"), pNode->GetName(), l2/calls, l2/frames, lhs/calls, lhs/frames );
  1065. }
  1066. if ( pNode->GetSibling() )
  1067. {
  1068. DumpPMC( pNode->GetSibling(), bPrintHeader, L2thresh, LHSthresh );
  1069. }
  1070. if ( pNode->GetChild() )
  1071. {
  1072. DumpPMC( pNode->GetChild(), bPrintHeader, L2thresh, LHSthresh );
  1073. }
  1074. }
  1075. #endif
  1076. //-------------------------------------
  1077. void CVProfile::SetOutputStream( StreamOut_t outputStream )
  1078. {
  1079. if ( outputStream != NULL )
  1080. m_pOutputStream = outputStream;
  1081. else
  1082. m_pOutputStream = Msg;
  1083. }
  1084. //-------------------------------------
  1085. void CVProfile::OutputReport( int type, const tchar *pszStartNode, int budgetGroupID )
  1086. {
  1087. m_pOutputStream( _T("******** BEGIN VPROF REPORT ********\n"));
  1088. #ifdef _MSC_VER
  1089. #if (_MSC_VER < 1300)
  1090. m_pOutputStream( _T(" (note: this report exceeds the output capacity of MSVC debug window. Use console window or console log.) \n"));
  1091. #endif
  1092. #endif
  1093. g_TotalFrames = max( NumFramesSampled() - 1, 1 );
  1094. if ( NumFramesSampled() == 0 || GetTotalTimeSampled() == 0)
  1095. m_pOutputStream( _T("No samples\n") );
  1096. else
  1097. {
  1098. if ( type & VPRT_SUMMARY )
  1099. {
  1100. m_pOutputStream( _T("-- Summary --\n") );
  1101. m_pOutputStream( _T("%d frames sampled for %.2f seconds\n"), g_TotalFrames, GetTotalTimeSampled() / 1000.0 );
  1102. m_pOutputStream( _T("Average %.2f fps, %.2f ms per frame\n"), 1000.0 / ( GetTotalTimeSampled() / g_TotalFrames ), GetTotalTimeSampled() / g_TotalFrames );
  1103. m_pOutputStream( _T("Peak %.2f ms frame\n"), GetPeakFrameTime() );
  1104. double timeAccountedFor = 100.0 - ( m_Root.GetTotalTimeLessChildren() / m_Root.GetTotalTime() );
  1105. m_pOutputStream( _T("%.0f pct of time accounted for\n"), min( 100.0, timeAccountedFor ) );
  1106. m_pOutputStream( _T("\n") );
  1107. }
  1108. if ( pszStartNode == NULL )
  1109. {
  1110. pszStartNode = GetRoot()->GetName();
  1111. }
  1112. SumTimes( pszStartNode, budgetGroupID );
  1113. // Dump the hierarchy
  1114. if ( type & VPRT_HIERARCHY )
  1115. {
  1116. m_pOutputStream( _T("-- Hierarchical Call Graph --\n"));
  1117. if ( pszStartNode == NULL )
  1118. g_pStartNode = NULL;
  1119. else
  1120. g_pStartNode = FindNode( GetRoot(), pszStartNode );
  1121. DumpNodes( (!g_pStartNode) ? GetRoot() : g_pStartNode, 0, false );
  1122. m_pOutputStream( _T("\n") );
  1123. }
  1124. if ( type & VPRT_HIERARCHY_TIME_PER_FRAME_AND_COUNT_ONLY )
  1125. {
  1126. m_pOutputStream( _T("-- Hierarchical Call Graph --\n"));
  1127. if ( pszStartNode == NULL )
  1128. g_pStartNode = NULL;
  1129. else
  1130. g_pStartNode = FindNode( GetRoot(), pszStartNode );
  1131. DumpNodes( (!g_pStartNode) ? GetRoot() : g_pStartNode, 0, true );
  1132. m_pOutputStream( _T("\n") );
  1133. }
  1134. int maxLen = ( type & VPRT_LIST_TOP_ITEMS_ONLY ) ? 25 : 999999;
  1135. if ( type & VPRT_LIST_BY_TIME )
  1136. {
  1137. DumpSorted( m_pOutputStream, _T("-- Profile scopes sorted by time (including children) --"), GetTotalTimeSampled(), TimeCompare, maxLen );
  1138. m_pOutputStream( _T("\n") );
  1139. }
  1140. if ( type & VPRT_LIST_BY_TIME_LESS_CHILDREN )
  1141. {
  1142. DumpSorted( m_pOutputStream, _T("-- Profile scopes sorted by time (without children) --"), GetTotalTimeSampled(), TimeLessChildrenCompare, maxLen );
  1143. m_pOutputStream( _T("\n") );
  1144. }
  1145. if ( type & VPRT_LIST_BY_AVG_TIME )
  1146. {
  1147. DumpSorted( m_pOutputStream, _T("-- Profile scopes sorted by average time (including children) --"), GetTotalTimeSampled(), AverageTimeCompare, maxLen );
  1148. m_pOutputStream( _T("\n") );
  1149. }
  1150. if ( type & VPRT_LIST_BY_AVG_TIME_LESS_CHILDREN )
  1151. {
  1152. DumpSorted( m_pOutputStream, _T("-- Profile scopes sorted by average time (without children) --"), GetTotalTimeSampled(), AverageTimeLessChildrenCompare, maxLen );
  1153. m_pOutputStream( _T("\n") );
  1154. }
  1155. if ( type & VPRT_LIST_BY_PEAK_TIME )
  1156. {
  1157. DumpSorted( m_pOutputStream, _T("-- Profile scopes sorted by peak --"), GetTotalTimeSampled(), PeakCompare, maxLen);
  1158. m_pOutputStream( _T("\n") );
  1159. }
  1160. if ( type & VPRT_LIST_BY_PEAK_OVER_AVERAGE )
  1161. {
  1162. DumpSorted( m_pOutputStream, _T("-- Profile scopes sorted by peak over average (including children) --"), GetTotalTimeSampled(), PeakOverAverageCompare, maxLen );
  1163. m_pOutputStream( _T("\n") );
  1164. }
  1165. // TODO: Functions by time less children
  1166. // TODO: Functions by time averages
  1167. // TODO: Functions by peak
  1168. // TODO: Peak deviation from average
  1169. g_TimesLessChildren.clear();
  1170. g_TimeSumsMap.clear();
  1171. g_TimeSums.clear();
  1172. #ifdef _X360
  1173. bool bPrintedHeader = true;
  1174. DumpPMC( FindNode( GetRoot(), pszStartNode ), bPrintedHeader );
  1175. #endif
  1176. }
  1177. m_pOutputStream( _T("******** END VPROF REPORT ********\n"));
  1178. }
  1179. //=============================================================================
  1180. CVProfile::CVProfile()
  1181. : m_Root( _T("Root"), 0, NULL, VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, 0 ),
  1182. m_pCurNode( &m_Root ),
  1183. m_nFrames( 0 ),
  1184. m_enabled( 0 ),
  1185. m_pausedEnabledDepth( 0 ),
  1186. m_fAtRoot( true ),
  1187. m_pOutputStream( Msg )
  1188. {
  1189. #ifdef VPROF_VTUNE_GROUP
  1190. m_GroupIDStackDepth = 1;
  1191. m_GroupIDStack[0] = 0; // VPROF_BUDGETGROUP_OTHER_UNACCOUNTED
  1192. #endif
  1193. m_TargetThreadId = ThreadGetCurrentId();
  1194. // Go ahead and allocate 32 slots for budget group names
  1195. MEM_ALLOC_CREDIT();
  1196. m_pBudgetGroups = new CVProfile::CBudgetGroup[32];
  1197. m_nBudgetGroupNames = 0;
  1198. m_nBudgetGroupNamesAllocated = 32;
  1199. // Add these here so that they will always be in the same order.
  1200. // VPROF_BUDGETGROUP_OTHER_UNACCOUNTED has to be FIRST!!!!
  1201. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
  1202. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_WORLD_RENDERING, BUDGETFLAG_CLIENT );
  1203. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_DISPLACEMENT_RENDERING, BUDGETFLAG_CLIENT );
  1204. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_GAME, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
  1205. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_PLAYER, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
  1206. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_NPCS, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
  1207. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_SERVER_ANIM, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
  1208. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_CLIENT_ANIMATION, BUDGETFLAG_CLIENT );
  1209. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_PHYSICS, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
  1210. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_STATICPROP_RENDERING, BUDGETFLAG_CLIENT );
  1211. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_MODEL_RENDERING, BUDGETFLAG_CLIENT );
  1212. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_MODEL_FAST_PATH_RENDERING,BUDGETFLAG_CLIENT );
  1213. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_LIGHTCACHE, BUDGETFLAG_CLIENT );
  1214. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_BRUSHMODEL_RENDERING, BUDGETFLAG_CLIENT );
  1215. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_SHADOW_RENDERING, BUDGETFLAG_CLIENT );
  1216. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_DETAILPROP_RENDERING, BUDGETFLAG_CLIENT );
  1217. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_PARTICLE_RENDERING, BUDGETFLAG_CLIENT );
  1218. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_ROPES, BUDGETFLAG_CLIENT );
  1219. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_DLIGHT_RENDERING, BUDGETFLAG_CLIENT );
  1220. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OTHER_NETWORKING, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
  1221. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OTHER_SOUND, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
  1222. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OTHER_VGUI, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
  1223. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OTHER_FILESYSTEM, BUDGETFLAG_OTHER | BUDGETFLAG_SERVER );
  1224. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_PREDICTION, BUDGETFLAG_CLIENT );
  1225. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_INTERPOLATION, BUDGETFLAG_CLIENT );
  1226. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_SWAP_BUFFERS, BUDGETFLAG_CLIENT );
  1227. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OCCLUSION, BUDGETFLAG_CLIENT );
  1228. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_OVERLAYS, BUDGETFLAG_CLIENT );
  1229. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_TOOLS, BUDGETFLAG_OTHER | BUDGETFLAG_CLIENT );
  1230. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_TEXTURE_CACHE, BUDGETFLAG_CLIENT );
  1231. BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_REPLAY, BUDGETFLAG_SERVER );
  1232. // BudgetGroupNameToBudgetGroupID( VPROF_BUDGETGROUP_DISP_HULLTRACES );
  1233. m_bPMEInit = false;
  1234. m_bPMEEnabled = false;
  1235. #ifdef _X360
  1236. m_UpdateMode = 0;
  1237. m_iCPUTraceEnabled = kDisabled;
  1238. m_bTraceCompleteEvent = false;
  1239. m_iSuccessiveTraceIndex = 0;
  1240. m_ReportMode = VXCONSOLE_REPORT_TIME;
  1241. m_pReportScale[VXCONSOLE_REPORT_TIME] = 1000.0f;
  1242. m_pReportScale[VXCONSOLE_REPORT_L2CACHE_MISSES] = 1.0f;
  1243. m_pReportScale[VXCONSOLE_REPORT_LOAD_HIT_STORE] = 0.1f;
  1244. m_nFrameCount = 0;
  1245. m_nFramesRemaining = 1;
  1246. m_WorstCycles = 0;
  1247. m_WorstTraceFilename[ 0 ] = 0;
  1248. #endif
  1249. }
  1250. CVProfile::~CVProfile()
  1251. {
  1252. Term();
  1253. }
  1254. void CVProfile::FreeNodes_R( CVProfNode *pNode )
  1255. {
  1256. CVProfNode *pNext;
  1257. for ( CVProfNode *pChild = pNode->GetChild(); pChild; pChild = pNext )
  1258. {
  1259. pNext = pChild->GetSibling();
  1260. FreeNodes_R( pChild );
  1261. }
  1262. if ( pNode == GetRoot() )
  1263. {
  1264. pNode->m_pChild = NULL;
  1265. }
  1266. else
  1267. {
  1268. delete pNode;
  1269. }
  1270. }
  1271. void CVProfile::Term()
  1272. {
  1273. int i;
  1274. for( i = 0; i < m_nBudgetGroupNames; i++ )
  1275. {
  1276. delete [] m_pBudgetGroups[i].m_pName;
  1277. }
  1278. delete m_pBudgetGroups;
  1279. m_nBudgetGroupNames = m_nBudgetGroupNamesAllocated = 0;
  1280. m_pBudgetGroups = NULL;
  1281. int n;
  1282. for( n = 0; n < m_NumCounters; n++ )
  1283. {
  1284. delete [] m_CounterNames[n];
  1285. m_CounterNames[n] = NULL;
  1286. }
  1287. m_NumCounters = 0;
  1288. // Free the nodes.
  1289. if ( GetRoot() )
  1290. {
  1291. FreeNodes_R( GetRoot() );
  1292. }
  1293. }
  1294. #define COLORMIN 160
  1295. #define COLORMAX 255
  1296. static int g_ColorLookup[4] =
  1297. {
  1298. COLORMIN,
  1299. COLORMAX,
  1300. COLORMIN+(COLORMAX-COLORMIN)/3,
  1301. COLORMIN+((COLORMAX-COLORMIN)*2)/3,
  1302. };
  1303. #define GET_BIT( val, bitnum ) ( ( val >> bitnum ) & 0x1 )
  1304. void CVProfile::GetBudgetGroupColor( int budgetGroupID, int &r, int &g, int &b, int &a )
  1305. {
  1306. budgetGroupID = budgetGroupID % ( 1 << 6 );
  1307. int index;
  1308. index = GET_BIT( budgetGroupID, 0 ) | ( GET_BIT( budgetGroupID, 5 ) << 1 );
  1309. r = g_ColorLookup[index];
  1310. index = GET_BIT( budgetGroupID, 1 ) | ( GET_BIT( budgetGroupID, 4 ) << 1 );
  1311. g = g_ColorLookup[index];
  1312. index = GET_BIT( budgetGroupID, 2 ) | ( GET_BIT( budgetGroupID, 3 ) << 1 );
  1313. b = g_ColorLookup[index];
  1314. a = 255;
  1315. }
  1316. // return -1 if it doesn't exist.
  1317. int CVProfile::FindBudgetGroupName( const tchar *pBudgetGroupName )
  1318. {
  1319. int i;
  1320. for( i = 0; i < m_nBudgetGroupNames; i++ )
  1321. {
  1322. if( _tcsicmp( pBudgetGroupName, m_pBudgetGroups[i].m_pName ) == 0 )
  1323. {
  1324. return i;
  1325. }
  1326. }
  1327. return -1;
  1328. }
  1329. int CVProfile::AddBudgetGroupName( const tchar *pBudgetGroupName, int budgetFlags )
  1330. {
  1331. MEM_ALLOC_CREDIT();
  1332. tchar *pNewString = new tchar[ _tcslen( pBudgetGroupName ) + 1 ];
  1333. _tcscpy( pNewString, pBudgetGroupName );
  1334. if( m_nBudgetGroupNames + 1 > m_nBudgetGroupNamesAllocated )
  1335. {
  1336. m_nBudgetGroupNamesAllocated *= 2;
  1337. m_nBudgetGroupNamesAllocated = max( m_nBudgetGroupNames + 6, m_nBudgetGroupNamesAllocated );
  1338. CBudgetGroup *pNew = new CBudgetGroup[ m_nBudgetGroupNamesAllocated ];
  1339. for ( int i=0; i < m_nBudgetGroupNames; i++ )
  1340. pNew[i] = m_pBudgetGroups[i];
  1341. delete [] m_pBudgetGroups;
  1342. m_pBudgetGroups = pNew;
  1343. }
  1344. m_pBudgetGroups[m_nBudgetGroupNames].m_pName = pNewString;
  1345. m_pBudgetGroups[m_nBudgetGroupNames].m_BudgetFlags = budgetFlags;
  1346. m_nBudgetGroupNames++;
  1347. if( m_pNumBudgetGroupsChangedCallBack )
  1348. {
  1349. (*m_pNumBudgetGroupsChangedCallBack)();
  1350. }
  1351. #if defined( _X360 )
  1352. // re-start with all the known budgets
  1353. VXProfileStart();
  1354. #endif
  1355. return m_nBudgetGroupNames - 1;
  1356. }
  1357. int CVProfile::BudgetGroupNameToBudgetGroupID( const tchar *pBudgetGroupName, int budgetFlagsToORIn )
  1358. {
  1359. int budgetGroupID = FindBudgetGroupName( pBudgetGroupName );
  1360. if( budgetGroupID == -1 )
  1361. {
  1362. budgetGroupID = AddBudgetGroupName( pBudgetGroupName, budgetFlagsToORIn );
  1363. }
  1364. else
  1365. {
  1366. m_pBudgetGroups[budgetGroupID].m_BudgetFlags |= budgetFlagsToORIn;
  1367. }
  1368. return budgetGroupID;
  1369. }
  1370. int CVProfile::BudgetGroupNameToBudgetGroupID( const tchar *pBudgetGroupName )
  1371. {
  1372. return BudgetGroupNameToBudgetGroupID( pBudgetGroupName, BUDGETFLAG_OTHER );
  1373. }
  1374. int CVProfile::GetNumBudgetGroups( void )
  1375. {
  1376. return m_nBudgetGroupNames;
  1377. }
  1378. void CVProfile::RegisterNumBudgetGroupsChangedCallBack( void (*pCallBack)(void) )
  1379. {
  1380. m_pNumBudgetGroupsChangedCallBack = pCallBack;
  1381. }
  1382. void CVProfile::HideBudgetGroup( int budgetGroupID, bool bHide )
  1383. {
  1384. if( budgetGroupID != -1 )
  1385. {
  1386. if ( bHide )
  1387. m_pBudgetGroups[budgetGroupID].m_BudgetFlags |= BUDGETFLAG_HIDDEN;
  1388. else
  1389. m_pBudgetGroups[budgetGroupID].m_BudgetFlags &= ~BUDGETFLAG_HIDDEN;
  1390. }
  1391. }
  1392. int *CVProfile::FindOrCreateCounter( const tchar *pName, CounterGroup_t eCounterGroup )
  1393. {
  1394. Assert( m_NumCounters+1 < MAXCOUNTERS );
  1395. if ( m_NumCounters + 1 >= MAXCOUNTERS || !InTargetThread() )
  1396. {
  1397. static int dummy;
  1398. return &dummy;
  1399. }
  1400. int i;
  1401. for( i = 0; i < m_NumCounters; i++ )
  1402. {
  1403. if( _tcsicmp( m_CounterNames[i], pName ) == 0 )
  1404. {
  1405. // found it!
  1406. return &m_Counters[i];
  1407. }
  1408. }
  1409. // NOTE: These get freed in ~CVProfile.
  1410. MEM_ALLOC_CREDIT();
  1411. tchar *pNewName = new tchar[_tcslen( pName ) + 1];
  1412. _tcscpy( pNewName, pName );
  1413. m_Counters[m_NumCounters] = 0;
  1414. m_CounterGroups[m_NumCounters] = (char)eCounterGroup;
  1415. m_CounterNames[m_NumCounters++] = pNewName;
  1416. return &m_Counters[m_NumCounters-1];
  1417. }
  1418. void CVProfile::ResetCounters( CounterGroup_t eCounterGroup )
  1419. {
  1420. int i;
  1421. for( i = 0; i < m_NumCounters; i++ )
  1422. {
  1423. if ( m_CounterGroups[i] == eCounterGroup )
  1424. m_Counters[i] = 0;
  1425. }
  1426. }
  1427. int CVProfile::GetNumCounters() const
  1428. {
  1429. return m_NumCounters;
  1430. }
  1431. const tchar *CVProfile::GetCounterName( int index ) const
  1432. {
  1433. Assert( index >= 0 && index < m_NumCounters );
  1434. return m_CounterNames[index];
  1435. }
  1436. int CVProfile::GetCounterValue( int index ) const
  1437. {
  1438. Assert( index >= 0 && index < m_NumCounters );
  1439. return m_Counters[index];
  1440. }
  1441. const tchar *CVProfile::GetCounterNameAndValue( int index, int &val ) const
  1442. {
  1443. Assert( index >= 0 && index < m_NumCounters );
  1444. val = m_Counters[index];
  1445. return m_CounterNames[index];
  1446. }
  1447. CounterGroup_t CVProfile::GetCounterGroup( int index ) const
  1448. {
  1449. Assert( index >= 0 && index < m_NumCounters );
  1450. return (CounterGroup_t)m_CounterGroups[index];
  1451. }
  1452. #ifdef _X360
  1453. void CVProfile::LatchMultiFrame( int64 cycles )
  1454. {
  1455. if ( cycles > m_WorstCycles )
  1456. {
  1457. strncpy( m_WorstTraceFilename, GetCPUTraceFilename(), sizeof( m_WorstTraceFilename ) );
  1458. m_WorstCycles = cycles;
  1459. }
  1460. }
  1461. void CVProfile::SpewWorstMultiFrame()
  1462. {
  1463. CCycleCount cc( m_WorstCycles );
  1464. m_pOutputStream( "%s == %.3f msec\n", m_WorstTraceFilename, cc.GetMillisecondsF() );
  1465. }
  1466. #endif
  1467. #ifdef DBGFLAG_VALIDATE
  1468. #ifdef _WIN64
  1469. #error the below is presumably broken on 64 bit
  1470. #endif // _WIN64
  1471. const int k_cSTLMapAllocOffset = 4;
  1472. #define GET_INTERNAL_MAP_ALLOC_PTR( pMap ) \
  1473. ( * ( (void **) ( ( ( byte * ) ( pMap ) ) + k_cSTLMapAllocOffset ) ) )
  1474. //-----------------------------------------------------------------------------
  1475. // Purpose: Ensure that all of our internal structures are consistent, and
  1476. // account for all memory that we've allocated.
  1477. // Input: validator - Our global validator object
  1478. // pchName - Our name (typically a member var in our container)
  1479. //-----------------------------------------------------------------------------
  1480. void CVProfile::Validate( CValidator &validator, tchar *pchName )
  1481. {
  1482. validator.Push( _T("CVProfile"), this, pchName );
  1483. m_Root.Validate( validator, _T("m_Root") );
  1484. for ( int iBudgetGroup=0; iBudgetGroup < m_nBudgetGroupNames; iBudgetGroup++ )
  1485. validator.ClaimMemory( m_pBudgetGroups[iBudgetGroup].m_pName );
  1486. validator.ClaimMemory( m_pBudgetGroups );
  1487. // The std template map class allocates memory internally, but offer no way to get
  1488. // access to their pointer. Since this is for debug purposes only and the
  1489. // std template classes don't change, just look at the well-known offset. This
  1490. // is arguably sick and wrong, kind of like marrying a squirrel.
  1491. validator.ClaimMemory( GET_INTERNAL_MAP_ALLOC_PTR( &g_TimesLessChildren ) );
  1492. validator.ClaimMemory( GET_INTERNAL_MAP_ALLOC_PTR( &g_TimeSumsMap ) );
  1493. validator.Pop( );
  1494. }
  1495. #endif // DBGFLAG_VALIDATE
  1496. #endif // VPROF_ENABLED
  1497. #ifdef RAD_TELEMETRY_ENABLED
  1498. TelemetryData g_Telemetry;
  1499. static HTELEMETRY g_tmContext;
  1500. static TmU8 *g_pTmMemoryArena = NULL;
  1501. static bool g_TelemetryLoaded = false;
  1502. static unsigned int g_TelemetryFrameCount = 0;
  1503. static bool g_fTelemetryLevelChanged = false;
  1504. static const TmU32 TELEMETRY_ARENA_SIZE = 8 * 1024 * 1024; // How much memory we want Telemetry to use.
  1505. struct ThreadNameInfo_t
  1506. {
  1507. TSLNodeBase_t base;
  1508. ThreadId_t ThreadID;
  1509. char szName[ 64 ];
  1510. };
  1511. static CTSSimpleList< ThreadNameInfo_t > g_ThreadNamesList;
  1512. static bool g_bThreadNameArrayChanged = false;
  1513. static int g_ThreadNameArrayCount = 0;
  1514. static ThreadNameInfo_t *g_ThreadNameArray[64];
  1515. void TelemetryThreadSetDebugName( ThreadId_t id, const char *pszName )
  1516. {
  1517. ThreadNameInfo_t *pThreadNameInfo = new ThreadNameInfo_t;
  1518. if( id == ( uint32 )-1 )
  1519. {
  1520. id = ThreadGetCurrentId();
  1521. }
  1522. pThreadNameInfo->ThreadID = id;
  1523. strncpy( pThreadNameInfo->szName, pszName, ARRAYSIZE( pThreadNameInfo->szName ) );
  1524. pThreadNameInfo->szName[ ARRAYSIZE( pThreadNameInfo->szName ) - 1 ] = 0;
  1525. g_ThreadNamesList.Push( pThreadNameInfo );
  1526. g_bThreadNameArrayChanged = true;
  1527. }
  1528. static void UpdateTelemetryThreadNames()
  1529. {
  1530. if( g_bThreadNameArrayChanged )
  1531. {
  1532. // Go through and add any new thread names we got in our thread safe list to our thread names array.
  1533. for( ThreadNameInfo_t *pThreadNameInfo = g_ThreadNamesList.Pop();
  1534. pThreadNameInfo;
  1535. pThreadNameInfo = g_ThreadNamesList.Pop() )
  1536. {
  1537. if( g_ThreadNameArrayCount < ARRAYSIZE( g_ThreadNameArray ) )
  1538. {
  1539. g_ThreadNameArray[ g_ThreadNameArrayCount ] = pThreadNameInfo;
  1540. g_ThreadNameArrayCount++;
  1541. }
  1542. else
  1543. {
  1544. delete pThreadNameInfo;
  1545. }
  1546. }
  1547. tmThreadName( g_tmContext, ThreadGetCurrentId(), "MainThrd" );
  1548. for( int i = 0; i < g_ThreadNameArrayCount; i++ )
  1549. {
  1550. tmThreadName( g_tmContext, g_ThreadNameArray[i]->ThreadID, g_ThreadNameArray[i]->szName );
  1551. }
  1552. g_bThreadNameArrayChanged = false;
  1553. }
  1554. }
  1555. static bool TelemetryInitialize()
  1556. {
  1557. if( g_tmContext )
  1558. {
  1559. TmConnectionStatus status = tmGetConnectionStatus( g_tmContext );
  1560. if( status == TMCS_CONNECTED || status == TMCS_CONNECTING )
  1561. return true;
  1562. }
  1563. TmErrorCode retVal;
  1564. if( !g_TelemetryLoaded )
  1565. {
  1566. // Pass in 0 if you want to use the release mode DLL or 1 if you want to
  1567. // use the checked DLL. The checked DLL is compiled with optimizations but
  1568. // does extra run time checks and reporting.
  1569. int nLoadTelemetry = tmLoadTelemetry( 0 );
  1570. retVal = tmStartup();
  1571. if ( retVal != TM_OK )
  1572. {
  1573. Warning( "TelemetryInit() failed: tmStartup() returned %d, tmLoadTelemetry() returned %d.\n", retVal, nLoadTelemetry );
  1574. return false;
  1575. }
  1576. if( !g_pTmMemoryArena )
  1577. {
  1578. g_pTmMemoryArena = new TmU8[ TELEMETRY_ARENA_SIZE ];
  1579. }
  1580. retVal = tmInitializeContext( &g_tmContext, g_pTmMemoryArena, TELEMETRY_ARENA_SIZE );
  1581. if ( retVal != TM_OK )
  1582. {
  1583. delete [] g_pTmMemoryArena;
  1584. g_pTmMemoryArena = NULL;
  1585. Warning( "TelemetryInit() failed: tmInitializeContext() returned %d.\n", retVal );
  1586. return false;
  1587. }
  1588. g_TelemetryLoaded = true;
  1589. }
  1590. const char *pGameName = "tf2";
  1591. #if defined( IS_WINDOWS_PC )
  1592. char baseExeFilename[512];
  1593. if( GetModuleFileName ( GetModuleHandle( NULL ), baseExeFilename, sizeof( baseExeFilename ) ) )
  1594. {
  1595. char *pExt = strrchr( baseExeFilename, '.' );
  1596. if( pExt )
  1597. *pExt = 0;
  1598. char *pSeparator = strrchr( baseExeFilename, '\\' );
  1599. pGameName = pSeparator ? ( pSeparator + 1 ) : baseExeFilename;
  1600. }
  1601. // If you've got \\perforce\symbols on your _NT_SYMBOL_PATH, tmOpen() can take a massively long
  1602. // time in the symInitialize() routine. Since we don't really need that, kill it here.
  1603. putenv( "_NT_SYMBOL_PATH=" );
  1604. #endif
  1605. const char *pServerAddress = g_Telemetry.ServerAddress[0] ? g_Telemetry.ServerAddress : "localhost";
  1606. TmConnectionType tmType = !V_tier0_stricmp( pServerAddress, "FILE" ) ? TMCT_FILE : TMCT_TCP;
  1607. Msg( "TELEMETRY: Calling tmOpen( %s )...\n", pServerAddress );
  1608. char szBuildInfo[ 2048 ];
  1609. _snprintf( szBuildInfo, ARRAYSIZE( szBuildInfo ), "%s: %s", __DATE__ __TIME__, Plat_GetCommandLineA() );
  1610. szBuildInfo[ ARRAYSIZE( szBuildInfo ) - 1 ] = 0;
  1611. TmU32 TmOpenFlags = TMOF_DEFAULT | TMOF_MINIMAL_CONTEXT_SWITCHES;
  1612. /* TmOpenFlags |= TMOF_DISABLE_CONTEXT_SWITCHES | TMOF_INIT_NETWORKING*/
  1613. retVal = tmOpen( g_tmContext, pGameName, szBuildInfo, pServerAddress, tmType,
  1614. TELEMETRY_DEFAULT_PORT, TmOpenFlags, 1000 );
  1615. if ( retVal != TM_OK )
  1616. {
  1617. Warning( "TelemetryInitialize() failed: tmOpen returned %d.\n", retVal );
  1618. return false;
  1619. }
  1620. Msg( "Telemetry initialized at level %u.\n", g_Telemetry.Level );
  1621. // Make sure we set all the thread names.
  1622. g_bThreadNameArrayChanged = true;
  1623. UpdateTelemetryThreadNames();
  1624. return true;
  1625. }
  1626. static void TelemetryShutdown( bool InDtor = false )
  1627. {
  1628. if( g_tmContext )
  1629. {
  1630. // Msg can't be called here as tier0 may have already been shut down...
  1631. if( !InDtor )
  1632. {
  1633. Msg( "Shutting down telemetry.\n" );
  1634. }
  1635. TmConnectionStatus status = tmGetConnectionStatus( g_tmContext );
  1636. if( status == TMCS_CONNECTED || status == TMCS_CONNECTING )
  1637. tmClose( g_tmContext );
  1638. // Discontinue new usage of the context before shutting it down (multithreading).
  1639. memset( g_Telemetry.tmContext, 0, sizeof( g_Telemetry.tmContext ) );
  1640. HTELEMETRY hShutdown = g_tmContext;
  1641. g_tmContext = NULL;
  1642. tmShutdownContext( hShutdown );
  1643. tmShutdown();
  1644. g_TelemetryLoaded = false;
  1645. }
  1646. }
  1647. // Helper class to initialize Telemetry.
  1648. class CTelemetryRegister
  1649. {
  1650. public:
  1651. CTelemetryRegister() {}
  1652. ~CTelemetryRegister() { TelemetryShutdown( true ); }
  1653. } g_TelemetryRegister;
  1654. PLATFORM_INTERFACE void TelemetrySetLevel( unsigned int Level )
  1655. {
  1656. if( Level != g_Telemetry.Level )
  1657. {
  1658. DevMsg( "TelemetrySetLevel changed from 0x%x to 0x%x\n", g_Telemetry.Level, Level );
  1659. g_Telemetry.Level = Level;
  1660. g_TelemetryFrameCount = g_Telemetry.FrameCount;
  1661. g_fTelemetryLevelChanged = true;
  1662. }
  1663. }
  1664. static void TelemetryPlots()
  1665. {
  1666. if( g_Telemetry.playbacktick )
  1667. {
  1668. tmPlotU32( TELEMETRY_LEVEL1, TMPT_INTEGER, 0, g_Telemetry.playbacktick, "game/PlaybackTick" );
  1669. g_Telemetry.playbacktick = 0;
  1670. }
  1671. for( int i = 0; i < g_VProfCurrentProfile.GetNumCounters(); i++ )
  1672. {
  1673. if( g_VProfCurrentProfile.GetCounterGroup( i ) == COUNTER_GROUP_TELEMETRY )
  1674. {
  1675. int val;
  1676. const char *name = g_VProfCurrentProfile.GetCounterNameAndValue( i, val );
  1677. tmPlotI32( TELEMETRY_LEVEL1, TMPT_INTEGER, 0, val, name );
  1678. }
  1679. }
  1680. g_VProfCurrentProfile.ResetCounters( COUNTER_GROUP_TELEMETRY );
  1681. }
  1682. PLATFORM_INTERFACE void TelemetryTick()
  1683. {
  1684. static double s_d0 = Plat_FloatTime();
  1685. static TmU64 s_t0 = tmFastTime();
  1686. if( !g_Telemetry.Level && g_Telemetry.DemoTickStart && ( (uint32)g_Telemetry.playbacktick > g_Telemetry.DemoTickStart ) )
  1687. {
  1688. TelemetrySetLevel( 2 );
  1689. g_Telemetry.DemoTickStart = 0;
  1690. }
  1691. if( g_Telemetry.Level && g_Telemetry.DemoTickEnd && ( (uint32)g_Telemetry.playbacktick > g_Telemetry.DemoTickEnd ) )
  1692. {
  1693. TelemetrySetLevel( 0 );
  1694. g_Telemetry.DemoTickEnd = ( uint32 )-1;
  1695. }
  1696. // People can NIL out contexts in the TelemetryData structure to control
  1697. // the level and what sections to log. We always need to do ticks though,
  1698. // so use master context for this.
  1699. if( g_tmContext )
  1700. {
  1701. // Update any new thread names.
  1702. UpdateTelemetryThreadNames();
  1703. if ( g_Telemetry.Level > 0 )
  1704. TelemetryPlots();
  1705. // Do a Telemetry Tick.
  1706. tmTick( g_tmContext );
  1707. // Update flRDTSCToMilliSeconds.
  1708. TmU64 s_t1 = tmFastTime();
  1709. double s_d1 = Plat_FloatTime();
  1710. g_Telemetry.flRDTSCToMilliSeconds = 1000.0f / ( ( s_t1 - s_t0 ) / ( s_d1 - s_d0 ) );
  1711. s_d0 = s_d1;
  1712. s_t0 = s_t1;
  1713. // Check if we're only supposed to run X amount of frames.
  1714. if( g_TelemetryFrameCount && !tmIsPaused( g_tmContext ) )
  1715. {
  1716. g_TelemetryFrameCount--;
  1717. if( !g_TelemetryFrameCount )
  1718. TelemetrySetLevel( 0 );
  1719. }
  1720. }
  1721. if( g_fTelemetryLevelChanged )
  1722. {
  1723. g_fTelemetryLevelChanged = false;
  1724. memset( g_Telemetry.tmContext, 0, sizeof( g_Telemetry.tmContext ) );
  1725. if( g_Telemetry.Level == 0 )
  1726. {
  1727. // Calling shutdown here invalidates all the telemetry context handles.
  1728. // Background threads in the middle of Tm__Zone'd calls may crash...
  1729. TelemetryShutdown();
  1730. }
  1731. else
  1732. {
  1733. if( !TelemetryInitialize() )
  1734. {
  1735. g_Telemetry.Level = 0;
  1736. }
  1737. else
  1738. {
  1739. tmPause( g_tmContext, 0 );
  1740. uint32 Level = MIN( g_Telemetry.Level, ARRAYSIZE( g_Telemetry.tmContext ) );
  1741. for( uint32 i = 0; i < Level; i++ )
  1742. {
  1743. g_Telemetry.tmContext[i] = g_tmContext;
  1744. }
  1745. }
  1746. }
  1747. // TM_SET_TIMELINE_SECTION_NAME( g_tmContext, "Level:0x%x", g_Telemetry.Level );
  1748. // To disable various telemetry features, use the tmEnable() function as so:
  1749. // TM_ENABLE( g_tmContext, TMO_SUPPORT_PLOT, 0 );
  1750. }
  1751. }
  1752. #endif // RAD_TELEMETRY_ENABLED