* @copyright © 2014 Bryan Davis and Wikimedia Foundation. * @ingroup Profiler * @see Xhprof * @see https://php.net/xhprof * @see https://github.com/facebook/hhvm/blob/master/hphp/doc/profiling.md */ class ProfilerXhprof extends Profiler { /** * @var Xhprof $xhprof */ protected $xhprof; /** * Type of report to send when logData() is called. * @var string $logType */ protected $logType; /** * Should profile report sent to in page content be visible? * @var bool $visible */ protected $visible; /** * @param array $params * @see Xhprof::__construct() */ public function __construct( array $params = array() ) { $params = array_merge( array( 'log' => 'text', 'visible' => false ), $params ); parent::__construct( $params ); $this->logType = $params['log']; $this->visible = $params['visible']; $this->xhprof = new Xhprof( $params ); } /** * No-op for xhprof profiling. * * Use the 'include' configuration key instead if you need to constrain * the functions that are profiled. * * @param string $functionname */ public function profileIn( $functionname ) { } /** * No-op for xhprof profiling. * * Use the 'include' configuration key instead if you need to constrain * the functions that are profiled. * * @param string $functionname */ public function profileOut( $functionname ) { } 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 } ); } /** * No-op for xhprof profiling. */ public function close() { } public function getFunctionStats() { $metrics = $this->xhprof->getCompleteMetrics(); $profile = array(); foreach ( $metrics as $fname => $stats ) { // Convert elapsed times from μs to ms to match ProfilerStandard $profile[] = array( 'name' => $fname, 'calls' => $stats['ct'], 'real' => $stats['wt']['total'] / 1000, '%real' => $stats['wt']['percent'], 'cpu' => isset( $stats['cpu'] ) ? $stats['cpu']['total'] / 1000 : 0, '%cpu' => isset( $stats['cpu'] ) ? $stats['cpu']['percent'] : 0, 'memory' => isset( $stats['mu'] ) ? $stats['mu']['total'] : 0, '%memory' => isset( $stats['mu'] ) ? $stats['mu']['percent'] : 0, 'min' => $stats['wt']['min'] / 1000, 'max' => $stats['wt']['max'] / 1000 ); } return $profile; } /** * Returns a profiling output to be stored in debug file * * @return string */ public function getOutput() { return $this->getFunctionReport(); } /** * Get a report of profiled functions sorted by inclusive wall clock time * in descending order. * * Each line of the report includes this data: * - Function name * - Number of times function was called * - Total wall clock time spent in function in microseconds * - Minimum wall clock time spent in function in microseconds * - Average wall clock time spent in function in microseconds * - Maximum wall clock time spent in function in microseconds * - Percentage of total wall clock time spent in function * - Total delta of memory usage from start to end of function in bytes * * @return string */ protected function getFunctionReport() { $data = $this->xhprof->getInclusiveMetrics(); uasort( $data, Xhprof::makeSortFunction( 'wt', 'total' ) ); $width = 140; $nameWidth = $width - 65; $format = "%-{$nameWidth}s %6d %9d %9d %9d %9d %7.3f%% %9d"; $out = array(); $out[] = sprintf( "%-{$nameWidth}s %6s %9s %9s %9s %9s %7s %9s", 'Name', 'Calls', 'Total', 'Min', 'Each', 'Max', '%', 'Mem' ); foreach ( $data as $func => $stats ) { $out[] = sprintf( $format, $func, $stats['ct'], $stats['wt']['total'], $stats['wt']['min'], $stats['wt']['mean'], $stats['wt']['max'], $stats['wt']['percent'], isset( $stats['mu'] ) ? $stats['mu']['total'] : 0 ); } return implode( "\n", $out ); } }