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. 7 * 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. 12 * 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 17 * 18 * @file 19 */ 20 21/** 22 * @ingroup Profiler 23 * @since 1.33 24 */ 25class ProfilerExcimer extends Profiler { 26 /** @var ExcimerProfiler */ 27 private $cpuProf; 28 /** @var ExcimerProfiler */ 29 private $realProf; 30 private $period; 31 32 /** 33 * @param array $params Associative array of parameters: 34 * - period: The sampling period 35 * - maxDepth: The maximum stack depth collected 36 * - cpuProfiler: A pre-started ExcimerProfiler instance for CPU 37 * profiling of the entire request including configuration. 38 * - realProfiler: A pre-started ExcimerProfiler instance for wall 39 * clock profiling of the entire request. 40 */ 41 public function __construct( array $params = [] ) { 42 parent::__construct( $params ); 43 44 $this->period = $params['period'] ?? 0.01; 45 $maxDepth = $params['maxDepth'] ?? 100; 46 47 if ( isset( $params['cpuProfiler'] ) ) { 48 $this->cpuProf = $params['cpuProfiler']; 49 } else { 50 $this->cpuProf = new ExcimerProfiler; 51 $this->cpuProf->setEventType( EXCIMER_CPU ); 52 $this->cpuProf->setPeriod( $this->period ); 53 $this->cpuProf->setMaxDepth( $maxDepth ); 54 $this->cpuProf->start(); 55 } 56 57 if ( isset( $params['realProfiler'] ) ) { 58 $this->realProf = $params['realProfiler']; 59 } else { 60 $this->realProf = new ExcimerProfiler; 61 $this->realProf->setEventType( EXCIMER_REAL ); 62 $this->realProf->setPeriod( $this->period ); 63 $this->realProf->setMaxDepth( $maxDepth ); 64 $this->realProf->start(); 65 } 66 } 67 68 public function scopedProfileIn( $section ) { 69 } 70 71 public function close() { 72 $this->cpuProf->stop(); 73 $this->realProf->stop(); 74 } 75 76 public function getFunctionStats() { 77 $this->close(); 78 $cpuStats = $this->cpuProf->getLog()->aggregateByFunction(); 79 $realStats = $this->realProf->getLog()->aggregateByFunction(); 80 $allNames = array_keys( $realStats + $cpuStats ); 81 $cpuSamples = $this->cpuProf->getLog()->getEventCount(); 82 $realSamples = $this->realProf->getLog()->getEventCount(); 83 84 $resultStats = [ [ 85 'name' => '-total', 86 'calls' => 1, 87 'memory' => 0, 88 '%memory' => 0, 89 'min_real' => 0, 90 'max_real' => 0, 91 'cpu' => $cpuSamples * $this->period * 1000, 92 '%cpu' => 100, 93 'real' => $realSamples * $this->period * 1000, 94 '%real' => 100, 95 ] ]; 96 97 foreach ( $allNames as $funcName ) { 98 $cpuEntry = $cpuStats[$funcName] ?? false; 99 $realEntry = $realStats[$funcName] ?? false; 100 $resultEntry = [ 101 'name' => $funcName, 102 'calls' => 0, 103 'memory' => 0, 104 '%memory' => 0, 105 'min_real' => 0, 106 'max_real' => 0, 107 ]; 108 109 if ( $cpuEntry ) { 110 $resultEntry['cpu'] = $cpuEntry['inclusive'] * $this->period * 1000; 111 $resultEntry['%cpu'] = $cpuEntry['inclusive'] / $cpuSamples * 100; 112 } else { 113 $resultEntry['cpu'] = 0; 114 $resultEntry['%cpu'] = 0; 115 } 116 if ( $realEntry ) { 117 $resultEntry['real'] = $realEntry['inclusive'] * $this->period * 1000; 118 $resultEntry['%real'] = $realEntry['inclusive'] / $realSamples * 100; 119 } else { 120 $resultEntry['real'] = 0; 121 $resultEntry['%real'] = 0; 122 } 123 124 $resultStats[] = $resultEntry; 125 } 126 return $resultStats; 127 } 128 129 public function getOutput() { 130 $this->close(); 131 $cpuLog = $this->cpuProf->getLog(); 132 $realLog = $this->realProf->getLog(); 133 $cpuStats = $cpuLog->aggregateByFunction(); 134 $realStats = $realLog->aggregateByFunction(); 135 $allNames = array_keys( $cpuStats + $realStats ); 136 $cpuSamples = $cpuLog->getEventCount(); 137 $realSamples = $realLog->getEventCount(); 138 139 $result = ''; 140 141 $titleFormat = "%-70s %10s %11s %10s %11s %10s %11s %10s %11s\n"; 142 $statsFormat = "%-70s %10d %10.1f%% %10d %10.1f%% %10d %10.1f%% %10d %10.1f%%\n"; 143 $result .= sprintf( $titleFormat, 144 'Name', 145 'CPU incl', 'CPU incl%', 'CPU self', 'CPU self%', 146 'Real incl', 'Real incl%', 'Real self', 'Real self%' 147 ); 148 149 foreach ( $allNames as $funcName ) { 150 $realEntry = $realStats[$funcName] ?? false; 151 $cpuEntry = $cpuStats[$funcName] ?? false; 152 $realIncl = $realEntry ? $realEntry['inclusive'] : 0; 153 $realSelf = $realEntry ? $realEntry['self'] : 0; 154 $cpuIncl = $cpuEntry ? $cpuEntry['inclusive'] : 0; 155 $cpuSelf = $cpuEntry ? $cpuEntry['self'] : 0; 156 $result .= sprintf( $statsFormat, 157 $funcName, 158 $cpuIncl * $this->period * 1000, 159 $cpuIncl == 0 ? 0 : $cpuIncl / $cpuSamples * 100, 160 $cpuSelf * $this->period * 1000, 161 $cpuSelf == 0 ? 0 : $cpuSelf / $cpuSamples * 100, 162 $realIncl * $this->period * 1000, 163 $realIncl == 0 ? 0 : $realIncl / $realSamples * 100, 164 $realSelf * $this->period * 1000, 165 $realSelf == 0 ? 0 : $realSelf / $realSamples * 100 166 ); 167 } 168 169 return $result; 170 } 171} 172