wp-profiler.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. <?php
  2. /*
  3. A simple manually-instrumented profiler for WordPress.
  4. This records basic execution time, and a summary of the actions and SQL queries run within each block.
  5. start() and stop() must be called in pairs, for example:
  6. function something_to_profile() {
  7. wppf_start(__FUNCTION__);
  8. do_stuff();
  9. wppf_stop();
  10. }
  11. Multiple profile blocks are permitted, and they may be nested.
  12. */
  13. class WPProfiler {
  14. var $stack;
  15. var $profile;
  16. // constructor
  17. function WPProfiler() {
  18. $this->stack = array();
  19. $this->profile = array();
  20. }
  21. function start($name) {
  22. $time = $this->microtime();
  23. if (!$this->stack) {
  24. // log all actions and filters
  25. add_filter('all', array($this, 'log_filter'));
  26. }
  27. // reset the wpdb queries log, storing it on the profile stack if necessary
  28. global $wpdb;
  29. if ($this->stack) {
  30. $this->stack[count($this->stack)-1]['queries'] = $wpdb->queries;
  31. }
  32. $wpdb->queries = array();
  33. global $wp_object_cache;
  34. $this->stack[] = array(
  35. 'start' => $time,
  36. 'name' => $name,
  37. 'cache_cold_hits' => $wp_object_cache->cold_cache_hits,
  38. 'cache_warm_hits' => $wp_object_cache->warm_cache_hits,
  39. 'cache_misses' => $wp_object_cache->cache_misses,
  40. 'cache_dirty_objects' => $this->_dirty_objects_count($wp_object_cache->dirty_objects),
  41. 'actions' => array(),
  42. 'filters' => array(),
  43. 'queries' => array(),
  44. );
  45. }
  46. function stop() {
  47. $item = array_pop($this->stack);
  48. $time = $this->microtime($item['start']);
  49. $name = $item['name'];
  50. global $wpdb;
  51. $item['queries'] = $wpdb->queries;
  52. global $wp_object_cache;
  53. $cache_dirty_count = $this->_dirty_objects_count($wp_object_cache->dirty_objects);
  54. $cache_dirty_delta = $this->array_sub($cache_dirty_count, $item['cache_dirty_objects']);
  55. if (isset($this->profile[$name])) {
  56. $this->profile[$name]['time'] += $time;
  57. $this->profile[$name]['calls'] ++;
  58. $this->profile[$name]['cache_cold_hits'] += ($wp_object_cache->cold_cache_hits - $item['cache_cold_hits']);
  59. $this->profile[$name]['cache_warm_hits'] += ($wp_object_cache->warm_cache_hits - $item['cache_warm_hits']);
  60. $this->profile[$name]['cache_misses'] += ($wp_object_cache->cache_misses - $item['cache_misses']);
  61. $this->profile[$name]['cache_dirty_objects'] = array_add( $this->profile[$name]['cache_dirty_objects'], $cache_dirty_delta) ;
  62. $this->profile[$name]['actions'] = array_add( $this->profile[$name]['actions'], $item['actions'] );
  63. $this->profile[$name]['filters'] = array_add( $this->profile[$name]['filters'], $item['filters'] );
  64. $this->profile[$name]['queries'] = array_add( $this->profile[$name]['queries'], $item['queries'] );
  65. #$this->_query_summary($item['queries'], $this->profile[$name]['queries']);
  66. }
  67. else {
  68. $queries = array();
  69. $this->_query_summary($item['queries'], $queries);
  70. $this->profile[$name] = array(
  71. 'time' => $time,
  72. 'calls' => 1,
  73. 'cache_cold_hits' => ($wp_object_cache->cold_cache_hits - $item['cache_cold_hits']),
  74. 'cache_warm_hits' => ($wp_object_cache->warm_cache_hits - $item['cache_warm_hits']),
  75. 'cache_misses' => ($wp_object_cache->cache_misses - $item['cache_misses']),
  76. 'cache_dirty_objects' => $cache_dirty_delta,
  77. 'actions' => $item['actions'],
  78. 'filters' => $item['filters'],
  79. # 'queries' => $item['queries'],
  80. 'queries' => $queries,
  81. );
  82. }
  83. if (!$this->stack) {
  84. remove_filter('all', array($this, 'log_filter'));
  85. }
  86. }
  87. function microtime($since = 0.0) {
  88. list($usec, $sec) = explode(' ', microtime());
  89. return (float)$sec + (float)$usec - $since;
  90. }
  91. function log_filter($tag) {
  92. if ($this->stack) {
  93. global $wp_actions;
  94. if ($tag == end($wp_actions))
  95. @$this->stack[count($this->stack)-1]['actions'][$tag] ++;
  96. else
  97. @$this->stack[count($this->stack)-1]['filters'][$tag] ++;
  98. }
  99. return $arg;
  100. }
  101. function log_action($tag) {
  102. if ($this->stack)
  103. @$this->stack[count($this->stack)-1]['actions'][$tag] ++;
  104. }
  105. function _current_action() {
  106. global $wp_actions;
  107. return $wp_actions[count($wp_actions)-1];
  108. }
  109. function results() {
  110. return $this->profile;
  111. }
  112. function _query_summary($queries, &$out) {
  113. foreach ($queries as $q) {
  114. $sql = $q[0];
  115. $sql = preg_replace('/(WHERE \w+ =) \d+/', '$1 x', $sql);
  116. $sql = preg_replace('/(WHERE \w+ =) \'\[-\w]+\'/', '$1 \'xxx\'', $sql);
  117. @$out[$sql] ++;
  118. }
  119. asort($out);
  120. return;
  121. }
  122. function _query_count($queries) {
  123. // this requires the savequeries patch at http://trac.wordpress.org/ticket/5218
  124. $out = array();
  125. foreach ($queries as $q) {
  126. if (empty($q[2]))
  127. @$out['unknown'] ++;
  128. else
  129. @$out[$q[2]] ++;
  130. }
  131. return $out;
  132. }
  133. function _dirty_objects_count($dirty_objects) {
  134. $out = array();
  135. foreach (array_keys($dirty_objects) as $group)
  136. $out[$group] = count($dirty_objects[$group]);
  137. return $out;
  138. }
  139. function array_add($a, $b) {
  140. $out = $a;
  141. foreach (array_keys($b) as $key)
  142. if (array_key_exists($key, $out))
  143. $out[$key] += $b[$key];
  144. else
  145. $out[$key] = $b[$key];
  146. return $out;
  147. }
  148. function array_sub($a, $b) {
  149. $out = $a;
  150. foreach (array_keys($b) as $key)
  151. if (array_key_exists($key, $b))
  152. $out[$key] -= $b[$key];
  153. return $out;
  154. }
  155. function print_summary() {
  156. $results = $this->results();
  157. printf("\nname calls time action filter warm cold misses dirty\n");
  158. foreach ($results as $name=>$stats) {
  159. 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']));
  160. }
  161. }
  162. }
  163. global $wppf;
  164. $wppf = new WPProfiler();
  165. function wppf_start($name) {
  166. $GLOBALS['wppf']->start($name);
  167. }
  168. function wppf_stop() {
  169. $GLOBALS['wppf']->stop();
  170. }
  171. function wppf_results() {
  172. return $GLOBALS['wppf']->results();
  173. }
  174. function wppf_print_summary() {
  175. $GLOBALS['wppf']->print_summary();
  176. }
  177. ?>