These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Show profiling data. |
||
4 | * |
||
5 | * Copyright 2005 Kate Turner. |
||
6 | * |
||
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
||
8 | * of this software and associated documentation files (the "Software"), to deal |
||
9 | * in the Software without restriction, including without limitation the rights |
||
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||
11 | * copies of the Software, and to permit persons to whom the Software is |
||
12 | * furnished to do so, subject to the following conditions: |
||
13 | * |
||
14 | * The above copyright notice and this permission notice shall be included in |
||
15 | * all copies or substantial portions of the Software. |
||
16 | * |
||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||
23 | * SOFTWARE. |
||
24 | * |
||
25 | * @file |
||
26 | */ |
||
27 | |||
28 | // This endpoint is supposed to be independent of request cookies and other |
||
29 | // details of the session. Log warnings for violations of the no-session |
||
30 | // constraint. |
||
31 | define( 'MW_NO_SESSION', 'warn' ); |
||
32 | |||
33 | ini_set( 'zlib.output_compression', 'off' ); |
||
34 | |||
35 | $wgEnableProfileInfo = false; |
||
36 | require __DIR__ . '/includes/WebStart.php'; |
||
37 | |||
38 | header( 'Content-Type: text/html; charset=utf-8' ); |
||
39 | |||
40 | ?> |
||
41 | <!DOCTYPE html> |
||
42 | <html> |
||
43 | <head> |
||
44 | <meta charset="UTF-8" /> |
||
45 | <title>Profiling data</title> |
||
46 | <style> |
||
47 | /* noc.wikimedia.org/base.css */ |
||
48 | |||
49 | * { |
||
50 | margin: 0; |
||
51 | padding: 0; |
||
52 | } |
||
53 | |||
54 | body { |
||
55 | padding: 0.5em 1em; |
||
56 | background: #fff; |
||
57 | font: 14px/1.6 sans-serif; |
||
58 | color: #333; |
||
59 | } |
||
60 | |||
61 | p, ul, ol, table { |
||
62 | margin: 0.5em 0; |
||
63 | } |
||
64 | |||
65 | a { |
||
66 | color: #0645AD; |
||
67 | text-decoration: none; |
||
68 | } |
||
69 | |||
70 | a:hover { |
||
71 | text-decoration: underline; |
||
72 | } |
||
73 | |||
74 | /*! |
||
75 | * Bootstrap v2.1.1 |
||
76 | * |
||
77 | * Copyright 2012 Twitter, Inc |
||
78 | * Licensed under the Apache License v2.0 |
||
79 | * http://www.apache.org/licenses/LICENSE-2.0 |
||
80 | * |
||
81 | * Designed and built with all the love in the world @twitter by @mdo and @fat. |
||
82 | */ |
||
83 | |||
84 | table { |
||
85 | max-width: 100%; |
||
86 | background-color: transparent; |
||
87 | border-collapse: collapse; |
||
88 | border-spacing: 0; |
||
89 | } |
||
90 | |||
91 | .table { |
||
92 | width: 100%; |
||
93 | margin-bottom: 20px; |
||
94 | } |
||
95 | |||
96 | .table th, |
||
97 | .table td { |
||
98 | padding: 0.1em; |
||
99 | text-align: left; |
||
100 | vertical-align: top; |
||
101 | border-top: 1px solid #ddd; |
||
102 | } |
||
103 | |||
104 | .table th { |
||
105 | font-weight: bold; |
||
106 | } |
||
107 | |||
108 | .table thead th { |
||
109 | vertical-align: bottom; |
||
110 | } |
||
111 | |||
112 | .table thead:first-child tr:first-child th, |
||
113 | .table thead:first-child tr:first-child td { |
||
114 | border-top: 0; |
||
115 | } |
||
116 | |||
117 | .table tbody + tbody { |
||
118 | border-top: 2px solid #ddd; |
||
119 | } |
||
120 | |||
121 | .table-condensed th, |
||
122 | .table-condensed td { |
||
123 | padding: 4px 5px; |
||
124 | } |
||
125 | |||
126 | .table-striped tbody tr:nth-child(odd) td, |
||
127 | .table-striped tbody tr:nth-child(odd) th { |
||
128 | background-color: #f9f9f9; |
||
129 | } |
||
130 | |||
131 | .table-hover tbody tr:hover td, |
||
132 | .table-hover tbody tr:hover th { |
||
133 | background-color: #f5f5f5; |
||
134 | } |
||
135 | |||
136 | hr { |
||
137 | margin: 20px 0; |
||
138 | border: 0; |
||
139 | border-top: 1px solid #eee; |
||
140 | border-bottom: 1px solid #fff; |
||
141 | } |
||
142 | </style> |
||
143 | </head> |
||
144 | <body> |
||
145 | <?php |
||
146 | |||
147 | if ( !$wgEnableProfileInfo ) { |
||
148 | echo '<p>Disabled</p>' |
||
149 | . '</body></html>'; |
||
150 | exit( 1 ); |
||
151 | } |
||
152 | |||
153 | $dbr = wfGetDB( DB_SLAVE ); |
||
154 | |||
155 | if ( !$dbr->tableExists( 'profiling' ) ) { |
||
156 | echo '<p>No <code>profiling</code> table exists, so we can\'t show you anything.</p>' |
||
157 | . '<p>If you want to log profiling data, enable <code>$wgProfiler[\'output\'] = \'db\'</code>' |
||
158 | . ' in your StartProfiler.php and run <code>maintenance/update.php</code> to' |
||
159 | . ' create the profiling table.' |
||
160 | . '</body></html>'; |
||
161 | exit( 1 ); |
||
162 | } |
||
163 | |||
164 | $expand = []; |
||
165 | if ( isset( $_REQUEST['expand'] ) ) { |
||
166 | foreach ( explode( ',', $_REQUEST['expand'] ) as $f ) { |
||
167 | $expand[$f] = true; |
||
168 | } |
||
169 | } |
||
170 | |||
171 | // @codingStandardsIgnoreStart |
||
172 | class profile_point { |
||
173 | // @codingStandardsIgnoreEnd |
||
174 | |||
175 | public $name; |
||
176 | public $count; |
||
177 | public $time; |
||
178 | public $children; |
||
179 | |||
180 | public static $totaltime, $totalmemory, $totalcount; |
||
181 | |||
182 | public function __construct( $name, $count, $time, $memory ) { |
||
183 | $this->name = $name; |
||
184 | $this->count = $count; |
||
185 | $this->time = $time; |
||
186 | $this->memory = $memory; |
||
187 | $this->children = []; |
||
188 | } |
||
189 | |||
190 | public function add_child( $child ) { |
||
191 | $this->children[] = $child; |
||
192 | } |
||
193 | |||
194 | public function display( $expand, $indent = 0.0 ) { |
||
195 | usort( $this->children, 'compare_point' ); |
||
196 | |||
197 | $ex = isset( $expand[$this->name()] ); |
||
198 | |||
199 | $anchor = str_replace( '"', '', $this->name() ); |
||
200 | |||
201 | if ( !$ex ) { |
||
202 | if ( count( $this->children ) ) { |
||
203 | $url = getEscapedProfileUrl( false, false, $expand + [ $this->name() => true ] ); |
||
204 | $extet = " <a id=\"{$anchor}\" href=\"{$url}#{$anchor}\">[+]</a>"; |
||
205 | } else { |
||
206 | $extet = ''; |
||
207 | } |
||
208 | } else { |
||
209 | $e = []; |
||
210 | foreach ( $expand as $name => $ep ) { |
||
211 | if ( $name != $this->name() ) { |
||
212 | $e += [ $name => $ep ]; |
||
213 | } |
||
214 | } |
||
215 | $url = getEscapedProfileUrl( false, false, $e ); |
||
216 | $extet = " <a id=\"{$anchor}\" href=\"{$url}#{$anchor}\">[–]</a>"; |
||
217 | } |
||
218 | ?> |
||
219 | <tr> |
||
220 | <th> |
||
221 | <div style="margin-left: <?php echo (int)$indent; ?>em;"> |
||
222 | <?php echo htmlspecialchars( str_replace( ',', ', ', $this->name() ) ) . $extet ?> |
||
223 | </div> |
||
224 | </th> |
||
225 | <?php //@codingStandardsIgnoreStart ?> |
||
226 | <td class="mw-profileinfo-timep"><?php echo @wfPercent( $this->time() / self::$totaltime * 100 ); ?></td> |
||
227 | <td class="mw-profileinfo-memoryp"><?php echo @wfPercent( $this->memory() / self::$totalmemory * 100 ); ?></td> |
||
228 | <td class="mw-profileinfo-count"><?php echo $this->count(); ?></td> |
||
229 | <td class="mw-profileinfo-cpr"><?php echo round( sprintf( '%.2f', $this->callsPerRequest() ), 2 ); ?></td> |
||
230 | <td class="mw-profileinfo-tpc"><?php echo round( sprintf( '%.2f', $this->timePerCall() ), 2 ); ?></td> |
||
231 | <td class="mw-profileinfo-mpc"><?php echo round( sprintf( '%.2f', $this->memoryPerCall() / 1024 ), 2 ); ?></td> |
||
232 | <td class="mw-profileinfo-tpr"><?php echo @round( sprintf( '%.2f', $this->time() / self::$totalcount ), 2 ); ?></td> |
||
233 | <td class="mw-profileinfo-mpr"><?php echo @round( sprintf( '%.2f', $this->memory() / self::$totalcount / 1024 ), 2 ); ?></td> |
||
234 | <?php //@codingStandardsIgnoreEnd ?> |
||
235 | </tr> |
||
236 | <?php |
||
237 | if ( $ex ) { |
||
238 | foreach ( $this->children as $child ) { |
||
239 | $child->display( $expand, $indent + 2 ); |
||
240 | } |
||
241 | } |
||
242 | } |
||
243 | |||
244 | public function name() { |
||
245 | return $this->name; |
||
246 | } |
||
247 | |||
248 | public function count() { |
||
249 | return $this->count; |
||
250 | } |
||
251 | |||
252 | public function time() { |
||
253 | return $this->time; |
||
254 | } |
||
255 | |||
256 | public function memory() { |
||
257 | return $this->memory; |
||
258 | } |
||
259 | |||
260 | public function timePerCall() { |
||
261 | // @codingStandardsIgnoreStart |
||
262 | return @( $this->time / $this->count ); |
||
263 | // @codingStandardsIgnoreEnd |
||
264 | } |
||
265 | |||
266 | public function memoryPerCall() { |
||
267 | // @codingStandardsIgnoreStart |
||
268 | return @( $this->memory / $this->count ); |
||
269 | // @codingStandardsIgnoreEnd |
||
270 | } |
||
271 | |||
272 | public function callsPerRequest() { |
||
273 | // @codingStandardsIgnoreStart |
||
274 | return @( $this->count / self::$totalcount ); |
||
275 | // @codingStandardsIgnoreEnd |
||
276 | } |
||
277 | |||
278 | public function timePerRequest() { |
||
279 | // @codingStandardsIgnoreStart |
||
280 | return @( $this->time / self::$totalcount ); |
||
281 | // @codingStandardsIgnoreEnd |
||
282 | } |
||
283 | |||
284 | public function memoryPerRequest() { |
||
285 | // @codingStandardsIgnoreStart |
||
286 | return @( $this->memory / self::$totalcount ); |
||
287 | // @codingStandardsIgnoreEnd |
||
288 | } |
||
289 | |||
290 | public function fmttime() { |
||
291 | return sprintf( '%5.02f', $this->time ); |
||
292 | } |
||
293 | }; |
||
294 | |||
295 | function compare_point( profile_point $a, profile_point $b ) { |
||
296 | // @codingStandardsIgnoreStart |
||
297 | global $sort; |
||
298 | // @codingStandardsIgnoreEnd |
||
299 | switch ( $sort ) { |
||
300 | case 'name': |
||
301 | return strcmp( $a->name(), $b->name() ); |
||
302 | case 'time': |
||
303 | return $a->time() > $b->time() ? -1 : 1; |
||
304 | case 'memory': |
||
305 | return $a->memory() > $b->memory() ? -1 : 1; |
||
306 | case 'count': |
||
307 | return $a->count() > $b->count() ? -1 : 1; |
||
308 | case 'time_per_call': |
||
309 | return $a->timePerCall() > $b->timePerCall() ? -1 : 1; |
||
310 | case 'memory_per_call': |
||
311 | return $a->memoryPerCall() > $b->memoryPerCall() ? -1 : 1; |
||
312 | case 'calls_per_req': |
||
313 | return $a->callsPerRequest() > $b->callsPerRequest() ? -1 : 1; |
||
314 | case 'time_per_req': |
||
315 | return $a->timePerRequest() > $b->timePerRequest() ? -1 : 1; |
||
316 | case 'memory_per_req': |
||
317 | return $a->memoryPerRequest() > $b->memoryPerRequest() ? -1 : 1; |
||
318 | } |
||
319 | } |
||
320 | |||
321 | $sorts = [ 'time', 'memory', 'count', 'calls_per_req', 'name', |
||
322 | 'time_per_call', 'memory_per_call', 'time_per_req', 'memory_per_req' ]; |
||
323 | $sort = 'time'; |
||
324 | if ( isset( $_REQUEST['sort'] ) && in_array( $_REQUEST['sort'], $sorts ) ) { |
||
325 | $sort = $_REQUEST['sort']; |
||
326 | } |
||
327 | |||
328 | $res = $dbr->select( |
||
329 | 'profiling', |
||
330 | '*', |
||
331 | [], |
||
332 | 'profileinfo.php', |
||
333 | [ 'ORDER BY' => 'pf_name ASC' ] |
||
334 | ); |
||
335 | |||
336 | if ( isset( $_REQUEST['filter'] ) ) { |
||
337 | $filter = $_REQUEST['filter']; |
||
338 | } else { |
||
339 | $filter = ''; |
||
340 | } |
||
341 | |||
342 | ?> |
||
343 | <form method="get" action="profileinfo.php"> |
||
344 | <p> |
||
345 | <input type="text" name="filter" value="<?php echo htmlspecialchars( $filter ); ?>"> |
||
346 | <input type="hidden" name="sort" value="<?php echo htmlspecialchars( $sort ); ?>"> |
||
347 | <input type="hidden" name="expand" value="<?php |
||
348 | echo htmlspecialchars( implode( ",", array_keys( $expand ) ) ); |
||
349 | ?>"> |
||
350 | <input type="submit" value="Filter"> |
||
351 | </p> |
||
352 | </form> |
||
353 | |||
354 | <table class="mw-profileinfo-table table table-striped table-hover"> |
||
355 | <thead> |
||
356 | <tr> |
||
357 | <th><a href="<?php |
||
358 | echo getEscapedProfileUrl( false, 'name' ); |
||
359 | ?>">Name</a></th> |
||
360 | <th><a href="<?php |
||
361 | echo getEscapedProfileUrl( false, 'time' ); |
||
362 | ?>">Time (%)</a></th> |
||
363 | <th><a href="<?php |
||
364 | echo getEscapedProfileUrl( false, 'memory' ); |
||
365 | ?>">Memory (%)</a></th> |
||
366 | <th><a href="<?php |
||
367 | echo getEscapedProfileUrl( false, 'count' ); |
||
368 | ?>">Count</a></th> |
||
369 | <th><a href="<?php |
||
370 | echo getEscapedProfileUrl( false, 'calls_per_req' ); |
||
371 | ?>">Calls/req</a></th> |
||
372 | <th><a href="<?php |
||
373 | echo getEscapedProfileUrl( false, 'time_per_call' ); |
||
374 | ?>">ms/call</a></th> |
||
375 | <th><a href="<?php |
||
376 | echo getEscapedProfileUrl( false, 'memory_per_call' ); |
||
377 | ?>">kb/call</a></th> |
||
378 | <th><a href="<?php |
||
379 | echo getEscapedProfileUrl( false, 'time_per_req' ); |
||
380 | ?>">ms/req</a></th> |
||
381 | <th><a href="<?php |
||
382 | echo getEscapedProfileUrl( false, 'memory_per_req' ); |
||
383 | ?>">kb/req</a></th> |
||
384 | </tr> |
||
385 | </thead> |
||
386 | <tbody> |
||
387 | <?php |
||
388 | profile_point::$totaltime = 0.0; |
||
389 | profile_point::$totalcount = 0; |
||
390 | profile_point::$totalmemory = 0.0; |
||
391 | |||
392 | function getEscapedProfileUrl( $_filter = false, $_sort = false, $_expand = false ) { |
||
393 | // @codingStandardsIgnoreStart |
||
394 | global $filter, $sort, $expand; |
||
395 | // @codingStandardsIgnoreEnd |
||
396 | |||
397 | if ( $_expand === false ) { |
||
398 | $_expand = $expand; |
||
399 | } |
||
400 | |||
401 | return htmlspecialchars( |
||
402 | '?' . |
||
403 | wfArrayToCgi( [ |
||
404 | 'filter' => $_filter ? $_filter : $filter, |
||
405 | 'sort' => $_sort ? $_sort : $sort, |
||
406 | 'expand' => implode( ',', array_keys( $_expand ) ) |
||
407 | ] ) |
||
408 | ); |
||
409 | } |
||
410 | |||
411 | $points = []; |
||
412 | $queries = []; |
||
413 | $sqltotal = 0.0; |
||
414 | |||
415 | $last = false; |
||
416 | foreach ( $res as $o ) { |
||
0 ignored issues
–
show
|
|||
417 | $next = new profile_point( $o->pf_name, $o->pf_count, $o->pf_time, $o->pf_memory ); |
||
418 | if ( $next->name() == '-total' || $next->name() == 'main()' ) { |
||
419 | profile_point::$totaltime = $next->time(); |
||
420 | profile_point::$totalcount = $next->count(); |
||
421 | profile_point::$totalmemory = $next->memory(); |
||
422 | } |
||
423 | if ( $last !== false ) { |
||
424 | if ( preg_match( '/^' . preg_quote( $last->name(), '/' ) . '/', $next->name() ) ) { |
||
425 | $last->add_child( $next ); |
||
426 | continue; |
||
427 | } |
||
428 | } |
||
429 | $last = $next; |
||
430 | if ( preg_match( '/^query: /', $next->name() ) || preg_match( '/^query-m: /', $next->name() ) ) { |
||
431 | $sqltotal += $next->time(); |
||
432 | $queries[] = $next; |
||
433 | } else { |
||
434 | $points[] = $next; |
||
435 | } |
||
436 | } |
||
437 | |||
438 | $s = new profile_point( 'SQL Queries', 0, $sqltotal, 0, 0 ); |
||
439 | foreach ( $queries as $q ) { |
||
440 | $s->add_child( $q ); |
||
441 | } |
||
442 | $points[] = $s; |
||
443 | |||
444 | // @codingStandardsIgnoreStart |
||
445 | @usort( $points, 'compare_point' ); |
||
446 | // @codingStandardsIgnoreEnd |
||
447 | |||
448 | foreach ( $points as $point ) { |
||
449 | if ( strlen( $filter ) && !strstr( $point->name(), $filter ) ) { |
||
450 | continue; |
||
451 | } |
||
452 | |||
453 | $point->display( $expand ); |
||
454 | } |
||
455 | ?> |
||
456 | </tbody> |
||
457 | </table> |
||
458 | <hr /> |
||
459 | <p>Total time: <code><?php printf( '%5.02f', profile_point::$totaltime ); ?></code></p> |
||
460 | |||
461 | <p>Total memory: <code><?php printf( '%5.02f', profile_point::$totalmemory / 1024 ); ?></code></p> |
||
462 | <hr /> |
||
463 | </body> |
||
464 | </html> |
||
465 |
There are different options of fixing this problem.
If you want to be on the safe side, you can add an additional type-check:
If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:
Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.