stack = array(); $this->profile = array(); } public function start( $name ) { $time = $this->microtime(); if ( ! $this->stack ) { // log all actions and filters add_filter( 'all', array( $this, 'log_filter' ) ); } // reset the wpdb queries log, storing it on the profile stack if necessary global $wpdb; if ( $this->stack ) { $this->stack[ count( $this->stack ) - 1 ]['queries'] = $wpdb->queries; } $wpdb->queries = array(); global $wp_object_cache; $this->stack[] = array( '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(), ); } public function stop() { $item = array_pop( $this->stack ); $time = $this->microtime( $item['start'] ); $name = $item['name']; global $wpdb; $item['queries'] = $wpdb->queries; global $wp_object_cache; $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'] ); #$this->_query_summary($item['queries'], $this->profile[$name]['queries']); } else { $queries = array(); $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, ); } if ( ! $this->stack ) { remove_filter( 'all', array( $this, 'log_filter' ) ); } } public function microtime( $since = 0.0 ) { list($usec, $sec) = explode( ' ', microtime() ); return (float) $sec + (float) $usec - $since; } public function log_filter( $tag ) { if ( $this->stack ) { global $wp_actions; if ( $tag == end( $wp_actions ) ) { @$this->stack[ count( $this->stack ) - 1 ]['actions'][ $tag ] ++; } else { @$this->stack[ count( $this->stack ) - 1 ]['filters'][ $tag ] ++; } } return $arg; } public function log_action( $tag ) { if ( $this->stack ) { @$this->stack[ count( $this->stack ) - 1 ]['actions'][ $tag ] ++; } } public function _current_action() { global $wp_actions; return $wp_actions[ count( $wp_actions ) - 1 ]; } public function results() { return $this->profile; } public function _query_summary( $queries, &$out ) { foreach ( $queries as $q ) { $sql = $q[0]; $sql = preg_replace( '/(WHERE \w+ =) \d+/', '$1 x', $sql ); $sql = preg_replace( '/(WHERE \w+ =) \'\[-\w]+\'/', '$1 \'xxx\'', $sql ); @$out[ $sql ] ++; } asort( $out ); return; } public function _query_count( $queries ) { // this requires the savequeries patch at https://core.trac.wordpress.org/ticket/5218 $out = array(); foreach ( $queries as $q ) { if ( empty( $q[2] ) ) { @$out['unknown'] ++; } else { @$out[ $q[2] ] ++; } } return $out; } public function _dirty_objects_count( $dirty_objects ) { $out = array(); foreach ( array_keys( $dirty_objects ) as $group ) { $out[ $group ] = count( $dirty_objects[ $group ] ); } return $out; } public function array_add( $a, $b ) { $out = $a; foreach ( array_keys( $b ) as $key ) { if ( array_key_exists( $key, $out ) ) { $out[ $key ] += $b[ $key ]; } else { $out[ $key ] = $b[ $key ]; } } return $out; } public function array_sub( $a, $b ) { $out = $a; foreach ( array_keys( $b ) as $key ) { if ( array_key_exists( $key, $b ) ) { $out[ $key ] -= $b[ $key ]; } } return $out; } public function print_summary() { $results = $this->results(); 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'] ) ); } } } global $wppf; $wppf = new WPProfiler(); function wppf_start( $name ) { $GLOBALS['wppf']->start( $name ); } function wppf_stop() { $GLOBALS['wppf']->stop(); } function wppf_results() { return $GLOBALS['wppf']->results(); } function wppf_print_summary() { $GLOBALS['wppf']->print_summary(); }