2013-08-07 08:38:38 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
2020-01-29 01:43:23 +01:00
|
|
|
* A simple manually-instrumented profiler for WordPress.
|
|
|
|
*
|
|
|
|
* This records basic execution time, and a summary of the actions and SQL queries run within each block.
|
|
|
|
*
|
|
|
|
* start() and stop() must be called in pairs, for example:
|
|
|
|
*
|
|
|
|
* function something_to_profile() {
|
|
|
|
* wppf_start(__FUNCTION__);
|
|
|
|
* do_stuff();
|
|
|
|
* wppf_stop();
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* Multiple profile blocks are permitted, and they may be nested.
|
|
|
|
*/
|
2013-08-07 08:38:38 +02:00
|
|
|
|
|
|
|
class WPProfiler {
|
2019-03-15 22:58:01 +01:00
|
|
|
public $stack;
|
|
|
|
public $profile;
|
2013-08-07 08:38:38 +02:00
|
|
|
|
2015-06-28 17:26:41 +02:00
|
|
|
/**
|
|
|
|
* PHP5 constructor.
|
|
|
|
*/
|
2019-03-15 22:58:01 +01:00
|
|
|
public function __construct() {
|
2017-12-01 00:09:33 +01:00
|
|
|
$this->stack = array();
|
2013-08-07 08:38:38 +02:00
|
|
|
$this->profile = array();
|
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function start( $name ) {
|
2013-08-07 08:38:38 +02:00
|
|
|
$time = $this->microtime();
|
|
|
|
|
2017-12-01 00:09:33 +01:00
|
|
|
if ( ! $this->stack ) {
|
2020-01-29 01:43:23 +01:00
|
|
|
// Log all actions and filters.
|
2017-12-01 00:09:33 +01:00
|
|
|
add_filter( 'all', array( $this, 'log_filter' ) );
|
2013-08-07 08:38:38 +02:00
|
|
|
}
|
|
|
|
|
2020-01-29 01:43:23 +01:00
|
|
|
// Reset the wpdb queries log, storing it on the profile stack if necessary.
|
2013-08-07 08:38:38 +02:00
|
|
|
global $wpdb;
|
2017-12-01 00:09:33 +01:00
|
|
|
if ( $this->stack ) {
|
|
|
|
$this->stack[ count( $this->stack ) - 1 ]['queries'] = $wpdb->queries;
|
2013-08-07 08:38:38 +02:00
|
|
|
}
|
|
|
|
$wpdb->queries = array();
|
|
|
|
|
|
|
|
global $wp_object_cache;
|
|
|
|
|
|
|
|
$this->stack[] = array(
|
2017-12-01 00:09:33 +01:00
|
|
|
'start' => $time,
|
|
|
|
'name' => $name,
|
|
|
|
'cache_cold_hits' => $wp_object_cache->cold_cache_hits,
|
|
|
|
'cache_warm_hits' => $wp_object_cache->warm_cache_hits,
|
|
|
|
'cache_misses' => $wp_object_cache->cache_misses,
|
|
|
|
'cache_dirty_objects' => $this->_dirty_objects_count( $wp_object_cache->dirty_objects ),
|
|
|
|
'actions' => array(),
|
|
|
|
'filters' => array(),
|
|
|
|
'queries' => array(),
|
2013-08-07 08:38:38 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function stop() {
|
2017-12-01 00:09:33 +01:00
|
|
|
$item = array_pop( $this->stack );
|
|
|
|
$time = $this->microtime( $item['start'] );
|
2013-08-07 08:38:38 +02:00
|
|
|
$name = $item['name'];
|
|
|
|
|
|
|
|
global $wpdb;
|
|
|
|
$item['queries'] = $wpdb->queries;
|
|
|
|
global $wp_object_cache;
|
|
|
|
|
2017-12-01 00:09:33 +01:00
|
|
|
$cache_dirty_count = $this->_dirty_objects_count( $wp_object_cache->dirty_objects );
|
|
|
|
$cache_dirty_delta = $this->array_sub( $cache_dirty_count, $item['cache_dirty_objects'] );
|
|
|
|
|
|
|
|
if ( isset( $this->profile[ $name ] ) ) {
|
|
|
|
$this->profile[ $name ]['time'] += $time;
|
|
|
|
$this->profile[ $name ]['calls'] ++;
|
|
|
|
$this->profile[ $name ]['cache_cold_hits'] += ( $wp_object_cache->cold_cache_hits - $item['cache_cold_hits'] );
|
|
|
|
$this->profile[ $name ]['cache_warm_hits'] += ( $wp_object_cache->warm_cache_hits - $item['cache_warm_hits'] );
|
|
|
|
$this->profile[ $name ]['cache_misses'] += ( $wp_object_cache->cache_misses - $item['cache_misses'] );
|
|
|
|
$this->profile[ $name ]['cache_dirty_objects'] = array_add( $this->profile[ $name ]['cache_dirty_objects'], $cache_dirty_delta );
|
|
|
|
$this->profile[ $name ]['actions'] = array_add( $this->profile[ $name ]['actions'], $item['actions'] );
|
|
|
|
$this->profile[ $name ]['filters'] = array_add( $this->profile[ $name ]['filters'], $item['filters'] );
|
|
|
|
$this->profile[ $name ]['queries'] = array_add( $this->profile[ $name ]['queries'], $item['queries'] );
|
2013-08-07 08:38:38 +02:00
|
|
|
#$this->_query_summary($item['queries'], $this->profile[$name]['queries']);
|
|
|
|
|
2017-12-01 00:09:33 +01:00
|
|
|
} else {
|
2013-08-07 08:38:38 +02:00
|
|
|
$queries = array();
|
2017-12-01 00:09:33 +01:00
|
|
|
$this->_query_summary( $item['queries'], $queries );
|
|
|
|
$this->profile[ $name ] = array(
|
|
|
|
'time' => $time,
|
|
|
|
'calls' => 1,
|
|
|
|
'cache_cold_hits' => ( $wp_object_cache->cold_cache_hits - $item['cache_cold_hits'] ),
|
|
|
|
'cache_warm_hits' => ( $wp_object_cache->warm_cache_hits - $item['cache_warm_hits'] ),
|
|
|
|
'cache_misses' => ( $wp_object_cache->cache_misses - $item['cache_misses'] ),
|
|
|
|
'cache_dirty_objects' => $cache_dirty_delta,
|
|
|
|
'actions' => $item['actions'],
|
|
|
|
'filters' => $item['filters'],
|
|
|
|
# 'queries' => $item['queries'],
|
|
|
|
'queries' => $queries,
|
2013-08-07 08:38:38 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-12-01 00:09:33 +01:00
|
|
|
if ( ! $this->stack ) {
|
|
|
|
remove_filter( 'all', array( $this, 'log_filter' ) );
|
2013-08-07 08:38:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function microtime( $since = 0.0 ) {
|
2017-12-01 00:09:33 +01:00
|
|
|
list($usec, $sec) = explode( ' ', microtime() );
|
|
|
|
return (float) $sec + (float) $usec - $since;
|
2013-08-07 08:38:38 +02:00
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function log_filter( $tag ) {
|
2017-12-01 00:09:33 +01:00
|
|
|
if ( $this->stack ) {
|
2013-08-07 08:38:38 +02:00
|
|
|
global $wp_actions;
|
2019-07-08 02:55:20 +02:00
|
|
|
if ( end( $wp_actions ) === $tag ) {
|
|
|
|
$this->stack[ count( $this->stack ) - 1 ]['actions'][ $tag ]++;
|
2017-12-01 00:09:33 +01:00
|
|
|
} else {
|
2019-07-08 02:55:20 +02:00
|
|
|
$this->stack[ count( $this->stack ) - 1 ]['filters'][ $tag ]++;
|
2017-12-01 00:09:33 +01:00
|
|
|
}
|
2013-08-07 08:38:38 +02:00
|
|
|
}
|
|
|
|
return $arg;
|
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function log_action( $tag ) {
|
2017-12-01 00:09:33 +01:00
|
|
|
if ( $this->stack ) {
|
2019-07-08 02:55:20 +02:00
|
|
|
$this->stack[ count( $this->stack ) - 1 ]['actions'][ $tag ]++;
|
2017-12-01 00:09:33 +01:00
|
|
|
}
|
2013-08-07 08:38:38 +02:00
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function _current_action() {
|
2013-08-07 08:38:38 +02:00
|
|
|
global $wp_actions;
|
2017-12-01 00:09:33 +01:00
|
|
|
return $wp_actions[ count( $wp_actions ) - 1 ];
|
2013-08-07 08:38:38 +02:00
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function results() {
|
2013-08-07 08:38:38 +02:00
|
|
|
return $this->profile;
|
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function _query_summary( $queries, &$out ) {
|
2017-12-01 00:09:33 +01:00
|
|
|
foreach ( $queries as $q ) {
|
2013-08-07 08:38:38 +02:00
|
|
|
$sql = $q[0];
|
2017-12-01 00:09:33 +01:00
|
|
|
$sql = preg_replace( '/(WHERE \w+ =) \d+/', '$1 x', $sql );
|
|
|
|
$sql = preg_replace( '/(WHERE \w+ =) \'\[-\w]+\'/', '$1 \'xxx\'', $sql );
|
2013-08-07 08:38:38 +02:00
|
|
|
|
2019-07-08 02:55:20 +02:00
|
|
|
$out[ $sql ] ++;
|
2013-08-07 08:38:38 +02:00
|
|
|
}
|
2017-12-01 00:09:33 +01:00
|
|
|
asort( $out );
|
2013-08-07 08:38:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function _query_count( $queries ) {
|
2020-01-29 01:43:23 +01:00
|
|
|
// This requires the SAVEQUERIES patch at https://core.trac.wordpress.org/ticket/5218
|
2013-08-07 08:38:38 +02:00
|
|
|
$out = array();
|
2017-12-01 00:09:33 +01:00
|
|
|
foreach ( $queries as $q ) {
|
|
|
|
if ( empty( $q[2] ) ) {
|
2019-07-08 02:55:20 +02:00
|
|
|
$out['unknown']++;
|
2017-12-01 00:09:33 +01:00
|
|
|
} else {
|
2019-07-08 02:55:20 +02:00
|
|
|
$out[ $q[2] ]++;
|
2017-12-01 00:09:33 +01:00
|
|
|
}
|
2013-08-07 08:38:38 +02:00
|
|
|
}
|
|
|
|
return $out;
|
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function _dirty_objects_count( $dirty_objects ) {
|
2013-08-07 08:38:38 +02:00
|
|
|
$out = array();
|
2017-12-01 00:09:33 +01:00
|
|
|
foreach ( array_keys( $dirty_objects ) as $group ) {
|
|
|
|
$out[ $group ] = count( $dirty_objects[ $group ] );
|
|
|
|
}
|
2013-08-07 08:38:38 +02:00
|
|
|
return $out;
|
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function array_add( $a, $b ) {
|
2013-08-07 08:38:38 +02:00
|
|
|
$out = $a;
|
2017-12-01 00:09:33 +01:00
|
|
|
foreach ( array_keys( $b ) as $key ) {
|
|
|
|
if ( array_key_exists( $key, $out ) ) {
|
|
|
|
$out[ $key ] += $b[ $key ];
|
|
|
|
} else {
|
|
|
|
$out[ $key ] = $b[ $key ];
|
|
|
|
}
|
|
|
|
}
|
2013-08-07 08:38:38 +02:00
|
|
|
return $out;
|
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function array_sub( $a, $b ) {
|
2013-08-07 08:38:38 +02:00
|
|
|
$out = $a;
|
2017-12-01 00:09:33 +01:00
|
|
|
foreach ( array_keys( $b ) as $key ) {
|
|
|
|
if ( array_key_exists( $key, $b ) ) {
|
|
|
|
$out[ $key ] -= $b[ $key ];
|
|
|
|
}
|
|
|
|
}
|
2013-08-07 08:38:38 +02:00
|
|
|
return $out;
|
|
|
|
}
|
|
|
|
|
2019-03-15 22:58:01 +01:00
|
|
|
public function print_summary() {
|
2013-08-07 08:38:38 +02:00
|
|
|
$results = $this->results();
|
|
|
|
|
2017-12-01 00:09:33 +01:00
|
|
|
printf( "\nname calls time action filter warm cold misses dirty\n" );
|
|
|
|
foreach ( $results as $name => $stats ) {
|
|
|
|
printf( "%24.24s %6d %6.4f %6d %6d %6d %6d %6d %6d\n", $name, $stats['calls'], $stats['time'], array_sum( $stats['actions'] ), array_sum( $stats['filters'] ), $stats['cache_warm_hits'], $stats['cache_cold_hits'], $stats['cache_misses'], array_sum( $stats['cache_dirty_objects'] ) );
|
2013-08-07 08:38:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
global $wppf;
|
|
|
|
$wppf = new WPProfiler();
|
|
|
|
|
2017-12-01 00:09:33 +01:00
|
|
|
function wppf_start( $name ) {
|
|
|
|
$GLOBALS['wppf']->start( $name );
|
2013-08-07 08:38:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function wppf_stop() {
|
|
|
|
$GLOBALS['wppf']->stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
function wppf_results() {
|
|
|
|
return $GLOBALS['wppf']->results();
|
|
|
|
}
|
|
|
|
|
|
|
|
function wppf_print_summary() {
|
|
|
|
$GLOBALS['wppf']->print_summary();
|
|
|
|
}
|
|
|
|
|
2017-12-01 00:09:33 +01:00
|
|
|
|