Merge "ResourceLoader: Deprecate ResourceLoader::makeConfigSetScript"
[mediawiki.git] / includes / profiler / Profiler.php
blob5d9cff6535e121f2cc8d3f2fd5d5b96e516619fc
1 <?php
2 /**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
18 * @file
21 use MediaWiki\Logger\LoggerFactory;
22 use MediaWiki\WikiMap\WikiMap;
23 use Psr\Log\LoggerInterface;
24 use Wikimedia\Rdbms\TransactionProfiler;
25 use Wikimedia\ScopedCallback;
27 /**
28 * @defgroup Profiler Profiler
31 /**
32 * Profiler base class that defines the interface and some shared
33 * functionality.
35 * @ingroup Profiler
37 abstract class Profiler {
38 /** @var string|false Profiler ID for bucketing data */
39 protected $profileID = false;
40 /** @var array All of the params passed from $wgProfiler */
41 protected $params = [];
42 /** @var TransactionProfiler */
43 protected $trxProfiler;
44 /** @var LoggerInterface */
45 protected $logger;
46 /** @var bool */
47 private $allowOutput = false;
49 /** @var Profiler */
50 private static $instance = null;
52 /**
53 * @param array $params See $wgProfiler.
55 public function __construct( array $params ) {
56 if ( isset( $params['profileID'] ) ) {
57 $this->profileID = $params['profileID'];
59 $this->params = $params;
60 $this->trxProfiler = new TransactionProfiler();
61 $this->logger = LoggerFactory::getInstance( 'profiler' );
64 /**
65 * @internal For use by Setup.php
66 * @param array $profilerConf Value from $wgProfiler
68 final public static function init( array $profilerConf ): void {
69 $params = $profilerConf + [
70 'class' => ProfilerStub::class,
71 'sampling' => 1,
72 'threshold' => 0.0,
73 'output' => [],
74 'cliEnable' => false,
77 // Avoid global func wfIsCLI() during setup
78 $isCLI = ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
79 $inSample = $params['sampling'] === 1 || mt_rand( 1, $params['sampling'] ) === 1;
80 if (
81 !$inSample ||
82 // On CLI, profiling is disabled by default, and can be explicitly enabled
83 // via the `--profiler` option, which MediaWiki\Maintenance\MaintenanceRunner::setup
84 // translates into 'cliEnable'.
85 // See also $wgProfiler docs.
87 // For this to work, Setup.php must call Profiler::init() after handling of
88 // MW_FINAL_SETUP_CALLBACK, which is what doMaintenance.php uses to call
89 // MaintenanceRunner::setup.
90 ( $isCLI && !$params['cliEnable'] )
91 ) {
92 $params['class'] = ProfilerStub::class;
95 if ( !is_array( $params['output'] ) ) {
96 $params['output'] = [ $params['output'] ];
99 self::$instance = new $params['class']( $params );
103 * @return Profiler
105 final public static function instance() {
106 if ( !self::$instance ) {
107 trigger_error( 'Called Profiler::instance before settings are loaded', E_USER_WARNING );
108 self::init( [] );
111 return self::$instance;
115 * @deprecated since 1.41, unused. Can override this base class.
116 * @param string $id
118 public function setProfileID( $id ) {
119 wfDeprecated( __METHOD__, '1.41' );
120 $this->profileID = $id;
124 * @return string
126 public function getProfileID() {
127 if ( $this->profileID === false ) {
128 return WikiMap::getCurrentWikiDbDomain()->getId();
129 } else {
130 return $this->profileID;
135 * Mark the start of a custom profiling frame (e.g. DB queries).
136 * The frame ends when the result of this method falls out of scope.
138 * @param string $section
139 * @return ScopedCallback|null
140 * @since 1.25
142 abstract public function scopedProfileIn( $section );
145 * @param SectionProfileCallback|null &$section
147 public function scopedProfileOut( ?SectionProfileCallback &$section = null ) {
148 $section = null;
152 * @return TransactionProfiler
153 * @since 1.25
155 public function getTransactionProfiler() {
156 return $this->trxProfiler;
160 * Close opened profiling sections
162 abstract public function close();
165 * Get all usable outputs.
167 * @return ProfilerOutput[]
168 * @since 1.25
170 private function getOutputs() {
171 $outputs = [];
172 foreach ( $this->params['output'] as $outputType ) {
173 // The class may be specified as either the full class name (for
174 // example, 'ProfilerOutputStats') or (for backward compatibility)
175 // the trailing portion of the class name (for example, 'stats').
176 $outputClass = strpos( $outputType, 'ProfilerOutput' ) === false
177 ? 'ProfilerOutput' . ucfirst( $outputType )
178 : $outputType;
179 if ( !class_exists( $outputClass ) ) {
180 throw new UnexpectedValueException( "'$outputType' is an invalid output type" );
182 $outputInstance = new $outputClass( $this, $this->params );
183 if ( $outputInstance->canUse() ) {
184 $outputs[] = $outputInstance;
187 return $outputs;
191 * Log data to all the applicable backing stores
193 * This logs the profiling data to the backing store for each configured ProfilerOutput
194 * instance. It also logs any request data for the TransactionProfiler instance.
196 * @since 1.25
198 public function logData() {
199 if ( $this->params['threshold'] > 0.0 ) {
200 // Note, this is also valid for CLI processes.
201 $timeElapsed = microtime( true ) - $_SERVER['REQUEST_TIME_FLOAT'];
202 if ( $timeElapsed <= $this->params['threshold'] ) {
203 return;
207 $outputs = [];
208 foreach ( $this->getOutputs() as $output ) {
209 if ( !$output->logsToOutput() ) {
210 $outputs[] = $output;
214 if ( $outputs ) {
215 $stats = $this->getFunctionStats();
216 foreach ( $outputs as $output ) {
217 $output->log( $stats );
223 * Log the data to the script/request output for all ProfilerOutput instances that do so
225 * @since 1.26
227 public function logDataPageOutputOnly() {
228 if ( !$this->allowOutput ) {
229 return;
232 $outputs = [];
233 foreach ( $this->getOutputs() as $output ) {
234 if ( $output->logsToOutput() ) {
235 $outputs[] = $output;
239 if ( $outputs ) {
240 $stats = $this->getFunctionStats();
241 foreach ( $outputs as $output ) {
242 $output->log( $stats );
248 * Get the Content-Type for deciding how to format appended profile output.
250 * Disabled by default. Enable via setAllowOutput().
252 * @see ProfilerOutputText
253 * @since 1.25
254 * @return string|null Returns null if disabled or no Content-Type found.
256 public function getContentType() {
257 if ( $this->allowOutput ) {
258 foreach ( headers_list() as $header ) {
259 if ( preg_match( '#^content-type: (\w+/\w+);?#i', $header, $m ) ) {
260 return $m[1];
264 return null;
268 * Enable appending profiles to standard output.
270 * @since 1.34
272 public function setAllowOutput() {
273 $this->allowOutput = true;
277 * Whether appending profiles is allowed.
279 * @deprecated since 1.41. Unused.
281 * @since 1.34
282 * @return bool
284 public function getAllowOutput() {
285 wfDeprecated( __METHOD__, '1.41' );
286 return $this->allowOutput;
290 * Get the aggregated inclusive profiling data for each method
292 * The percent time for each time is based on the current "total" time
293 * used is based on all methods so far. This method can therefore be
294 * called several times in between several profiling calls without the
295 * delays in usage of the profiler skewing the results. A "-total" entry
296 * is always included in the results.
298 * When a call chain involves a method invoked within itself, any
299 * entries for the cyclic invocation should be demarked with "@".
300 * This makes filtering them out easier and follows the xhprof style.
302 * @return array[] List of method entries arrays, each having:
303 * - name : method name
304 * - calls : the number of invoking calls
305 * - real : real time elapsed (ms)
306 * - %real : percent real time
307 * - cpu : CPU time elapsed (ms)
308 * - %cpu : percent CPU time
309 * - memory : memory used (bytes)
310 * - %memory : percent memory used
311 * - min_real : min real time in a call (ms)
312 * - max_real : max real time in a call (ms)
313 * @since 1.25
315 abstract public function getFunctionStats();
318 * Returns a profiling output to be stored in debug file
320 * @return string
322 abstract public function getOutput();