if ( !$this->mAllowGenerator ) {
unset( $result['generator'] );
} elseif ( $flags & ApiBase::GET_VALUES_FOR_HELP ) {
- $result['generator'][ApiBase::PARAM_TYPE] = $this->getGenerators();
- foreach ( $result['generator'][ApiBase::PARAM_TYPE] as $g ) {
+ foreach ( $this->getGenerators() as $g ) {
$result['generator'][ApiBase::PARAM_TYPE][] = $g;
$result['generator'][ApiBase::PARAM_VALUE_LINKS][$g] = "Special:ApiHelp/query+$g";
}
* @see https://github.com/facebook/hhvm/blob/master/hphp/doc/profiling.md
*/
class ProfilerXhprof extends Profiler {
-
/**
* @var Xhprof $xhprof
*/
protected $xhprof;
+ /**
+ * Profiler for explicit, arbitrary, frame labels
+ * @var SectionProfiler
+ */
+ protected $sprofiler;
+
/**
* Type of report to send when logData() is called.
* @var string $logType
$this->logType = $params['log'];
$this->visible = $params['visible'];
$this->xhprof = new Xhprof( $params );
+ $this->sprofiler = new SectionProfiler();
}
/**
}
public function scopedProfileIn( $section ) {
- static $exists = null;
- // Only HHVM supports this, not the standard PECL extension
- if ( $exists === null ) {
- $exists = function_exists( 'xhprof_frame_begin' );
- }
-
- if ( $exists ) {
- xhprof_frame_begin( $section );
- return new ScopedCallback( function () {
- xhprof_frame_end();
- } );
- }
-
- return new ScopedCallback( function () {
- // no-op
- } );
+ return $this->sprofiler->scopedProfileIn( $section );
}
/**
$metrics = $this->xhprof->getCompleteMetrics();
$profile = array();
+ $main = null; // units in ms
foreach ( $metrics as $fname => $stats ) {
// Convert elapsed times from μs to ms to match ProfilerStandard
- $profile[] = array(
+ $entry = array(
'name' => $fname,
'calls' => $stats['ct'],
'real' => $stats['wt']['total'] / 1000,
'min_real' => $stats['wt']['min'] / 1000,
'max_real' => $stats['wt']['max'] / 1000
);
+ $profile[] = $entry;
+ if ( $fname === 'main()' ) {
+ $main = $entry;
+ }
+ }
+
+ // Merge in all of the custom profile sections
+ foreach ( $this->sprofiler->getFunctionStats() as $stats ) {
+ // @note: getFunctionStats() values already in ms
+ $stats['%real'] = $stats['real'] / $main['real'];
+ $stats['%cpu'] = $main['cpu'] ? $stats['cpu'] / $main['cpu'] * 100 : 0;
+ $stats['%memory'] = $main['memory'] ? $stats['memory'] / $main['memory'] * 100 : 0;
+ $profile[] = $stats; // assume no section names collide with $metrics
}
return $profile;
* - %cpu : percent real time
* - memory : memory used (bytes)
* - %memory : percent memory used
+ * - min_real : min real time in a call (ms)
+ * - max_real : max real time in a call (ms)
*/
public function getFunctionStats() {
$this->collateData();
'%cpu' => $totalCpu ? 100 * $data['cpu'] / $totalCpu : 0,
'memory' => $data['memory'],
'%memory' => $totalMem ? 100 * $data['memory'] / $totalMem : 0,
+ 'min_real' => 1000 * $data['min_real'],
+ 'max_real' => 1000 * $data['max_real']
);
}
'%cpu' => 100,
'memory' => $totalMem,
'%memory' => 100,
+ 'min_real' => 1000 * $totalReal,
+ 'max_real' => 1000 * $totalReal
);
return $profile;
'cpu' => 0.0,
'real' => 0.0,
'memory' => 0,
- 'count' => 0
+ 'count' => 0,
+ 'min_real' => 0.0,
+ 'max_real' => 0.0
);
}
$entry['real'] += $elapsedReal;
$entry['memory'] += $memChange > 0 ? $memChange : 0;
$entry['count']++;
+ $entry['min_real'] = min( $entry['min_real'], $elapsedReal );
+ $entry['max_real'] = max( $entry['max_real'], $elapsedReal );
}
/**
);
}
+ /**
+ * Remove empty values from the end of an array.
+ *
+ * Values considered empty:
+ *
+ * - null
+ * - empty array
+ *
+ * @param Array $array
+ */
+ private static function trimArray( Array &$array ) {
+ $i = count( $array );
+ while ( $i-- ) {
+ if ( $array[$i] === null || $array[$i] === array() ) {
+ unset( $array[$i] );
+ } else {
+ break;
+ }
+ }
+ }
+
/**
* Returns JS code which calls mw.loader.register with the given
* parameters. Has three calling conventions:
if ( is_array( $name ) ) {
// Build module name index
$index = array();
- foreach ( $name as $i => $module ) {
+ foreach ( $name as $i => &$module ) {
$index[$module[0]] = $i;
}
}
}
+ array_walk( $name, array( 'self', 'trimArray' ) );
+
return Xml::encodeJsCall(
'mw.loader.register',
array( $name ),
ResourceLoader::inDebugMode()
);
} else {
- $version = (int) $version;
+ $registration = array( $name, $version, $dependencies, $group, $source, $skip );
+ self::trimArray( $registration );
return Xml::encodeJsCall(
'mw.loader.register',
- array( $name, $version, $dependencies, $group, $source, $skip ),
+ $registration,
ResourceLoader::inDebugMode()
);
}
continue;
}
- if (
- !count( $data['dependencies'] ) &&
- $data['group'] === null &&
- $data['source'] === 'local' &&
- $data['skip'] === null
- ) {
- // Modules with no dependencies, group, foreign source or skip function;
- // call mw.loader.register(name, timestamp)
- $registrations[] = array( $name, $data['version'] );
- } elseif (
- $data['group'] === null &&
- $data['source'] === 'local' &&
- $data['skip'] === null
- ) {
- // Modules with dependencies but no group, foreign source or skip function;
- // call mw.loader.register(name, timestamp, dependencies)
- $registrations[] = array(
- $name,
- $data['version'],
- $data['dependencies']
- );
- } elseif (
- $data['source'] === 'local' &&
- $data['skip'] === null
- ) {
- // Modules with a group but no foreign source or skip function;
- // call mw.loader.register(name, timestamp, dependencies, group)
- $registrations[] = array(
- $name,
- $data['version'],
- $data['dependencies'],
- $data['group']
- );
- } elseif ( $data['skip'] === null ) {
- // Modules with a foreign source but no skip function;
- // call mw.loader.register(name, timestamp, dependencies, group, source)
- $registrations[] = array(
- $name,
- $data['version'],
- $data['dependencies'],
- $data['group'],
- $data['source']
- );
- } else {
- // Modules with a skip function;
- // call mw.loader.register(name, timestamp, dependencies, group, source, skip)
- $registrations[] = array(
- $name,
- $data['version'],
- $data['dependencies'],
- $data['group'],
- $data['source'],
- $data['skip']
- );
- }
+ // Call mw.loader.register(name, timestamp, dependencies, group, source, skip)
+ $registrations[] = array(
+ $name,
+ $data['version'],
+ $data['dependencies'],
+ $data['group'],
+ // Swap default (local) for null
+ $data['source'] === 'local' ? null : $data['source'],
+ $data['skip']
+ );
}
// Register modules
1388534400,
[],
null,
- "local",
+ null,
"return true;"
],
[
1388534400,
[],
null,
- "local",
+ null,
"return !!( window.JSON \u0026\u0026 JSON.parse \u0026\u0026 JSON.stringify);"
],
[
'mw.loader.addSource({"local":"/w/load.php"});'
. 'mw.loader.register(['
. '["test.blank",1388534400],'
-. '["test.min",1388534400,[0],null,"local",'
+. '["test.min",1388534400,[0],null,null,'
. '"return!!(window.JSON\u0026\u0026JSON.parse\u0026\u0026JSON.stringify);"'
. ']]);',
$module->getModuleRegistrations( $context ),
0
],
null,
- "local",
+ null,
"return !!( window.JSON \u0026\u0026 JSON.parse \u0026\u0026 JSON.stringify);"
]
] );',