MediaWiki
REL1_24
|
00001 <?php 00035 class ProfilerMwprof extends Profiler { 00037 protected $mWorkStack = array(); 00038 00040 protected $mCollated = array(); 00042 protected $mErrorEntry; 00043 00044 // Message types 00045 const TYPE_SINGLE = 1; 00046 const TYPE_RUNNING = 2; 00047 00048 public function isStub() { 00049 return false; 00050 } 00051 00052 public function isPersistent() { 00053 return true; 00054 } 00055 00064 public function profileIn( $inName ) { 00065 $this->mWorkStack[] = array( $inName, count( $this->mWorkStack ), 00066 $this->getTime(), $this->getTime( 'cpu' ), 0 ); 00067 } 00068 00077 public function profileOut( $outName ) { 00078 list( $inName, $inCount, $inWall, $inCpu ) = array_pop( $this->mWorkStack ); 00079 00080 // Check for unbalanced profileIn / profileOut calls. 00081 // Bad entries are logged but not sent. 00082 if ( $inName !== $outName ) { 00083 $this->debugGroup( 'ProfilerUnbalanced', json_encode( array( $inName, $outName ) ) ); 00084 return; 00085 } 00086 00087 $elapsedCpu = $this->getTime( 'cpu' ) - $inCpu; 00088 $elapsedWall = $this->getTime() - $inWall; 00089 $this->updateRunningEntry( $outName, $elapsedCpu, $elapsedWall ); 00090 $this->trxProfiler->recordFunctionCompletion( $outName, $elapsedWall ); 00091 } 00092 00100 public function updateRunningEntry( $name, $elapsedCpu, $elapsedWall ) { 00101 // If this is the first measurement for this entry, store plain values. 00102 // Many profiled functions will only be called once per request. 00103 if ( !isset( $this->mCollated[$name] ) ) { 00104 $this->mCollated[$name] = array( 00105 'cpu' => $elapsedCpu, 00106 'wall' => $elapsedWall, 00107 'count' => 1, 00108 ); 00109 return; 00110 } 00111 00112 $entry = &$this->mCollated[$name]; 00113 00114 // If it's the second measurement, convert the plain values to 00115 // RunningStat instances, so we can push the incoming values on top. 00116 if ( $entry['count'] === 1 ) { 00117 $cpu = new RunningStat(); 00118 $cpu->push( $entry['cpu'] ); 00119 $entry['cpu'] = $cpu; 00120 00121 $wall = new RunningStat(); 00122 $wall->push( $entry['wall'] ); 00123 $entry['wall'] = $wall; 00124 } 00125 00126 $entry['count']++; 00127 $entry['cpu']->push( $elapsedCpu ); 00128 $entry['wall']->push( $elapsedWall ); 00129 } 00130 00134 public function getRawData() { 00135 // This method is called before shutdown in the footer method on Skins. 00136 // If some outer methods have not yet called wfProfileOut(), work around 00137 // that by clearing anything in the work stack to just the "-total" entry. 00138 if ( count( $this->mWorkStack ) > 1 ) { 00139 $oldWorkStack = $this->mWorkStack; 00140 $this->mWorkStack = array( $this->mWorkStack[0] ); // just the "-total" one 00141 } else { 00142 $oldWorkStack = null; 00143 } 00144 $this->close(); 00145 // If this trick is used, then the old work stack is swapped back afterwards. 00146 // This means that logData() will still make use of all the method data since 00147 // the missing wfProfileOut() calls should be made by the time it is called. 00148 if ( $oldWorkStack ) { 00149 $this->mWorkStack = $oldWorkStack; 00150 } 00151 00152 $totalWall = 0.0; 00153 $profile = array(); 00154 foreach ( $this->mCollated as $fname => $data ) { 00155 if ( $data['count'] == 1 ) { 00156 $profile[] = array( 00157 'name' => $fname, 00158 'calls' => $data['count'], 00159 'elapsed' => $data['wall'] * 1000, 00160 'memory' => 0, // not supported 00161 'min' => $data['wall'] * 1000, 00162 'max' => $data['wall'] * 1000, 00163 'overhead' => 0, // not supported 00164 'periods' => array() // not supported 00165 ); 00166 $totalWall += $data['wall']; 00167 } else { 00168 $profile[] = array( 00169 'name' => $fname, 00170 'calls' => $data['count'], 00171 'elapsed' => $data['wall']->n * $data['wall']->getMean() * 1000, 00172 'memory' => 0, // not supported 00173 'min' => $data['wall']->min * 1000, 00174 'max' => $data['wall']->max * 1000, 00175 'overhead' => 0, // not supported 00176 'periods' => array() // not supported 00177 ); 00178 $totalWall += $data['wall']->n * $data['wall']->getMean(); 00179 } 00180 } 00181 $totalWall = $totalWall * 1000; 00182 00183 foreach ( $profile as &$item ) { 00184 $item['percent'] = $totalWall ? 100 * $item['elapsed'] / $totalWall : 0; 00185 } 00186 00187 return $profile; 00188 } 00189 00198 public function logData() { 00199 global $wgUDPProfilerHost, $wgUDPProfilerPort; 00200 00201 $this->close(); 00202 00203 if ( !function_exists( 'socket_create' ) ) { 00204 return; // avoid fatal 00205 } 00206 00207 $sock = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP ); 00208 socket_connect( $sock, $wgUDPProfilerHost, $wgUDPProfilerPort ); 00209 $bufferLength = 0; 00210 $buffer = ''; 00211 foreach ( $this->mCollated as $name => $entry ) { 00212 $count = $entry['count']; 00213 $cpu = $entry['cpu']; 00214 $wall = $entry['wall']; 00215 00216 if ( $count === 1 ) { 00217 $data = array( self::TYPE_SINGLE, $name, $cpu, $wall ); 00218 } else { 00219 $data = array( self::TYPE_RUNNING, $name, $count, 00220 $cpu->m1, $cpu->m2, $cpu->min, $cpu->max, 00221 $wall->m1, $wall->m2, $wall->min, $wall->max ); 00222 } 00223 00224 $encoded = MWMessagePack::pack( $data ); 00225 $length = strlen( $encoded ); 00226 00227 // If adding this entry would cause the size of the buffer to 00228 // exceed the standard ethernet MTU size less the UDP header, 00229 // send all pending data and reset the buffer. Otherwise, continue 00230 // accumulating entries into the current buffer. 00231 if ( $length + $bufferLength > 1450 ) { 00232 socket_send( $sock, $buffer, $bufferLength, 0 ); 00233 $buffer = ''; 00234 $bufferLength = 0; 00235 } 00236 $buffer .= $encoded; 00237 $bufferLength += $length; 00238 } 00239 if ( $bufferLength !== 0 ) { 00240 socket_send( $sock, $buffer, $bufferLength, 0 ); 00241 } 00242 } 00243 00247 public function close() { 00248 while ( count( $this->mWorkStack ) ) { 00249 $this->profileOut( 'close' ); 00250 } 00251 } 00252 00253 public function getOutput() { 00254 return ''; // no report 00255 } 00256 }