MediaWiki  1.27.2
profileinfo.php
Go to the documentation of this file.
1 <?php
28 ini_set( 'zlib.output_compression', 'off' );
29 
31 require __DIR__ . '/includes/WebStart.php';
32 
33 header( 'Content-Type: text/html; charset=utf-8' );
34 
35 ?>
36 <!DOCTYPE html>
37 <html>
38 <head>
39  <meta charset="UTF-8" />
40  <title>Profiling data</title>
41  <style>
42  /* noc.wikimedia.org/base.css */
43 
44  * {
45  margin: 0;
46  padding: 0;
47  }
48 
49  body {
50  padding: 0.5em 1em;
51  background: #fff;
52  font: 14px/1.6 sans-serif;
53  color: #333;
54  }
55 
56  p, ul, ol, table {
57  margin: 0.5em 0;
58  }
59 
60  a {
61  color: #0645AD;
62  text-decoration: none;
63  }
64 
65  a:hover {
66  text-decoration: underline;
67  }
68 
79  table {
80  max-width: 100%;
81  background-color: transparent;
82  border-collapse: collapse;
83  border-spacing: 0;
84  }
85 
86  .table {
87  width: 100%;
88  margin-bottom: 20px;
89  }
90 
91  .table th,
92  .table td {
93  padding: 0.1em;
94  text-align: left;
95  vertical-align: top;
96  border-top: 1px solid #ddd;
97  }
98 
99  .table th {
100  font-weight: bold;
101  }
102 
103  .table thead th {
104  vertical-align: bottom;
105  }
106 
107  .table thead:first-child tr:first-child th,
108  .table thead:first-child tr:first-child td {
109  border-top: 0;
110  }
111 
112  .table tbody + tbody {
113  border-top: 2px solid #ddd;
114  }
115 
116  .table-condensed th,
117  .table-condensed td {
118  padding: 4px 5px;
119  }
120 
121  .table-striped tbody tr:nth-child(odd) td,
122  .table-striped tbody tr:nth-child(odd) th {
123  background-color: #f9f9f9;
124  }
125 
126  .table-hover tbody tr:hover td,
127  .table-hover tbody tr:hover th {
128  background-color: #f5f5f5;
129  }
130 
131  hr {
132  margin: 20px 0;
133  border: 0;
134  border-top: 1px solid #eee;
135  border-bottom: 1px solid #fff;
136  }
137  </style>
138 </head>
139 <body>
140 <?php
141 
142 if ( !$wgEnableProfileInfo ) {
143  echo '<p>Disabled</p>'
144  . '</body></html>';
145  exit( 1 );
146 }
147 
149 
150 if ( !$dbr->tableExists( 'profiling' ) ) {
151  echo '<p>No <code>profiling</code> table exists, so we can\'t show you anything.</p>'
152  . '<p>If you want to log profiling data, enable <code>$wgProfiler[\'output\'] = \'db\'</code>'
153  . ' in your StartProfiler.php and run <code>maintenance/update.php</code> to'
154  . ' create the profiling table.'
155  . '</body></html>';
156  exit( 1 );
157 }
158 
159 $expand = [];
160 if ( isset( $_REQUEST['expand'] ) ) {
161  foreach ( explode( ',', $_REQUEST['expand'] ) as $f ) {
162  $expand[$f] = true;
163  }
164 }
165 
166 // @codingStandardsIgnoreStart
168  // @codingStandardsIgnoreEnd
169 
170  public $name;
171  public $count;
172  public $time;
173  public $children;
174 
176 
177  public function __construct( $name, $count, $time, $memory ) {
178  $this->name = $name;
179  $this->count = $count;
180  $this->time = $time;
181  $this->memory = $memory;
182  $this->children = [];
183  }
184 
185  public function add_child( $child ) {
186  $this->children[] = $child;
187  }
188 
189  public function display( $expand, $indent = 0.0 ) {
190  usort( $this->children, 'compare_point' );
191 
192  $ex = isset( $expand[$this->name()] );
193 
194  $anchor = str_replace( '"', '', $this->name() );
195 
196  if ( !$ex ) {
197  if ( count( $this->children ) ) {
198  $url = getEscapedProfileUrl( false, false, $expand + [ $this->name() => true ] );
199  $extet = " <a id=\"{$anchor}\" href=\"{$url}#{$anchor}\">[+]</a>";
200  } else {
201  $extet = '';
202  }
203  } else {
204  $e = [];
205  foreach ( $expand as $name => $ep ) {
206  if ( $name != $this->name() ) {
207  $e += [ $name => $ep ];
208  }
209  }
210  $url = getEscapedProfileUrl( false, false, $e );
211  $extet = " <a id=\"{$anchor}\" href=\"{$url}#{$anchor}\">[–]</a>";
212  }
213  ?>
214  <tr>
215  <th>
216  <div style="margin-left: <?php echo (int)$indent; ?>em;">
217  <?php echo htmlspecialchars( str_replace( ',', ', ', $this->name() ) ) . $extet ?>
218  </div>
219  </th>
220  <?php //@codingStandardsIgnoreStart ?>
221  <td class="mw-profileinfo-timep"><?php echo @wfPercent( $this->time() / self::$totaltime * 100 ); ?></td>
222  <td class="mw-profileinfo-memoryp"><?php echo @wfPercent( $this->memory() / self::$totalmemory * 100 ); ?></td>
223  <td class="mw-profileinfo-count"><?php echo $this->count(); ?></td>
224  <td class="mw-profileinfo-cpr"><?php echo round( sprintf( '%.2f', $this->callsPerRequest() ), 2 ); ?></td>
225  <td class="mw-profileinfo-tpc"><?php echo round( sprintf( '%.2f', $this->timePerCall() ), 2 ); ?></td>
226  <td class="mw-profileinfo-mpc"><?php echo round( sprintf( '%.2f', $this->memoryPerCall() / 1024 ), 2 ); ?></td>
227  <td class="mw-profileinfo-tpr"><?php echo @round( sprintf( '%.2f', $this->time() / self::$totalcount ), 2 ); ?></td>
228  <td class="mw-profileinfo-mpr"><?php echo @round( sprintf( '%.2f', $this->memory() / self::$totalcount / 1024 ), 2 ); ?></td>
229  <?php //@codingStandardsIgnoreEnd ?>
230  </tr>
231  <?php
232  if ( $ex ) {
233  foreach ( $this->children as $child ) {
234  $child->display( $expand, $indent + 2 );
235  }
236  }
237  }
238 
239  public function name() {
240  return $this->name;
241  }
242 
243  public function count() {
244  return $this->count;
245  }
246 
247  public function time() {
248  return $this->time;
249  }
250 
251  public function memory() {
252  return $this->memory;
253  }
254 
255  public function timePerCall() {
256  // @codingStandardsIgnoreStart
257  return @( $this->time / $this->count );
258  // @codingStandardsIgnoreEnd
259  }
260 
261  public function memoryPerCall() {
262  // @codingStandardsIgnoreStart
263  return @( $this->memory / $this->count );
264  // @codingStandardsIgnoreEnd
265  }
266 
267  public function callsPerRequest() {
268  // @codingStandardsIgnoreStart
269  return @( $this->count / self::$totalcount );
270  // @codingStandardsIgnoreEnd
271  }
272 
273  public function timePerRequest() {
274  // @codingStandardsIgnoreStart
275  return @( $this->time / self::$totalcount );
276  // @codingStandardsIgnoreEnd
277  }
278 
279  public function memoryPerRequest() {
280  // @codingStandardsIgnoreStart
281  return @( $this->memory / self::$totalcount );
282  // @codingStandardsIgnoreEnd
283  }
284 
285  public function fmttime() {
286  return sprintf( '%5.02f', $this->time );
287  }
288 };
289 
291  // @codingStandardsIgnoreStart
292  global $sort;
293  // @codingStandardsIgnoreEnd
294  switch ( $sort ) {
295  case 'name':
296  return strcmp( $a->name(), $b->name() );
297  case 'time':
298  return $a->time() > $b->time() ? -1 : 1;
299  case 'memory':
300  return $a->memory() > $b->memory() ? -1 : 1;
301  case 'count':
302  return $a->count() > $b->count() ? -1 : 1;
303  case 'time_per_call':
304  return $a->timePerCall() > $b->timePerCall() ? -1 : 1;
305  case 'memory_per_call':
306  return $a->memoryPerCall() > $b->memoryPerCall() ? -1 : 1;
307  case 'calls_per_req':
308  return $a->callsPerRequest() > $b->callsPerRequest() ? -1 : 1;
309  case 'time_per_req':
310  return $a->timePerRequest() > $b->timePerRequest() ? -1 : 1;
311  case 'memory_per_req':
312  return $a->memoryPerRequest() > $b->memoryPerRequest() ? -1 : 1;
313  }
314 }
315 
316 $sorts = [ 'time', 'memory', 'count', 'calls_per_req', 'name',
317  'time_per_call', 'memory_per_call', 'time_per_req', 'memory_per_req' ];
318 $sort = 'time';
319 if ( isset( $_REQUEST['sort'] ) && in_array( $_REQUEST['sort'], $sorts ) ) {
320  $sort = $_REQUEST['sort'];
321 }
322 
323 $res = $dbr->select(
324  'profiling',
325  '*',
326  [],
327  'profileinfo.php',
328  [ 'ORDER BY' => 'pf_name ASC' ]
329 );
330 
331 if ( isset( $_REQUEST['filter'] ) ) {
332  $filter = $_REQUEST['filter'];
333 } else {
334  $filter = '';
335 }
336 
337 ?>
338 <form method="get" action="profileinfo.php">
339  <p>
340  <input type="text" name="filter" value="<?php echo htmlspecialchars( $filter ); ?>">
341  <input type="hidden" name="sort" value="<?php echo htmlspecialchars( $sort ); ?>">
342  <input type="hidden" name="expand" value="<?php
343  echo htmlspecialchars( implode( ",", array_keys( $expand ) ) );
344  ?>">
345  <input type="submit" value="Filter">
346  </p>
347 </form>
348 
349 <table class="mw-profileinfo-table table table-striped table-hover">
350  <thead>
351  <tr>
352  <th><a href="<?php
353  echo getEscapedProfileUrl( false, 'name' );
354  ?>">Name</a></th>
355  <th><a href="<?php
356  echo getEscapedProfileUrl( false, 'time' );
357  ?>">Time (%)</a></th>
358  <th><a href="<?php
359  echo getEscapedProfileUrl( false, 'memory' );
360  ?>">Memory (%)</a></th>
361  <th><a href="<?php
362  echo getEscapedProfileUrl( false, 'count' );
363  ?>">Count</a></th>
364  <th><a href="<?php
365  echo getEscapedProfileUrl( false, 'calls_per_req' );
366  ?>">Calls/req</a></th>
367  <th><a href="<?php
368  echo getEscapedProfileUrl( false, 'time_per_call' );
369  ?>">ms/call</a></th>
370  <th><a href="<?php
371  echo getEscapedProfileUrl( false, 'memory_per_call' );
372  ?>">kb/call</a></th>
373  <th><a href="<?php
374  echo getEscapedProfileUrl( false, 'time_per_req' );
375  ?>">ms/req</a></th>
376  <th><a href="<?php
377  echo getEscapedProfileUrl( false, 'memory_per_req' );
378  ?>">kb/req</a></th>
379  </tr>
380  </thead>
381  <tbody>
382  <?php
383  profile_point::$totaltime = 0.0;
386 
387  function getEscapedProfileUrl( $_filter = false, $_sort = false, $_expand = false ) {
388  // @codingStandardsIgnoreStart
389  global $filter, $sort, $expand;
390  // @codingStandardsIgnoreEnd
391 
392  if ( $_expand === false ) {
393  $_expand = $expand;
394  }
395 
396  return htmlspecialchars(
397  '?' .
398  wfArrayToCgi( [
399  'filter' => $_filter ? $_filter : $filter,
400  'sort' => $_sort ? $_sort : $sort,
401  'expand' => implode( ',', array_keys( $_expand ) )
402  ] )
403  );
404  }
405 
406  $points = [];
407  $queries = [];
408  $sqltotal = 0.0;
409 
410  $last = false;
411  foreach ( $res as $o ) {
412  $next = new profile_point( $o->pf_name, $o->pf_count, $o->pf_time, $o->pf_memory );
413  if ( $next->name() == '-total' || $next->name() == 'main()' ) {
414  profile_point::$totaltime = $next->time();
415  profile_point::$totalcount = $next->count();
416  profile_point::$totalmemory = $next->memory();
417  }
418  if ( $last !== false ) {
419  if ( preg_match( '/^' . preg_quote( $last->name(), '/' ) . '/', $next->name() ) ) {
420  $last->add_child( $next );
421  continue;
422  }
423  }
424  $last = $next;
425  if ( preg_match( '/^query: /', $next->name() ) || preg_match( '/^query-m: /', $next->name() ) ) {
426  $sqltotal += $next->time();
427  $queries[] = $next;
428  } else {
429  $points[] = $next;
430  }
431  }
432 
433  $s = new profile_point( 'SQL Queries', 0, $sqltotal, 0, 0 );
434  foreach ( $queries as $q ) {
435  $s->add_child( $q );
436  }
437  $points[] = $s;
438 
439  // @codingStandardsIgnoreStart
440  @usort( $points, 'compare_point' );
441  // @codingStandardsIgnoreEnd
442 
443  foreach ( $points as $point ) {
444  if ( strlen( $filter ) && !strstr( $point->name(), $filter ) ) {
445  continue;
446  }
447 
448  $point->display( $expand );
449  }
450  ?>
451  </tbody>
452 </table>
453 <hr />
454 <p>Total time: <code><?php printf( '%5.02f', profile_point::$totaltime ); ?></code></p>
455 
456 <p>Total memory: <code><?php printf( '%5.02f', profile_point::$totalmemory / 1024 ); ?></code></p>
457 <hr />
458 </body>
459 </html>
and how to run hooks for an and one after Each event has a preferably in CamelCase For ArticleDelete hook A clump of code and data that should be run when an event happens This can be either a function and a chunk of data
Definition: hooks.txt:6
null means default in associative array form
Definition: hooks.txt:1798
wfGetDB($db, $groups=[], $wiki=false)
Get a Database object.
foreach($res as $o) $s
wfPercent($nr, $acc=2, $round=true)
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException'returning false will NOT prevent logging $e
Definition: hooks.txt:1932
width
$sort
when a variable name is used in a it is silently declared as a new local masking the global
Definition: design.txt:93
display($expand, $indent=0.0)
$last
Bar style
$points
if(!$dbr->tableExists( 'profiling')) $expand
$sorts
static $totalmemory
title
const DB_SLAVE
Definition: Defines.php:46
$sqltotal
if(isset($_REQUEST['sort'])&&in_array($_REQUEST['sort'], $sorts)) $res
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition: design.txt:12
MediaWiki has optional support for a high distributed memory object caching system For general information on but for a larger site with heavy like it should help lighten the load on the database servers by caching data and objects in memory
Definition: memcached.txt:10
__construct($name, $count, $time, $memory)
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
f9f9f9
static $totalcount
in the sidebar</td >< td > font color
getEscapedProfileUrl($_filter=false, $_sort=false, $_expand=false)
if(!$wgEnableProfileInfo) $dbr
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
static $totaltime
and how to run hooks for an and one after Each event has a preferably in CamelCase For ArticleDelete hook A clump of code and data that should be run when an event happens This can be either a function and a chunk of or an object and a method hook function The function part of a third party developers and local administrators to define code that will be run at certain points in the mainline code
Definition: hooks.txt:23
action
wfArrayToCgi($array1, $array2=null, $prefix= '')
This function takes one or two arrays as input, and returns a CGI-style string, e.g.
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at name
Definition: design.txt:12
compare_point(profile_point $a, profile_point $b)
add_child($child)
$queries
$wgEnableProfileInfo
Definition: profileinfo.php:30