Extension:PerformanceMonitor

MediaWiki extensions manual
PerformanceMonitor
Release status: unmaintained
Implementation Special page , MyWiki
Description Profiles calls to MediaWiki in Json logfiles and offers a special page to browse, visualize the results, drill down and compare
Author(s) Achim Bode (Achimbodetalk)
Latest version 0.1.0 (2014-05-09)
MediaWiki 1.20 - 1.25
PHP 5.3+
License GNU General Public License 2.0
Download

The PerformanceMonitor extension profiles the work of PHP functions and methods on your server. These "profiles" (i.e. logfiles) are stored as Json-Trees in the file system. After profiling you can use the Special Page Special:PerformanceMonitor to visualise the results as diagrams. The diagrams allow drilling down to more detailed levels and show you, whatever the server does all the time you are waiting for it.

Technically the extension uses a JavaScript library called InfoVis for visualisation, which you have to download separately and put it inside the extension folder.

Download edit

Download PerformanceMonitor edit

The code of this extension is available for download on GitHub. Put it into the folder /extensions/PerformanceMonitor.

Download the InfoVis Toolkit edit

Download the InfoVis Toolkit from http://philogb.github.io/jit/downloads/Jit-2.0.1.zip and put it inside the /extensions/PerformanceMonitor/js-includes/jit-2.0.1 folder of the extension.

Download Bootstrap Library edit

Download the Bootstrap Library from http://getbootstrap.com/getting-started/ and put it inside the /extensions/PerformanceMonitor/js-includes/bootstrap-3.1.1-dist folder of the extension.

Installation edit

LocalSettings.php edit

$wgLanguageCode = 'en'; // other languages untested

// the following were my settings during development,
// you probably do not need all of it...:

error_reporting( 1 );
ini_set( 'display_errors', 1 );

$wgDebugLogFile = '$IP/DebugLogFile.log';
// myWiki/DebugLogFile.log must exist and have the server must have write access

$wgDebugToolbar = false;
$wgShowSQLErrors = true;
$wgDebugDumpSql  = true;
$wgShowDBErrorBacktrace = true;

$wgRawHtml = true;
$wgAllowUserJS = true;
$wgDisableOutputCompression = false; // Debug javascript: $wgDisableOutputCompression = true;
$wgResourceLoaderDebug = false; // Debug javascript: $wgResourceLoaderDebug = true;

// include the Extension:
require_once( "$IP/extensions/PerformanceMonitor/PerformanceMonitor.php" );
// PerformanceMonitor: Don't forget to adapt StartProfiler.php!!!
$egPerformanceMonitorServerName = "Propellerbook"; // whatever is the name of your server
$egPerformanceMonitorExtensionPath = "/Applications/MAMP/htdocs/mediawiki-1.20.8/extensions/PerformanceMonitor/";
$egPerformanceMonitorLogfilesPath = "/Applications/MAMP/htdocs/mediawiki-1.20.8/logs/autologs/"; // make sure to configure this folder writable your server

use /index.php5/Main_Page

$wgScriptPath       = "";
$wgScriptExtension  = ".php5";

instead of /wiki/Main_Page

$wgScriptPath       = "";
$wgScriptExtension  = ".php5";
$wgArticlePath      = "/wiki/$1";
$wgUsePathInfo      = true; // Enable use of pretty URLs

StartProfiler.php edit

The file StartProfiler.php is located (if it exists) in the main folder of the wiki, i.e. the same as LocalSettings.php. If it exists, comment out the current code and add the following code. Otherwise create it and put the following inside:

<?php
require_once( dirname(__FILE__).'/includes/profiler/ProfilerSimple.php' );
$monitorPath = "/Applications/MAMP/htdocs/mediawiki-1.20.8/extensions/PerformanceMonitor/";
require_once( $monitorPath . "ProfilerSimpleJson.php" );
$wgProfiler['class'] = 'ProfilerSimpleJson';

ProfilerSimple.php edit

