MediaWiki  REL1_24
ProfilerMwprof.php
Go to the documentation of this file.
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 }