Created
July 22, 2019 04:44
-
-
Save userid/fc114b7ceed35a303d91f62764aba844 to your computer and use it in GitHub Desktop.
Xdebug and tracing memory usage ,https://derickrethans.nl/xdebug-and-tracing-memory-usage.html, https://raw.githubusercontent.com/derickr/xdebug/master/contrib/tracefile-analyser.php
This file contains hidden or 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 | | |
| +----------------------------------------------------------------------+ | |
| | Copyright (c) 2002-2018 Derick Rethans | | |
| +----------------------------------------------------------------------+ | |
| | This source file is subject to version 1.01 of the Xdebug license, | | |
| | that is bundled with this package in the file LICENSE, and is | | |
| | available at through the world-wide-web at | | |
| | https://xdebug.org/license.php | | |
| | If you did not receive a copy of the Xdebug license and are unable | | |
| | to obtain it through the world-wide-web, please send a note to | | |
| | derick@xdebug.org so we can mail you a copy immediately. | | |
| +----------------------------------------------------------------------+ | |
| | Authors: Derick Rethans <derick@xdebug.org> | | |
| +----------------------------------------------------------------------+ | |
| */ | |
| 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(); | |
| $header1 = fgets( $this->handle ); | |
| $header2 = fgets( $this->handle ); | |
| if ( !preg_match( '@Version: [23].*@', $header1 ) || !preg_match( '@File format: [2-4]@', $header2 ) ) | |
| { | |
| echo "\nThis file is not an Xdebug trace file made with format option '1' and version 2 to 4.\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; | |
| } | |
| } | |
| ?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment