PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.9.2
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.9.2
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
code-profiler / lib / class-profiler.php

class-profiler.php in Code Profiler – WordPress Performance Profiling and Debugging Made Easy 1.9.2, at lib/class-profiler.php

282 lines 7.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 +=====================================================================+
4 | ____ _ ____ __ _ _ |
5 | / ___|___ __| | ___ | _ \ _ __ ___ / _(_) | ___ _ __ |
6 | | | / _ \ / _` |/ _ \ | |_) | '__/ _ \| |_| | |/ _ \ '__| |
7 | | |__| (_) | (_| | __/ | __/| | | (_) | _| | | __/ | |
8 | \____\___/ \__,_|\___| |_| |_| \___/|_| |_|_|\___|_| |
9 | |
10 | (c) Jerome Bruandet ~ https://nintechnet.com/codeprofiler/ |
11 +=====================================================================+
12 */
13
14 if (! defined('ABSPATH') ) { die('Forbidden'); }
15
16 // =====================================================================
17
18 class CodeProfiler_Profiler {
19
20 static $tick_list;
21 static $buffer;
22 static $metrics;
23 static $fh;
24 static $connections_list = [];
25 static $connections_start;
26 static $exclusions = [];
27 private $tmp_iostats;
28 private $tmp_summary;
29 private $tmp_diskio;
30 private $tmp_calls;
31 private $tmp_connections;
32 private $tmp_rerun;
33
34 /**
35 * Initialize
36 */
37 public function __construct() {
38
39 $microtime = sanitize_file_name( $_REQUEST['CODE_PROFILER_ON'] );
40
41 $this->tmp_summary = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
42 CODE_PROFILER_TMP_SUMMARY_LOG;
43 $this->tmp_iostats = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
44 CODE_PROFILER_TMP_IOSTATS_LOG;
45 $this->tmp_calls = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
46 CODE_PROFILER_TMP_CALLS_LOG;
47 $this->tmp_diskio = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
48 CODE_PROFILER_TMP_DISKIO_LOG;
49 $this->tmp_connections = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
50 CODE_PROFILER_TMP_CONNECTIONS_LOG;
51 $this->tmp_rerun = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
52 CODE_PROFILER_TMP_RERUN_LOG;
53
54 if ( function_exists('hrtime') ) {
55 // PHP >=7.3
56 self::$metrics = 'hrtime';
57 } else {
58 self::$metrics = 'microtime';
59 }
60
61 // Select theme (stylesheet::template)
62 if (! empty( $_SERVER['HTTP_THEME'] ) ) {
63
64 $theme = explode('::', $_SERVER['HTTP_THEME'] );
65 if (! empty( $theme[1] ) ) {
66 define('CODE_PROFILER_STYLESHEET', sanitize_file_name( $theme[0] ) );
67 define('CODE_PROFILER_TEMPLATE', sanitize_file_name( $theme[1] ) );
68
69 if ( is_file( WP_CONTENT_DIR .'/themes/'. CODE_PROFILER_STYLESHEET .'/style.css' ) ) {
70
71 add_filter('pre_option_template', function () {
72 return CODE_PROFILER_TEMPLATE;
73 }, 1000 );
74
75 add_filter('pre_option_stylesheet', function () {
76 return CODE_PROFILER_STYLESHEET;
77 }, 1000 );
78
79 code_profiler_log_debug(
80 sprintf(
81 // We cannot load translation here.
82 'Setting theme to %s',
83 CODE_PROFILER_STYLESHEET
84 )
85 );
86 } else {
87 code_profiler_log_error(
88 sprintf(
89 // We cannot load translation here.
90 'Unable to switch theme, %s not found',
91 CODE_PROFILER_STYLESHEET
92 )
93 );
94 }
95 }
96 }
97
98 $cp_options = get_option('code-profiler');
99 if ( empty( $cp_options['accuracy'] ) ) {
100 define('CODE_PROFILER_TICKS', 1 );
101 } else {
102 define('CODE_PROFILER_TICKS', (int) $cp_options['accuracy'] );
103 }
104 define('CODE_PROFILER_LENGTH', 17 + strlen( (string) CODE_PROFILER_TICKS ) );
105
106 if ( empty( $cp_options['buffer'] ) ||
107 ! preg_match('/^(?:[1-9]|10)$/', $cp_options['buffer'] ) ) {
108
109 $recommended = code_profiler_suggested_memory();
110 self::$buffer = (int) $recommended * 1000000;
111 } else {
112 self::$buffer = $cp_options['buffer'] * 1000000;
113 }
114 code_profiler_log_debug(
115 sprintf(
116 // We cannot load translation here.
117 'Setting size of memory buffer to %sMB',
118 self::$buffer / 1000000
119 )
120 );
121
122 // File & folder exclusions
123 if ( isset( $cp_options['exclusions'] ) ) {
124 self::$exclusions = json_decode( $cp_options['exclusions'] );
125 }
126
127 add_filter('pre_http_request', [ $this, 'pre_http_request'], 10000, 3 );
128 add_action('http_api_debug', [ $this, 'http_api_debug'], 10000, 5 );
129 register_shutdown_function( [ $this, 'code_profiler_shutdown'] );
130 code_profiler_log_debug(
131 // We cannot load translation here.
132 'Starting profiler'
133 );
134 require 'class-stream.php';
135
136 // Clear the temporary log because the buffer will be appended to it
137 if ( file_exists( $this->tmp_calls ) ) {
138 unlink( $this->tmp_calls );
139 }
140 self::$fh = fopen( $this->tmp_calls, 'wb');
141
142 CodeProfiler_Stream::start();
143 register_tick_function( array( $this, 'code_profiler_tick_handler') );
144 }
145
146 /**
147 * Save data and check for potential PHP errors.
148 */
149 public function code_profiler_shutdown() {
150
151 CodeProfiler_Stream::stop();
152
153 fclose( self::$fh );
154
155 // Summary file (metadata)
156 $summary = [];
157 if ( is_file( $this->tmp_rerun ) ) {
158 $summary['rerun'] = json_decode( file_get_contents( $this->tmp_rerun ), true );
159 unlink( $this->tmp_rerun );
160 }
161 $summary['memory'] = memory_get_peak_usage();
162 $summary['queries'] = get_num_queries() - 2;
163 $summary['precision'] = CODE_PROFILER_TICKS;
164 file_put_contents( $this->tmp_summary, json_encode( $summary ) );
165
166 // Catch potential error
167 $e = error_get_last();
168 if ( isset( $e['type'] ) && $e['type'] === E_ERROR ) {
169 $err = str_replace( "\n", ' - ', $e['message'] );
170 code_profiler_log_error( sprintf(
171 /* Translators: Error message, line number and script */
172 esc_html__('Error: E_ERROR (%s - line %s in %s)', 'code-profiler'),
173 esc_html( $err ),
174 (int) $e['line'],
175 esc_html( $e['file'] )
176 ));
177 }
178
179 $err_msg = esc_html__('Cannot open file for writting: %s', 'code-profiler');
180 $res = file_put_contents( $this->tmp_iostats, json_encode( CodeProfiler_Stream::$io_stats ) );
181 if ( $res === false ) {
182 $msg = sprintf( $err_msg, $this->tmp_iostats );
183 code_profiler_log_error( $msg );
184 }
185 $res = file_put_contents( $this->tmp_calls, self::$tick_list, FILE_APPEND );
186 if ( $res === false ) {
187 $msg = sprintf( $err_msg, $this->tmp_calls );
188 code_profiler_log_error( $msg );
189 }
190 $res = file_put_contents(
191 $this->tmp_diskio, "read\t". CodeProfiler_Stream::$io_read ."\nwrite\t".
192 CodeProfiler_Stream::$io_write
193 );
194 if (! empty( self::$connections_list ) ) {
195 file_put_contents( $this->tmp_connections, json_encode( self::$connections_list) );
196 }
197 }
198
199 /**
200 * Our tick handler.
201 */
202 public function code_profiler_tick_handler() {
203
204 $start = $this->time();
205
206 $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
207 if ( isset( $backtrace[1] ) ) {
208 $el = $backtrace[1];
209 } else {
210 $el = $backtrace[0];
211 }
212
213 if ( empty( $el['file'] ) ) {
214 if ( isset( $backtrace[0]['file'] ) ) {
215 $el['file'] = $backtrace[0]['file'];
216 } else {
217 return;
218 }
219 }
220
221 self::$tick_list .= "{$el['file']}\t";
222 if ( isset( $backtrace[0]['file'][0] ) ) {
223 self::$tick_list .= $backtrace[0]['file'];
224 } else {
225 self::$tick_list .= '-';
226 }
227 $backtrace = null;
228
229 // Buffer can grow *very* big hence we flush it every 10MB by default
230 if ( strlen( self::$tick_list ) > self::$buffer ) {
231 CodeProfiler_Stream::stop();
232 fwrite( self::$fh, self::$tick_list ."\t{$start}\t". $this->time() ."\n" );
233 CodeProfiler_Stream::start();
234 self::$tick_list = '';
235
236 } else {
237 self::$tick_list .="\t{$start}\t". $this->time() ."\n";
238 }
239 }
240
241
242 /**
243 * HTTP API request.
244 */
245 public function pre_http_request( $preempt, $r, $url ) {
246
247 if ( empty( $url ) ) {
248 return false;
249 }
250 $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS );
251 self::$connections_start = $this->time();
252 self::$connections_list[ self::$connections_start ]['bt'] = $backtrace;
253 return false;
254 }
255
256
257 /**
258 * HTTP API response.
259 */
260 public function http_api_debug( $response, $context, $class, $parsed_args, $url ) {
261
262 self::$connections_list[ self::$connections_start ]['stop'] = $this->time();
263 }
264
265 /**
266 * Return time
267 */
268 private function time() {
269
270 if ( self::$metrics == 'hrtime') {
271 return hrtime( true );
272 } else {
273 return microtime( true );
274 }
275 }
276 }
277
278 new CodeProfiler_Profiler();
279
280 // =====================================================================
281 // EOF
282