Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
if ( $argc <= 1 || $argc > 4 )
3
{
4
	showUsage();
5
}
6
 
7
$fileName = $argv[1];
8
$sortKey  = 'time-own';
9
$elements = 25;
10
if ( $argc > 2 )
11
{
12
	$sortKey = $argv[2];
13
	if ( !in_array( $sortKey, array( 'calls', 'time-inclusive', 'memory-inclusive', 'time-own', 'memory-own' ) ) )
14
	{
15
		showUsage();
16
	}
17
}
18
if ( $argc > 3 )
19
{
20
	$elements = (int) $argv[3];
21
}
22
 
23
$o = new drXdebugTraceFileParser( $argv[1] );
24
$o->parse();
25
$functions = $o->getFunctions( $sortKey );
26
 
27
// find longest function name
28
$maxLen = 0;
29
foreach( $functions as $name => $f )
30
{
31
	if ( strlen( $name ) > $maxLen )
32
	{
33
		$maxLen = strlen( $name );
34
	}
35
}
36
 
37
echo "Showing the {$elements} most costly calls sorted by '{$sortKey}'.\n\n";
38
 
39
echo "        ", str_repeat( ' ', $maxLen - 8 ), "        Inclusive        Own\n";
40
echo "function", str_repeat( ' ', $maxLen - 8 ), "#calls  time     memory  time     memory\n";
41
echo "--------", str_repeat( '-', $maxLen - 8 ), "----------------------------------------\n";
42
 
43
// display functions
44
$c = 0;
45
foreach( $functions as $name => $f )
46
{
47
	$c++;
48
	if ( $c > $elements )
49
	{
50
		break;
51
	}
52
	printf( "%-{$maxLen}s %5d  %3.4f %8d  %3.4f %8d\n",
53
		$name, $f['calls'],
54
		$f['time-inclusive'], $f['memory-inclusive'],
55
		$f['time-own'], $f['memory-own'] );
56
}
57
 
58
function showUsage()
59
{
60
	echo "usage:\n\tphp run-cli tracefile [sortkey] [elements]\n\n";
61
	echo "Allowed sortkeys:\n\tcalls, time-inclusive, memory-inclusive, time-own, memory-own\n";
62
	die();
63
}
64
 
65
class drXdebugTraceFileParser
66
{
67
	protected $handle;
68
 
69
	/**
70
	 * Stores the last function, time and memory for the entry point per
71
	 * stack depth. int=>array(string, float, int).
72
	 */
73
	protected $stack;
74
 
75
	/**
76
	 * Stores per function the total time and memory increases and calls
77
	 * string=>array(float, int, int)
78
	 */
79
	protected $functions;
80
 
81
	/**
82
	 * Stores which functions are on the stack
83
	 */
84
	protected $stackFunctions;
85
 
86
	public function __construct( $fileName )
87
	{
88
		$this->handle = fopen( $fileName, 'r' );
89
		if ( !$this->handle )
90
		{
91
			throw new Exception( "Can't open '$fileName'" );
92
		}
93
		$this->stack[-1] = array( '', 0, 0, 0, 0 );
94
		$this->stack[ 0] = array( '', 0, 0, 0, 0 );
95
 
96
		$this->stackFunctions = array();
97
	}
98
 
99
	public function parse()
100
	{
101
		echo "\nparsing...\n";
102
		$c = 0;
103
		$size = fstat( $this->handle );
104
		$size = $size['size'];
105
		$read = 0;
106
 
107
		while ( !feof( $this->handle ) )
108
		{
109
			$buffer = fgets( $this->handle, 4096 );
110
			$read += strlen( $buffer );
111
			$this->parseLine( $buffer );
112
			$c++;
113
 
114
			if ( $c % 25000 === 0 )
115
			{
116
				printf( " (%5.2f%%)\n", ( $read / $size ) * 100 );
117
			}
118
		}
119
		echo "\nDone.\n\n";
120
	}
121
 
122
	private function parseLine( $line )
123
	{
124
	/*
125
		if ( preg_match( '@^Version: (.*)@', $line, $matches ) )
126
		{
127
		}
128
		else if ( preg_match( '@^File format: (.*)@', $line, $matches ) )
129
		{
130
		}
131
		else if ( preg_match( '@^TRACE.*@', $line, $matches ) )
132
		{
133
		}
134
		else // assume a normal line
135
		*/
136
		{
137
			$parts = explode( "\t", $line );
138
			if ( count( $parts ) < 5 )
139
			{
140
				return;
141
			}
142
			$depth = $parts[0];
143
			$funcNr = $parts[1];
144
			$time = $parts[3];
145
			$memory = $parts[4];
146
			if ( $parts[2] == '0' ) // function entry
147
			{
148
				$funcName = $parts[5];
149
				$intFunc = $parts[6];
150
 
151
				$this->stack[$depth] = array( $funcName, $time, $memory, 0, 0 );
152
 
153
				array_push( $this->stackFunctions, $funcName );
154
			}
155
			else if ( $parts[2] == '1' ) // function exit
156
			{
157
				list( $funcName, $prevTime, $prevMem, $nestedTime, $nestedMemory ) = $this->stack[$depth];
158
 
159
				// collapse data onto functions array
160
				$dTime   = $time   - $prevTime;
161
				$dMemory = $memory - $prevMem;
162
 
163
				$this->stack[$depth - 1][3] += $dTime;
164
				$this->stack[$depth - 1][4] += $dMemory;
165
 
166
				array_pop( $this->stackFunctions );
167
 
168
				$this->addToFunction( $funcName, $dTime, $dMemory, $nestedTime, $nestedMemory );
169
			}
170
		}
171
	}
172
 
173
	protected function addToFunction( $function, $time, $memory, $nestedTime, $nestedMemory )
174
	{
175
		if ( !isset( $this->functions[$function] ) )
176
		{
177
			$this->functions[$function] = array( 0, 0, 0, 0, 0 );
178
		}
179
 
180
		$elem = &$this->functions[$function];
181
		$elem[0]++;
182
		if ( !in_array( $function, $this->stackFunctions ) ) {
183
			$elem[1] += $time;
184
			$elem[2] += $memory;
185
			$elem[3] += $nestedTime;
186
			$elem[4] += $nestedMemory;
187
		}
188
	}
189
 
190
	public function getFunctions( $sortKey = null )
191
	{
192
		$result = array();
193
		foreach ( $this->functions as $name => $function )
194
		{
195
			$result[$name] = array(
196
				'calls'                 => $function[0],
197
				'time-inclusive'        => $function[1],
198
				'memory-inclusive'      => $function[2],
199
				'time-children'         => $function[3],
200
				'memory-children'       => $function[4],
201
				'time-own'              => $function[1] - $function[3],
202
				'memory-own'            => $function[2] - $function[4]
203
			);
204
		}
205
 
206
		if ( $sortKey !== null )
207
		{
208
			uasort( $result,
209
				function( $a, $b ) use ( $sortKey )
210
				{
211
					return ( $a[$sortKey] > $b[$sortKey] ) ? -1 : ( $a[$sortKey] < $b[$sortKey] ? 1 : 0 );
212
				}
213
			);
214
		}
215
 
216
		return $result;
217
	}
218
}
219
?>