Skip to content

Instantly share code, notes, and snippets.

@d1rk
Created March 27, 2012 13:32
Show Gist options
  • Save d1rk/2215886 to your computer and use it in GitHub Desktop.
Save d1rk/2215886 to your computer and use it in GitHub Desktop.
parsing an xdebug dump into something meaningful
<?php
xdebug_start_trace('/tmp/mytrace');
// do stuff
xdebug_stop_trace();
?>
<?php
if ( $argc <= 1 || $argc > 4 )
{
showUsage();
}
$fileName = $argv[1];
$sortKey = 'memory-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();
$header1 = fgets( $this->handle );
$header2 = fgets( $this->handle );
if ( !preg_match( '@Version: 2.*@', $header1 ) || !preg_match( '@File format: 2@', $header2 ) )
{
echo "\nThis file is not an Xdebug trace file made with format option '1'.\n";
showUsage();
}
}
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 )
{
uasort( $result,
function( $a, $b ) use ( $sortKey )
{
return ( $a[$sortKey] > $b[$sortKey] ) ? -1 : ( $a[$sortKey] < $b[$sortKey] ? 1 : 0 );
}
);
}
return $result;
}
}
?>
[xdebug]
xdebug.file_link_format="txmt://open?url=file://%f&line=%l"
xdebug.show_exception_trace=Off
xdebug.max_nesting_level=1000
xdebug.collect_params=1
xdebug.cli_color=On
xdebug.profiler_output_dir = /tmp/
xdebug.profiler_output_name = cachegrind.out.%s
xdebug.profiler_enable = 0
xdebug.profiler_enable_trigger = 1
xdebug.show_mem_delta = 1
xdebug.trace_format = 1 # 0 is text, 1 is machine, 2 is html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment