Skip to content

Instantly share code, notes, and snippets.

@Nycto
Created October 15, 2010 19:41
Show Gist options
  • Select an option

  • Save Nycto/628803 to your computer and use it in GitHub Desktop.

Select an option

Save Nycto/628803 to your computer and use it in GitHub Desktop.

Revisions

  1. Nycto revised this gist Oct 15, 2010. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion traceAnalyser.php
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@
    * 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
    * 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
    */
  2. Nycto revised this gist Oct 15, 2010. 1 changed file with 5 additions and 0 deletions.
    5 changes: 5 additions & 0 deletions traceAnalyser.php
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,11 @@
    <?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
    *
    * This version was created to work in PHP 5.2
    */

    if ( $argc <= 1 || $argc > 4 )
  3. Nycto revised this gist Oct 15, 2010. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions traceAnalyser.php
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,7 @@
    <?php
    /**
    * Analyzes the output of an XDebug script trace
    */

    if ( $argc <= 1 || $argc > 4 )
    {
  4. @invalid-email-address Anonymous created this gist Oct 15, 2010.
    229 changes: 229 additions & 0 deletions traceAnalyser.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,229 @@
    <?php

    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'));
    }
    }

    ?>