Merge "mw.ui: button: Update focus state"
[lhc/web/wiklou.git] / includes / profiler / Profiler.php
1 <?php
2 /**
3 * Base class for profiling.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Profiler
22 * @defgroup Profiler Profiler
23 */
24
25 /**
26 * Profiler base class that defines the interface and some trivial
27 * functionality
28 *
29 * @ingroup Profiler
30 */
31 abstract class Profiler {
32 /** @var string|bool Profiler ID for bucketing data */
33 protected $profileID = false;
34 /** @var bool Whether MediaWiki is in a SkinTemplate output context */
35 protected $templated = false;
36 /** @var array All of the params passed from $wgProfiler */
37 protected $params = array();
38
39 /** @var TransactionProfiler */
40 protected $trxProfiler;
41
42 /**
43 * @var array Mapping of output type to class name
44 */
45 private static $outputTypes = array(
46 'db' => 'ProfilerOutputDb',
47 'text' => 'ProfilerOutputText',
48 'udp' => 'ProfilerOutputUdp',
49 'dump' => 'ProfilerOutputDump',
50 );
51
52 /** @var Profiler */
53 private static $instance = null;
54
55 /**
56 * @param array $params
57 */
58 public function __construct( array $params ) {
59 if ( isset( $params['profileID'] ) ) {
60 $this->profileID = $params['profileID'];
61 }
62 $this->params = $params;
63 $this->trxProfiler = new TransactionProfiler();
64 }
65
66 /**
67 * Singleton
68 * @return Profiler
69 */
70 final public static function instance() {
71 if ( self::$instance === null ) {
72 global $wgProfiler;
73 if ( is_array( $wgProfiler ) ) {
74 $class = isset( $wgProfiler['class'] ) ? $wgProfiler['class'] : 'ProfilerStub';
75 $factor = isset( $wgProfiler['sampling'] ) ? $wgProfiler['sampling'] : 1;
76 if ( PHP_SAPI === 'cli' || mt_rand( 0, $factor - 1 ) != 0 ) {
77 $class = 'ProfilerStub';
78 }
79 self::$instance = new $class( $wgProfiler );
80 } else {
81 self::$instance = new ProfilerStub( array() );
82 }
83 }
84 return self::$instance;
85 }
86
87 /**
88 * Replace the current profiler with $profiler if no non-stub profiler is set
89 *
90 * @param Profiler $profiler
91 * @throws MWException
92 * @since 1.25
93 */
94 final public static function replaceStubInstance( Profiler $profiler ) {
95 if ( self::$instance && !( self::$instance instanceof ProfilerStub ) ) {
96 throw new MWException( 'Could not replace non-stub profiler instance.' );
97 } else {
98 self::$instance = $profiler;
99 }
100 }
101
102 /**
103 * @param string $id
104 */
105 public function setProfileID( $id ) {
106 $this->profileID = $id;
107 }
108
109 /**
110 * @return string
111 */
112 public function getProfileID() {
113 if ( $this->profileID === false ) {
114 return wfWikiID();
115 } else {
116 return $this->profileID;
117 }
118 }
119
120 // Kept BC for now, remove when possible
121 public function profileIn( $functionname ) {}
122 public function profileOut( $functionname ) {}
123
124 /**
125 * Mark the start of a custom profiling frame (e.g. DB queries).
126 * The frame ends when the result of this method falls out of scope.
127 *
128 * @param string $section
129 * @return ScopedCallback|null
130 * @since 1.25
131 */
132 abstract public function scopedProfileIn( $section );
133
134 /**
135 * @param ScopedCallback $section
136 */
137 public function scopedProfileOut( ScopedCallback &$section ) {
138 $section = null;
139 }
140
141 /**
142 * @return TransactionProfiler
143 * @since 1.25
144 */
145 public function getTransactionProfiler() {
146 return $this->trxProfiler;
147 }
148
149 /**
150 * Close opened profiling sections
151 */
152 abstract public function close();
153
154 /**
155 * Log the data to some store or even the page output
156 *
157 * @throws MWException
158 * @since 1.25
159 */
160 public function logData() {
161 $output = isset( $this->params['output'] ) ? $this->params['output'] : null;
162
163 if ( !$output || $this instanceof ProfilerStub ) {
164 // return early when no output classes defined or we're a stub
165 return;
166 }
167
168 if ( !is_array( $output ) ) {
169 $output = array( $output );
170 }
171 $stats = null;
172 foreach ( $output as $outType ) {
173 if ( !isset( self::$outputTypes[$outType] ) ) {
174 throw new MWException( "'$outType' is an invalid output type" );
175 }
176 $class = self::$outputTypes[$outType];
177
178 /** @var ProfilerOutput $profileOut */
179 $profileOut = new $class( $this, $this->params );
180 if ( $profileOut->canUse() ) {
181 if ( is_null( $stats ) ) {
182 $stats = $this->getFunctionStats();
183 }
184 $profileOut->log( $stats );
185 }
186 }
187 }
188
189 /**
190 * Get the content type sent out to the client.
191 * Used for profilers that output instead of store data.
192 * @return string
193 * @since 1.25
194 */
195 public function getContentType() {
196 foreach ( headers_list() as $header ) {
197 if ( preg_match( '#^content-type: (\w+/\w+);?#i', $header, $m ) ) {
198 return $m[1];
199 }
200 }
201 return null;
202 }
203
204 /**
205 * Mark this call as templated or not
206 *
207 * @param bool $t
208 */
209 public function setTemplated( $t ) {
210 $this->templated = $t;
211 }
212
213 /**
214 * Was this call as templated or not
215 *
216 * @return bool
217 */
218 public function getTemplated() {
219 return $this->templated;
220 }
221
222 /**
223 * Get the aggregated inclusive profiling data for each method
224 *
225 * The percent time for each time is based on the current "total" time
226 * used is based on all methods so far. This method can therefore be
227 * called several times in between several profiling calls without the
228 * delays in usage of the profiler skewing the results. A "-total" entry
229 * is always included in the results.
230 *
231 * When a call chain involves a method invoked within itself, any
232 * entries for the cyclic invocation should be be demarked with "@".
233 * This makes filtering them out easier and follows the xhprof style.
234 *
235 * @return array List of method entries arrays, each having:
236 * - name : method name
237 * - calls : the number of invoking calls
238 * - real : real time ellapsed (ms)
239 * - %real : percent real time
240 * - cpu : CPU time ellapsed (ms)
241 * - %cpu : percent CPU time
242 * - memory : memory used (bytes)
243 * - %memory : percent memory used
244 * - min_real : min real time in a call (ms)
245 * - max_real : max real time in a call (ms)
246 * @since 1.25
247 */
248 abstract public function getFunctionStats();
249
250 /**
251 * Returns a profiling output to be stored in debug file
252 *
253 * @return string
254 */
255 abstract public function getOutput();
256 }