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