MediaWiki  1.33.0
profileinfo.php
Go to the documentation of this file.
1 <?php
39 // This endpoint is supposed to be independent of request cookies and other
40 // details of the session. Enforce this constraint with respect to session use.
41 define( 'MW_NO_SESSION', 1 );
42 
43 ini_set( 'zlib.output_compression', 'off' );
44 
45 require __DIR__ . '/includes/WebStart.php';
46 
47 header( 'Content-Type: text/html; charset=utf-8' );
48 
49 ?>
50 <!DOCTYPE html>
51 <html>
52 <head>
53  <meta charset="UTF-8" />
54  <title>Profiling data</title>
55  <style>
56  /* noc.wikimedia.org/base.css */
57 
58  * {
59  margin: 0;
60  padding: 0;
61  }
62 
63  body {
64  padding: 0.5em 1em;
65  background: #fff;
66  font: 14px/1.6 sans-serif;
67  color: #333;
68  }
69 
70  p, ul, ol, table {
71  margin: 0.5em 0;
72  }
73 
74  a {
75  color: #0645AD;
76  text-decoration: none;
77  }
78 
79  a:hover {
80  text-decoration: underline;
81  }
82 
93  table {
94  max-width: 100%;
95  background-color: transparent;
96  border-collapse: collapse;
97  border-spacing: 0;
98  }
99 
100  .table {
101  width: 100%;
102  margin-bottom: 20px;
103  }
104 
105  .table th,
106  .table td {
107  padding: 0.1em;
108  text-align: left;
109  vertical-align: top;
110  border-top: 1px solid #ddd;
111  }
112 
113  .table th {
114  font-weight: bold;
115  }
116 
117  .table thead th {
118  vertical-align: bottom;
119  }
120 
121  .table thead:first-child tr:first-child th,
122  .table thead:first-child tr:first-child td {
123  border-top: 0;
124  }
125 
126  .table tbody + tbody {
127  border-top: 2px solid #ddd;
128  }
129 
130  .table-condensed th,
131  .table-condensed td {
132  padding: 4px 5px;
133  }
134 
135  .table-striped tbody tr:nth-child(odd) td,
136  .table-striped tbody tr:nth-child(odd) th {
137  background-color: #f9f9f9;
138  }
139 
140  .table-hover tbody tr:hover td,
141  .table-hover tbody tr:hover th {
142  background-color: #f5f5f5;
143  }
144 
145  hr {
146  margin: 20px 0;
147  border: 0;
148  border-top: 1px solid #eee;
149  border-bottom: 1px solid #fff;
150  }
151  </style>
152 </head>
153 <body>
154 <?php
155 
156 if ( !$wgEnableProfileInfo ) {
157  echo '<p>Disabled</p>'
158  . '</body></html>';
159  exit( 1 );
160 }
161 
163 
164 if ( !$dbr->tableExists( 'profiling' ) ) {
165  echo '<p>No <code>profiling</code> table exists, so we can\'t show you anything.</p>'
166  . '<p>If you want to log profiling data, enable <code>$wgProfiler[\'output\'] = \'db\'</code>'
167  . ' in LocalSettings.php and run <code>maintenance/update.php</code> to'
168  . ' create the profiling table.'
169  . '</body></html>';
170  exit( 1 );
171 }
172 
173 $expand = [];
174 if ( isset( $_REQUEST['expand'] ) ) {
175  foreach ( explode( ',', $_REQUEST['expand'] ) as $f ) {
176  $expand[$f] = true;
177  }
178 }
179 
180 // phpcs:ignore Squiz.Classes.ValidClassName.NotCamelCaps
182 
183  public $name;
184  public $count;
185  public $time;
186  public $children;
187 
189 
190  public function __construct( $name, $count, $time, $memory ) {
191  $this->name = $name;
192  $this->count = $count;
193  $this->time = $time;
194  $this->memory = $memory;
195  $this->children = [];
196  }
197 
198  public function add_child( $child ) {
199  $this->children[] = $child;
200  }
201 
202  public function display( $expand, $indent = 0.0 ) {
203  usort( $this->children, 'compare_point' );
204 
205  $ex = isset( $expand[$this->name()] );
206 
207  $anchor = str_replace( '"', '', $this->name() );
208 
209  if ( !$ex ) {
210  if ( count( $this->children ) ) {
211  $url = getEscapedProfileUrl( false, false, $expand + [ $this->name() => true ] );
212  $extet = " <a id=\"{$anchor}\" href=\"{$url}#{$anchor}\">[+]</a>";
213  } else {
214  $extet = '';
215  }
216  } else {
217  $e = [];
218  foreach ( $expand as $name => $ep ) {
219  if ( $name != $this->name() ) {
220  $e += [ $name => $ep ];
221  }
222  }
223  $url = getEscapedProfileUrl( false, false, $e );
224  $extet = " <a id=\"{$anchor}\" href=\"{$url}#{$anchor}\">[–]</a>";
225  }
226  ?>
227  <tr>
228  <th>
229  <div style="margin-left: <?php echo (int)$indent; ?>em;">
230  <?php echo htmlspecialchars( str_replace( ',', ', ', $this->name() ) ) . $extet ?>
231  </div>
232  </th>
233  <?php // phpcs:disable Generic.Files.LineLength,Generic.PHP.NoSilencedErrors ?>
234  <td class="mw-profileinfo-timep"><?php echo @wfPercent( $this->time() / self::$totaltime * 100 ); ?></td>
235  <td class="mw-profileinfo-memoryp"><?php echo @wfPercent( $this->memory() / self::$totalmemory * 100 ); ?></td>
236  <td class="mw-profileinfo-count"><?php echo $this->count(); ?></td>
237  <td class="mw-profileinfo-cpr"><?php echo round( sprintf( '%.2f', $this->callsPerRequest() ), 2 ); ?></td>
238  <td class="mw-profileinfo-tpc"><?php echo round( sprintf( '%.2f', $this->timePerCall() ), 2 ); ?></td>
239  <td class="mw-profileinfo-mpc"><?php echo round( sprintf( '%.2f', $this->memoryPerCall() / 1024 ), 2 ); ?></td>
240  <td class="mw-profileinfo-tpr"><?php echo @round( sprintf( '%.2f', $this->time() / self::$totalcount ), 2 ); ?></td>
241  <td class="mw-profileinfo-mpr"><?php echo @round( sprintf( '%.2f', $this->memory() / self::$totalcount / 1024 ), 2 ); ?></td>
242  <?php // phpcs:enable ?>
243  </tr>
244  <?php
245  if ( $ex ) {
246  foreach ( $this->children as $child ) {
247  $child->display( $expand, $indent + 2 );
248  }
249  }
250  }
251 
252  public function name() {
253  return $this->name;
254  }
255 
256  public function count() {
257  return $this->count;
258  }
259 
260  public function time() {
261  return $this->time;
262  }
263 
264  public function memory() {
265  return $this->memory;
266  }
267 
268  public function timePerCall() {
269  // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
270  return @( $this->time / $this->count );
271  }
272 
273  public function memoryPerCall() {
274  // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
275  return @( $this->memory / $this->count );
276  }
277 
278  public function callsPerRequest() {
279  // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
280  return @( $this->count / self::$totalcount );
281  }
282 
283  public function timePerRequest() {
284  // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
285  return @( $this->time / self::$totalcount );
286  }
287 
288  public function memoryPerRequest() {
289  // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
290  return @( $this->memory / self::$totalcount );
291  }
292 
293  public function fmttime() {
294  return sprintf( '%5.02f', $this->time );
295  }
296 }
297 
299  // phpcs:ignore MediaWiki.NamingConventions.ValidGlobalName.wgPrefix
300  global $sort;
301 
302  switch ( $sort ) {
303  // Sorted ascending:
304  case 'name':
305  return strcmp( $a->name(), $b->name() );
306  // Sorted descending:
307  case 'time':
308  return $b->time() <=> $a->time();
309  case 'memory':
310  return $b->memory() <=> $a->memory();
311  case 'count':
312  return $b->count() <=> $a->count();
313  case 'time_per_call':
314  return $b->timePerCall() <=> $a->timePerCall();
315  case 'memory_per_call':
316  return $b->memoryPerCall() <=> $a->memoryPerCall();
317  case 'calls_per_req':
318  return $b->callsPerRequest() <=> $a->callsPerRequest();
319  case 'time_per_req':
320  return $b->timePerRequest() <=> $a->timePerRequest();
321  case 'memory_per_req':
322  return $b->memoryPerRequest() <=> $a->memoryPerRequest();
323  }
324 }
325 
326 $sorts = [ 'time', 'memory', 'count', 'calls_per_req', 'name',
327  'time_per_call', 'memory_per_call', 'time_per_req', 'memory_per_req' ];
328 $sort = 'time';
329 if ( isset( $_REQUEST['sort'] ) && in_array( $_REQUEST['sort'], $sorts ) ) {
330  $sort = $_REQUEST['sort'];
331 }
332 
333 $res = $dbr->select(
334  'profiling',
335  '*',
336  [],
337  'profileinfo.php',
338  [ 'ORDER BY' => 'pf_name ASC' ]
339 );
340 
341 $filter = $_REQUEST['filter'] ?? '';
342 
343 ?>
344 <form method="get" action="profileinfo.php">
345  <p>
346  <input type="text" name="filter" value="<?php echo htmlspecialchars( $filter ); ?>">
347  <input type="hidden" name="sort" value="<?php echo htmlspecialchars( $sort ); ?>">
348  <input type="hidden" name="expand" value="<?php
349  echo htmlspecialchars( implode( ",", array_keys( $expand ) ) );
350  ?>">
351  <input type="submit" value="Filter">
352  </p>
353 </form>
354 
355 <table class="mw-profileinfo-table table table-striped table-hover">
356  <thead>
357  <tr>
358  <th><a href="<?php
359  echo getEscapedProfileUrl( false, 'name' );
360  ?>">Name</a></th>
361  <th><a href="<?php
362  echo getEscapedProfileUrl( false, 'time' );
363  ?>">Time (%)</a></th>
364  <th><a href="<?php
365  echo getEscapedProfileUrl( false, 'memory' );
366  ?>">Memory (%)</a></th>
367  <th><a href="<?php
368  echo getEscapedProfileUrl( false, 'count' );
369  ?>">Count</a></th>
370  <th><a href="<?php
371  echo getEscapedProfileUrl( false, 'calls_per_req' );
372  ?>">Calls/req</a></th>
373  <th><a href="<?php
374  echo getEscapedProfileUrl( false, 'time_per_call' );
375  ?>">ms/call</a></th>
376  <th><a href="<?php
377  echo getEscapedProfileUrl( false, 'memory_per_call' );
378  ?>">kb/call</a></th>
379  <th><a href="<?php
380  echo getEscapedProfileUrl( false, 'time_per_req' );
381  ?>">ms/req</a></th>
382  <th><a href="<?php
383  echo getEscapedProfileUrl( false, 'memory_per_req' );
384  ?>">kb/req</a></th>
385  </tr>
386  </thead>
387  <tbody>
388  <?php
392 
393  function getEscapedProfileUrl( $_filter = false, $_sort = false, $_expand = false ) {
394  // phpcs:ignore MediaWiki.NamingConventions.ValidGlobalName.wgPrefix
395  global $filter, $sort, $expand;
396 
397  if ( $_expand === false ) {
398  $_expand = $expand;
399  }
400 
401  return htmlspecialchars(
402  '?' .
403  wfArrayToCgi( [
404  'filter' => $_filter ?: $filter,
405  'sort' => $_sort ?: $sort,
406  'expand' => implode( ',', array_keys( $_expand ) )
407  ] )
408  );
409  }
410 
411  $points = [];
412  $queries = [];
413  $sqltotal = 0.0;
414 
416  $last = false;
417  foreach ( $res as $o ) {
418  $next = new profile_point( $o->pf_name, $o->pf_count, $o->pf_time, $o->pf_memory );
419  if ( $next->name() == '-total' || $next->name() == 'main()' ) {
420  profile_point::$totaltime = $next->time();
421  profile_point::$totalcount = $next->count();
422  profile_point::$totalmemory = $next->memory();
423  }
424  if ( $last !== false ) {
425  if ( preg_match( '/^' . preg_quote( $last->name(), '/' ) . '/', $next->name() ) ) {
426  $last->add_child( $next );
427  continue;
428  }
429  }
430  $last = $next;
431  if ( preg_match( '/^query: /', $next->name() ) || preg_match( '/^query-m: /', $next->name() ) ) {
432  $sqltotal += $next->time();
433  $queries[] = $next;
434  } else {
435  $points[] = $next;
436  }
437  }
438 
439  $s = new profile_point( 'SQL Queries', 0, $sqltotal, 0 );
440  foreach ( $queries as $q ) {
441  $s->add_child( $q );
442  }
443  $points[] = $s;
444 
445  // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
446  @usort( $points, 'compare_point' );
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>
$filter
$filter
Definition: profileinfo.php:341
wfPercent
wfPercent( $nr, $acc=2, $round=true)
Definition: GlobalFunctions.php:2076
profile_point\callsPerRequest
callsPerRequest()
Definition: profileinfo.php:278
profile_point\$name
$name
Definition: profileinfo.php:183
compare_point
compare_point(profile_point $a, profile_point $b)
Definition: profileinfo.php:298
$sqltotal
$sqltotal
Definition: profileinfo.php:413
profile_point\__construct
__construct( $name, $count, $time, $memory)
Definition: profileinfo.php:190
profile_point\timePerRequest
timePerRequest()
Definition: profileinfo.php:283
captcha-old.font
font
Definition: captcha-old.py:244
profile_point\$totalmemory
static $totalmemory
Definition: profileinfo.php:188
profile_point\$time
$time
Definition: profileinfo.php:185
a
</source > ! result< div class="mw-highlight mw-content-ltr" dir="ltr">< pre >< span ></span >< span class="kd"> var</span >< span class="nx"> a</span >< span class="p"></span ></pre ></div > ! end ! test Multiline< source/> in lists !input *< source > a b</source > *foo< source > a b</source > ! html< ul >< li >< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul >< ul >< li > foo< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul > ! html tidy< ul >< li >< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul >< ul >< li > foo< div class="mw-highlight mw-content-ltr" dir="ltr">< pre > a b</pre ></div ></li ></ul > ! end ! test Custom attributes !input< source lang="javascript" id="foo" class="bar" dir="rtl" style="font-size: larger;"> var a
Definition: parserTests.txt:85
value
if( $inline) $status value
Definition: SyntaxHighlight.php:345
$dbr
if(! $wgEnableProfileInfo) $dbr
Definition: profileinfo.php:162
$last
$last
Definition: profileinfo.php:416
profile_point\$count
$count
Definition: profileinfo.php:184
data
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
$s
foreach( $res as $o) $s
Definition: profileinfo.php:439
profile_point\$children
$children
Definition: profileinfo.php:186
memory
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
php
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
profile_point\name
< td class="mw-profileinfo-timep"></tr > if( $ex) name()
Definition: profileinfo.php:252
profile_point\$totaltime
static $totaltime
Definition: profileinfo.php:188
none
</source > ! result< p > Text< code class="mw-highlight" dir="ltr">< span class="kd"> var</span >< span class="nx"> a</span >< span class="p"></span ></code ></p > ! end ! test Enclose none(inline code) !!input Text< source lang
name
and how to run hooks for an and one after Each event has a name
Definition: hooks.txt:6
profile_point\memoryPerCall
memoryPerCall()
Definition: profileinfo.php:273
$expand
if(! $dbr->tableExists( 'profiling')) $expand
Definition: profileinfo.php:173
$wgEnableProfileInfo
$wgEnableProfileInfo
Allow the profileinfo.php entrypoint to be used.
Definition: DefaultSettings.php:6401
profile_point\display
display( $expand, $indent=0.0)
Definition: profileinfo.php:202
table
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global then executing the whole list after the page is displayed We don t do anything smart like collating updates to the same table or such because the list is almost always going to have just one item on if so it s not worth the trouble Since there is a job queue in the jobs table
Definition: deferred.txt:11
$sorts
$sorts
Definition: profileinfo.php:326
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2636
$points
$points
Definition: profileinfo.php:411
getEscapedProfileUrl
getEscapedProfileUrl( $_filter=false, $_sort=false, $_expand=false)
Definition: profileinfo.php:393
profile_point\memory
memory()
Definition: profileinfo.php:264
div
div
Definition: parserTests.txt:6850
form
null means default in associative array form
Definition: hooks.txt:1985
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
profile_point\$totalcount
static $totalcount
Definition: profileinfo.php:188
$sort
$sort
Definition: profileinfo.php:328
captcha-old.action
action
Definition: captcha-old.py:212
code
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 administrators to define code that will be run at certain points in the mainline code
Definition: hooks.txt:23
$res
if(isset( $_REQUEST['sort']) &&in_array( $_REQUEST['sort'], $sorts)) $res
Definition: profileinfo.php:333
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:2162
profile_point
Definition: profileinfo.php:181
profile_point\timePerCall
timePerCall()
Definition: profileinfo.php:268
title
title
Definition: parserTests.txt:245
f9f9f9
f9f9f9
Definition: parserTests.txt:183
profile_point\memoryPerRequest
memoryPerRequest()
Definition: profileinfo.php:288
captcha-old.p
p
Definition: captcha-old.py:275
style
Bar style
Definition: parserTests.txt:218
profile_point\fmttime
fmttime()
Definition: profileinfo.php:293
text
This list may contain false positives That usually means there is additional text with links below the first Each row contains links to the first and second as well as the first line of the second redirect text
Definition: All_system_messages.txt:1267
profile_point\count
count()
Definition: profileinfo.php:256
color
in the sidebar</td >< td > font color
Definition: All_system_messages.txt:425
profile_point\time
time()
Definition: profileinfo.php:260
as
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
width
width
Definition: parserTests.txt:189
$f
$f
Definition: router.php:79
profile_point\add_child
add_child( $child)
Definition: profileinfo.php:198
type
This document describes the state of Postgres support in and is fairly well maintained The main code is very well while extensions are very hit and miss it is probably the most supported database after MySQL Much of the work in making MediaWiki database agnostic came about through the work of creating Postgres as and are nearing end of but without copying over all the usage comments General notes on the but these can almost always be programmed around *Although Postgres has a true BOOLEAN type
Definition: postgres.txt:22
href
shown</td >< td > a href
Definition: All_system_messages.txt:2667
$queries
$queries
Definition: profileinfo.php:412
wfArrayToCgi
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
Definition: GlobalFunctions.php:371