MediaWiki  1.27.2
Xhprof.php
Go to the documentation of this file.
1 <?php
22 
32 class Xhprof {
33 
37  protected $config;
38 
43  protected $hieraData;
44 
49  protected $inclusive;
50 
55  protected $complete;
56 
73  public function __construct( array $config = [] ) {
74  $this->config = array_merge(
75  [
76  'flags' => 0,
77  'exclude' => [],
78  'include' => null,
79  'sort' => 'wt',
80  ],
81  $config
82  );
83 
84  xhprof_enable( $this->config['flags'], [
85  'ignored_functions' => $this->config['exclude']
86  ] );
87  }
88 
98  public function stop() {
99  if ( $this->hieraData === null ) {
100  $this->hieraData = $this->pruneData( xhprof_disable() );
101  }
102  return $this->hieraData;
103  }
104 
116  public function loadRawData( array $data ) {
117  $this->stop();
118  $this->inclusive = null;
119  $this->complete = null;
120  $this->hieraData = $this->pruneData( $data );
121  }
122 
153  public function getRawData() {
154  return $this->stop();
155  }
156 
166  public static function splitKey( $key ) {
167  return array_pad( explode( '==>', $key, 2 ), -2, null );
168  }
169 
177  protected function pruneData( $data ) {
178  if ( !$this->config['include'] ) {
179  return $data;
180  }
181 
182  $want = array_fill_keys( $this->config['include'], true );
183  $want['main()'] = true;
184 
185  $keep = [];
186  foreach ( $data as $key => $stats ) {
187  list( $parent, $child ) = self::splitKey( $key );
188  if ( isset( $want[$parent] ) || isset( $want[$child] ) ) {
189  $keep[$key] = $stats;
190  }
191  }
192  return $keep;
193  }
194 
216  public function getInclusiveMetrics() {
217  if ( $this->inclusive === null ) {
218  // Make sure we have data to work with
219  $this->stop();
220 
221  $main = $this->hieraData['main()'];
222  $hasCpu = isset( $main['cpu'] );
223  $hasMu = isset( $main['mu'] );
224  $hasAlloc = isset( $main['alloc'] );
225 
226  $this->inclusive = [];
227  foreach ( $this->hieraData as $key => $stats ) {
228  list( $parent, $child ) = self::splitKey( $key );
229  if ( !isset( $this->inclusive[$child] ) ) {
230  $this->inclusive[$child] = [
231  'ct' => 0,
232  'wt' => new RunningStat(),
233  ];
234  if ( $hasCpu ) {
235  $this->inclusive[$child]['cpu'] = new RunningStat();
236  }
237  if ( $hasMu ) {
238  $this->inclusive[$child]['mu'] = new RunningStat();
239  $this->inclusive[$child]['pmu'] = new RunningStat();
240  }
241  if ( $hasAlloc ) {
242  $this->inclusive[$child]['alloc'] = new RunningStat();
243  $this->inclusive[$child]['free'] = new RunningStat();
244  }
245  }
246 
247  $this->inclusive[$child]['ct'] += $stats['ct'];
248  foreach ( $stats as $stat => $value ) {
249  if ( $stat === 'ct' ) {
250  continue;
251  }
252 
253  if ( !isset( $this->inclusive[$child][$stat] ) ) {
254  // Ignore unknown stats
255  continue;
256  }
257 
258  for ( $i = 0; $i < $stats['ct']; $i++ ) {
259  $this->inclusive[$child][$stat]->addObservation(
260  $value / $stats['ct']
261  );
262  }
263  }
264  }
265 
266  // Convert RunningStat instances to static arrays and add
267  // percentage stats.
268  foreach ( $this->inclusive as $func => $stats ) {
269  foreach ( $stats as $name => $value ) {
270  if ( $value instanceof RunningStat ) {
271  $total = $value->m1 * $value->n;
272  $percent = ( isset( $main[$name] ) && $main[$name] )
273  ? 100 * $total / $main[$name]
274  : 0;
275  $this->inclusive[$func][$name] = [
276  'total' => $total,
277  'min' => $value->min,
278  'mean' => $value->m1,
279  'max' => $value->max,
280  'variance' => $value->m2,
281  'percent' => $percent,
282  ];
283  }
284  }
285  }
286 
287  uasort( $this->inclusive, self::makeSortFunction(
288  $this->config['sort'], 'total'
289  ) );
290  }
291  return $this->inclusive;
292  }
293 
308  public function getCompleteMetrics() {
309  if ( $this->complete === null ) {
310  // Start with inclusive data
311  $this->complete = $this->getInclusiveMetrics();
312 
313  foreach ( $this->complete as $func => $stats ) {
314  foreach ( $stats as $stat => $value ) {
315  if ( $stat === 'ct' ) {
316  continue;
317  }
318  // Initialize exclusive data with inclusive totals
319  $this->complete[$func][$stat]['exclusive'] = $value['total'];
320  }
321  // Add sapce for call tree information to be filled in later
322  $this->complete[$func]['calls'] = [];
323  $this->complete[$func]['subcalls'] = [];
324  }
325 
326  foreach ( $this->hieraData as $key => $stats ) {
327  list( $parent, $child ) = self::splitKey( $key );
328  if ( $parent !== null ) {
329  // Track call tree information
330  $this->complete[$child]['calls'][$parent] = $stats;
331  $this->complete[$parent]['subcalls'][$child] = $stats;
332  }
333 
334  if ( isset( $this->complete[$parent] ) ) {
335  // Deduct child inclusive data from exclusive data
336  foreach ( $stats as $stat => $value ) {
337  if ( $stat === 'ct' ) {
338  continue;
339  }
340 
341  if ( !isset( $this->complete[$parent][$stat] ) ) {
342  // Ignore unknown stats
343  continue;
344  }
345 
346  $this->complete[$parent][$stat]['exclusive'] -= $value;
347  }
348  }
349  }
350 
351  uasort( $this->complete, self::makeSortFunction(
352  $this->config['sort'], 'exclusive'
353  ) );
354  }
355  return $this->complete;
356  }
357 
365  public function getCallers( $function ) {
366  $edges = $this->getCompleteMetrics();
367  if ( isset( $edges[$function]['calls'] ) ) {
368  return array_keys( $edges[$function]['calls'] );
369  } else {
370  return [];
371  }
372  }
373 
381  public function getCallees( $function ) {
382  $edges = $this->getCompleteMetrics();
383  if ( isset( $edges[$function]['subcalls'] ) ) {
384  return array_keys( $edges[$function]['subcalls'] );
385  } else {
386  return [];
387  }
388  }
389 
396  public function getCriticalPath( $metric = 'wt' ) {
397  $this->stop();
398  $func = 'main()';
399  $path = [
400  $func => $this->hieraData[$func],
401  ];
402  while ( $func ) {
403  $callees = $this->getCallees( $func );
404  $maxCallee = null;
405  $maxCall = null;
406  foreach ( $callees as $callee ) {
407  $call = "{$func}==>{$callee}";
408  if ( $maxCall === null ||
409  $this->hieraData[$call][$metric] >
410  $this->hieraData[$maxCall][$metric]
411  ) {
412  $maxCallee = $callee;
413  $maxCall = $call;
414  }
415  }
416  if ( $maxCall !== null ) {
417  $path[$maxCall] = $this->hieraData[$maxCall];
418  }
419  $func = $maxCallee;
420  }
421  return $path;
422  }
423 
432  public static function makeSortFunction( $key, $sub ) {
433  return function ( $a, $b ) use ( $key, $sub ) {
434  if ( isset( $a[$key] ) && isset( $b[$key] ) ) {
435  // Descending sort: larger values will be first in result.
436  // Assumes all values are numeric.
437  // Values for 'main()' will not have sub keys
438  $valA = is_array( $a[$key] ) ? $a[$key][$sub] : $a[$key];
439  $valB = is_array( $b[$key] ) ? $b[$key][$sub] : $b[$key];
440  return $valB - $valA;
441  } else {
442  // Sort datum with the key before those without
443  return isset( $a[$key] ) ? -1 : 1;
444  }
445  };
446  }
447 }
Convenience class for working with XHProf https://github.com/phacility/xhprof.
Definition: Xhprof.php:32
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 list
Definition: deferred.txt:11
stop()
Stop collecting profiling data.
Definition: Xhprof.php:98
the array() calling protocol came about after MediaWiki 1.4rc1.
magic word the default is to use $key to get the and $key value or $key value text $key value html to format the value $key
Definition: hooks.txt:2321
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
getCompleteMetrics()
Get the inclusive and exclusive metrics for each function call.
Definition: Xhprof.php:308
$value
getRawData()
Get raw data collected by xhprof.
Definition: Xhprof.php:153
static makeSortFunction($key, $sub)
Make a closure to use as a sort function.
Definition: Xhprof.php:432
getCallees($function)
Get a list of all callees from a given function.
Definition: Xhprof.php:381
__construct(array $config=[])
Configuration data can contain:
Definition: Xhprof.php:73
$config
Definition: Xhprof.php:37
static splitKey($key)
Convert an xhprof data key into an array of ['parent', 'child'] function names.
Definition: Xhprof.php:166
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
pruneData($data)
Remove data for functions that are not included in the 'include' configuration array.
Definition: Xhprof.php:177
loadRawData(array $data)
Load raw data from a prior run for analysis.
Definition: Xhprof.php:116
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
$complete
Per-function inclusive and exclusive data.
Definition: Xhprof.php:55
getInclusiveMetrics()
Get the inclusive metrics for each function call.
Definition: Xhprof.php:216
$inclusive
Per-function inclusive data.
Definition: Xhprof.php:49
Returning false makes less sense for events where the action is complete
Definition: hooks.txt:199
getCriticalPath($metric= 'wt')
Find the critical path for the given metric.
Definition: Xhprof.php:396
$hieraData
Hierarchical profiling data returned by xhprof.
Definition: Xhprof.php:43
getCallers($function)
Get a list of all callers of a given function.
Definition: Xhprof.php:365
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:310