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 +134 -58 1.5.31.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,9 +33,10 @@
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
@@ -41,37 +42,40 @@
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 page.', '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') );
@@ -83,20 +87,22 @@
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 and processed: %s MB (%s plugins and 1 theme) - Memory used: %s MB', '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 - number_format( memory_get_peak_usage( false ) / 1024 / 1024, 2 )
103 + number_format( memory_get_peak_usage( false ) / 1024 / 1024, 2 ),
104 + number_format( microtime( true ) - $this->microtime, 2 )
99 105 ));
100 106
101 107 return $this->microtime;
102 108 }
@@ -133,9 +139,16 @@
133 139 foreach( $this->plugins as $content => $content_array ) {
134 140 $this->total_plugins++;
135 141 // Slugs
136 142 $this->summary_list['time'] += $content_array['time'];
137 - $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 + }
138 151 if ( empty( $content_array['name'] ) ) {
139 152 // E.g., a plugin without a folder
140 153 $content_array['name'] = $content;
141 154 }
@@ -148,9 +161,16 @@
148 161 }
149 162 foreach( $this->themes as $content => $content_array ) {
150 163 // Slugs
151 164 $this->summary_list['time'] += $content_array['time'];
152 - $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 + }
153 173 $slugs_buffer .= "$content\t$time\t{$content_array['name']}\ttheme\n";
154 174 }
155 175
156 176 $error = '';
@@ -167,27 +187,44 @@
167 187 // Summary
168 188 $s = json_decode( file_get_contents( $this->tmp_summary ), true );
169 189 unlink( $this->tmp_summary );
170 190 if ( empty( $s['memory'] ) ) {
171 - $this->summary_list['memory'] = '-';
191 + $this->summary_list['memory'] = 0;
172 192 } else {
173 193 $this->summary_list['memory'] = $s['memory'] / 1024 / 1024;
174 194 }
175 195 if ( empty( $s['queries'] ) ) {
176 - $this->summary_list['queries']= '-';
196 + $this->summary_list['queries']= 0;
177 197 } else {
178 198 $this->summary_list['queries']= $s['queries'];
179 199 }
180 200 if ( empty( $this->total_io ) ) {
181 - $this->summary_list['io'] = '-';
201 + $this->summary_list['io'] = 0;
182 202 } else {
183 203 $this->summary_list['io'] = $this->total_io;
184 204 }
205 + if (! empty( $s['precision'] ) ) {
206 + $this->summary_list['precision'] = $s['precision'];
207 + }
185 208 $this->summary_list['items'] = $this->total_plugins + 1; // Add the theme
186 209 $this->summary_list['time'] = $this->convert_2_seconds( $this->summary_list['time'] );
187 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 +
188 226 file_put_contents( $summary_json, json_encode( $this->summary_list ) );
189 -
190 227 }
191 228
192 229
193 230 /**
@@ -194,9 +231,11 @@
194 231 * Convert microtime (PHP<7.3) or hrtime (PHP>=7.3) to seconds
195 232 */
196 233 private function convert_2_seconds( $time ) {
197 234
198 - if ( empty( $time ) ) { return '0'; }
235 + if ( $time < 0 || empty( $time ) ) {
236 + return '0';
237 + }
199 238 if ( strpos( $time, '.' ) !== false ) {
200 239 // Looks like microtime
201 240 $time = number_format( $time, 6 );
202 241 } else {
@@ -211,9 +250,13 @@
211 250 * Check if a slug is from a plugin or theme.
212 251 */
213 252 private function plugin_or_theme( $script ) {
214 253
215 - $res = ['theme' => '', 'plugin' => '', 'script' => ''];
254 + $res = [
255 + 'theme' => '',
256 + 'plugin' => '',
257 + 'script' => ''
258 + ];
216 259
217 260 if ( preg_match("`^{$this->plugins_dir}[\\\/](?:(.+?)[\\\/]|([^\\\/]+\.php)$)`", $script, $slug ) ) {
218 261 if (! empty( $slug[1] ) ) {
219 262 $res['plugin'] = $slug[1];
@@ -275,15 +318,15 @@
275 318
276 319 /**
277 320 * Parse, filter and sort the data
278 321 */
279 - private function parse_ticks() {
322 + private function parse_calls() {
280 323
281 - $fh = fopen( $this->tmp_ticks, 'rb');
324 + $fh = fopen( $this->tmp_calls, 'rb');
282 325 if ( $fh === false ) {
283 326 $error = sprintf(
284 327 esc_html__('Cannot open file for reading: %s', 'code-profiler'),
285 - $this->tmp_ticks
328 + $this->tmp_calls
286 329 );
287 330 $this->return_error( $error );
288 331 }
289 332 while(! feof( $fh ) ) {
@@ -345,16 +388,8 @@
345 388 if (! isset( $this->plugins[ $slug['plugin'] ]['time'] ) ) {
346 389 $this->plugins[ $slug['plugin'] ]['time'] = 0;
347 390 }
348 391 }
349 -
350 - // Look for multiple copies of composer and warn the user
351 - if ( preg_match("`^{$this->plugins_dir}[\\\/](?:[^\\\/]+)[\\\/].+?[\\\/]composer[\\\/]autoload_real\.php`", $caller ) ) {
352 - // Save the slug first, we'll fetch the name later
353 - if (! isset( $this->composer[ $slug['plugin'] ] ) ) {
354 - $this->composer[ $slug['plugin'] ] = '';
355 - }
356 - }
357 392 }
358 393
359 394 // We don't have a slug: if there's an old record, update it
360 395 } else {
@@ -386,12 +421,12 @@
386 421 }
387 422 }
388 423
389 424 fclose( $fh );
390 - unlink( $this->tmp_ticks );
425 + unlink( $this->tmp_calls );
391 426 }
392 427
393 - /**
428 + /********************************************************************
394 429 * Save IO stats
395 430 */
396 431 private function save_iostats() {
397 432
@@ -422,9 +457,9 @@
422 457
423 458 /**
424 459 * Save Read/Write stats
425 460 */
426 - function save_diskio() {
461 + private function save_diskio() {
427 462
428 463 rename(
429 464 $this->tmp_diskio,
430 465 CODE_PROFILER_UPLOAD_DIR ."/{$this->microtime}.{$this->profile_name}.diskio.profile"
@@ -431,24 +466,65 @@
431 466 );
432 467 }
433 468
434 469
435 - /**
436 - * Save list of plugins using composer
437 - */
438 - function save_composer() {
470 + /**
471 + * External connections.
472 + */
473 + private function save_connections() {
439 474
440 - if (! empty( $this->composer ) ) {
441 - // Try to get the plugin's name
442 - foreach( $this->composer as $slug => $v ) {
443 - if ( isset( $this->plugins[ $slug ]['name'] ) ) {
444 - $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 + }
445 525 }
446 526 }
447 - file_put_contents(
448 - CODE_PROFILER_UPLOAD_DIR ."/{$this->microtime}.{$this->profile_name}.composer.profile",
449 - json_encode( $this->composer )
450 - );
451 527 }
452 528 }
453 529
454 530 }