We found that ProfilerSimple.php has been removed in one of the last MediaWiki releases, when "MediaWiki 1.25 profiling was completely rewritten" (see Manual:Profiling ). You can find the old file from back then (should be MW 1.20), that used to be shipped in /includes/profiler/ProfilerSimple.php - it was only used to derive a profiler from it, that creates a json file instead of the normal line-by-line protocol. Feel free to adapt this for newer versions of MediaWiki...

<?php
/**
 * Base class for simple 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
 **/

/**
 * Simple profiler base class.
 * @todo document methods (?)
 * @ingroup Profiler
 */
class ProfilerSimple extends Profiler {
	var $mMinimumTime = 0;

	var $zeroEntry = array('cpu'=> 0.0, 'cpu_sq' => 0.0, 'real' => 0.0, 'real_sq' => 0.0, 'count' => 0);
	var $errorEntry;

	public function isPersistent() {
		/* Implement in output subclasses */
		return false;
	}

	protected function addInitialStack() {
		$this->errorEntry = $this->zeroEntry;
		$this->errorEntry['count'] = 1;

		$initialTime = $this->getInitialTime();
		$initialCpu = $this->getInitialTime( 'cpu' );
		if ( $initialTime !== null && $initialCpu !== null ) {
			$this->mWorkStack[] = array( '-total', 0, $initialTime, $initialCpu );
			$this->mWorkStack[] = array( '-setup', 1, $initialTime, $initialCpu );

			$this->profileOut( '-setup' );
		} else {
			$this->profileIn( '-total' );
		}
	}

	function setMinimum( $min ) {
		$this->mMinimumTime = $min;
	}

	function profileIn($functionname) {
		global $wgDebugFunctionEntry;
		if ($wgDebugFunctionEntry) {
			$this->debug(str_repeat(' ', count($this->mWorkStack)).'Entering '.$functionname."\n");
		}
		$this->mWorkStack[] = array( $functionname, count( $this->mWorkStack ), $this->getTime(), $this->getTime( 'cpu' ) );
	}

	function profileOut($functionname) {
		global $wgDebugFunctionEntry;

		if ($wgDebugFunctionEntry) {
			$this->debug(str_repeat(' ', count($this->mWorkStack) - 1).'Exiting '.$functionname."\n");
		}

		list($ofname, /* $ocount */ ,$ortime,$octime) = array_pop($this->mWorkStack);

		if (!$ofname) {
			$this->debug("Profiling error: $functionname\n");
		} else {
			if ($functionname == 'close') {
				$message = "Profile section ended by close(): {$ofname}";
				$functionname = $ofname;
				$this->debug( "$message\n" );
				$this->mCollated[$message] = $this->errorEntry;
			}
			elseif ($ofname != $functionname) {
				$message = "Profiling error: in({$ofname}), out($functionname)";
				$this->debug( "$message\n" );
				$this->mCollated[$message] = $this->errorEntry;
			}
			$entry =& $this->mCollated[$functionname];
			$elapsedcpu = $this->getTime( 'cpu' ) - $octime;
			$elapsedreal = $this->getTime() - $ortime;
			if (!is_array($entry)) {
				$entry = $this->zeroEntry;
				$this->mCollated[$functionname] =& $entry;
			}
			$entry['cpu'] += $elapsedcpu;
			$entry['cpu_sq'] += $elapsedcpu*$elapsedcpu;
			$entry['real'] += $elapsedreal;
			$entry['real_sq'] += $elapsedreal*$elapsedreal;
			$entry['count']++;

		}
	}

	public function getFunctionReport() {
		/* Implement in output subclasses */
		return '';
	}

	public function logData() {
		/* Implement in subclasses */
	}

	/**
	 * Get the actual CPU time or the initial one if $ru is set.
	 *
	 * @deprecated in 1.20
	 * @return float|null
	 */
	function getCpuTime( $ru = null ) {
		wfDeprecated( __METHOD__, '1.20' );

		if ( $ru === null ) {
			return $this->getTime( 'cpu' );
		} else {
			# It theory we should use $ru here, but it always $wgRUstart that is passed here
			return $this->getInitialTime( 'cpu' );
		}
	}
}