Created
March 27, 2012 13:32
-
-
Save d1rk/2215886 to your computer and use it in GitHub Desktop.
parsing an xdebug dump into something meaningful
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
xdebug_start_trace('/tmp/mytrace'); | |
// do stuff | |
xdebug_stop_trace(); | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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; | |
} | |
} | |
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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