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 : #ifndef INCLUDED_PROFILE
23 : #define INCLUDED_PROFILE
24 :
25 : #include <vector>
26 :
27 : #include "lib/adts/ring_buf.h"
28 : #include "lib/posix/posix_pthread.h"
29 : #include "ps/Profiler2.h"
30 : #include "ps/Singleton.h"
31 :
32 :
33 : #define PROFILE_AMORTIZE_FRAMES 30
34 : #define PROFILE_AMORTIZE_TURNS 5
35 :
36 : class CProfileManager;
37 : class CProfileNodeTable;
38 :
39 : // To profile scripts usefully, we use a call hook that's called on every enter/exit,
40 : // and need to find the function name. But most functions are anonymous so we make do
41 : // with filename plus line number instead.
42 : // Computing the names is fairly expensive, and we need to return an interned char*
43 : // for the profiler to hold a copy of, so we use boost::flyweight to construct interned
44 : // strings per call location.
45 : //
46 : // TODO: Check again how much the overhead for getting filename and line really is and if
47 : // it has increased with the new approach after the SpiderMonkey 31 upgrade.
48 :
49 : class CProfileNode
50 : {
51 : NONCOPYABLE(CProfileNode);
52 : public:
53 : typedef std::vector<CProfileNode*>::iterator profile_iterator;
54 : typedef std::vector<CProfileNode*>::const_iterator const_profile_iterator;
55 :
56 : CProfileNode( const char* name, CProfileNode* parent );
57 : ~CProfileNode();
58 :
59 0 : const char* GetName() const { return name; }
60 :
61 : double GetFrameCalls() const;
62 : double GetFrameTime() const;
63 : double GetTurnCalls() const;
64 : double GetTurnTime() const;
65 :
66 : const CProfileNode* GetChild( const char* name ) const;
67 : const CProfileNode* GetScriptChild( const char* name ) const;
68 0 : const std::vector<CProfileNode*>* GetChildren() const { return( &children ); }
69 0 : const std::vector<CProfileNode*>* GetScriptChildren() const { return( &script_children ); }
70 :
71 : bool CanExpand();
72 :
73 : CProfileNode* GetChild( const char* name );
74 : CProfileNode* GetScriptChild( const char* name );
75 0 : CProfileNode* GetParent() const { return( parent ); }
76 :
77 : // Resets timing information for this node and all its children
78 : void Reset();
79 : // Resets frame timings for this node and all its children
80 : void Frame();
81 : // Resets turn timings for this node and all its children
82 : void Turn();
83 : // Enters the node
84 : void Call();
85 : // Leaves the node. Returns true if the node has actually been left
86 : bool Return();
87 :
88 : private:
89 : friend class CProfileManager;
90 : friend class CProfileNodeTable;
91 :
92 : const char* name;
93 :
94 : int calls_frame_current;
95 : int calls_turn_current;
96 : RingBuf<int, PROFILE_AMORTIZE_FRAMES> calls_per_frame;
97 : RingBuf<int, PROFILE_AMORTIZE_TURNS> calls_per_turn;
98 :
99 : double time_frame_current;
100 : double time_turn_current;
101 : RingBuf<double, PROFILE_AMORTIZE_FRAMES> time_per_frame;
102 : RingBuf<double, PROFILE_AMORTIZE_TURNS> time_per_turn;
103 :
104 : double start;
105 : int recursion;
106 :
107 : CProfileNode* parent;
108 : std::vector<CProfileNode*> children;
109 : std::vector<CProfileNode*> script_children;
110 : CProfileNodeTable* display_table;
111 : };
112 :
113 : class CProfileManager : public Singleton<CProfileManager>
114 : {
115 : public:
116 : CProfileManager();
117 : ~CProfileManager();
118 :
119 : // Begins timing for a named subsection
120 : void Start( const char* name );
121 : void StartScript( const char* name );
122 :
123 : // Ends timing for the current subsection
124 : void Stop();
125 :
126 : // Resets all timing information
127 : void Reset();
128 : // Resets frame timing information
129 : void Frame();
130 : // Resets turn timing information
131 : // (Must not be called before Frame)
132 : void Turn();
133 : // Resets absolutely everything, at the end of this frame
134 : void StructuralReset();
135 :
136 : inline const CProfileNode* GetCurrent() { return( current ); }
137 : inline const CProfileNode* GetRoot() { return( root ); }
138 :
139 : private:
140 : CProfileNode* root;
141 : CProfileNode* current;
142 :
143 : bool needs_structural_reset;
144 :
145 : void PerformStructuralReset();
146 : };
147 :
148 : #define g_Profiler CProfileManager::GetSingleton()
149 :
150 : class CProfileSample
151 : {
152 : public:
153 : CProfileSample(const char* name);
154 : ~CProfileSample();
155 : };
156 :
157 : // Put a PROFILE("xyz") block at the start of all code to be profiled.
158 : // Profile blocks last until the end of the containing scope.
159 : #define PROFILE(name) CProfileSample __profile(name)
160 : // Cheat a bit to make things slightly easier on the user
161 : #define PROFILE_START(name) { CProfileSample __profile(name)
162 : #define PROFILE_END(name) }
163 :
164 : // Do both old and new profilers simultaneously (1+2=3), for convenience.
165 : #define PROFILE3(name) PROFILE(name); PROFILE2(name)
166 :
167 : // Also do GPU
168 : #define PROFILE3_GPU(name) PROFILE(name); PROFILE2(name); PROFILE2_GPU(name)
169 :
170 : #endif // INCLUDED_PROFILE
|