LCOV - code coverage report
Current view: top level - source/ps - Profile.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 14 236 5.9 %
Date: 2022-06-14 00:41:00 Functions: 3 39 7.7 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : /*
      19             :  * GPG3-style hierarchical profiler
      20             :  */
      21             : 
      22             : #include "precompiled.h"
      23             : 
      24             : #include "Profile.h"
      25             : #include "ProfileViewer.h"
      26             : #include "ThreadUtil.h"
      27             : 
      28             : #include "lib/timer.h"
      29             : 
      30             : #include <numeric>
      31             : 
      32             : ///////////////////////////////////////////////////////////////////////////////////////////////
      33             : // CProfileNodeTable
      34             : 
      35             : 
      36             : 
      37             : /**
      38             :  * Class CProfileNodeTable: Implement ProfileViewer's AbstractProfileTable
      39             :  * interface in order to display profiling data in-game.
      40             :  */
      41             : class CProfileNodeTable : public AbstractProfileTable
      42             : {
      43             : public:
      44             :     CProfileNodeTable(CProfileNode* n);
      45             :     virtual ~CProfileNodeTable();
      46             : 
      47             :     // Implementation of AbstractProfileTable interface
      48             :     virtual CStr GetName();
      49             :     virtual CStr GetTitle();
      50             :     virtual size_t GetNumberRows();
      51             :     virtual const std::vector<ProfileColumn>& GetColumns();
      52             : 
      53             :     virtual CStr GetCellText(size_t row, size_t col);
      54             :     virtual AbstractProfileTable* GetChild(size_t row);
      55             :     virtual bool IsHighlightRow(size_t row);
      56             : 
      57             : private:
      58             :     /**
      59             :      * struct ColumnDescription: The only purpose of this helper structure
      60             :      * is to provide the global constructor that sets up the column
      61             :      * description.
      62             :      */
      63             :     struct ColumnDescription
      64             :     {
      65             :         std::vector<ProfileColumn> columns;
      66             : 
      67           1 :         ColumnDescription()
      68           1 :         {
      69           1 :             columns.push_back(ProfileColumn("Name", 230));
      70           1 :             columns.push_back(ProfileColumn("calls/frame", 80));
      71           1 :             columns.push_back(ProfileColumn("msec/frame", 80));
      72           1 :             columns.push_back(ProfileColumn("calls/turn", 80));
      73           1 :             columns.push_back(ProfileColumn("msec/turn", 80));
      74           1 :         }
      75             :     };
      76             : 
      77             :     /// The node represented by this table
      78             :     CProfileNode* node;
      79             : 
      80             :     /// Columns description (shared by all instances)
      81             :     static ColumnDescription columnDescription;
      82             : };
      83             : 
      84             : CProfileNodeTable::ColumnDescription CProfileNodeTable::columnDescription;
      85             : 
      86             : 
      87             : // Constructor/Destructor
      88           0 : CProfileNodeTable::CProfileNodeTable(CProfileNode* n)
      89             : {
      90           0 :     node = n;
      91           0 : }
      92             : 
      93           0 : CProfileNodeTable::~CProfileNodeTable()
      94             : {
      95           0 : }
      96           0 : 
      97             : // Short name (= name of profile node)
      98           0 : CStr CProfileNodeTable::GetName()
      99           0 : {
     100             :     return node->GetName();
     101           0 : }
     102             : 
     103             : // Title (= explanatory text plus time totals)
     104           0 : CStr CProfileNodeTable::GetTitle()
     105             : {
     106           0 :     char buf[512];
     107             :     sprintf_s(buf, ARRAY_SIZE(buf), "Profiling Information for: %s (Time in node: %.3f msec/frame)", node->GetName(), node->GetFrameTime() * 1000.0f );
     108             :     return buf;
     109             : }
     110           0 : 
     111             : // Total number of children
     112           0 : size_t CProfileNodeTable::GetNumberRows()
     113           0 : {
     114           0 :     return node->GetChildren()->size() + node->GetScriptChildren()->size() + 1;
     115             : }
     116             : 
     117             : // Column description
     118           0 : const std::vector<ProfileColumn>& CProfileNodeTable::GetColumns()
     119             : {
     120           0 :     return columnDescription.columns;
     121             : }
     122             : 
     123             : // Retrieve cell text
     124           0 : CStr CProfileNodeTable::GetCellText(size_t row, size_t col)
     125             : {
     126           0 :     CProfileNode* child;
     127             :     size_t nrchildren = node->GetChildren()->size();
     128             :     size_t nrscriptchildren = node->GetScriptChildren()->size();
     129             :     char buf[256] = "?";
     130           0 : 
     131             :     if (row < nrchildren)
     132           0 :         child = (*node->GetChildren())[row];
     133           0 :     else if (row < nrchildren + nrscriptchildren)
     134           0 :         child = (*node->GetScriptChildren())[row - nrchildren];
     135           0 :     else if (row > nrchildren + nrscriptchildren)
     136             :         return "!bad row!";
     137           0 :     else
     138           0 :     {
     139           0 :         // "unlogged" row
     140           0 :         if (col == 0)
     141           0 :             return "unlogged";
     142           0 :         else if (col == 1)
     143             :             return "";
     144             :         else if (col == 4)
     145             :             return "";
     146           0 : 
     147           0 :         double unlogged_time_frame = node->GetFrameTime();
     148           0 :         double unlogged_time_turn = node->GetTurnTime();
     149           0 :         CProfileNode::const_profile_iterator it;
     150           0 : 
     151           0 :         for (it = node->GetChildren()->begin(); it != node->GetChildren()->end(); ++it)
     152             :         {
     153           0 :             unlogged_time_frame -= (*it)->GetFrameTime();
     154           0 :             unlogged_time_turn -= (*it)->GetTurnTime();
     155             :         }
     156             :         for (it = node->GetScriptChildren()->begin(); it != node->GetScriptChildren()->end(); ++it)
     157           0 :         {
     158             :             unlogged_time_frame -= (*it)->GetFrameTime();
     159           0 :             unlogged_time_turn -= (*it)->GetTurnTime();
     160           0 :         }
     161             : 
     162           0 :         // The root node can't easily count per-turn values (since Turn isn't called until
     163             :         // halfway though a frame), so just reset them the zero to prevent weird displays
     164           0 :         if (!node->GetParent())
     165           0 :         {
     166             :             unlogged_time_turn = 0.0;
     167             :         }
     168             : 
     169             :         if (col == 2)
     170           0 :             sprintf_s(buf, ARRAY_SIZE(buf), "%.3f", unlogged_time_frame * 1000.0f);
     171             :         else if (col == 4)
     172             :             sprintf_s(buf, ARRAY_SIZE(buf), "%.3f", unlogged_time_turn * 1000.f);
     173             : 
     174             :         return CStr(buf);
     175           0 :     }
     176           0 : 
     177             :     switch(col)
     178             :     {
     179             :     default:
     180           0 :     case 0:
     181             :         return child->GetName();
     182             : 
     183           0 :     case 1:
     184             :         sprintf_s(buf, ARRAY_SIZE(buf), "%.1f", child->GetFrameCalls());
     185           0 :         break;
     186           0 :     case 2:
     187           0 :         sprintf_s(buf, ARRAY_SIZE(buf), "%.3f", child->GetFrameTime() * 1000.0f);
     188             :         break;
     189           0 :     case 3:
     190           0 :         sprintf_s(buf, ARRAY_SIZE(buf), "%.1f", child->GetTurnCalls());
     191             :         break;
     192           0 :     case 4:
     193           0 :         sprintf_s(buf, ARRAY_SIZE(buf), "%.3f", child->GetTurnTime() * 1000.0f);
     194             :         break;
     195           0 :     }
     196           0 :     return CStr(buf);
     197             : }
     198           0 : 
     199           0 : // Return a pointer to the child table if the child node is expandable
     200             : AbstractProfileTable* CProfileNodeTable::GetChild(size_t row)
     201             : {
     202           0 :     CProfileNode* child;
     203             :     size_t nrchildren = node->GetChildren()->size();
     204             :     size_t nrscriptchildren = node->GetScriptChildren()->size();
     205             : 
     206           0 :     if (row < nrchildren)
     207             :         child = (*node->GetChildren())[row];
     208           0 :     else if (row < nrchildren + nrscriptchildren)
     209           0 :         child = (*node->GetScriptChildren())[row - nrchildren];
     210           0 :     else
     211             :         return 0;
     212           0 : 
     213           0 :     if (child->CanExpand())
     214           0 :         return child->display_table;
     215           0 : 
     216             :     return 0;
     217             : }
     218             : 
     219           0 : // Highlight all script nodes
     220           0 : bool CProfileNodeTable::IsHighlightRow(size_t row)
     221             : {
     222             :     size_t nrchildren = node->GetChildren()->size();
     223             :     size_t nrscriptchildren = node->GetScriptChildren()->size();
     224             : 
     225             :     return (row >= nrchildren && row < (nrchildren + nrscriptchildren));
     226           0 : }
     227             : 
     228           0 : ///////////////////////////////////////////////////////////////////////////////////////////////
     229           0 : // CProfileNode implementation
     230             : 
     231           0 : 
     232             : // Note: As with the GPG profiler, name is assumed to be a pointer to a constant string; only pointer equality is checked.
     233             : CProfileNode::CProfileNode( const char* _name, CProfileNode* _parent )
     234             : {
     235             :     name = _name;
     236             :     recursion = 0;
     237             : 
     238             :     Reset();
     239           0 : 
     240             :     parent = _parent;
     241           0 : 
     242           0 :     display_table = new CProfileNodeTable(this);
     243             : }
     244           0 : 
     245             : CProfileNode::~CProfileNode()
     246           0 : {
     247             :     profile_iterator it;
     248           0 :     for( it = children.begin(); it != children.end(); ++it )
     249           0 :         delete( *it );
     250             :     for( it = script_children.begin(); it != script_children.end(); ++it )
     251           0 :         delete( *it );
     252             : 
     253           0 :     delete display_table;
     254           0 : }
     255           0 : 
     256           0 : template<typename T>
     257           0 : static double average(const T& collection)
     258             : {
     259           0 :     if (collection.empty())
     260           0 :         return 0.0;
     261             :     return std::accumulate(collection.begin(), collection.end(), 0.0) / collection.size();
     262             : }
     263             : 
     264             : double CProfileNode::GetFrameCalls() const
     265           0 : {
     266             :     return average(calls_per_frame);
     267           0 : }
     268             : 
     269             : double CProfileNode::GetFrameTime() const
     270           0 : {
     271             :     return average(time_per_frame);
     272           0 : }
     273             : 
     274             : double CProfileNode::GetTurnCalls() const
     275           0 : {
     276             :     return average(calls_per_turn);
     277           0 : }
     278             : 
     279             : double CProfileNode::GetTurnTime() const
     280           0 : {
     281             :     return average(time_per_turn);
     282           0 : }
     283             : 
     284             : const CProfileNode* CProfileNode::GetChild( const char* childName ) const
     285           0 : {
     286             :     const_profile_iterator it;
     287           0 :     for( it = children.begin(); it != children.end(); ++it )
     288             :         if( (*it)->name == childName )
     289             :             return( *it );
     290           0 : 
     291             :     return( NULL );
     292           0 : }
     293           0 : 
     294           0 : const CProfileNode* CProfileNode::GetScriptChild( const char* childName ) const
     295             : {
     296             :     const_profile_iterator it;
     297             :     for( it = script_children.begin(); it != script_children.end(); ++it )
     298             :         if( (*it)->name == childName )
     299             :             return( *it );
     300           0 : 
     301             :     return( NULL );
     302           0 : }
     303           0 : 
     304           0 : CProfileNode* CProfileNode::GetChild( const char* childName )
     305             : {
     306             :     profile_iterator it;
     307             :     for( it = children.begin(); it != children.end(); ++it )
     308             :         if( (*it)->name == childName )
     309             :             return( *it );
     310           0 : 
     311             :     CProfileNode* newNode = new CProfileNode( childName, this );
     312           0 :     children.push_back( newNode );
     313           0 :     return( newNode );
     314           0 : }
     315             : 
     316             : CProfileNode* CProfileNode::GetScriptChild( const char* childName )
     317           0 : {
     318           0 :     profile_iterator it;
     319           0 :     for( it = script_children.begin(); it != script_children.end(); ++it )
     320             :         if( (*it)->name == childName )
     321             :             return( *it );
     322           0 : 
     323             :     CProfileNode* newNode = new CProfileNode( childName, this );
     324           0 :     script_children.push_back( newNode );
     325           0 :     return( newNode );
     326           0 : }
     327             : 
     328             : bool CProfileNode::CanExpand()
     329           0 : {
     330           0 :     return( !( children.empty() && script_children.empty() ) );
     331           0 : }
     332             : 
     333             : void CProfileNode::Reset()
     334           0 : {
     335             :     calls_per_frame.clear();
     336           0 :     calls_per_turn.clear();
     337             :     calls_frame_current = 0;
     338             :     calls_turn_current = 0;
     339           0 : 
     340             :     time_per_frame.clear();
     341           0 :     time_per_turn.clear();
     342           0 :     time_frame_current = 0.0;
     343           0 :     time_turn_current = 0.0;
     344           0 : 
     345             :     profile_iterator it;
     346           0 :     for (it = children.begin(); it != children.end(); ++it)
     347           0 :         (*it)->Reset();
     348           0 :     for (it = script_children.begin(); it != script_children.end(); ++it)
     349           0 :         (*it)->Reset();
     350             : }
     351           0 : 
     352           0 : void CProfileNode::Frame()
     353           0 : {
     354           0 :     calls_per_frame.push_back(calls_frame_current);
     355           0 :     time_per_frame.push_back(time_frame_current);
     356           0 : 
     357             :     calls_frame_current = 0;
     358           0 :     time_frame_current = 0.0;
     359             : 
     360           0 :     profile_iterator it;
     361           0 :     for (it = children.begin(); it != children.end(); ++it)
     362             :         (*it)->Frame();
     363           0 :     for (it = script_children.begin(); it != script_children.end(); ++it)
     364           0 :         (*it)->Frame();
     365             : }
     366           0 : 
     367           0 : void CProfileNode::Turn()
     368           0 : {
     369           0 :     calls_per_turn.push_back(calls_turn_current);
     370           0 :     time_per_turn.push_back(time_turn_current);
     371           0 : 
     372             :     calls_turn_current = 0;
     373           0 :     time_turn_current = 0.0;
     374             : 
     375           0 :     profile_iterator it;
     376           0 :     for (it = children.begin(); it != children.end(); ++it)
     377             :         (*it)->Turn();
     378           0 :     for (it = script_children.begin(); it != script_children.end(); ++it)
     379           0 :         (*it)->Turn();
     380             : }
     381           0 : 
     382           0 : void CProfileNode::Call()
     383           0 : {
     384           0 :     calls_frame_current++;
     385           0 :     calls_turn_current++;
     386           0 :     if (recursion++ == 0)
     387             :     {
     388           0 :         start = timer_Time();
     389             :     }
     390           0 : }
     391           0 : 
     392           0 : bool CProfileNode::Return()
     393             : {
     394           0 :     if (--recursion != 0)
     395             :         return false;
     396           0 : 
     397             :     double now = timer_Time();
     398           0 :     time_frame_current += (now - start);
     399             :     time_turn_current += (now - start);
     400           0 :     return true;
     401             : }
     402             : 
     403           0 : CProfileManager::CProfileManager() :
     404           0 :     root(NULL), current(NULL), needs_structural_reset(false)
     405           0 : {
     406           0 :     PerformStructuralReset();
     407             : }
     408             : 
     409           0 : CProfileManager::~CProfileManager()
     410           0 : {
     411             :     delete root;
     412           0 : }
     413           0 : 
     414             : void CProfileManager::Start( const char* name )
     415           0 : {
     416             :     if( name != current->GetName() )
     417           0 :         current = current->GetChild( name );
     418           0 :     current->Call();
     419             : }
     420           0 : 
     421             : void CProfileManager::StartScript( const char* name )
     422           0 : {
     423           0 :     if( name != current->GetName() )
     424           0 :         current = current->GetScriptChild( name );
     425           0 :     current->Call();
     426             : }
     427           0 : 
     428             : void CProfileManager::Stop()
     429           0 : {
     430           0 :     if (current->Return())
     431           0 :         current = current->GetParent();
     432           0 : }
     433             : 
     434           0 : void CProfileManager::Reset()
     435             : {
     436           0 :     root->Reset();
     437           0 : }
     438           0 : 
     439             : void CProfileManager::Frame()
     440           0 : {
     441             :     root->time_frame_current += (timer_Time() - root->start);
     442           0 : 
     443           0 :     root->Frame();
     444             : 
     445           0 :     if (needs_structural_reset)
     446             :     {
     447           0 :         PerformStructuralReset();
     448             :         needs_structural_reset = false;
     449           0 :     }
     450             : 
     451           0 :     root->start = timer_Time();
     452             : }
     453           0 : 
     454           0 : void CProfileManager::Turn()
     455             : {
     456             :     root->Turn();
     457           0 : }
     458           0 : 
     459             : void CProfileManager::StructuralReset()
     460           0 : {
     461             :     // We can't immediately perform the reset, because we're probably already
     462           0 :     // nested inside the profile tree and it will get very confused if we delete
     463           0 :     // the tree when we're not currently at the root.
     464             :     // So just set a flag to perform the reset at the end of the frame.
     465           0 : 
     466             :     needs_structural_reset = true;
     467             : }
     468             : 
     469             : void CProfileManager::PerformStructuralReset()
     470             : {
     471             :     delete root;
     472           0 :     root = new CProfileNode("root", NULL);
     473           0 :     root->Call();
     474             :     current = root;
     475           0 :     g_ProfileViewer.AddRootTable(root->display_table, true);
     476             : }
     477           0 : 
     478           0 : CProfileSample::CProfileSample(const char* name)
     479           0 : {
     480           0 :     if (CProfileManager::IsInitialised())
     481           0 :     {
     482           0 :         // The profiler is only safe to use on the main thread
     483             :         if(Threading::IsMainThread())
     484        3163 :             g_Profiler.Start(name);
     485             :     }
     486        6326 : }
     487             : 
     488             : CProfileSample::~CProfileSample()
     489           0 : {
     490           0 :     if (CProfileManager::IsInitialised())
     491             :         if(Threading::IsMainThread())
     492        3163 :             g_Profiler.Stop();
     493             : }

Generated by: LCOV version 1.13