performance.drush.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. <?php
  2. /**
  3. * @file
  4. * Drush integration for the Performance module.
  5. */
  6. /**
  7. * Implements hook_drush_command().
  8. */
  9. function performance_drush_command() {
  10. $items = array();
  11. $limit = "Limit the number of records. Defaults to 25.";
  12. $direction = "Sort direction, defaults to descending: asc or desc.";
  13. $items['performance-summary'] = array(
  14. 'callback' => 'drush_performance_summary',
  15. 'description' => "Display the Performance Logging and Monitoring summary entries.",
  16. 'aliases' => array('perf-sm'),
  17. 'arguments' => array(
  18. 'limit' => $limit,
  19. 'orderby' => "The column name to sort on, defaults to last_access: path, last_access, bytes_max, bytes_avg, ms_max, ms_avg, [query_count_max, query_count_avg, query_timer_max, query_timer_avg] or num_accesses.",
  20. 'direction' => $direction,
  21. ),
  22. 'examples' => array(
  23. 'performance-summary 25' => 'Retrieve last 25 entries.',
  24. 'performance-summary 10 path' => 'Retrieve last 10 entries sorted by path, descending.',
  25. 'performance-summary 40 bytes_avg asc' => 'Retrieve last 40 entries sorted by average size, ascending.',
  26. ),
  27. );
  28. $items['performance-detail'] = array(
  29. 'callback' => 'drush_performance_details',
  30. 'description' => "Display the Performance Logging and Monitoring detail entries.",
  31. 'aliases' => array('perf-dt'),
  32. 'arguments' => array(
  33. 'limit' => $limit,
  34. 'orderby' => "The column name to sort on, defaults to timestamp: pid, timestamp, bytes, ms, [query_count, query_timer], anon or path.",
  35. 'direction' => $direction,
  36. ),
  37. 'examples' => array(
  38. 'performance-detail 15' => 'Retrieve last 15 entries.',
  39. 'performance-detail 20 query_count' => 'Retrieve last 20 entries sorted by the number of queries, descending.',
  40. 'performance-detail 35 bytes asc' => 'Retrieve last 35 entries sorted by size, ascending.',
  41. ),
  42. );
  43. return $items;
  44. }
  45. /**
  46. * Summary page callback.
  47. * Differs a little from the version in the module because drush_print_table()
  48. * works differently.
  49. */
  50. function drush_performance_summary() {
  51. $args = func_get_args();
  52. // Collect arguments.
  53. $orderby = 'last_access';
  54. $columns = array('path', 'last_access', 'bytes_max', 'bytes_avg', 'ms_max', 'ms_avg', 'num_accesses');
  55. if (variable_get(PERFORMANCE_QUERY_VAR, 0)) {
  56. $columns[] = 'query_count_max';
  57. $columns[] = 'query_count_avg';
  58. $columns[] = 'query_timer_max';
  59. $columns[] = 'query_timer_avg';
  60. }
  61. $arguments = drush_performance_parse_args($args, $orderby, $columns);
  62. // Error thrown, abort.
  63. if (!is_array($arguments)) {
  64. return $arguments;
  65. }
  66. // Go back no more than 1 hour.
  67. $arguments['timestamp'] = REQUEST_TIME - 60 * 60;
  68. // Get actual data.
  69. $data_list = performance_traverse_cache('drush_performance_get_summary', $arguments);
  70. $data_list = performance_sort_summary($data_list, $arguments['direction'], $arguments['orderby']);
  71. if (empty($data_list) && !variable_get('performance_summary', 0)) {
  72. return drush_set_error(dt('Summary performance log is not enabled. Go to the settings page to enable it.'));
  73. }
  74. elseif (empty($data_list)) {
  75. drush_print("\033[1m" . dt('No log messages available.') . "\033[0m\n", 1);
  76. drush_print(dt('Possible causes:'), 1);
  77. drush_print('- ' . dt('no data stored yet'), 2);
  78. drush_print('- ' . dt('all data stored is older than one hour'), 2);
  79. return drush_print('- $base_url ' . dt('not properly set: run drush with the --uri parameter'), 2);
  80. }
  81. elseif (!variable_get('performance_summary', 0)) {
  82. drush_print("\033[1;33m" . dt('Summary performance log is not enabled! Showing stored logs.') . "\033[0m\n", 1);
  83. }
  84. // Build table header.
  85. $header = array(
  86. dt('Path'),
  87. dt('Last access'),
  88. dt('# accesses'),
  89. dt('MB Memory (Max)'),
  90. dt('MB Memory (Avg)'),
  91. dt('ms (Max)'),
  92. dt('ms (Avg)'),
  93. );
  94. if (variable_get(PERFORMANCE_QUERY_VAR, 0)) {
  95. $header[] = dt('Query ms (Max)');
  96. $header[] = dt('Query ms (Avg)');
  97. $header[] = dt('Query Count (Max)');
  98. $header[] = dt('Query Count (Avg)');
  99. }
  100. $rows[] = $header;
  101. // Format data into table.
  102. $threshold = variable_get('performance_threshold_accesses', 0);
  103. $total_rows = $shown = $last_max = $total_bytes = $total_ms = $total_accesses = 0;
  104. $last_min = REQUEST_TIME;
  105. foreach ($data_list as $data) {
  106. $total_rows++;
  107. $last_max = max($last_max, $data['last_access']);
  108. $last_min = min($last_min, $data['last_access']);
  109. // Calculate running averages.
  110. $total_bytes += $data['bytes_sum'] / $data['num_accesses'];
  111. $total_ms += $data['ms_sum'] / $data['num_accesses'];
  112. $total_accesses += $data['num_accesses'];
  113. $row_data = array();
  114. if ($data['num_accesses'] > $threshold) {
  115. $shown++;
  116. $row_data[] = check_plain($data['path']);
  117. $row_data[] = format_date($data['last_access'], 'small');
  118. $row_data[] = $data['num_accesses'];
  119. $row_data[] = number_format($data['bytes_max'] / 1024 / 1024, 2);
  120. $row_data[] = number_format($data['bytes_avg'] / 1024 / 1024, 2);
  121. $row_data[] = number_format($data['ms_max'], 1);
  122. $row_data[] = number_format($data['ms_avg'], 1);
  123. if (variable_get(PERFORMANCE_QUERY_VAR, 0)) {
  124. $row_data[] = number_format($data['query_timer_max'], 1);
  125. $row_data[] = number_format($data['query_timer_avg'], 1);
  126. $row_data[] = $data['query_count_max'];
  127. $row_data[] = $data['query_count_avg'];
  128. }
  129. }
  130. $rows[] = $row_data;
  131. }
  132. if (!$rows) {
  133. $rows[] = dt('No statistics available yet.');
  134. }
  135. if ($threshold) {
  136. drush_print("\n" . dt('Showing !shown paths with more than !threshold accesses, out of !total total paths.',
  137. array('!threshold' => $threshold, '!shown' => $shown, '!total' => $total_rows)));
  138. }
  139. else {
  140. drush_print("\n" . dt('Showing all !total paths.', array('!total' => $total_rows)));
  141. }
  142. // Protect against divide by zero.
  143. if ($total_rows > 0) {
  144. $mb_avg = number_format($total_bytes / $total_rows / 1024 / 1024, 1);
  145. $ms_avg = number_format($total_ms / $total_rows, 2);
  146. }
  147. else {
  148. $mb_avg = 'n/a';
  149. $ms_avg = 'n/a';
  150. }
  151. drush_print(dt('Average memory per page: !mb_avg MB', array('!mb_avg' => $mb_avg)));
  152. drush_print(dt('Average duration per page: !ms_avg ms', array('!ms_avg' => $ms_avg)));
  153. drush_print(dt('Total number of page accesses: !accesses', array('!accesses' => $total_accesses)));
  154. drush_print(dt('First access: !access.', array('!access' => format_date($last_min, 'small'))));
  155. drush_print(dt('Last access: !access.', array('!access' => format_date($last_max, 'small'))) . "\n");
  156. drush_print("\033[1m" . dt('Performance log summary: !rows entries ordered by !column, !direction', array('!rows' => count($rows) - 1, '!column' => $arguments['orderby'], '!direction' => $arguments['direction'])) . "\033[0m", 1);
  157. // TODO: add 'running averages' here, like in the Drupal backend.
  158. drush_print_table($rows, TRUE);
  159. }
  160. function drush_performance_details() {
  161. $header = array(
  162. dt('#'),
  163. dt('Date'),
  164. dt('Path'),
  165. dt('Memory (MB)'),
  166. dt('ms (Total)'),
  167. dt('Anonymous?'),
  168. );
  169. if (variable_get(PERFORMANCE_QUERY_VAR, 0)) {
  170. $header[] = dt('# Queries');
  171. $header[] = dt('Query ms');
  172. }
  173. $rows[] = $header;
  174. // Collect arguments.
  175. $args = func_get_args();
  176. $orderby = 'timestamp';
  177. $columns = array('pid', 'timestamp', 'bytes', 'ms', 'anon', 'path');
  178. if (variable_get(PERFORMANCE_QUERY_VAR, 0)) {
  179. $columns[] = 'query_count';
  180. $columns[] = 'query_timer';
  181. }
  182. $arguments = drush_performance_parse_args($args, $orderby, $columns);
  183. // Error thrown, abort.
  184. if (!is_array($arguments)) {
  185. return $arguments;
  186. }
  187. $data_list = drush_performance_get_detail($arguments);
  188. if (empty($data_list) && !variable_get('performance_detail', 0)) {
  189. return drush_set_error(dt('Detail performance log is not enabled! Go to the settings page to enable it.'));
  190. }
  191. elseif (empty($data_list)) {
  192. return drush_print("\033[1m" . dt('No log messages available.') . "\033[0m", 1);
  193. }
  194. elseif (!variable_get('performance_detail', 0)) {
  195. drush_print("\033[1;33m" . dt('Detail performance log is not enabled! Showing stored logs.') . "\033[0m\n", 1);
  196. }
  197. foreach ($data_list as $data) {
  198. // Cast to object because of the DB API now returning row objects by default.
  199. $data = is_array($data) ? (object)$data : $data;
  200. $row_data = array();
  201. $row_data[] = $data->pid;
  202. $row_data[] = format_date($data->timestamp, 'small');
  203. $row_data[] = check_plain($data->path);
  204. $row_data[] = number_format($data->bytes/1024/1024, 2);
  205. $row_data[] = $data->ms;
  206. $row_data[] = ($data->anon) ? t('Yes') : t('No');
  207. if (variable_get(PERFORMANCE_QUERY_VAR, 0)) {
  208. $row_data[] = $data->query_count;
  209. $row_data[] = $data->query_timer;
  210. }
  211. $rows[] = $row_data;
  212. }
  213. drush_print("\033[1m" . dt('Performance log details: !rows entries ordered by !column, !direction', array('!rows' => count($rows) - 1, '!column' => $arguments['orderby'], '!direction' => $arguments['direction'])) . "\033[0m", 1);
  214. drush_print_table($rows, TRUE);
  215. }
  216. /**
  217. * Helper function to parse the arguments which are the same for both commands.
  218. * Only columns differ.
  219. */
  220. function drush_performance_parse_args($args, $orderby, $columns) {
  221. $arguments = array();
  222. $default = TRUE;
  223. // Set limit from arguments or fall back to default.
  224. $arguments['limit'] = 25;
  225. if ((count($args) > 2 || count($args) == 1) && isset($args[0])) {
  226. if (is_numeric($args[0])) {
  227. $arguments['limit'] = $args[0];
  228. }
  229. else {
  230. return drush_set_error(dt('First argument must be numeric!'));
  231. }
  232. }
  233. else {
  234. // 1st parameter was most likely omitted, so we prepend the default to the
  235. // arguments to make the following checks check the right argument :-)
  236. array_unshift($args, $arguments['limit']);
  237. }
  238. // Order by column name.
  239. $arguments['orderby'] = $orderby;
  240. if (isset($args[1])) {
  241. if (in_array($args[1], $columns)) {
  242. $arguments['orderby'] = $args[1];
  243. }
  244. // Let the user know what the options are.
  245. else {
  246. return drush_set_error(dt('Unknown column name. Possible values are: !columns', array('!columns' => implode(', ', $columns))));
  247. }
  248. }
  249. // Sort direction.
  250. $arguments['direction'] = 'desc';
  251. $options = array('asc', 'desc');
  252. if (isset($args[2])) {
  253. if (in_array($args[2], $options)) {
  254. $arguments['direction'] = $args[2];
  255. }
  256. // Let the user know what the options are.
  257. else {
  258. return drush_set_error(dt('Unknown sort direction. Possible values are: !options', array('!options' => implode(', ', $options))));
  259. }
  260. }
  261. return $arguments;
  262. }
  263. /**
  264. * Callback used by performance_traverse_cache() for fetching summary data.
  265. *
  266. * @param $cache cache object
  267. * @param $arguments associative array containing limit and timestamp
  268. * @return the processed data or NULL
  269. *
  270. * @see performance_traverse_cache()
  271. */
  272. function drush_performance_get_summary($cache, $arguments) {
  273. static $count = 0;
  274. if ($cache->data['last_access'] >= $arguments['timestamp'] && $count < $arguments['limit']) {
  275. $count++;
  276. return $cache->data;
  277. }
  278. return;
  279. }
  280. /**
  281. * Drupal version independant variation of performance_db_get_data() for easy
  282. * maintenance of the drush commands.
  283. */
  284. function drush_performance_get_detail($arguments) {
  285. $data_list = array();
  286. $result = drush_db_select('performance_detail', '*', NULL, NULL, 0, $arguments['limit'], $arguments['orderby'], $arguments['direction']);
  287. if (drush_drupal_major_version() >= 7) {
  288. foreach ($result as $row) {
  289. $data_list[] = $row;
  290. }
  291. }
  292. else {
  293. while ($row = db_fetch_object($result)) {
  294. $data_list[] = $row;
  295. }
  296. }
  297. return $data_list;
  298. }