MediaWiki
REL1_24
|
00001 <?php 00033 function wfGetRusage() { 00034 if ( !function_exists( 'getrusage' ) ) { 00035 return false; 00036 } elseif ( defined ( 'HHVM_VERSION' ) ) { 00037 return getrusage( 2 /* RUSAGE_THREAD */ ); 00038 } else { 00039 return getrusage( 0 /* RUSAGE_SELF */ ); 00040 } 00041 } 00042 00047 function wfProfileIn( $functionname ) { 00048 if ( Profiler::$__instance === null ) { // use this directly to reduce overhead 00049 Profiler::instance(); 00050 } 00051 if ( !( Profiler::$__instance instanceof ProfilerStub ) ) { 00052 Profiler::$__instance->profileIn( $functionname ); 00053 } 00054 } 00055 00060 function wfProfileOut( $functionname = 'missing' ) { 00061 if ( Profiler::$__instance === null ) { // use this directly to reduce overhead 00062 Profiler::instance(); 00063 } 00064 if ( !( Profiler::$__instance instanceof ProfilerStub ) ) { 00065 Profiler::$__instance->profileOut( $functionname ); 00066 } 00067 } 00068 00074 class ProfileSection { 00075 protected $name; // string; method name 00076 protected $enabled = false; // boolean; whether profiling is enabled 00077 00089 public function __construct( $name ) { 00090 $this->name = $name; 00091 if ( Profiler::$__instance === null ) { // use this directly to reduce overhead 00092 Profiler::instance(); 00093 } 00094 if ( !( Profiler::$__instance instanceof ProfilerStub ) ) { 00095 $this->enabled = true; 00096 Profiler::$__instance->profileIn( $this->name ); 00097 } 00098 } 00099 00100 function __destruct() { 00101 if ( $this->enabled ) { 00102 Profiler::$__instance->profileOut( $this->name ); 00103 } 00104 } 00105 } 00106 00112 abstract class Profiler { 00114 protected $mProfileID = false; 00116 protected $mTemplated = false; 00117 00119 protected $trxProfiler; 00120 00121 // @codingStandardsIgnoreStart PSR2.Classes.PropertyDeclaration.Underscore 00123 public static $__instance = null; 00124 // @codingStandardsIgnoreEnd 00125 00129 public function __construct( array $params ) { 00130 if ( isset( $params['profileID'] ) ) { 00131 $this->mProfileID = $params['profileID']; 00132 } 00133 $this->trxProfiler = new TransactionProfiler(); 00134 } 00135 00140 final public static function instance() { 00141 if ( self::$__instance === null ) { 00142 global $wgProfiler; 00143 if ( is_array( $wgProfiler ) ) { 00144 if ( !isset( $wgProfiler['class'] ) ) { 00145 $class = 'ProfilerStub'; 00146 } elseif ( $wgProfiler['class'] === 'Profiler' ) { 00147 $class = 'ProfilerStub'; // b/c; don't explode 00148 } else { 00149 $class = $wgProfiler['class']; 00150 } 00151 self::$__instance = new $class( $wgProfiler ); 00152 } elseif ( $wgProfiler instanceof Profiler ) { 00153 self::$__instance = $wgProfiler; // back-compat 00154 } else { 00155 self::$__instance = new ProfilerStub( array() ); 00156 } 00157 } 00158 return self::$__instance; 00159 } 00160 00165 final public static function setInstance( Profiler $p ) { 00166 self::$__instance = $p; 00167 } 00168 00174 abstract public function isStub(); 00175 00186 abstract public function isPersistent(); 00187 00191 public function setProfileID( $id ) { 00192 $this->mProfileID = $id; 00193 } 00194 00198 public function getProfileID() { 00199 if ( $this->mProfileID === false ) { 00200 return wfWikiID(); 00201 } else { 00202 return $this->mProfileID; 00203 } 00204 } 00205 00211 abstract public function profileIn( $functionname ); 00212 00218 abstract public function profileOut( $functionname ); 00219 00229 public function transactionWritingIn( $server, $db, $id = '' ) { 00230 $this->trxProfiler->transactionWritingIn( $server, $db, $id ); 00231 } 00232 00244 public function transactionWritingOut( $server, $db, $id = '' ) { 00245 $this->trxProfiler->transactionWritingOut( $server, $db, $id ); 00246 } 00247 00251 abstract public function close(); 00252 00256 abstract public function logData(); 00257 00263 public function setTemplated( $t ) { 00264 $this->mTemplated = $t; 00265 } 00266 00272 abstract public function getOutput(); 00273 00277 abstract public function getRawData(); 00278 00290 protected function getTime( $metric = 'wall' ) { 00291 if ( $metric === 'cpu' || $metric === 'user' ) { 00292 $ru = wfGetRusage(); 00293 if ( !$ru ) { 00294 return 0; 00295 } 00296 $time = $ru['ru_utime.tv_sec'] + $ru['ru_utime.tv_usec'] / 1e6; 00297 if ( $metric === 'cpu' ) { 00298 # This is the time of system calls, added to the user time 00299 # it gives the total CPU time 00300 $time += $ru['ru_stime.tv_sec'] + $ru['ru_stime.tv_usec'] / 1e6; 00301 } 00302 return $time; 00303 } else { 00304 return microtime( true ); 00305 } 00306 } 00307 00319 protected function getInitialTime( $metric = 'wall' ) { 00320 global $wgRequestTime, $wgRUstart; 00321 00322 if ( $metric === 'cpu' || $metric === 'user' ) { 00323 if ( !count( $wgRUstart ) ) { 00324 return null; 00325 } 00326 00327 $time = $wgRUstart['ru_utime.tv_sec'] + $wgRUstart['ru_utime.tv_usec'] / 1e6; 00328 if ( $metric === 'cpu' ) { 00329 # This is the time of system calls, added to the user time 00330 # it gives the total CPU time 00331 $time += $wgRUstart['ru_stime.tv_sec'] + $wgRUstart['ru_stime.tv_usec'] / 1e6; 00332 } 00333 return $time; 00334 } else { 00335 if ( empty( $wgRequestTime ) ) { 00336 return null; 00337 } else { 00338 return $wgRequestTime; 00339 } 00340 } 00341 } 00342 00348 protected function debug( $s ) { 00349 if ( function_exists( 'wfDebug' ) ) { 00350 wfDebug( $s ); 00351 } 00352 } 00353 00360 protected function debugGroup( $group, $s ) { 00361 if ( function_exists( 'wfDebugLog' ) ) { 00362 wfDebugLog( $group, $s ); 00363 } 00364 } 00365 } 00366 00375 class TransactionProfiler { 00377 protected $mDBLockThreshold = 3.0; 00379 protected $mDBTrxHoldingLocks = array(); 00381 protected $mDBTrxMethodTimes = array(); 00382 00392 public function transactionWritingIn( $server, $db, $id ) { 00393 $name = "{$server} ({$db}) (TRX#$id)"; 00394 if ( isset( $this->mDBTrxHoldingLocks[$name] ) ) { 00395 wfDebugLog( 'DBPerformance', "Nested transaction for '$name' - out of sync." ); 00396 } 00397 $this->mDBTrxHoldingLocks[$name] = 00398 array( 'start' => microtime( true ), 'conns' => array() ); 00399 $this->mDBTrxMethodTimes[$name] = array(); 00400 00401 foreach ( $this->mDBTrxHoldingLocks as $name => &$info ) { 00402 $info['conns'][$name] = 1; // track all DBs in transactions for this transaction 00403 } 00404 } 00405 00414 public function recordFunctionCompletion( $method, $realtime ) { 00415 if ( !$this->mDBTrxHoldingLocks ) { 00416 return; // short-circuit 00417 // @todo hardcoded check is a tad janky (what about FOR UPDATE?) 00418 } elseif ( !preg_match( '/^query-m: (?!SELECT)/', $method ) 00419 && $realtime < $this->mDBLockThreshold 00420 ) { 00421 return; // not a DB master query nor slow enough 00422 } 00423 $now = microtime( true ); 00424 foreach ( $this->mDBTrxHoldingLocks as $name => $info ) { 00425 // Hacky check to exclude entries from before the first TRX write 00426 if ( ( $now - $realtime ) >= $info['start'] ) { 00427 $this->mDBTrxMethodTimes[$name][] = array( $method, $realtime ); 00428 } 00429 } 00430 } 00431 00443 public function transactionWritingOut( $server, $db, $id ) { 00444 $name = "{$server} ({$db}) (TRX#$id)"; 00445 if ( !isset( $this->mDBTrxMethodTimes[$name] ) ) { 00446 wfDebugLog( 'DBPerformance', "Detected no transaction for '$name' - out of sync." ); 00447 return; 00448 } 00449 $slow = false; 00450 foreach ( $this->mDBTrxMethodTimes[$name] as $info ) { 00451 $realtime = $info[1]; 00452 if ( $realtime >= $this->mDBLockThreshold ) { 00453 $slow = true; 00454 break; 00455 } 00456 } 00457 if ( $slow ) { 00458 $dbs = implode( ', ', array_keys( $this->mDBTrxHoldingLocks[$name]['conns'] ) ); 00459 $msg = "Sub-optimal transaction on DB(s) [{$dbs}]:\n"; 00460 foreach ( $this->mDBTrxMethodTimes[$name] as $i => $info ) { 00461 list( $method, $realtime ) = $info; 00462 $msg .= sprintf( "%d\t%.6f\t%s\n", $i, $realtime, $method ); 00463 } 00464 wfDebugLog( 'DBPerformance', $msg ); 00465 } 00466 unset( $this->mDBTrxHoldingLocks[$name] ); 00467 unset( $this->mDBTrxMethodTimes[$name] ); 00468 } 00469 }