Ori.livneh has uploaded a new change for review. https://gerrit.wikimedia.org/r/201384
Change subject: Introduce ProfilerOutputContext ...................................................................... Introduce ProfilerOutputContext * Associate Profiler objects with a request context by adding a $context property with a getter and a setter. * Introduce ProfilerOutputContext, which writes profiling data to the stats buffer associated with the current request context. * Make it the Profiler class's responsibility to enforce $wgProfilerLimit. * Deprecate $wgProfilerLimit in favor of the (more aptly named, IMO) $wgProfiler['threshold'] config setting. * Tidy up Profiler instance creation code in Profiler::instance(). * Add Profiler::getOutputs, which returns an array of ProfilerOutput instances which are configured for the current profiler and whose canUse() method returns true. * Make ProfilerStub not log by creating a stub ProfilerStub::logData() method which does not call the parent. Previously the parent class checked if $this was an instance of ProfilerStub and returned early if so. Task: T90623 Task: T85641 Change-Id: Icf644ad3435c1f30d0a49957a97b481808a3153d --- M autoload.php M includes/DefaultSettings.php M includes/GlobalFunctions.php M includes/profiler/Profiler.php M includes/profiler/ProfilerStub.php A includes/profiler/output/ProfilerOutputContext.php 6 files changed, 150 insertions(+), 47 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core refs/changes/84/201384/1 diff --git a/autoload.php b/autoload.php index dcd7879..2e355c8 100644 --- a/autoload.php +++ b/autoload.php @@ -908,6 +908,7 @@ 'ProfileSection' => __DIR__ . '/includes/profiler/ProfileSection.php', 'Profiler' => __DIR__ . '/includes/profiler/Profiler.php', 'ProfilerOutput' => __DIR__ . '/includes/profiler/output/ProfilerOutput.php', + 'ProfilerOutputContext' => __DIR__ . '/includes/profiler/output/ProfilerOutputContext.php', 'ProfilerOutputDb' => __DIR__ . '/includes/profiler/output/ProfilerOutputDb.php', 'ProfilerOutputDump' => __DIR__ . '/includes/profiler/output/ProfilerOutputDump.php', 'ProfilerOutputText' => __DIR__ . '/includes/profiler/output/ProfilerOutputText.php', diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 84dc3aa..5e81d0f 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -5423,6 +5423,7 @@ /** * Only record profiling info for pages that took longer than this + * @deprecated since 1.25: set $wgProfiler['threshold'] instead. */ $wgProfileLimit = 0.0; diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 70473ca..4492ab1 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -1237,10 +1237,15 @@ * @todo document */ function wfLogProfilingData() { - global $wgDebugLogGroups, $wgDebugRawPage, $wgProfileLimit; + global $wgDebugLogGroups, $wgDebugRawPage; $context = RequestContext::getMain(); $request = $context->getRequest(); + + $profiler = Profiler::instance(); + $profiler->setContext( $context ); + $profiler->logData(); + $config = $context->getConfig(); if ( $config->has( 'StatsdServer' ) ) { $statsdServer = explode( ':', $config->get( 'StatsdServer' ) ); @@ -1250,22 +1255,6 @@ $statsdClient = new StatsdClient( $statsdSender ); $statsdClient->send( $context->getStats()->getBuffer() ); } - - $profiler = Profiler::instance(); - - # Profiling must actually be enabled... - if ( $profiler instanceof ProfilerStub ) { - return; - } - - // Get total page request time and only show pages that longer than - // $wgProfileLimit time (default is 0) - $elapsed = $request->getElapsedTime(); - if ( $elapsed <= $wgProfileLimit ) { - return; - } - - $profiler->logData(); if ( isset( $wgDebugLogGroups['profileoutput'] ) && $wgDebugLogGroups['profileoutput'] === false @@ -1277,7 +1266,7 @@ return; } - $ctx = array( 'elapsed' => $elapsed ); + $ctx = array( 'elapsed' => $request->getElapsedTime() ); if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { $ctx['forwarded_for'] = $_SERVER['HTTP_X_FORWARDED_FOR']; } diff --git a/includes/profiler/Profiler.php b/includes/profiler/Profiler.php index 1c9824a..3837bdd 100644 --- a/includes/profiler/Profiler.php +++ b/includes/profiler/Profiler.php @@ -35,6 +35,8 @@ protected $templated = false; /** @var array All of the params passed from $wgProfiler */ protected $params = array(); + /** @var IContextSource Current request context */ + protected $context = null; /** @var TransactionProfiler */ protected $trxProfiler; @@ -47,6 +49,7 @@ 'text' => 'ProfilerOutputText', 'udp' => 'ProfilerOutputUdp', 'dump' => 'ProfilerOutputDump', + 'context' => 'ProfilerOutputContext', ); /** @var Profiler */ @@ -69,17 +72,28 @@ */ final public static function instance() { if ( self::$instance === null ) { - global $wgProfiler; + global $wgProfiler, $wgProfileLimit; + + $params = array( + 'class' => 'ProfilerStub', + 'sampling' => 1, + 'threshold' => $wgProfileLimit, + 'output' => array(), + ); if ( is_array( $wgProfiler ) ) { - $class = isset( $wgProfiler['class'] ) ? $wgProfiler['class'] : 'ProfilerStub'; - $factor = isset( $wgProfiler['sampling'] ) ? $wgProfiler['sampling'] : 1; - if ( PHP_SAPI === 'cli' || mt_rand( 0, $factor - 1 ) != 0 ) { - $class = 'ProfilerStub'; - } - self::$instance = new $class( $wgProfiler ); - } else { - self::$instance = new ProfilerStub( array() ); + $params = array_merge( $params, $wgProfiler ); } + + $inSample = mt_rand( 0, $params['sampling'] - 1 ) === 0; + if ( PHP_SAPI === 'cli' || !$inSample ) { + $params['class'] = 'ProfilerStub'; + } + + if ( !is_array( $params['output'] ) ) { + $params['output'] = array( $params['output'] ); + } + + self::$instance = new $params['class']( $params ); } return self::$instance; } @@ -114,6 +128,32 @@ return wfWikiID(); } else { return $this->profileID; + } + } + + /** + * Sets the context for this Profiler + * + * @param IContextSource $context + * @since 1.25 + */ + public function setContext( $context ) { + $this->context = $context; + } + + /** + * Gets the context for this Profiler + * + * @return IContextSource + * @since 1.25 + */ + public function getContext() { + if ( $this->context ) { + return $this->context; + } else { + wfDebug( __METHOD__ . " called and \$context is null. " . + "Return RequestContext::getMain(); for sanity\n" ); + return RequestContext::getMain(); } } @@ -152,37 +192,49 @@ abstract public function close(); /** - * Log the data to some store or even the page output + * Get all usable outputs. * * @throws MWException + * @return array Array of ProfilerOutput instances. + * @since 1.25 + */ + private function getOutputs() { + $outputs = array(); + foreach ( $this->params['output'] as $outputType ) { + if ( !isset( self::$outputTypes[$outputType] ) ) { + throw new MWException( "'$outputType' is an invalid output type" ); + } + $outputClass = self::$outputTypes[$outputType]; + $outputInstance = new $outputClass( $this, $this->params ); + if ( $outputInstance->canUse() ) { + $outputs[] = $outputInstance; + } + } + return $outputs; + } + + /** + * Log the data to some store or even the page output + * * @since 1.25 */ public function logData() { - $output = isset( $this->params['output'] ) ? $this->params['output'] : null; + $request = $this->getContext()->getRequest(); - if ( !$output || $this instanceof ProfilerStub ) { - // return early when no output classes defined or we're a stub + $timeElapsed = $request->getElapsedTime(); + $timeElapsedThreshold = $this->params['threshold']; + if ( $timeElapsed <= $timeElapsedThreshold ) { return; } - if ( !is_array( $output ) ) { - $output = array( $output ); + $outputs = $this->getOutputs(); + if ( !$outputs ) { + return; } - $stats = null; - foreach ( $output as $outType ) { - if ( !isset( self::$outputTypes[$outType] ) ) { - throw new MWException( "'$outType' is an invalid output type" ); - } - $class = self::$outputTypes[$outType]; - /** @var ProfilerOutput $profileOut */ - $profileOut = new $class( $this, $this->params ); - if ( $profileOut->canUse() ) { - if ( is_null( $stats ) ) { - $stats = $this->getFunctionStats(); - } - $profileOut->log( $stats ); - } + $stats = $this->getFunctionStats(); + foreach ( $outputs as $output ) { + $output->log( $stats ); } } diff --git a/includes/profiler/ProfilerStub.php b/includes/profiler/ProfilerStub.php index 1d04536..244b4e4 100644 --- a/includes/profiler/ProfilerStub.php +++ b/includes/profiler/ProfilerStub.php @@ -43,4 +43,7 @@ public function getCurrentSection() { return ''; } + + public function logData() { + } } diff --git a/includes/profiler/output/ProfilerOutputContext.php b/includes/profiler/output/ProfilerOutputContext.php new file mode 100644 index 0000000..544422b --- /dev/null +++ b/includes/profiler/output/ProfilerOutputContext.php @@ -0,0 +1,57 @@ +<?php +/** + * ProfilerOutput class that flushes profiling data to the profiling + * context's stats buffer. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Profiler + */ + +/** + * ProfilerOutput class that flushes profiling data to the profiling + * context's stats buffer. + * + * @ingroup Profiler + * @since 1.25 + */ +class ProfilerOutputContext extends ProfilerOutput { + + /** + * Flush profiling data to the current profiling context's stats buffer. + * + * @param array $stats + */ + public function log( array $stats ) { + $contextStats = $this->collector->getContext()->getStats(); + + foreach ( $stats as $stat ) { + // Sanitize the key + $key = str_replace( '::', '.', $stat['name'] ); + $key = preg_replace( '/[^a-z.]+/i', '_', $key ); + $key = trim( $key, '_.' ); + + // Convert fractional seconds to whole milliseconds + $cpu = round( $stat['cpu'] * 1000 ); + $real = round( $stat['real'] * 1000 ); + + $contextStats->increment( "{$key}.calls" ); + $contextStats->timing( "{$key}.cpu", $cpu ); + $contextStats->timing( "{$key}.real", $real ); + } + } +} -- To view, visit https://gerrit.wikimedia.org/r/201384 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Icf644ad3435c1f30d0a49957a97b481808a3153d Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: Ori.livneh <o...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits