diff options
Diffstat (limited to 'www/wiki/includes/profiler/Profiler.php')
-rw-r--r-- | www/wiki/includes/profiler/Profiler.php | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/www/wiki/includes/profiler/Profiler.php b/www/wiki/includes/profiler/Profiler.php new file mode 100644 index 00000000..d02011fa --- /dev/null +++ b/www/wiki/includes/profiler/Profiler.php @@ -0,0 +1,321 @@ +<?php +/** + * Base class for profiling. + * + * 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 + * @defgroup Profiler Profiler + */ +use Wikimedia\ScopedCallback; +use Wikimedia\Rdbms\TransactionProfiler; + +/** + * Profiler base class that defines the interface and some trivial + * functionality + * + * @ingroup Profiler + */ +abstract class Profiler { + /** @var string|bool Profiler ID for bucketing data */ + protected $profileID = false; + /** @var bool Whether MediaWiki is in a SkinTemplate output context */ + protected $templated = false; + /** @var array All of the params passed from $wgProfiler */ + protected $params = []; + /** @var IContextSource Current request context */ + protected $context = null; + /** @var TransactionProfiler */ + protected $trxProfiler; + /** @var Profiler */ + private static $instance = null; + + /** + * @param array $params + */ + public function __construct( array $params ) { + if ( isset( $params['profileID'] ) ) { + $this->profileID = $params['profileID']; + } + $this->params = $params; + $this->trxProfiler = new TransactionProfiler(); + } + + /** + * Singleton + * @return Profiler + */ + final public static function instance() { + if ( self::$instance === null ) { + global $wgProfiler, $wgProfileLimit; + + $params = [ + 'class' => ProfilerStub::class, + 'sampling' => 1, + 'threshold' => $wgProfileLimit, + 'output' => [], + ]; + if ( is_array( $wgProfiler ) ) { + $params = array_merge( $params, $wgProfiler ); + } + + $inSample = mt_rand( 0, $params['sampling'] - 1 ) === 0; + // wfIsCLI() is not available yet + if ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' || !$inSample ) { + $params['class'] = ProfilerStub::class; + } + + if ( !is_array( $params['output'] ) ) { + $params['output'] = [ $params['output'] ]; + } + + self::$instance = new $params['class']( $params ); + } + return self::$instance; + } + + /** + * Replace the current profiler with $profiler if no non-stub profiler is set + * + * @param Profiler $profiler + * @throws MWException + * @since 1.25 + */ + final public static function replaceStubInstance( Profiler $profiler ) { + if ( self::$instance && !( self::$instance instanceof ProfilerStub ) ) { + throw new MWException( 'Could not replace non-stub profiler instance.' ); + } else { + self::$instance = $profiler; + } + } + + /** + * @param string $id + */ + public function setProfileID( $id ) { + $this->profileID = $id; + } + + /** + * @return string + */ + public function getProfileID() { + if ( $this->profileID === false ) { + 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(); + } + } + + // Kept BC for now, remove when possible + public function profileIn( $functionname ) { + } + + public function profileOut( $functionname ) { + } + + /** + * Mark the start of a custom profiling frame (e.g. DB queries). + * The frame ends when the result of this method falls out of scope. + * + * @param string $section + * @return ScopedCallback|null + * @since 1.25 + */ + abstract public function scopedProfileIn( $section ); + + /** + * @param SectionProfileCallback &$section + */ + public function scopedProfileOut( SectionProfileCallback &$section = null ) { + $section = null; + } + + /** + * @return TransactionProfiler + * @since 1.25 + */ + public function getTransactionProfiler() { + return $this->trxProfiler; + } + + /** + * Close opened profiling sections + */ + abstract public function close(); + + /** + * Get all usable outputs. + * + * @throws MWException + * @return ProfilerOutput[] + * @since 1.25 + */ + private function getOutputs() { + $outputs = []; + foreach ( $this->params['output'] as $outputType ) { + // The class may be specified as either the full class name (for + // example, 'ProfilerOutputStats') or (for backward compatibility) + // the trailing portion of the class name (for example, 'stats'). + $outputClass = strpos( $outputType, 'ProfilerOutput' ) === false + ? 'ProfilerOutput' . ucfirst( $outputType ) + : $outputType; + if ( !class_exists( $outputClass ) ) { + throw new MWException( "'$outputType' is an invalid output type" ); + } + $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() { + $request = $this->getContext()->getRequest(); + + $timeElapsed = $request->getElapsedTime(); + $timeElapsedThreshold = $this->params['threshold']; + if ( $timeElapsed <= $timeElapsedThreshold ) { + return; + } + + $outputs = $this->getOutputs(); + if ( !$outputs ) { + return; + } + + $stats = $this->getFunctionStats(); + foreach ( $outputs as $output ) { + $output->log( $stats ); + } + } + + /** + * Output current data to the page output if configured to do so + * + * @throws MWException + * @since 1.26 + */ + public function logDataPageOutputOnly() { + foreach ( $this->getOutputs() as $output ) { + if ( $output instanceof ProfilerOutputText ) { + $stats = $this->getFunctionStats(); + $output->log( $stats ); + } + } + } + + /** + * Get the content type sent out to the client. + * Used for profilers that output instead of store data. + * @return string + * @since 1.25 + */ + public function getContentType() { + foreach ( headers_list() as $header ) { + if ( preg_match( '#^content-type: (\w+/\w+);?#i', $header, $m ) ) { + return $m[1]; + } + } + return null; + } + + /** + * Mark this call as templated or not + * + * @param bool $t + */ + public function setTemplated( $t ) { + $this->templated = $t; + } + + /** + * Was this call as templated or not + * + * @return bool + */ + public function getTemplated() { + return $this->templated; + } + + /** + * Get the aggregated inclusive profiling data for each method + * + * The percent time for each time is based on the current "total" time + * used is based on all methods so far. This method can therefore be + * called several times in between several profiling calls without the + * delays in usage of the profiler skewing the results. A "-total" entry + * is always included in the results. + * + * When a call chain involves a method invoked within itself, any + * entries for the cyclic invocation should be be demarked with "@". + * This makes filtering them out easier and follows the xhprof style. + * + * @return array List of method entries arrays, each having: + * - name : method name + * - calls : the number of invoking calls + * - real : real time elapsed (ms) + * - %real : percent real time + * - cpu : CPU time elapsed (ms) + * - %cpu : percent CPU time + * - memory : memory used (bytes) + * - %memory : percent memory used + * - min_real : min real time in a call (ms) + * - max_real : max real time in a call (ms) + * @since 1.25 + */ + abstract public function getFunctionStats(); + + /** + * Returns a profiling output to be stored in debug file + * + * @return string + */ + abstract public function getOutput(); +} |