From 6373e3d1d4100482feab0e63a65d188062127a31 Mon Sep 17 00:00:00 2001 From: Tim Starling Date: Wed, 28 Nov 2018 14:36:22 +1100 Subject: [PATCH] Add ProfilerExcimer Add a Profiler subclass for the new excimer extension. Since it does not provide function counts, it's a little bit awkward to return it in the format required by getFunctionStats(), but getOutput() works quite well. Fix totally broken ProfilerOutputDb, the first parameter to the onTransactionCommitOrIdle callback is not a Database. It is in the second parameter, but most callers do not use it. Change-Id: Icb20f3a5b0b09ff2905f1711f3681c398aa026e2 Depends-On: I6a9ccf5a12ef998e029033adf08af95c42fb7f8e --- autoload.php | 1 + includes/profiler/ProfilerExcimer.php | 131 ++++++++++++++++++ includes/profiler/output/ProfilerOutputDb.php | 3 +- tests/phan/config.php | 1 + tests/phan/stubs/excimer.php | 86 ++++++++++++ 5 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 includes/profiler/ProfilerExcimer.php create mode 100644 tests/phan/stubs/excimer.php diff --git a/autoload.php b/autoload.php index 02e35a85c3..c79ca23502 100644 --- a/autoload.php +++ b/autoload.php @@ -1127,6 +1127,7 @@ $wgAutoloadLocalClasses = [ 'ProcessCacheLRU' => __DIR__ . '/includes/libs/ProcessCacheLRU.php', 'Processor' => __DIR__ . '/includes/registration/Processor.php', 'Profiler' => __DIR__ . '/includes/profiler/Profiler.php', + 'ProfilerExcimer' => __DIR__ . '/includes/profiler/ProfilerExcimer.php', 'ProfilerOutput' => __DIR__ . '/includes/profiler/output/ProfilerOutput.php', 'ProfilerOutputDb' => __DIR__ . '/includes/profiler/output/ProfilerOutputDb.php', 'ProfilerOutputDump' => __DIR__ . '/includes/profiler/output/ProfilerOutputDump.php', diff --git a/includes/profiler/ProfilerExcimer.php b/includes/profiler/ProfilerExcimer.php new file mode 100644 index 0000000000..776136fe4d --- /dev/null +++ b/includes/profiler/ProfilerExcimer.php @@ -0,0 +1,131 @@ +period = $params['period'] ?? 0.01; + $maxDepth = $params['maxDepth'] ?? 100; + + $this->cpuProf = new ExcimerProfiler; + $this->cpuProf->setEventType( EXCIMER_CPU ); + $this->cpuProf->setPeriod( $this->period ); + $this->cpuProf->setMaxDepth( $maxDepth ); + + $this->realProf = new ExcimerProfiler; + $this->realProf->setEventType( EXCIMER_REAL ); + $this->realProf->setPeriod( $this->period ); + $this->realProf->setMaxDepth( $maxDepth ); + + $this->cpuProf->start(); + $this->realProf->start(); + } + + public function scopedProfileIn( $section ) { + } + + public function close() { + $this->cpuProf->stop(); + $this->realProf->stop(); + } + + public function getFunctionStats() { + $this->close(); + $cpuStats = $this->cpuProf->getLog()->aggregateByFunction(); + $realStats = $this->realProf->getLog()->aggregateByFunction(); + $allNames = array_keys( $realStats + $cpuStats ); + $cpuSamples = $this->cpuProf->getLog()->getEventCount(); + $realSamples = $this->realProf->getLog()->getEventCount(); + + $resultStats = [ [ + 'name' => '-total', + 'calls' => 1, + 'memory' => 0, + '%memory' => 0, + 'min_real' => 0, + 'max_real' => 0, + 'cpu' => $cpuSamples * $this->period * 1000, + '%cpu' => 100, + 'real' => $realSamples * $this->period * 1000, + '%real' => 100, + ] ]; + + foreach ( $allNames as $funcName ) { + $cpuEntry = $cpuStats[$funcName] ?? false; + $realEntry = $realStats[$funcName] ?? false; + $resultEntry = [ + 'name' => $funcName, + 'calls' => 0, + 'memory' => 0, + '%memory' => 0, + 'min_real' => 0, + 'max_real' => 0, + ]; + + if ( $cpuEntry ) { + $resultEntry['cpu'] = $cpuEntry['inclusive'] * $this->period * 1000; + $resultEntry['%cpu'] = $cpuEntry['inclusive'] / $cpuSamples * 100; + } else { + $resultEntry['cpu'] = 0; + $resultEntry['%cpu'] = 0; + } + if ( $realEntry ) { + $resultEntry['real'] = $realEntry['inclusive'] * $this->period * 1000; + $resultEntry['%real'] = $realEntry['inclusive'] / $realSamples * 100; + } else { + $resultEntry['real'] = 0; + $resultEntry['%real'] = 0; + } + + $resultStats[] = $resultEntry; + } + return $resultStats; + } + + public function getOutput() { + $this->close(); + $cpuLog = $this->cpuProf->getLog(); + $realLog = $this->realProf->getLog(); + $cpuStats = $cpuLog->aggregateByFunction(); + $realStats = $realLog->aggregateByFunction(); + $allNames = array_keys( $cpuStats + $realStats ); + $cpuSamples = $cpuLog->getEventCount(); + $realSamples = $realLog->getEventCount(); + + $result = ''; + + $titleFormat = "%-70s %10s %11s %10s %11s %10s %11s %10s %11s\n"; + $statsFormat = "%-70s %10d %10.1f%% %10d %10.1f%% %10d %10.1f%% %10d %10.1f%%\n"; + $result .= sprintf( $titleFormat, + 'Name', + 'CPU incl', 'CPU incl%', 'CPU self', 'CPU self%', + 'Real incl', 'Real incl%', 'Real self', 'Real self%' + ); + + foreach ( $allNames as $funcName ) { + $realEntry = $realStats[$funcName] ?? false; + $cpuEntry = $cpuStats[$funcName] ?? false; + $realIncl = $realEntry ? $realEntry['inclusive'] : 0; + $realSelf = $realEntry ? $realEntry['self'] : 0; + $cpuIncl = $cpuEntry ? $cpuEntry['inclusive'] : 0; + $cpuSelf = $cpuEntry ? $cpuEntry['self'] : 0; + $result .= sprintf( $statsFormat, + $funcName, + $cpuIncl * $this->period * 1000, + $cpuIncl == 0 ? 0 : $cpuIncl / $cpuSamples * 100, + $cpuSelf * $this->period * 1000, + $cpuSelf == 0 ? 0 : $cpuSelf / $cpuSamples * 100, + $realIncl * $this->period * 1000, + $realIncl == 0 ? 0 : $realIncl / $realSamples * 100, + $realSelf * $this->period * 1000, + $realSelf == 0 ? 0 : $realSelf / $realSamples * 100 + ); + } + + return $result; + } +} diff --git a/includes/profiler/output/ProfilerOutputDb.php b/includes/profiler/output/ProfilerOutputDb.php index 6e0085d8fc..ea5f7ad14b 100644 --- a/includes/profiler/output/ProfilerOutputDb.php +++ b/includes/profiler/output/ProfilerOutputDb.php @@ -21,7 +21,6 @@ * @ingroup Profiler */ -use Wikimedia\Rdbms\Database; use Wikimedia\Rdbms\DBError; /** @@ -56,7 +55,7 @@ class ProfilerOutputDb extends ProfilerOutput { } $fname = __METHOD__; - $dbw->onTransactionCommitOrIdle( function ( Database $dbw ) use ( $stats, $fname ) { + $dbw->onTransactionCommitOrIdle( function () use ( $stats, $fname, $dbw ) { $pfhost = $this->perHost ? wfHostname() : ''; // Sqlite: avoid excess b-tree rebuilds (mostly for non-WAL mode) // non-Sqlite: lower contention with small transactions diff --git a/tests/phan/config.php b/tests/phan/config.php index 585ebb975b..fa351ea123 100644 --- a/tests/phan/config.php +++ b/tests/phan/config.php @@ -42,6 +42,7 @@ return [ // Load the interface for the version of PHPUnit that isn't installed. // Phan only supports PHP 7.0+ (and not HHVM), so we only need to stub PHPUnit 4. class_exists( PHPUnit_TextUI_Command::class ) ? [] : [ 'tests/phan/stubs/phpunit4.php' ], + class_exists( ProfilerExcimer::class ) ? [] : [ 'tests/phan/stubs/excimer.php' ], [ 'maintenance/7zip.inc', 'maintenance/cleanupTable.inc', diff --git a/tests/phan/stubs/excimer.php b/tests/phan/stubs/excimer.php new file mode 100644 index 0000000000..af3a67398c --- /dev/null +++ b/tests/phan/stubs/excimer.php @@ -0,0 +1,86 @@ +