3 * Base class for profiling.
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.
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.
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
22 * @defgroup Profiler Profiler
26 * Profiler base class that defines the interface and some trivial
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 /** @var IContextSource Current request context */
39 protected $context = null;
40 /** @var TransactionProfiler */
41 protected $trxProfiler;
43 private static $instance = null;
46 * @param array $params
48 public function __construct( array $params ) {
49 if ( isset( $params['profileID'] ) ) {
50 $this->profileID
= $params['profileID'];
52 $this->params
= $params;
53 $this->trxProfiler
= new TransactionProfiler();
60 final public static function instance() {
61 if ( self
::$instance === null ) {
62 global $wgProfiler, $wgProfileLimit;
65 'class' => 'ProfilerStub',
67 'threshold' => $wgProfileLimit,
70 if ( is_array( $wgProfiler ) ) {
71 $params = array_merge( $params, $wgProfiler );
74 $inSample = mt_rand( 0, $params['sampling'] - 1 ) === 0;
75 if ( PHP_SAPI
=== 'cli' ||
!$inSample ) {
76 $params['class'] = 'ProfilerStub';
79 if ( !is_array( $params['output'] ) ) {
80 $params['output'] = array( $params['output'] );
83 self
::$instance = new $params['class']( $params );
85 return self
::$instance;
89 * Replace the current profiler with $profiler if no non-stub profiler is set
91 * @param Profiler $profiler
95 final public static function replaceStubInstance( Profiler
$profiler ) {
96 if ( self
::$instance && !( self
::$instance instanceof ProfilerStub
) ) {
97 throw new MWException( 'Could not replace non-stub profiler instance.' );
99 self
::$instance = $profiler;
106 public function setProfileID( $id ) {
107 $this->profileID
= $id;
113 public function getProfileID() {
114 if ( $this->profileID
=== false ) {
117 return $this->profileID
;
122 * Sets the context for this Profiler
124 * @param IContextSource $context
127 public function setContext( $context ) {
128 $this->context
= $context;
132 * Gets the context for this Profiler
134 * @return IContextSource
137 public function getContext() {
138 if ( $this->context
) {
139 return $this->context
;
141 wfDebug( __METHOD__
. " called and \$context is null. " .
142 "Return RequestContext::getMain(); for sanity\n" );
143 return RequestContext
::getMain();
147 // Kept BC for now, remove when possible
148 public function profileIn( $functionname ) {}
149 public function profileOut( $functionname ) {}
152 * Mark the start of a custom profiling frame (e.g. DB queries).
153 * The frame ends when the result of this method falls out of scope.
155 * @param string $section
156 * @return ScopedCallback|null
159 abstract public function scopedProfileIn( $section );
162 * @param ScopedCallback $section
164 public function scopedProfileOut( ScopedCallback
&$section = null ) {
169 * @return TransactionProfiler
172 public function getTransactionProfiler() {
173 return $this->trxProfiler
;
177 * Close opened profiling sections
179 abstract public function close();
182 * Get all usable outputs.
184 * @throws MWException
185 * @return array Array of ProfilerOutput instances.
188 private function getOutputs() {
190 foreach ( $this->params
['output'] as $outputType ) {
191 // The class may be specified as either the full class name (for
192 // example, 'ProfilerOutputUdp') or (for backward compatibility)
193 // the trailing portion of the class name (for example, 'udp').
194 $outputClass = strpos( $outputType, 'ProfilerOutput' ) === false
195 ?
'ProfilerOutput' . ucfirst( $outputType )
197 if ( !class_exists( $outputClass ) ) {
198 throw new MWException( "'$outputType' is an invalid output type" );
200 $outputInstance = new $outputClass( $this, $this->params
);
201 if ( $outputInstance->canUse() ) {
202 $outputs[] = $outputInstance;
209 * Log the data to some store or even the page output
213 public function logData() {
214 $request = $this->getContext()->getRequest();
216 $timeElapsed = $request->getElapsedTime();
217 $timeElapsedThreshold = $this->params
['threshold'];
218 if ( $timeElapsed <= $timeElapsedThreshold ) {
222 $outputs = $this->getOutputs();
227 $stats = $this->getFunctionStats();
228 foreach ( $outputs as $output ) {
229 $output->log( $stats );
234 * Output current data to the page output if configured to do so
236 * @throws MWException
239 public function logDataPageOutputOnly() {
240 foreach ( $this->getOutputs() as $output ) {
241 if ( $output instanceof ProfilerOutputText
) {
242 $stats = $this->getFunctionStats();
243 $output->log( $stats );
249 * Get the content type sent out to the client.
250 * Used for profilers that output instead of store data.
254 public function getContentType() {
255 foreach ( headers_list() as $header ) {
256 if ( preg_match( '#^content-type: (\w+/\w+);?#i', $header, $m ) ) {
264 * Mark this call as templated or not
268 public function setTemplated( $t ) {
269 $this->templated
= $t;
273 * Was this call as templated or not
277 public function getTemplated() {
278 return $this->templated
;
282 * Get the aggregated inclusive profiling data for each method
284 * The percent time for each time is based on the current "total" time
285 * used is based on all methods so far. This method can therefore be
286 * called several times in between several profiling calls without the
287 * delays in usage of the profiler skewing the results. A "-total" entry
288 * is always included in the results.
290 * When a call chain involves a method invoked within itself, any
291 * entries for the cyclic invocation should be be demarked with "@".
292 * This makes filtering them out easier and follows the xhprof style.
294 * @return array List of method entries arrays, each having:
295 * - name : method name
296 * - calls : the number of invoking calls
297 * - real : real time ellapsed (ms)
298 * - %real : percent real time
299 * - cpu : CPU time ellapsed (ms)
300 * - %cpu : percent CPU time
301 * - memory : memory used (bytes)
302 * - %memory : percent memory used
303 * - min_real : min real time in a call (ms)
304 * - max_real : max real time in a call (ms)
307 abstract public function getFunctionStats();
310 * Returns a profiling output to be stored in debug file
314 abstract public function getOutput();