81 $this->enabled =
true;
87 if ( $this->enabled ) {
113 if ( isset(
$params[
'timeMetric'] ) ) {
114 $this->mTimeMetric =
$params[
'timeMetric'];
116 if ( isset(
$params[
'profileID'] ) ) {
117 $this->mProfileID =
$params[
'profileID'];
128 if ( self::$__instance ===
null ) {
132 $class =
'ProfilerStub';
151 self::$__instance = $p;
174 $this->mProfileID = $id;
178 if ( $this->mProfileID ===
false ) {
191 if ( $initial !==
null ) {
192 $this->mWorkStack[] =
array(
'-total', 0, $initial, 0 );
193 $this->mStack[] =
array(
'-setup', 1, $initial, 0, $this->
getTime(), 0 );
205 global $wgDebugFunctionEntry;
206 if ( $wgDebugFunctionEntry ) {
207 $this->
debug( str_repeat(
' ', count( $this->mWorkStack ) ) .
'Entering ' . $functionname .
"\n" );
210 $this->mWorkStack[] =
array( $functionname, count( $this->mWorkStack ), $this->
getTime(), memory_get_usage() );
219 global $wgDebugFunctionEntry;
220 $memory = memory_get_usage();
223 if ( $wgDebugFunctionEntry ) {
224 $this->
debug( str_repeat(
' ', count( $this->mWorkStack ) - 1 ) .
'Exiting ' . $functionname .
"\n" );
227 $bit = array_pop( $this->mWorkStack );
230 $this->
debugGroup(
'profileerror',
"Profiling error, !\$bit: $functionname" );
232 if ( $functionname ==
'close' ) {
233 if ( $bit[0] !=
'-total' ) {
234 $message =
"Profile section ended by close(): {$bit[0]}";
235 $this->
debugGroup(
'profileerror', $message );
236 $this->mStack[] =
array( $message, 0, 0.0, 0, 0.0, 0 );
238 } elseif ( $bit[0] != $functionname ) {
239 $message =
"Profiling error: in({$bit[0]}), out($functionname)";
240 $this->
debugGroup(
'profileerror', $message );
241 $this->mStack[] =
array( $message, 0, 0.0, 0, 0.0, 0 );
245 $this->mStack[] = $bit;
254 while ( count( $this->mWorkStack ) ) {
268 $name =
"{$server} ({$db})";
269 if ( isset( $this->mDBTrxHoldingLocks[
$name] ) ) {
270 ++$this->mDBTrxHoldingLocks[
$name][
'refs'];
272 $this->mDBTrxHoldingLocks[
$name] =
array(
'refs' => 1,
'start' => microtime(
true ) );
284 if ( !$this->mDBTrxHoldingLocks ) {
287 } elseif ( !preg_match(
'/^query-m: (?!SELECT)/', $method )
288 && $realtime < $this->mDBLockThreshold
292 $now = microtime(
true );
293 foreach ( $this->mDBTrxHoldingLocks
as $name => $info ) {
295 if ( ( $now - $realtime ) >= $info[
'start'] ) {
296 $this->mDBTrxMethodTimes[
$name][] =
array( $method, $realtime );
312 $name =
"{$server} ({$db})";
313 if ( --$this->mDBTrxHoldingLocks[
$name][
'refs'] <= 0 ) {
315 foreach ( $this->mDBTrxMethodTimes[
$name]
as $info ) {
316 list( $method, $realtime ) = $info;
317 if ( $realtime >= $this->mDBLockThreshold ) {
323 $dbs = implode(
', ', array_keys( $this->mDBTrxHoldingLocks ) );
324 $msg =
"Sub-optimal transaction on DB(s) {$dbs}:\n";
325 foreach ( $this->mDBTrxMethodTimes[
$name]
as $i => $info ) {
326 list( $method, $realtime ) = $info;
327 $msg .= sprintf(
"%d\t%.6f\t%s\n", $i, $realtime, $method );
331 unset( $this->mDBTrxHoldingLocks[
$name] );
332 unset( $this->mDBTrxMethodTimes[
$name] );
342 $this->mTemplated =
$t;
351 global $wgDebugFunctionEntry, $wgProfileCallTree;
352 $wgDebugFunctionEntry =
false;
354 if ( !count( $this->mStack ) && !count( $this->mCollated ) ) {
355 return "No profiling output\n";
358 if ( $wgProfileCallTree ) {
370 return implode(
'', array_map(
array( &$this,
'getCallTreeLine' ), $this->
remapCallTree( $this->mStack ) ) );
380 if ( count( $stack ) < 2 ) {
384 for ( $max = count( $stack ) - 1; $max > 0; ) {
386 $level = $stack[$max][1];
388 for ( $i = $max -1; $i >= 0; $i-- ) {
389 if ( $stack[$i][1] > $level ) {
390 $working[] = $stack[$i];
395 $working = $this->
remapCallTree( array_reverse( $working ) );
397 foreach ( $working
as $item ) {
400 array_unshift(
$output, $stack[$max] );
403 array_unshift( $outputs,
$output );
419 list(
$fname, $level, $start, , $end ) = $entry;
420 $delta = $end - $start;
421 $space = str_repeat(
' ', $level );
422 # The ugly double sprintf is to work around a PHP bug,
423 # which has been fixed in recent releases.
424 return sprintf(
"%10s %s %s\n", trim( sprintf(
"%7.3f", $delta * 1000.0 ) ), $space,
$fname );
439 if ( $metric ===
false ) {
443 if ( $metric ===
'cpu' || $this->mTimeMetric ===
'user' ) {
444 if ( !function_exists(
'getrusage' ) ) {
448 $time = $ru[
'ru_utime.tv_sec'] + $ru[
'ru_utime.tv_usec'] / 1e6;
449 if ( $metric ===
'cpu' ) {
450 # This is the time of system calls, added to the user time
451 # it gives the total CPU time
452 $time += $ru[
'ru_stime.tv_sec'] + $ru[
'ru_stime.tv_usec'] / 1e6;
456 return microtime(
true );
474 if ( $metric ===
false ) {
478 if ( $metric ===
'cpu' || $this->mTimeMetric ===
'user' ) {
479 if ( !count( $wgRUstart ) ) {
483 $time = $wgRUstart[
'ru_utime.tv_sec'] + $wgRUstart[
'ru_utime.tv_usec'] / 1e6;
484 if ( $metric ===
'cpu' ) {
485 # This is the time of system calls, added to the user time
486 # it gives the total CPU time
487 $time += $wgRUstart[
'ru_stime.tv_sec'] + $wgRUstart[
'ru_stime.tv_usec'] / 1e6;
500 if ( $this->mCollateDone ) {
503 $this->mCollateDone =
true;
507 $this->mCollated =
array();
508 $this->mCalls =
array();
509 $this->mMemory =
array();
511 # Estimate profiling overhead
512 $profileCount = count( $this->mStack );
515 # First, subtract the overhead!
516 $overheadTotal = $overheadMemory = $overheadInternal =
array();
517 foreach ( $this->mStack
as $entry ) {
521 $elapsed = $end - $start;
522 $memory = $entry[5] - $entry[3];
524 if (
$fname ==
'-overhead-total' ) {
525 $overheadTotal[] = $elapsed;
526 $overheadMemory[] = $memory;
527 } elseif (
$fname ==
'-overhead-internal' ) {
528 $overheadInternal[] = $elapsed;
531 $overheadTotal = $overheadTotal ? array_sum( $overheadTotal ) / count( $overheadInternal ) : 0;
532 $overheadMemory = $overheadMemory ? array_sum( $overheadMemory ) / count( $overheadInternal ) : 0;
533 $overheadInternal = $overheadInternal ? array_sum( $overheadInternal ) / count( $overheadInternal ) : 0;
536 foreach ( $this->mStack
as $index => $entry ) {
540 $elapsed = $end - $start;
542 $memory = $entry[5] - $entry[3];
545 if ( !preg_match(
'/^-overhead/',
$fname ) ) {
546 # Adjust for profiling overhead (except special values with elapsed=0
548 $elapsed -= $overheadInternal;
549 $elapsed -= ( $subcalls * $overheadTotal );
550 $memory -= ( $subcalls * $overheadMemory );
554 if ( !array_key_exists(
$fname, $this->mCollated ) ) {
555 $this->mCollated[
$fname] = 0;
556 $this->mCalls[
$fname] = 0;
557 $this->mMemory[
$fname] = 0;
558 $this->mMin[
$fname] = 1 << 24;
560 $this->mOverhead[
$fname] = 0;
564 $this->mCollated[
$fname] += $elapsed;
566 $this->mMemory[
$fname] += $memory;
567 $this->mMin[
$fname] = min( $this->mMin[
$fname], $elapsed );
568 $this->mMax[
$fname] = max( $this->mMax[
$fname], $elapsed );
569 $this->mOverhead[
$fname] += $subcalls;
570 $this->mPeriods[
$fname][] = compact(
'start',
'end',
'memory',
'subcalls' );
573 $this->mCalls[
'-overhead-total'] = $profileCount;
574 arsort( $this->mCollated, SORT_NUMERIC );
586 $nameWidth = $width - 65;
587 $format =
"%-{$nameWidth}s %6d %13.3f %13.3f %13.3f%% %9d (%13.3f -%13.3f) [%d]\n";
588 $titleFormat =
"%-{$nameWidth}s %6s %13s %13s %13s %9s\n";
589 $prof =
"\nProfiling data\n";
590 $prof .= sprintf( $titleFormat,
'Name',
'Calls',
'Total',
'Each',
'%',
'Mem' );
592 $total = isset( $this->mCollated[
'-total'] ) ? $this->mCollated[
'-total'] : 0;
594 foreach ( $this->mCollated
as $fname => $elapsed ) {
595 $calls = $this->mCalls[
$fname];
597 $memory = $this->mMemory[
$fname];
598 $prof .= sprintf( $format,
599 substr(
$fname, 0, $nameWidth ),
601 (
float)( $elapsed * 1000 ),
602 (
float)( $elapsed * 1000 ) / $calls,
605 ( $this->mMin[
$fname] * 1000.0 ),
606 ( $this->mMax[
$fname] * 1000.0 ),
610 $prof .=
"\nTotal: $total\n\n";
622 $total = isset( $this->mCollated[
'-total'] ) ? $this->mCollated[
'-total'] : 0;
623 foreach ( $this->mCollated
as $fname => $elapsed ) {
625 foreach ( $this->mPeriods[
$fname]
as $period ) {
626 $period[
'start'] *= 1000;
627 $period[
'end'] *= 1000;
628 $periods[] = $period;
632 'calls' => $this->mCalls[
$fname],
633 'elapsed' => $elapsed * 1000,
635 'memory' => $this->mMemory[
$fname],
636 'min' => $this->mMin[
$fname] * 1000,
637 'max' => $this->mMax[
$fname] * 1000,
638 'overhead' => $this->mOverhead[
$fname],
639 'periods' => $periods,
651 for ( $i = 0; $i < $profileCount; $i++ ) {
668 $level = $stack[$start][1];
670 for ( $i = $start -1; $i >= 0 && $stack[$i][1] > $level; $i-- ) {
680 global $wgProfilePerHost, $wgProfileToDatabase;
682 # Do not log anything if database is readonly (bug 5375)
683 if (
wfReadOnly() || !$wgProfileToDatabase ) {
688 if ( !is_object( $dbw ) ) {
692 if ( $wgProfilePerHost ) {
701 foreach ( $this->mCollated
as $name => $elapsed ) {
702 $eventCount = $this->mCalls[
$name];
703 $timeSum = (float)( $elapsed * 1000 );
704 $memorySum = (float)$this->mMemory[
$name];
708 $timeSum = $timeSum >= 0 ? $timeSum : 0;
709 $memorySum = $memorySum >= 0 ? $memorySum : 0;
711 $dbw->update(
'profiling',
713 "pf_count=pf_count+{$eventCount}",
714 "pf_time=pf_time+{$timeSum}",
715 "pf_memory=pf_memory+{$memorySum}",
719 'pf_server' => $pfhost,
723 $rc = $dbw->affectedRows();
725 $dbw->insert(
'profiling',
array(
'pf_name' =>
$name,
'pf_count' => $eventCount,
726 'pf_time' => $timeSum,
'pf_memory' => $memorySum,
'pf_server' => $pfhost ),
727 __METHOD__,
array(
'IGNORE' ) );
743 $elt = end( $this->mWorkStack );
753 if ( function_exists(
'wfDebug' ) ) {
765 if ( function_exists(
'wfDebugLog' ) ) {
776 foreach ( headers_list()
as $header ) {
777 if ( preg_match(
'#^content-type: (\w+/\w+);?#i', $header, $m ) ) {