/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ /* * GPG3-style hierarchical profiler */ #ifndef INCLUDED_PROFILE #define INCLUDED_PROFILE #include #include "lib/adts/ring_buf.h" #include "lib/posix/posix_pthread.h" #include "ps/Profiler2.h" #include "ps/Singleton.h" #define PROFILE_AMORTIZE_FRAMES 30 #define PROFILE_AMORTIZE_TURNS 1 class CProfileManager; class CProfileNodeTable; // To profile scripts usefully, we use a call hook that's called on every enter/exit, // and need to find the function name. But most functions are anonymous so we make do // with filename plus line number instead. // Computing the names is fairly expensive, and we need to return an interned char* // for the profiler to hold a copy of, so we use boost::flyweight to construct interned // strings per call location. // // TODO: Check again how much the overhead for getting filename and line really is and if // it has increased with the new approach after the SpiderMonkey 31 upgrade. class CProfileNode { NONCOPYABLE(CProfileNode); public: typedef std::vector::iterator profile_iterator; typedef std::vector::const_iterator const_profile_iterator; CProfileNode( const char* name, CProfileNode* parent ); ~CProfileNode(); const char* GetName() const { return name; } double GetFrameCalls() const; double GetFrameTime() const; double GetTurnCalls() const; double GetTurnTime() const; double GetFrameMallocs() const; double GetTurnMallocs() const; const CProfileNode* GetChild( const char* name ) const; const CProfileNode* GetScriptChild( const char* name ) const; const std::vector* GetChildren() const { return( &children ); } const std::vector* GetScriptChildren() const { return( &script_children ); } bool CanExpand(); CProfileNode* GetChild( const char* name ); CProfileNode* GetScriptChild( const char* name ); CProfileNode* GetParent() const { return( parent ); } // Resets timing information for this node and all its children void Reset(); // Resets frame timings for this node and all its children void Frame(); // Resets turn timings for this node and all its children void Turn(); // Enters the node void Call(); // Leaves the node. Returns true if the node has actually been left bool Return(); private: friend class CProfileManager; friend class CProfileNodeTable; const char* name; int calls_frame_current; int calls_turn_current; RingBuf calls_per_frame; RingBuf calls_per_turn; double time_frame_current; double time_turn_current; RingBuf time_per_frame; RingBuf time_per_turn; long mallocs_frame_current; long mallocs_turn_current; RingBuf mallocs_per_frame; RingBuf mallocs_per_turn; double start; long start_mallocs; int recursion; CProfileNode* parent; std::vector children; std::vector script_children; CProfileNodeTable* display_table; }; class CProfileManager : public Singleton { public: CProfileManager(); ~CProfileManager(); // Begins timing for a named subsection void Start( const char* name ); void StartScript( const char* name ); // Ends timing for the current subsection void Stop(); // Resets all timing information void Reset(); // Resets frame timing information void Frame(); // Resets turn timing information // (Must not be called before Frame) void Turn(); // Resets absolutely everything, at the end of this frame void StructuralReset(); inline const CProfileNode* GetCurrent() { return( current ); } inline const CProfileNode* GetRoot() { return( root ); } private: CProfileNode* root; CProfileNode* current; bool needs_structural_reset; void PerformStructuralReset(); }; #define g_Profiler CProfileManager::GetSingleton() class CProfileSample { public: CProfileSample(const char* name); ~CProfileSample(); }; // Put a PROFILE("xyz") block at the start of all code to be profiled. // Profile blocks last until the end of the containing scope. #define PROFILE(name) CProfileSample __profile(name) // Cheat a bit to make things slightly easier on the user #define PROFILE_START(name) { CProfileSample __profile(name) #define PROFILE_END(name) } // Do both old and new profilers simultaneously (1+2=3), for convenience. #define PROFILE3(name) PROFILE(name); PROFILE2(name) // Also do GPU #define PROFILE3_GPU(name) PROFILE(name); PROFILE2(name); PROFILE2_GPU(name) #endif // INCLUDED_PROFILE