Skip to content

Instantly share code, notes, and snippets.

@jeffery
Forked from Nycto/traceAnalyser.php
Created March 21, 2012 23:40
Show Gist options
  • Save jeffery/2154198 to your computer and use it in GitHub Desktop.
Save jeffery/2154198 to your computer and use it in GitHub Desktop.
A helper script for analyzing the output of a PHP xdebug code trace
<?php
/**
* Analyzes the output of an XDebug script trace
*
* The original version can be found here:
* http://svn.xdebug.org/cgi-bin/viewvc.cgi/xdebug/trunk/contrib/tracefile-analyser.php?root=xdebug
*
* This version was created to work in PHP 5.2
*/
if ( $argc <= 1 || $argc > 4 )
{
showUsage();
}
$fileName = $argv[1];
$sortKey = 'time-own';
$elements = 25;
if ( $argc > 2 )
{
$sortKey = $argv[2];
if ( !in_array( $sortKey, array( 'calls', 'time-inclusive', 'memory-inclusive', 'time-own', 'memory-own' ) ) )
{
showUsage();
}
}
if ( $argc > 3 )
{
$elements = (int) $argv[3];
}
$o = new drXdebugTraceFileParser( $argv[1] );
$o->parse();
$functions = $o->getFunctions( $sortKey );
// find longest function name
$maxLen = 0;
foreach( $functions as $name => $f )
{
if ( strlen( $name ) > $maxLen )
{
$maxLen = strlen( $name );
}
}
echo "Showing the {$elements} most costly calls sorted by '{$sortKey}'.\n\n";
echo " ", str_repeat( ' ', $maxLen - 8 ), " Inclusive Own\n";
echo "function", str_repeat( ' ', $maxLen - 8 ), "#calls time memory time memory\n";
echo "--------", str_repeat( '-', $maxLen - 8 ), "----------------------------------------\n";
// display functions
$c = 0;
foreach( $functions as $name => $f )
{
$c++;
if ( $c > $elements )
{
break;
}
printf( "%-{$maxLen}s %5d %3.4f %8d %3.4f %8d\n",
$name, $f['calls'],
$f['time-inclusive'], $f['memory-inclusive'],
$f['time-own'], $f['memory-own'] );
}
function showUsage()
{
echo "usage:\n\tphp run-cli tracefile [sortkey] [elements]\n\n";
echo "Allowed sortkeys:\n\tcalls, time-inclusive, memory-inclusive, time-own, memory-own\n";
die();
}
class drXdebugTraceFileParser
{
protected $handle;
/**
* Stores the last function, time and memory for the entry point per
* stack depth. int=>array(string, float, int).
*/
protected $stack;
/**
* Stores per function the total time and memory increases and calls
* string=>array(float, int, int)
*/
protected $functions;
/**
* Stores which functions are on the stack
*/
protected $stackFunctions;
public function __construct( $fileName )
{
$this->handle = fopen( $fileName, 'r' );
if ( !$this->handle )
{
throw new Exception( "Can't open '$fileName'" );
}
$this->stack[-1] = array( '', 0, 0, 0, 0 );
$this->stack[ 0] = array( '', 0, 0, 0, 0 );
$this->stackFunctions = array();
}
public function parse()
{
echo "\nparsing...\n";
$c = 0;
$size = fstat( $this->handle );
$size = $size['size'];
$read = 0;
while ( !feof( $this->handle ) )
{
$buffer = fgets( $this->handle, 4096 );
$read += strlen( $buffer );
$this->parseLine( $buffer );
$c++;
if ( $c % 25000 === 0 )
{
printf( " (%5.2f%%)\n", ( $read / $size ) * 100 );
}
}
echo "\nDone.\n\n";
}
private function parseLine( $line )
{
/*
if ( preg_match( '@^Version: (.*)@', $line, $matches ) )
{
}
else if ( preg_match( '@^File format: (.*)@', $line, $matches ) )
{
}
else if ( preg_match( '@^TRACE.*@', $line, $matches ) )
{
}
else // assume a normal line
*/
{
$parts = explode( "\t", $line );
if ( count( $parts ) < 5 )
{
return;
}
$depth = $parts[0];
$funcNr = $parts[1];
$time = $parts[3];
$memory = $parts[4];
if ( $parts[2] == '0' ) // function entry
{
$funcName = $parts[5];
$intFunc = $parts[6];
$this->stack[$depth] = array( $funcName, $time, $memory, 0, 0 );
array_push( $this->stackFunctions, $funcName );
}
else if ( $parts[2] == '1' ) // function exit
{
list( $funcName, $prevTime, $prevMem, $nestedTime, $nestedMemory ) = $this->stack[$depth];
// collapse data onto functions array
$dTime = $time - $prevTime;
$dMemory = $memory - $prevMem;
$this->stack[$depth - 1][3] += $dTime;
$this->stack[$depth - 1][4] += $dMemory;
array_pop( $this->stackFunctions );
$this->addToFunction( $funcName, $dTime, $dMemory, $nestedTime, $nestedMemory );
}
}
}
protected function addToFunction( $function, $time, $memory, $nestedTime, $nestedMemory )
{
if ( !isset( $this->functions[$function] ) )
{
$this->functions[$function] = array( 0, 0, 0, 0, 0 );
}
$elem = &$this->functions[$function];
$elem[0]++;
if ( !in_array( $function, $this->stackFunctions ) ) {
$elem[1] += $time;
$elem[2] += $memory;
$elem[3] += $nestedTime;
$elem[4] += $nestedMemory;
}
}
public function getFunctions( $sortKey = null )
{
$result = array();
foreach ( $this->functions as $name => $function )
{
$result[$name] = array(
'calls' => $function[0],
'time-inclusive' => $function[1],
'memory-inclusive' => $function[2],
'time-children' => $function[3],
'memory-children' => $function[4],
'time-own' => $function[1] - $function[3],
'memory-own' => $function[2] - $function[4]
);
}
if ( $sortKey !== null )
{
$sort = new SortByKey($sortKey);
$sort->sort($result);
}
return $result;
}
}
class SortByKey
{
private $key;
public function __construct($key) { $this->key = $key; }
private function sortCallback ($a, $b) {
return ( $a[$this->key] > $b[$this->key] ) ? -1 : ( $a[$this->key] < $b[$this->key] ? 1 : 0 );
}
public function sort( array &$data ) {
uasort($data, array($this, 'sortCallback'));
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment