1 /*
2    +----------------------------------------------------------------------+
3    | Xdebug                                                               |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 2002-2021 Derick Rethans                               |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 1.01 of the Xdebug license,   |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available at through the world-wide-web at                           |
10    | https://xdebug.org/license.php                                       |
11    | If you did not receive a copy of the Xdebug license and are unable   |
12    | to obtain it through the world-wide-web, please send a note to       |
13    | derick@xdebug.org so we can mail you a copy immediately.             |
14    +----------------------------------------------------------------------+
15  */
16 #include "php.h"
17 #include "ext/standard/php_string.h"
18 
19 #include "php_xdebug.h"
20 #include "tracing_private.h"
21 #include "trace_textual.h"
22 
23 #include "lib/lib_private.h"
24 #include "lib/var_export_line.h"
25 
26 extern ZEND_DECLARE_MODULE_GLOBALS(xdebug);
27 
xdebug_trace_textual_init(char * fname,zend_string * script_filename,long options)28 void *xdebug_trace_textual_init(char *fname, zend_string *script_filename, long options)
29 {
30 	xdebug_trace_textual_context *tmp_textual_context;
31 
32 	tmp_textual_context = xdmalloc(sizeof(xdebug_trace_textual_context));
33 	tmp_textual_context->trace_file = xdebug_trace_open_file(fname, script_filename, options);
34 
35 	return tmp_textual_context->trace_file ? tmp_textual_context : NULL;
36 }
37 
xdebug_trace_textual_deinit(void * ctxt)38 void xdebug_trace_textual_deinit(void *ctxt)
39 {
40 	xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt;
41 
42 	xdebug_file_close(context->trace_file);
43 	xdebug_file_dtor(context->trace_file);
44 	context->trace_file = NULL;
45 
46 	xdfree(context);
47 }
48 
xdebug_trace_textual_write_header(void * ctxt)49 void xdebug_trace_textual_write_header(void *ctxt)
50 {
51 	xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt;
52 	char *str_time;
53 
54 	str_time = xdebug_nanotime_to_chars(xdebug_get_nanotime(), 6);
55 	xdebug_file_printf(context->trace_file, "TRACE START [%s]\n", str_time);
56 	xdfree(str_time);
57 
58 	xdebug_file_flush(context->trace_file);
59 }
60 
xdebug_trace_textual_write_footer(void * ctxt)61 void xdebug_trace_textual_write_footer(void *ctxt)
62 {
63 	xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt;
64 	char     *str_time;
65 	uint64_t  nanotime;
66 	char     *tmp;
67 
68 	nanotime = xdebug_get_nanotime();
69 	tmp = xdebug_sprintf("%10.4F ", XDEBUG_SECONDS_SINCE_START(nanotime));
70 	xdebug_file_printf(context->trace_file, "%s", tmp);
71 	xdfree(tmp);
72 	xdebug_file_printf(context->trace_file, "%10zu", zend_memory_usage(0));
73 	xdebug_file_printf(context->trace_file, "\n");
74 
75 	str_time = xdebug_nanotime_to_chars(nanotime, 6);
76 	xdebug_file_printf(context->trace_file, "TRACE END   [%s]\n\n", str_time);
77 	xdfree(str_time);
78 
79 	xdebug_file_flush(context->trace_file);
80 }
81 
xdebug_trace_textual_get_filename(void * ctxt)82 char *xdebug_trace_textual_get_filename(void *ctxt)
83 {
84 	xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt;
85 
86 	return context->trace_file->name;
87 }
88 
add_single_value(xdebug_str * str,zval * zv)89 static void add_single_value(xdebug_str *str, zval *zv)
90 {
91 	xdebug_str *tmp_value = NULL;
92 
93 	tmp_value = xdebug_get_zval_value_line(zv, 0, NULL);
94 
95 	if (tmp_value) {
96 		xdebug_str_add_str(str, tmp_value);
97 		xdebug_str_free(tmp_value);
98 	} else {
99 		xdebug_str_add_literal(str, "???");
100 	}
101 }
102 
add_arguments(xdebug_str * line_entry,function_stack_entry * fse)103 static void add_arguments(xdebug_str *line_entry, function_stack_entry *fse)
104 {
105 	unsigned int j = 0; /* Counter */
106 	int c = 0; /* Comma flag */
107 	int variadic_opened = 0;
108 	int variadic_count  = 0;
109 	int sent_variables = fse->varc;
110 
111 	if (sent_variables > 0 && fse->var[sent_variables-1].is_variadic && Z_ISUNDEF(fse->var[sent_variables-1].data)) {
112 		sent_variables--;
113 	}
114 
115 	for (j = 0; j < sent_variables; j++) {
116 		if (c) {
117 			xdebug_str_add_literal(line_entry, ", ");
118 		} else {
119 			c = 1;
120 		}
121 
122 		if (fse->var[j].is_variadic) {
123 			xdebug_str_add_literal(line_entry, "...");
124 			variadic_opened = 1;
125 			c = 0;
126 		}
127 
128 		if (fse->var[j].name) {
129 			xdebug_str_addc(line_entry, '$');
130 			xdebug_str_add_zstr(line_entry, fse->var[j].name);
131 			if (variadic_opened && !fse->var[j].is_variadic) {
132 				xdebug_str_add_literal(line_entry, " => ");
133 			} else {
134 				xdebug_str_add_literal(line_entry, " = ");
135 			}
136 		}
137 
138 		if (fse->var[j].is_variadic) {
139 			xdebug_str_add_literal(line_entry, "variadic(");
140 			if (Z_ISUNDEF(fse->var[j].data)) {
141 				continue;
142 			}
143 			c = 1;
144 		}
145 
146 		if (variadic_opened && (!fse->var[j].name || fse->var[j].is_variadic)) {
147 			xdebug_str_add_fmt(line_entry, "%d => ", variadic_count++);
148 		}
149 
150 		if (!Z_ISUNDEF(fse->var[j].data)) {
151 			add_single_value(line_entry, &fse->var[j].data);
152 		} else {
153 			xdebug_str_add_literal(line_entry, "???");
154 		}
155 	}
156 
157 	if (variadic_opened) {
158 		xdebug_str_addc(line_entry, ')');
159 	}
160 }
161 
xdebug_trace_textual_function_entry(void * ctxt,function_stack_entry * fse,int function_nr)162 void xdebug_trace_textual_function_entry(void *ctxt, function_stack_entry *fse, int function_nr)
163 {
164 	xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt;
165 	unsigned int j = 0; /* Counter */
166 	char *tmp_name;
167 	xdebug_str str = XDEBUG_STR_INITIALIZER;
168 
169 	tmp_name = xdebug_show_fname(fse->function, XDEBUG_SHOW_FNAME_DEFAULT);
170 
171 	xdebug_str_add_fmt(&str, "%10.4F ", XDEBUG_SECONDS_SINCE_START(fse->nanotime));
172 	xdebug_str_add_fmt(&str, "%10lu ", fse->memory);
173 	for (j = 0; j < fse->level; j++) {
174 		xdebug_str_add_literal(&str, "  ");
175 	}
176 	xdebug_str_add_fmt(&str, "-> %s(", tmp_name);
177 
178 	xdfree(tmp_name);
179 
180 	add_arguments(&str, fse);
181 
182 	if (fse->include_filename) {
183 		if (fse->function.type == XFUNC_EVAL) {
184 			zend_string *escaped;
185 #if PHP_VERSION_ID >= 70300
186 			escaped = php_addcslashes(fse->include_filename, (char*) "'\\\0..\37", 6);
187 #else
188 			escaped = php_addcslashes(fse->include_filename, 0, (char*) "'\\\0..\37", 6);
189 #endif
190 			xdebug_str_addc(&str, '\'');
191 			xdebug_str_add_zstr(&str, escaped);
192 			xdebug_str_addc(&str, '\'');
193 			zend_string_release(escaped);
194 		} else {
195 			xdebug_str_add_zstr(&str, fse->include_filename);
196 		}
197 	}
198 
199 	xdebug_str_add_fmt(&str, ") %s:%d\n", ZSTR_VAL(fse->filename), fse->lineno);
200 
201 	xdebug_file_printf(context->trace_file, "%s", str.d);
202 	xdebug_file_flush(context->trace_file);
203 
204 	xdfree(str.d);
205 }
206 
207 /* Used for normal return values, and generator return values */
xdebug_return_trace_stack_common(xdebug_str * str,function_stack_entry * fse)208 static void xdebug_return_trace_stack_common(xdebug_str *str, function_stack_entry *fse)
209 {
210 	unsigned int j = 0; /* Counter */
211 
212 	xdebug_str_add_fmt(str, "%10.4F ", XDEBUG_SECONDS_SINCE_START(xdebug_get_nanotime()));
213 	xdebug_str_add_fmt(str, "%10lu ", zend_memory_usage(0));
214 
215 	for (j = 0; j < fse->level; j++) {
216 		xdebug_str_add_literal(str, "  ");
217 	}
218 	xdebug_str_add_literal(str, " >=> ");
219 }
220 
221 
xdebug_trace_textual_function_return_value(void * ctxt,function_stack_entry * fse,int function_nr,zval * return_value)222 void xdebug_trace_textual_function_return_value(void *ctxt, function_stack_entry *fse, int function_nr, zval *return_value)
223 {
224 	xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt;
225 	xdebug_str                    str = XDEBUG_STR_INITIALIZER;
226 	xdebug_str                   *tmp_value;
227 
228 	xdebug_return_trace_stack_common(&str, fse);
229 
230 	tmp_value = xdebug_get_zval_value_line(return_value, 0, NULL);
231 	if (tmp_value) {
232 		xdebug_str_add_str(&str, tmp_value);
233 		xdebug_str_free(tmp_value);
234 	}
235 	xdebug_str_addc(&str, '\n');
236 
237 	xdebug_file_printf(context->trace_file, "%s", str.d);
238 	xdebug_file_flush(context->trace_file);
239 
240 	xdebug_str_destroy(&str);
241 }
242 
xdebug_trace_textual_generator_return_value(void * ctxt,function_stack_entry * fse,int function_nr,zend_generator * generator)243 void xdebug_trace_textual_generator_return_value(void *ctxt, function_stack_entry *fse, int function_nr, zend_generator *generator)
244 {
245 	xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt;
246 	xdebug_str                    str = XDEBUG_STR_INITIALIZER;
247 	xdebug_str                   *tmp_value = NULL;
248 
249 	if (! (generator->flags & ZEND_GENERATOR_CURRENTLY_RUNNING)) {
250 		return;
251 	}
252 
253 	if (generator->execute_data == NULL) {
254 		return;
255 	}
256 
257 	/* Generator key */
258 	tmp_value = xdebug_get_zval_value_line(&generator->key, 0, NULL);
259 	if (!tmp_value) {
260 		return;
261 	}
262 
263 	xdebug_return_trace_stack_common(&str, fse);
264 
265 	xdebug_str_addc(&str, '(');
266 	xdebug_str_add_str(&str, tmp_value);
267 	xdebug_str_add_literal(&str, " => ");
268 
269 	tmp_value = xdebug_get_zval_value_line(&generator->value, 0, NULL);
270 	if (tmp_value) {
271 		xdebug_str_add_str(&str, tmp_value);
272 		xdebug_str_free(tmp_value);
273 	}
274 
275 	xdebug_str_add_literal(&str, ")\n");
276 
277 	xdebug_file_printf(context->trace_file, "%s", str.d);
278 	xdebug_file_flush(context->trace_file);
279 
280 	xdebug_str_destroy(&str);
281 }
282 
xdebug_trace_textual_assignment(void * ctxt,function_stack_entry * fse,char * full_varname,zval * retval,char * right_full_varname,const char * op,char * filename,int lineno)283 void xdebug_trace_textual_assignment(void *ctxt, function_stack_entry *fse, char *full_varname, zval *retval, char *right_full_varname, const char *op, char *filename, int lineno)
284 {
285 	xdebug_trace_textual_context *context = (xdebug_trace_textual_context*) ctxt;
286 	unsigned int                  j = 0;
287 	xdebug_str                    str = XDEBUG_STR_INITIALIZER;
288 	xdebug_str                   *tmp_value;
289 
290 	xdebug_str_add_literal(&str, "                    ");
291 	for (j = 0; j <= fse->level; j++) {
292 		xdebug_str_add_literal(&str, "  ");
293 	}
294 	xdebug_str_add_literal(&str, "   => ");
295 
296 	xdebug_str_add(&str, full_varname, 0);
297 
298 	if (op[0] != '\0' ) { /* pre/post inc/dec ops are special */
299 		xdebug_str_addc(&str, ' ');
300 		xdebug_str_add(&str, op, 0);
301 		xdebug_str_addc(&str, ' ');
302 
303 		if (right_full_varname) {
304 			xdebug_str_add(&str, right_full_varname, 0);
305 		} else {
306 			tmp_value = xdebug_get_zval_value_line(retval, 0, NULL);
307 
308 			if (tmp_value) {
309 				xdebug_str_add_str(&str, tmp_value);
310 				xdebug_str_free(tmp_value);
311 			} else {
312 				xdebug_str_add_literal(&str, "NULL");
313 			}
314 		}
315 
316 	}
317 	xdebug_str_add_fmt(&str, " %s:%d\n", filename, lineno);
318 
319 	xdebug_file_printf(context->trace_file, "%s", str.d);
320 	xdebug_file_flush(context->trace_file);
321 
322 	xdfree(str.d);
323 }
324 
325 xdebug_trace_handler_t xdebug_trace_handler_textual =
326 {
327 	xdebug_trace_textual_init,
328 	xdebug_trace_textual_deinit,
329 	xdebug_trace_textual_write_header,
330 	xdebug_trace_textual_write_footer,
331 	xdebug_trace_textual_get_filename,
332 	xdebug_trace_textual_function_entry,
333 	NULL /*xdebug_trace_textual_function_exit */,
334 	xdebug_trace_textual_function_return_value,
335 	xdebug_trace_textual_generator_return_value,
336 	xdebug_trace_textual_assignment
337 };
338