PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.7.4
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.7.4
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 1.6.8 All 40 releases
← All changes | lib/class-report.php +139 -42 1.5.51.7.4 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'); }
@@ -22,8 +22,9 @@
22 22 private $themes = [];
23 23 private $plugins = [];
24 24 private $composer = [];
25 25 private $summary_list = [];
26 + private $cx_buffer = [];
26 27 private $parsed_data = 0;
27 28 private $plugins_dir;
28 29 private $themes_dir;
29 30 private $mu_dir;
@@ -33,45 +34,49 @@
33 34 private $microtime;
34 35 private $tmp_iostats;
35 36 private $tmp_summary;
36 37 private $tmp_diskio;
37 - private $tmp_ticks;
38 + private $tmp_connections;
39 + private $tmp_calls;
38 40
39 41
40 - /********************************************************************
42 + /**
41 43 * Initialize
42 44 */
43 45 public function __construct( $profile_name, $microtime ) {
44 46
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;
47 + $this->tmp_summary = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
48 + CODE_PROFILER_TMP_SUMMARY_LOG;
49 + $this->tmp_iostats = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
50 + CODE_PROFILER_TMP_IOSTATS_LOG;
51 + $this->tmp_calls = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
52 + CODE_PROFILER_TMP_CALLS_LOG;
53 + $this->tmp_diskio = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
54 + CODE_PROFILER_TMP_DISKIO_LOG;
55 + $this->tmp_connections = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
56 + CODE_PROFILER_TMP_CONNECTIONS_LOG;
53 57 // Used for naming, not metrics
54 58 $this->microtime = $microtime;
55 59
56 60 // Make sure all files are there
57 - if (! file_exists( $this->tmp_ticks ) ) {
58 - $cp_error = 1;
61 + if (! file_exists( $this->tmp_calls ) ) {
62 + $cp_error = 'calls';
59 63 } elseif (! file_exists( $this->tmp_iostats ) ) {
60 - $cp_error = 2;
64 + $cp_error = 'iostats';
61 65 }
62 66 if (! empty( $cp_error ) ) {
63 67 $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 - );
68 + esc_html__('Cannot create the report: the profiler did not generate a data file (%s). '.
69 + 'You may find more details about this error in the "Log" tab or your PHP error log.',
70 + 'code-profiler'),
71 + $cp_error
72 + );
68 73 $this->return_error( $error );
69 74 }
70 75
71 76 // Total data analyzed
72 77 $this->parsed_data += filesize( $this->tmp_iostats );
73 - $this->parsed_data += filesize( $this->tmp_ticks );
78 + $this->parsed_data += filesize( $this->tmp_calls );
74 79
75 80 $this->profile_name = $profile_name;
76 81 $this->plugins_dir = preg_quote( realpath( WP_PLUGIN_DIR ) );
77 82 $this->themes_dir = preg_quote( realpath( WP_CONTENT_DIR .'/themes') );
@@ -78,22 +83,24 @@
78 83 $this->mu_dir = preg_quote( realpath( WPMU_PLUGIN_DIR ) );
79 84 }
80 85
81 86
82 - /********************************************************************
87 + /**
83 88 * Filter and save the profiler's data
84 89 */
85 90 public function prepare_report() {
86 91
87 - $this->parse_ticks();
92 + $this->parse_calls();
88 93 $this->get_plugins_theme_name();
89 94 $this->save_iostats();
95 + $this->save_connections();
90 96 $this->save_data();
91 97 $this->save_diskio();
92 98 $this->save_composer();
93 99
94 100 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'),
101 + __('Volume of code and data analyzed: %1$sMB (%2$s plugins and '.
102 + '1 theme) in %4$ss - Memory used: %3$sMB', 'code-profiler'),
96 103 number_format( $this->parsed_data / 1024 / 1024, 2 ),
97 104 (int) $this->total_plugins,
98 105 number_format( memory_get_peak_usage( false ) / 1024 / 1024, 2 ),
99 106 number_format( microtime( true ) - $this->microtime, 2 )
@@ -102,9 +109,9 @@
102 109 return $this->microtime;
103 110 }
104 111
105 112
106 - /********************************************************************
113 + /**
107 114 * Return a json-encoded error for AJAX, write to log and quit.
108 115 */
109 116 private function return_error( $error ) {
110 117
@@ -114,9 +121,9 @@
114 121 wp_send_json( $response );
115 122 }
116 123
117 124
118 - /********************************************************************
125 + /**
119 126 * Save all data to disk
120 127 */
121 128 private function save_data() {
122 129
@@ -134,9 +141,16 @@
134 141 foreach( $this->plugins as $content => $content_array ) {
135 142 $this->total_plugins++;
136 143 // Slugs
137 144 $this->summary_list['time'] += $content_array['time'];
138 - $time = $this->convert_2_seconds( $content_array['time'] );
145 + if ( isset( $this->cx_buffer['plugin'][ $content ] ) ) {
146 + $time = $this->convert_2_seconds(
147 + $content_array['time'] + $this->cx_buffer['plugin'][ $content ]
148 + );
149 + $this->summary_list['time'] += $this->cx_buffer['plugin'][ $content ];
150 + } else {
151 + $time = $this->convert_2_seconds( $content_array['time'] );
152 + }
139 153 if ( empty( $content_array['name'] ) ) {
140 154 // E.g., a plugin without a folder
141 155 $content_array['name'] = $content;
142 156 }
@@ -149,9 +163,16 @@
149 163 }
150 164 foreach( $this->themes as $content => $content_array ) {
151 165 // Slugs
152 166 $this->summary_list['time'] += $content_array['time'];
153 - $time = $this->convert_2_seconds( $content_array['time'] );
167 + if ( isset( $this->cx_buffer['theme'][ $content ] ) ) {
168 + $time = $this->convert_2_seconds(
169 + $content_array['time'] + $this->cx_buffer['theme'][ $content ]
170 + );
171 + $this->summary_list['time'] += $this->cx_buffer['theme'][ $content ];
172 + } else {
173 + $time = $this->convert_2_seconds( $content_array['time'] );
174 + }
154 175 $slugs_buffer .= "$content\t$time\t{$content_array['name']}\ttheme\n";
155 176 }
156 177
157 178 $error = '';
@@ -168,31 +189,43 @@
168 189 // Summary
169 190 $s = json_decode( file_get_contents( $this->tmp_summary ), true );
170 191 unlink( $this->tmp_summary );
171 192 if ( empty( $s['memory'] ) ) {
172 - $this->summary_list['memory'] = '-';
193 + $this->summary_list['memory'] = 0;
173 194 } else {
174 195 $this->summary_list['memory'] = $s['memory'] / 1024 / 1024;
175 196 }
176 197 if ( empty( $s['queries'] ) ) {
177 - $this->summary_list['queries']= '-';
198 + $this->summary_list['queries']= 0;
178 199 } else {
179 200 $this->summary_list['queries']= $s['queries'];
180 201 }
181 202 if ( empty( $this->total_io ) ) {
182 - $this->summary_list['io'] = '-';
203 + $this->summary_list['io'] = 0;
183 204 } else {
184 205 $this->summary_list['io'] = $this->total_io;
185 206 }
207 + if (! empty( $s['precision'] ) ) {
208 + $this->summary_list['precision'] = $s['precision'];
209 + }
186 210 $this->summary_list['items'] = $this->total_plugins + 1; // Add the theme
187 211 $this->summary_list['time'] = $this->convert_2_seconds( $this->summary_list['time'] );
188 212 $this->summary_list['time'] = number_format( $this->summary_list['time'], 4 );
213 +
214 + // Save Code Profiler, PHP and WordPress' versions
215 + global $wp_version;
216 + $this->summary_list['versions'] = [
217 + 'wp' => $wp_version,
218 + 'cp' => CODE_PROFILER_VERSION,
219 + 'php' => PHP_VERSION
220 + ];
221 +
189 222 file_put_contents( $summary_json, json_encode( $this->summary_list ) );
190 223
191 224 }
192 225
193 226
194 - /********************************************************************
227 + /**
195 228 * Convert microtime (PHP<7.3) or hrtime (PHP>=7.3) to seconds
196 229 */
197 230 private function convert_2_seconds( $time ) {
198 231
@@ -209,14 +242,18 @@
209 242 return $time;
210 243 }
211 244
212 245
213 - /********************************************************************
246 + /**
214 247 * Check if a slug is from a plugin or theme.
215 248 */
216 249 private function plugin_or_theme( $script ) {
217 250
218 - $res = ['theme' => '', 'plugin' => '', 'script' => ''];
251 + $res = [
252 + 'theme' => '',
253 + 'plugin' => '',
254 + 'script' => ''
255 + ];
219 256
220 257 if ( preg_match("`^{$this->plugins_dir}[\\\/](?:(.+?)[\\\/]|([^\\\/]+\.php)$)`", $script, $slug ) ) {
221 258 if (! empty( $slug[1] ) ) {
222 259 $res['plugin'] = $slug[1];
@@ -237,9 +274,9 @@
237 274 return $res;
238 275 }
239 276
240 277
241 - /********************************************************************
278 + /**
242 279 * Retrieve the full name for all plugins and themes.
243 280 */
244 281 private function get_plugins_theme_name() {
245 282
@@ -275,18 +312,18 @@
275 312 }
276 313 }
277 314
278 315
279 - /********************************************************************
316 + /**
280 317 * Parse, filter and sort the data
281 318 */
282 - private function parse_ticks() {
319 + private function parse_calls() {
283 320
284 - $fh = fopen( $this->tmp_ticks, 'rb');
321 + $fh = fopen( $this->tmp_calls, 'rb');
285 322 if ( $fh === false ) {
286 323 $error = sprintf(
287 324 esc_html__('Cannot open file for reading: %s', 'code-profiler'),
288 - $this->tmp_ticks
325 + $this->tmp_calls
289 326 );
290 327 $this->return_error( $error );
291 328 }
292 329 while(! feof( $fh ) ) {
@@ -389,9 +426,9 @@
389 426 }
390 427 }
391 428
392 429 fclose( $fh );
393 - unlink( $this->tmp_ticks );
430 + unlink( $this->tmp_calls );
394 431 }
395 432
396 433 /********************************************************************
397 434 * Save IO stats
@@ -422,12 +459,12 @@
422 459 unlink( $this->tmp_iostats );
423 460 }
424 461
425 462
426 - /********************************************************************
463 + /**
427 464 * Save Read/Write stats
428 465 */
429 - function save_diskio() {
466 + private function save_diskio() {
430 467
431 468 rename(
432 469 $this->tmp_diskio,
433 470 CODE_PROFILER_UPLOAD_DIR ."/{$this->microtime}.{$this->profile_name}.diskio.profile"
@@ -434,12 +471,12 @@
434 471 );
435 472 }
436 473
437 474
438 - /********************************************************************
475 + /**
439 476 * Save list of plugins using composer
440 477 */
441 - function save_composer() {
478 + private function save_composer() {
442 479
443 480 if (! empty( $this->composer ) ) {
444 481 // Try to get the plugin's name
445 482 foreach( $this->composer as $slug => $v ) {
@@ -450,8 +487,68 @@
450 487 file_put_contents(
451 488 CODE_PROFILER_UPLOAD_DIR ."/{$this->microtime}.{$this->profile_name}.composer.profile",
452 489 json_encode( $this->composer )
453 490 );
491 + }
492 + }
493 +
494 + /**
495 + * External connections.
496 + */
497 + private function save_connections() {
498 +
499 + if (! file_exists( $this->tmp_connections ) ) {
500 + return;
501 + }
502 +
503 + $connections = json_decode( file_get_contents( $this->tmp_connections ), true );
504 + unlink( $this->tmp_connections );
505 +
506 + if ( empty( $connections ) ) {
507 + return;
508 + }
509 +
510 + foreach( $connections as $connection => $array ) {
511 + $found_call = 0;
512 + $found_caller = 0;
513 +
514 + foreach( $array['bt'] as $k =>$v ) {
515 + if ( isset( $v['file'] ) && isset( $v['function'] ) && isset( $v['line'] ) ) {
516 +
517 + if (! $found_caller && ( $found_call ||
518 + in_array( $v['function'], [
519 + 'wp_remote_get',
520 + 'wp_safe_remote_get',
521 + 'wp_remote_post',
522 + 'wp_safe_remote_post'
523 + ] )
524 + ) ) {
525 +
526 + $found_call = 1;
527 +
528 + $type = $this->plugin_or_theme( $v['file'] );
529 +
530 + if (! empty( $type['theme'] ) ) {
531 + // Save it for later use
532 + if ( isset( $this->cx_buffer['theme'][ $type['theme'] ] ) ) {
533 + $this->cx_buffer['theme'][ $type['theme'] ] += $array['stop'] - $connection;
534 + } else {
535 + $this->cx_buffer['theme'][ $type['theme'] ] = $array['stop'] - $connection;
536 + }
537 + $found_caller = 1;
538 +
539 + } elseif (! empty( $type['plugin'] ) ) {
540 + // Save it for later use
541 + if ( isset( $this->cx_buffer['plugin'][ $type['plugin'] ] ) ) {
542 + $this->cx_buffer['plugin'][ $type['plugin'] ] += $array['stop'] - $connection;
543 + } else {
544 + $this->cx_buffer['plugin'][ $type['plugin'] ] = $array['stop'] - $connection;
545 + }
546 + $found_caller = 1;
547 + }
548 + }
549 + }
550 + }
454 551 }
455 552 }
456 553
457 554 }