PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.9.6
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.9.6
1.9.6 1.9.5 1.9.4 1.9.3 trunk 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.6 1.6.1 1.6.10 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 All 41 releases
← All changes | lib/class-report.php +136 -63 1.61.9.6 View file →
@@ -6,9 +6,9 @@
6 6 | | | / _ \ / _` |/ _ \ | |_) | '__/ _ \| |_| | |/ _ \ '__| |
7 7 | | |__| (_) | (_| | __/ | __/| | | (_) | _| | | __/ | |
8 8 | \____\___/ \__,_|\___| |_| |_| \___/|_| |_|_|\___|_| |
9 9 | |
10 - | (c) Jerome Bruandet ~ https://code-profiler.com/ |
10 + | (c) Jerome Bruandet ~ https://nintechnet.com/codeprofiler/ |
11 11 +=====================================================================+
12 12 */
13 13
14 14 if (! defined('ABSPATH') ) { die('Forbidden'); }
@@ -20,10 +20,10 @@
20 20 private $last_type;
21 21 private $last_time = 0;
22 22 private $themes = [];
23 23 private $plugins = [];
24 - private $composer = [];
25 24 private $summary_list = [];
25 + private $cx_buffer = [];
26 26 private $parsed_data = 0;
27 27 private $plugins_dir;
28 28 private $themes_dir;
29 29 private $mu_dir;
@@ -33,45 +33,49 @@
33 33 private $microtime;
34 34 private $tmp_iostats;
35 35 private $tmp_summary;
36 36 private $tmp_diskio;
37 - private $tmp_ticks;
37 + private $tmp_connections;
38 + private $tmp_calls;
38 39
39 40
40 - /********************************************************************
41 + /**
41 42 * Initialize
42 43 */
43 44 public function __construct( $profile_name, $microtime ) {
44 45
45 - $this->tmp_summary = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
46 - CODE_PROFILER_TMP_SUMMARY_LOG;
47 - $this->tmp_iostats = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
48 - CODE_PROFILER_TMP_IOSTATS_LOG;
49 - $this->tmp_ticks = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
50 - CODE_PROFILER_TMP_TICKS_LOG;
51 - $this->tmp_diskio = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
52 - CODE_PROFILER_TMP_DISKIO_LOG;
46 + $this->tmp_summary = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
47 + CODE_PROFILER_TMP_SUMMARY_LOG;
48 + $this->tmp_iostats = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
49 + CODE_PROFILER_TMP_IOSTATS_LOG;
50 + $this->tmp_calls = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
51 + CODE_PROFILER_TMP_CALLS_LOG;
52 + $this->tmp_diskio = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
53 + CODE_PROFILER_TMP_DISKIO_LOG;
54 + $this->tmp_connections = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
55 + CODE_PROFILER_TMP_CONNECTIONS_LOG;
53 56 // Used for naming, not metrics
54 57 $this->microtime = $microtime;
55 58
56 59 // Make sure all files are there
57 - if (! file_exists( $this->tmp_ticks ) ) {
58 - $cp_error = 1;
60 + if (! file_exists( $this->tmp_calls ) ) {
61 + $cp_error = 'calls';
59 62 } elseif (! file_exists( $this->tmp_iostats ) ) {
60 - $cp_error = 2;
63 + $cp_error = 'iostats';
61 64 }
62 65 if (! empty( $cp_error ) ) {
63 66 $error = sprintf(
64 - esc_html__('Cannot create the report: the profiler did not generate a data file (#%s). Make sure the following directory is writable: %s. If you are using a caching plugin or an opcode cache, try to disable it. You may also find more details about the error in the "Log" tab.', 'code-profiler'),
65 - $cp_error,
66 - CODE_PROFILER_UPLOAD_DIR .'/'
67 - );
67 + esc_html__('Cannot create the report: the profiler did not generate a data file (%s). '.
68 + 'You may find more details about this error in the "Log" tab or your PHP error log.',
69 + 'code-profiler'),
70 + $cp_error
71 + );
68 72 $this->return_error( $error );
69 73 }
70 74
71 75 // Total data analyzed
72 76 $this->parsed_data += filesize( $this->tmp_iostats );
73 - $this->parsed_data += filesize( $this->tmp_ticks );
77 + $this->parsed_data += filesize( $this->tmp_calls );
74 78
75 79 $this->profile_name = $profile_name;
76 80 $this->plugins_dir = preg_quote( realpath( WP_PLUGIN_DIR ) );
77 81 $this->themes_dir = preg_quote( realpath( WP_CONTENT_DIR .'/themes') );
@@ -78,22 +82,23 @@
78 82 $this->mu_dir = preg_quote( realpath( WPMU_PLUGIN_DIR ) );
79 83 }
80 84
81 85
82 - /********************************************************************
86 + /**
83 87 * Filter and save the profiler's data
84 88 */
85 89 public function prepare_report() {
86 90
87 - $this->parse_ticks();
91 + $this->parse_calls();
88 92 $this->get_plugins_theme_name();
89 93 $this->save_iostats();
94 + $this->save_connections();
90 95 $this->save_data();
91 96 $this->save_diskio();
92 - $this->save_composer();
93 97
94 98 code_profiler_log_info( sprintf(
95 - __('Volume of code and data analyzed: %1$sMB (%2$s plugins and 1 theme) in %4$ss - Memory used: %3$sMB', 'code-profiler'),
99 + __('Volume of code and data analyzed: %1$sMB (%2$s plugins and '.
100 + '1 theme) in %4$ss - Memory used: %3$sMB', 'code-profiler'),
96 101 number_format( $this->parsed_data / 1024 / 1024, 2 ),
97 102 (int) $this->total_plugins,
98 103 number_format( memory_get_peak_usage( false ) / 1024 / 1024, 2 ),
99 104 number_format( microtime( true ) - $this->microtime, 2 )
@@ -102,9 +107,9 @@
102 107 return $this->microtime;
103 108 }
104 109
105 110
106 - /********************************************************************
111 + /**
107 112 * Return a json-encoded error for AJAX, write to log and quit.
108 113 */
109 114 private function return_error( $error ) {
110 115
@@ -114,9 +119,9 @@
114 119 wp_send_json( $response );
115 120 }
116 121
117 122
118 - /********************************************************************
123 + /**
119 124 * Save all data to disk
120 125 */
121 126 private function save_data() {
122 127
@@ -134,9 +139,16 @@
134 139 foreach( $this->plugins as $content => $content_array ) {
135 140 $this->total_plugins++;
136 141 // Slugs
137 142 $this->summary_list['time'] += $content_array['time'];
138 - $time = $this->convert_2_seconds( $content_array['time'] );
143 + if ( isset( $this->cx_buffer['plugin'][ $content ] ) ) {
144 + $time = $this->convert_2_seconds(
145 + $content_array['time'] + $this->cx_buffer['plugin'][ $content ]
146 + );
147 + $this->summary_list['time'] += $this->cx_buffer['plugin'][ $content ];
148 + } else {
149 + $time = $this->convert_2_seconds( $content_array['time'] );
150 + }
139 151 if ( empty( $content_array['name'] ) ) {
140 152 // E.g., a plugin without a folder
141 153 $content_array['name'] = $content;
142 154 }
@@ -149,9 +161,16 @@
149 161 }
150 162 foreach( $this->themes as $content => $content_array ) {
151 163 // Slugs
152 164 $this->summary_list['time'] += $content_array['time'];
153 - $time = $this->convert_2_seconds( $content_array['time'] );
165 + if ( isset( $this->cx_buffer['theme'][ $content ] ) ) {
166 + $time = $this->convert_2_seconds(
167 + $content_array['time'] + $this->cx_buffer['theme'][ $content ]
168 + );
169 + $this->summary_list['time'] += $this->cx_buffer['theme'][ $content ];
170 + } else {
171 + $time = $this->convert_2_seconds( $content_array['time'] );
172 + }
154 173 $slugs_buffer .= "$content\t$time\t{$content_array['name']}\ttheme\n";
155 174 }
156 175
157 176 $error = '';
@@ -168,31 +187,48 @@
168 187 // Summary
169 188 $s = json_decode( file_get_contents( $this->tmp_summary ), true );
170 189 unlink( $this->tmp_summary );
171 190 if ( empty( $s['memory'] ) ) {
172 - $this->summary_list['memory'] = '-';
191 + $this->summary_list['memory'] = 0;
173 192 } else {
174 193 $this->summary_list['memory'] = $s['memory'] / 1024 / 1024;
175 194 }
176 195 if ( empty( $s['queries'] ) ) {
177 - $this->summary_list['queries']= '-';
196 + $this->summary_list['queries']= 0;
178 197 } else {
179 198 $this->summary_list['queries']= $s['queries'];
180 199 }
181 200 if ( empty( $this->total_io ) ) {
182 - $this->summary_list['io'] = '-';
201 + $this->summary_list['io'] = 0;
183 202 } else {
184 203 $this->summary_list['io'] = $this->total_io;
185 204 }
205 + if (! empty( $s['precision'] ) ) {
206 + $this->summary_list['precision'] = $s['precision'];
207 + }
186 208 $this->summary_list['items'] = $this->total_plugins + 1; // Add the theme
187 209 $this->summary_list['time'] = $this->convert_2_seconds( $this->summary_list['time'] );
188 210 $this->summary_list['time'] = number_format( $this->summary_list['time'], 4 );
211 +
212 + // Re-run options
213 + if (! empty( $s['rerun'] ) ) {
214 + $this->summary_list['rerun'] = $s['rerun'];
215 + $this->summary_list['rerun']['profile'] = $this->profile_name;
216 + }
217 +
218 + // Save Code Profiler, PHP and WordPress' versions
219 + global $wp_version;
220 + $this->summary_list['versions'] = [
221 + 'wp' => $wp_version,
222 + 'cp' => CODE_PROFILER_VERSION,
223 + 'php' => PHP_VERSION
224 + ];
225 +
189 226 file_put_contents( $summary_json, json_encode( $this->summary_list ) );
190 -
191 227 }
192 228
193 229
194 - /********************************************************************
230 + /**
195 231 * Convert microtime (PHP<7.3) or hrtime (PHP>=7.3) to seconds
196 232 */
197 233 private function convert_2_seconds( $time ) {
198 234
@@ -209,14 +245,18 @@
209 245 return $time;
210 246 }
211 247
212 248
213 - /********************************************************************
249 + /**
214 250 * Check if a slug is from a plugin or theme.
215 251 */
216 252 private function plugin_or_theme( $script ) {
217 253
218 - $res = ['theme' => '', 'plugin' => '', 'script' => ''];
254 + $res = [
255 + 'theme' => '',
256 + 'plugin' => '',
257 + 'script' => ''
258 + ];
219 259
220 260 if ( preg_match("`^{$this->plugins_dir}[\\\/](?:(.+?)[\\\/]|([^\\\/]+\.php)$)`", $script, $slug ) ) {
221 261 if (! empty( $slug[1] ) ) {
222 262 $res['plugin'] = $slug[1];
@@ -237,9 +277,9 @@
237 277 return $res;
238 278 }
239 279
240 280
241 - /********************************************************************
281 + /**
242 282 * Retrieve the full name for all plugins and themes.
243 283 */
244 284 private function get_plugins_theme_name() {
245 285
@@ -275,18 +315,18 @@
275 315 }
276 316 }
277 317
278 318
279 - /********************************************************************
319 + /**
280 320 * Parse, filter and sort the data
281 321 */
282 - private function parse_ticks() {
322 + private function parse_calls() {
283 323
284 - $fh = fopen( $this->tmp_ticks, 'rb');
324 + $fh = fopen( $this->tmp_calls, 'rb');
285 325 if ( $fh === false ) {
286 326 $error = sprintf(
287 327 esc_html__('Cannot open file for reading: %s', 'code-profiler'),
288 - $this->tmp_ticks
328 + $this->tmp_calls
289 329 );
290 330 $this->return_error( $error );
291 331 }
292 332 while(! feof( $fh ) ) {
@@ -348,16 +388,8 @@
348 388 if (! isset( $this->plugins[ $slug['plugin'] ]['time'] ) ) {
349 389 $this->plugins[ $slug['plugin'] ]['time'] = 0;
350 390 }
351 391 }
352 -
353 - // Look for multiple copies of composer and warn the user
354 - if ( preg_match("`^{$this->plugins_dir}[\\\/](?:[^\\\/]+)[\\\/].+?[\\\/]composer[\\\/]autoload_real\.php`", $caller ) ) {
355 - // Save the slug first, we'll fetch the name later
356 - if (! isset( $this->composer[ $slug['plugin'] ] ) ) {
357 - $this->composer[ $slug['plugin'] ] = '';
358 - }
359 - }
360 392 }
361 393
362 394 // We don't have a slug: if there's an old record, update it
363 395 } else {
@@ -389,9 +421,9 @@
389 421 }
390 422 }
391 423
392 424 fclose( $fh );
393 - unlink( $this->tmp_ticks );
425 + unlink( $this->tmp_calls );
394 426 }
395 427
396 428 /********************************************************************
397 429 * Save IO stats
@@ -422,9 +454,9 @@
422 454 unlink( $this->tmp_iostats );
423 455 }
424 456
425 457
426 - /********************************************************************
458 + /**
427 459 * Save Read/Write stats
428 460 */
429 461 private function save_diskio() {
430 462
@@ -434,24 +466,65 @@
434 466 );
435 467 }
436 468
437 469
438 - /********************************************************************
439 - * Save list of plugins using composer
440 - */
441 - private function save_composer() {
470 + /**
471 + * External connections.
472 + */
473 + private function save_connections() {
442 474
443 - if (! empty( $this->composer ) ) {
444 - // Try to get the plugin's name
445 - foreach( $this->composer as $slug => $v ) {
446 - if ( isset( $this->plugins[ $slug ]['name'] ) ) {
447 - $this->composer[ $slug ] = $this->plugins[ $slug ]['name'];
475 + if (! file_exists( $this->tmp_connections ) ) {
476 + return;
477 + }
478 +
479 + $connections = json_decode( file_get_contents( $this->tmp_connections ), true );
480 + unlink( $this->tmp_connections );
481 +
482 + if ( empty( $connections ) ) {
483 + return;
484 + }
485 +
486 + foreach( $connections as $connection => $array ) {
487 + $found_call = 0;
488 + $found_caller = 0;
489 +
490 + foreach( $array['bt'] as $k =>$v ) {
491 + if ( isset( $v['file'] ) && isset( $v['function'] ) && isset( $v['line'] ) ) {
492 +
493 + if (! $found_caller && ( $found_call ||
494 + in_array( $v['function'], [
495 + 'wp_remote_get',
496 + 'wp_safe_remote_get',
497 + 'wp_remote_post',
498 + 'wp_safe_remote_post'
499 + ] )
500 + ) ) {
501 +
502 + $found_call = 1;
503 +
504 + $type = $this->plugin_or_theme( $v['file'] );
505 +
506 + if (! empty( $type['theme'] ) ) {
507 + // Save it for later use
508 + if ( isset( $this->cx_buffer['theme'][ $type['theme'] ] ) ) {
509 + $this->cx_buffer['theme'][ $type['theme'] ] += $array['stop'] - $connection;
510 + } else {
511 + $this->cx_buffer['theme'][ $type['theme'] ] = $array['stop'] - $connection;
512 + }
513 + $found_caller = 1;
514 +
515 + } elseif (! empty( $type['plugin'] ) ) {
516 + // Save it for later use
517 + if ( isset( $this->cx_buffer['plugin'][ $type['plugin'] ] ) ) {
518 + $this->cx_buffer['plugin'][ $type['plugin'] ] += $array['stop'] - $connection;
519 + } else {
520 + $this->cx_buffer['plugin'][ $type['plugin'] ] = $array['stop'] - $connection;
521 + }
522 + $found_caller = 1;
523 + }
524 + }
448 525 }
449 526 }
450 - file_put_contents(
451 - CODE_PROFILER_UPLOAD_DIR ."/{$this->microtime}.{$this->profile_name}.composer.profile",
452 - json_encode( $this->composer )
453 - );
454 527 }
455 528 }
456 529
457 530 }