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();
39 /** @var TransactionProfiler */
40 protected $trxProfiler;
43 * @var array Mapping of output type to class name
45 private static $outputTypes = array(
46 'db' => 'ProfilerOutputDb',
47 'text' => 'ProfilerOutputText',
48 'udp' => 'ProfilerOutputUdp',
52 private static $instance = null;
55 * @param array $params
57 public function __construct( array $params ) {
58 if ( isset( $params['profileID'] ) ) {
59 $this->profileID
= $params['profileID'];
61 $this->params
= $params;
62 $this->trxProfiler
= new TransactionProfiler();
69 final public static function instance() {
70 if ( self
::$instance === null ) {
72 if ( is_array( $wgProfiler ) ) {
73 $class = isset( $wgProfiler['class'] ) ?
$wgProfiler['class'] : 'ProfilerStub';
74 $factor = isset( $wgProfiler['sampling'] ) ?
$wgProfiler['sampling'] : 1;
75 if ( PHP_SAPI
=== 'cli' ||
mt_rand( 0, $factor - 1 ) != 0 ) {
76 $class = 'ProfilerStub';
78 self
::$instance = new $class( $wgProfiler );
80 self
::$instance = new ProfilerStub( array() );
83 return self
::$instance;
87 * Replace the current profiler with $profiler if no non-stub profiler is set
89 * @param Profiler $profiler
93 final public static function replaceStubInstance( Profiler
$profiler ) {
94 if ( self
::$instance && !( self
::$instance instanceof ProfilerStub
) ) {
95 throw new MWException( 'Could not replace non-stub profiler instance.' );
97 self
::$instance = $profiler;
104 public function setProfileID( $id ) {
105 $this->profileID
= $id;
111 public function getProfileID() {
112 if ( $this->profileID
=== false ) {
115 return $this->profileID
;
119 // Kept BC for now, remove when possible
120 public function profileIn( $functionname ) {}
121 public function profileOut( $functionname ) {}
124 * Mark the start of a custom profiling frame (e.g. DB queries).
125 * The frame ends when the result of this method falls out of scope.
127 * @param string $section
128 * @return ScopedCallback|null
131 abstract public function scopedProfileIn( $section );
134 * @param ScopedCallback $section
136 public function scopedProfileOut( ScopedCallback
&$section ) {
141 * @return TransactionProfiler
144 public function getTransactionProfiler() {
145 return $this->trxProfiler
;
149 * Close opened profiling sections
151 abstract public function close();
154 * Log the data to some store or even the page output
156 * @throws MWException
159 public function logData() {
160 $output = isset( $this->params
['output'] ) ?
$this->params
['output'] : null;
162 if ( !$output ||
$this instanceof ProfilerStub
) {
163 // return early when no output classes defined or we're a stub
167 if ( !is_array( $output ) ) {
168 $output = array( $output );
171 foreach ( $output as $outType ) {
172 if ( !isset( self
::$outputTypes[$outType] ) ) {
173 throw new MWException( "'$outType' is an invalid output type" );
175 $class = self
::$outputTypes[$outType];
177 /** @var ProfilerOutput $profileOut */
178 $profileOut = new $class( $this, $this->params
);
179 if ( $profileOut->canUse() ) {
180 $profileOut->log( $this->getFunctionStats() );
186 * Get the content type sent out to the client.
187 * Used for profilers that output instead of store data.
191 public function getContentType() {
192 foreach ( headers_list() as $header ) {
193 if ( preg_match( '#^content-type: (\w+/\w+);?#i', $header, $m ) ) {
201 * Mark this call as templated or not
205 public function setTemplated( $t ) {
206 $this->templated
= $t;
210 * Was this call as templated or not
214 public function getTemplated() {
215 return $this->templated
;
219 * Get the aggregated inclusive profiling data for each method
221 * The percent time for each time is based on the current "total" time
222 * used is based on all methods so far. This method can therefore be
223 * called several times in between several profiling calls without the
224 * delays in usage of the profiler skewing the results. A "-total" entry
225 * is always included in the results.
227 * When a call chain involves a method invoked within itself, any
228 * entries for the cyclic invocation should be be demarked with "@".
229 * This makes filtering them out easier and follows the xhprof style.
231 * @return array List of method entries arrays, each having:
232 * - name : method name
233 * - calls : the number of invoking calls
234 * - real : real time ellapsed (ms)
235 * - %real : percent real time
236 * - cpu : CPU time ellapsed (ms)
237 * - %cpu : percent CPU time
238 * - memory : memory used (bytes)
239 * - %memory : percent memory used
240 * - min_real : min real time in a call (ms)
241 * - max_real : max real time in a call (ms)
244 abstract public function getFunctionStats();
247 * Returns a profiling output to be stored in debug file
251 abstract public function getOutput();