]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/profiler/Profiler.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.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 use Wikimedia\ScopedCallback;
25 use Wikimedia\Rdbms\TransactionProfiler;
26
27 /**
28  * Profiler base class that defines the interface and some trivial
29  * functionality
30  *
31  * @ingroup Profiler
32  */
33 abstract class Profiler {
34         /** @var string|bool Profiler ID for bucketing data */
35         protected $profileID = false;
36         /** @var bool Whether MediaWiki is in a SkinTemplate output context */
37         protected $templated = false;
38         /** @var array All of the params passed from $wgProfiler */
39         protected $params = [];
40         /** @var IContextSource Current request context */
41         protected $context = null;
42         /** @var TransactionProfiler */
43         protected $trxProfiler;
44         /** @var Profiler */
45         private static $instance = null;
46
47         /**
48          * @param array $params
49          */
50         public function __construct( array $params ) {
51                 if ( isset( $params['profileID'] ) ) {
52                         $this->profileID = $params['profileID'];
53                 }
54                 $this->params = $params;
55                 $this->trxProfiler = new TransactionProfiler();
56         }
57
58         /**
59          * Singleton
60          * @return Profiler
61          */
62         final public static function instance() {
63                 if ( self::$instance === null ) {
64                         global $wgProfiler, $wgProfileLimit;
65
66                         $params = [
67                                 'class'     => 'ProfilerStub',
68                                 'sampling'  => 1,
69                                 'threshold' => $wgProfileLimit,
70                                 'output'    => [],
71                         ];
72                         if ( is_array( $wgProfiler ) ) {
73                                 $params = array_merge( $params, $wgProfiler );
74                         }
75
76                         $inSample = mt_rand( 0, $params['sampling'] - 1 ) === 0;
77                         if ( PHP_SAPI === 'cli' || !$inSample ) {
78                                 $params['class'] = 'ProfilerStub';
79                         }
80
81                         if ( !is_array( $params['output'] ) ) {
82                                 $params['output'] = [ $params['output'] ];
83                         }
84
85                         self::$instance = new $params['class']( $params );
86                 }
87                 return self::$instance;
88         }
89
90         /**
91          * Replace the current profiler with $profiler if no non-stub profiler is set
92          *
93          * @param Profiler $profiler
94          * @throws MWException
95          * @since 1.25
96          */
97         final public static function replaceStubInstance( Profiler $profiler ) {
98                 if ( self::$instance && !( self::$instance instanceof ProfilerStub ) ) {
99                         throw new MWException( 'Could not replace non-stub profiler instance.' );
100                 } else {
101                         self::$instance = $profiler;
102                 }
103         }
104
105         /**
106          * @param string $id
107          */
108         public function setProfileID( $id ) {
109                 $this->profileID = $id;
110         }
111
112         /**
113          * @return string
114          */
115         public function getProfileID() {
116                 if ( $this->profileID === false ) {
117                         return wfWikiID();
118                 } else {
119                         return $this->profileID;
120                 }
121         }
122
123         /**
124          * Sets the context for this Profiler
125          *
126          * @param IContextSource $context
127          * @since 1.25
128          */
129         public function setContext( $context ) {
130                 $this->context = $context;
131         }
132
133         /**
134          * Gets the context for this Profiler
135          *
136          * @return IContextSource
137          * @since 1.25
138          */
139         public function getContext() {
140                 if ( $this->context ) {
141                         return $this->context;
142                 } else {
143                         wfDebug( __METHOD__ . " called and \$context is null. " .
144                                 "Return RequestContext::getMain(); for sanity\n" );
145                         return RequestContext::getMain();
146                 }
147         }
148
149         // Kept BC for now, remove when possible
150         public function profileIn( $functionname ) {
151         }
152
153         public function profileOut( $functionname ) {
154         }
155
156         /**
157          * Mark the start of a custom profiling frame (e.g. DB queries).
158          * The frame ends when the result of this method falls out of scope.
159          *
160          * @param string $section
161          * @return ScopedCallback|null
162          * @since 1.25
163          */
164         abstract public function scopedProfileIn( $section );
165
166         /**
167          * @param SectionProfileCallback &$section
168          */
169         public function scopedProfileOut( SectionProfileCallback &$section = null ) {
170                 $section = null;
171         }
172
173         /**
174          * @return TransactionProfiler
175          * @since 1.25
176          */
177         public function getTransactionProfiler() {
178                 return $this->trxProfiler;
179         }
180
181         /**
182          * Close opened profiling sections
183          */
184         abstract public function close();
185
186         /**
187          * Get all usable outputs.
188          *
189          * @throws MWException
190          * @return array Array of ProfilerOutput instances.
191          * @since 1.25
192          */
193         private function getOutputs() {
194                 $outputs = [];
195                 foreach ( $this->params['output'] as $outputType ) {
196                         // The class may be specified as either the full class name (for
197                         // example, 'ProfilerOutputStats') or (for backward compatibility)
198                         // the trailing portion of the class name (for example, 'stats').
199                         $outputClass = strpos( $outputType, 'ProfilerOutput' ) === false
200                                 ? 'ProfilerOutput' . ucfirst( $outputType )
201                                 : $outputType;
202                         if ( !class_exists( $outputClass ) ) {
203                                 throw new MWException( "'$outputType' is an invalid output type" );
204                         }
205                         $outputInstance = new $outputClass( $this, $this->params );
206                         if ( $outputInstance->canUse() ) {
207                                 $outputs[] = $outputInstance;
208                         }
209                 }
210                 return $outputs;
211         }
212
213         /**
214          * Log the data to some store or even the page output
215          *
216          * @since 1.25
217          */
218         public function logData() {
219                 $request = $this->getContext()->getRequest();
220
221                 $timeElapsed = $request->getElapsedTime();
222                 $timeElapsedThreshold = $this->params['threshold'];
223                 if ( $timeElapsed <= $timeElapsedThreshold ) {
224                         return;
225                 }
226
227                 $outputs = $this->getOutputs();
228                 if ( !$outputs ) {
229                         return;
230                 }
231
232                 $stats = $this->getFunctionStats();
233                 foreach ( $outputs as $output ) {
234                         $output->log( $stats );
235                 }
236         }
237
238         /**
239          * Output current data to the page output if configured to do so
240          *
241          * @throws MWException
242          * @since 1.26
243          */
244         public function logDataPageOutputOnly() {
245                 foreach ( $this->getOutputs() as $output ) {
246                         if ( $output instanceof ProfilerOutputText ) {
247                                 $stats = $this->getFunctionStats();
248                                 $output->log( $stats );
249                         }
250                 }
251         }
252
253         /**
254          * Get the content type sent out to the client.
255          * Used for profilers that output instead of store data.
256          * @return string
257          * @since 1.25
258          */
259         public function getContentType() {
260                 foreach ( headers_list() as $header ) {
261                         if ( preg_match( '#^content-type: (\w+/\w+);?#i', $header, $m ) ) {
262                                 return $m[1];
263                         }
264                 }
265                 return null;
266         }
267
268         /**
269          * Mark this call as templated or not
270          *
271          * @param bool $t
272          */
273         public function setTemplated( $t ) {
274                 $this->templated = $t;
275         }
276
277         /**
278          * Was this call as templated or not
279          *
280          * @return bool
281          */
282         public function getTemplated() {
283                 return $this->templated;
284         }
285
286         /**
287          * Get the aggregated inclusive profiling data for each method
288          *
289          * The percent time for each time is based on the current "total" time
290          * used is based on all methods so far. This method can therefore be
291          * called several times in between several profiling calls without the
292          * delays in usage of the profiler skewing the results. A "-total" entry
293          * is always included in the results.
294          *
295          * When a call chain involves a method invoked within itself, any
296          * entries for the cyclic invocation should be be demarked with "@".
297          * This makes filtering them out easier and follows the xhprof style.
298          *
299          * @return array List of method entries arrays, each having:
300          *   - name     : method name
301          *   - calls    : the number of invoking calls
302          *   - real     : real time elapsed (ms)
303          *   - %real    : percent real time
304          *   - cpu      : CPU time elapsed (ms)
305          *   - %cpu     : percent CPU time
306          *   - memory   : memory used (bytes)
307          *   - %memory  : percent memory used
308          *   - min_real : min real time in a call (ms)
309          *   - max_real : max real time in a call (ms)
310          * @since 1.25
311          */
312         abstract public function getFunctionStats();
313
314         /**
315          * Returns a profiling output to be stored in debug file
316          *
317          * @return string
318          */
319         abstract public function getOutput();
320 }