1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2006-2014 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Georg Richter <georg@mysql.com> |
16 | Andrey Hristov <andrey@mysql.com> |
17 | Ulf Wendel <uwendel@mysql.com> |
18 +----------------------------------------------------------------------+
19 */
20
21 /* $Id: mysqlnd_debug.c 309303 2011-03-16 12:42:59Z andrey $ */
22
23 #include "php.h"
24 #include "Zend/zend_builtin_functions.h"
25
26 /* Follows code borrowed from zend_builtin_functions.c because the functions there are static */
27
28 #if MYSQLND_UNICODE
29 /* {{{ gettraceasstring() macros */
30 #define TRACE_APPEND_CHR(chr) \
31 *str = (char*)erealloc(*str, *len + 1 + 1); \
32 (*str)[(*len)++] = chr
33
34 #define TRACE_APPEND_STRL(val, vallen) \
35 { \
36 int l = vallen; \
37 *str = (char*)erealloc(*str, *len + l + 1); \
38 memcpy((*str) + *len, val, l); \
39 *len += l; \
40 }
41
42 #define TRACE_APPEND_USTRL(val, vallen) \
43 { \
44 zval tmp, copy; \
45 int use_copy; \
46 ZVAL_UNICODEL(&tmp, val, vallen, 1); \
47 zend_make_printable_zval(&tmp, ©, &use_copy); \
48 TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
49 zval_dtor(©); \
50 zval_dtor(&tmp); \
51 }
52
53 #define TRACE_APPEND_ZVAL(zv) \
54 if (Z_TYPE_P((zv)) == IS_UNICODE) { \
55 zval copy; \
56 int use_copy; \
57 zend_make_printable_zval((zv), ©, &use_copy); \
58 TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
59 zval_dtor(©); \
60 } else { \
61 TRACE_APPEND_STRL(Z_STRVAL_P((zv)), Z_STRLEN_P((zv))); \
62 }
63
64 #define TRACE_APPEND_STR(val) \
65 TRACE_APPEND_STRL(val, sizeof(val)-1)
66
67 #define TRACE_APPEND_KEY(key) \
68 if (zend_ascii_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
69 if (Z_TYPE_PP(tmp) == IS_UNICODE) { \
70 zval copy; \
71 int use_copy; \
72 zend_make_printable_zval(*tmp, ©, &use_copy); \
73 TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
74 zval_dtor(©); \
75 } else { \
76 TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \
77 } \
78 }
79 /* }}} */
80
81
82 /* {{{ mysqlnd_build_trace_args */
mysqlnd_build_trace_args(zval ** arg TSRMLS_DC,int num_args,va_list args,zend_hash_key * hash_key)83 static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
84 {
85 char **str;
86 int *len;
87
88 str = va_arg(args, char**);
89 len = va_arg(args, int*);
90
91 /* the trivial way would be to do:
92 * conver_to_string_ex(arg);
93 * append it and kill the now tmp arg.
94 * but that could cause some E_NOTICE and also damn long lines.
95 */
96
97 switch (Z_TYPE_PP(arg)) {
98 case IS_NULL:
99 TRACE_APPEND_STR("NULL, ");
100 break;
101 case IS_STRING: {
102 int l_added;
103 TRACE_APPEND_CHR('\'');
104 if (Z_STRLEN_PP(arg) > 15) {
105 TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
106 TRACE_APPEND_STR("...', ");
107 l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
108 } else {
109 l_added = Z_STRLEN_PP(arg);
110 TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
111 TRACE_APPEND_STR("', ");
112 l_added += 3 + 1;
113 }
114 while (--l_added) {
115 if ((unsigned char)(*str)[*len - l_added] < 32) {
116 (*str)[*len - l_added] = '?';
117 }
118 }
119 break;
120 }
121 case IS_UNICODE: {
122 int l_added;
123
124 /*
125 * We do not want to apply current error mode here, since
126 * zend_make_printable_zval() uses output encoding converter.
127 * Temporarily set output encoding converter to escape offending
128 * chars with \uXXXX notation.
129 */
130 zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, ZEND_CONV_ERROR_ESCAPE_JAVA);
131 TRACE_APPEND_CHR('\'');
132 if (Z_USTRLEN_PP(arg) > 15) {
133 TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), 15);
134 TRACE_APPEND_STR("...', ");
135 l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
136 } else {
137 l_added = Z_USTRLEN_PP(arg);
138 TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), l_added);
139 TRACE_APPEND_STR("', ");
140 l_added += 3 + 1;
141 }
142 /*
143 * Reset output encoding converter error mode.
144 */
145 zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, UG(from_error_mode));
146 while (--l_added) {
147 if ((unsigned char)(*str)[*len - l_added] < 32) {
148 (*str)[*len - l_added] = '?';
149 }
150 }
151 break;
152 }
153 case IS_BOOL:
154 if (Z_LVAL_PP(arg)) {
155 TRACE_APPEND_STR("true, ");
156 } else {
157 TRACE_APPEND_STR("false, ");
158 }
159 break;
160 case IS_RESOURCE:
161 TRACE_APPEND_STR("Resource id #");
162 /* break; */
163 case IS_LONG: {
164 long lval = Z_LVAL_PP(arg);
165 char s_tmp[MAX_LENGTH_OF_LONG + 1];
166 int l_tmp = zend_sprintf(s_tmp, "%ld", lval); /* SAFE */
167 TRACE_APPEND_STRL(s_tmp, l_tmp);
168 TRACE_APPEND_STR(", ");
169 break;
170 }
171 case IS_DOUBLE: {
172 double dval = Z_DVAL_PP(arg);
173 char *s_tmp;
174 int l_tmp;
175
176 s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
177 l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval); /* SAFE */
178 TRACE_APPEND_STRL(s_tmp, l_tmp);
179 /* %G already handles removing trailing zeros from the fractional part, yay */
180 efree(s_tmp);
181 TRACE_APPEND_STR(", ");
182 break;
183 }
184 case IS_ARRAY:
185 TRACE_APPEND_STR("Array, ");
186 break;
187 case IS_OBJECT: {
188 zval tmp;
189 zstr class_name;
190 zend_uint class_name_len;
191 int dup;
192
193 TRACE_APPEND_STR("Object(");
194
195 dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
196
197 ZVAL_UNICODEL(&tmp, class_name.u, class_name_len, 1);
198 convert_to_string_with_converter(&tmp, ZEND_U_CONVERTER(UG(output_encoding_conv)));
199 TRACE_APPEND_STRL(Z_STRVAL(tmp), Z_STRLEN(tmp));
200 zval_dtor(&tmp);
201
202 if(!dup) {
203 efree(class_name.v);
204 }
205
206 TRACE_APPEND_STR("), ");
207 break;
208 }
209 default:
210 break;
211 }
212 return ZEND_HASH_APPLY_KEEP;
213 }
214 /* }}} */
215
216
mysqlnd_build_trace_string(zval ** frame TSRMLS_DC,int num_args,va_list args,zend_hash_key * hash_key)217 static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
218 {
219 char *s_tmp, **str;
220 int *len, *num;
221 long line;
222 HashTable *ht = Z_ARRVAL_PP(frame);
223 zval **file, **tmp;
224 uint * level;
225
226 level = va_arg(args, uint *);
227 str = va_arg(args, char**);
228 len = va_arg(args, int*);
229 num = va_arg(args, int*);
230
231 if (!*level) {
232 return ZEND_HASH_APPLY_KEEP;
233 }
234 --*level;
235
236 s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
237 sprintf(s_tmp, "#%d ", (*num)++);
238 TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
239 efree(s_tmp);
240 if (zend_ascii_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
241 if (zend_ascii_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
242 line = Z_LVAL_PP(tmp);
243 } else {
244 line = 0;
245 }
246 TRACE_APPEND_ZVAL(*file);
247 s_tmp = emalloc(MAX_LENGTH_OF_LONG + 2 + 1);
248 sprintf(s_tmp, "(%ld): ", line);
249 TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
250 efree(s_tmp);
251 } else {
252 TRACE_APPEND_STR("[internal function]: ");
253 }
254 TRACE_APPEND_KEY("class");
255 TRACE_APPEND_KEY("type");
256 TRACE_APPEND_KEY("function");
257 TRACE_APPEND_CHR('(');
258 if (zend_ascii_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
259 int last_len = *len;
260 zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
261 if (last_len != *len) {
262 *len -= 2; /* remove last ', ' */
263 }
264 }
265 TRACE_APPEND_STR(")\n");
266 return ZEND_HASH_APPLY_KEEP;
267 }
268 /* }}} */
269
270
271 #else /* PHP 5*/
272
273
274 /* {{{ gettraceasstring() macros */
275 #define TRACE_APPEND_CHR(chr) \
276 *str = (char*)erealloc(*str, *len + 1 + 1); \
277 (*str)[(*len)++] = chr
278
279 #define TRACE_APPEND_STRL(val, vallen) \
280 { \
281 int l = vallen; \
282 *str = (char*)erealloc(*str, *len + l + 1); \
283 memcpy((*str) + *len, val, l); \
284 *len += l; \
285 }
286
287 #define TRACE_APPEND_STR(val) \
288 TRACE_APPEND_STRL(val, sizeof(val)-1)
289
290 #define TRACE_APPEND_KEY(key) \
291 if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
292 TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); \
293 }
294
295 /* }}} */
296
297
mysqlnd_build_trace_args(zval ** arg TSRMLS_DC,int num_args,va_list args,zend_hash_key * hash_key)298 static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
299 {
300 char **str;
301 int *len;
302
303 str = va_arg(args, char**);
304 len = va_arg(args, int*);
305
306 /* the trivial way would be to do:
307 * conver_to_string_ex(arg);
308 * append it and kill the now tmp arg.
309 * but that could cause some E_NOTICE and also damn long lines.
310 */
311
312 switch (Z_TYPE_PP(arg)) {
313 case IS_NULL:
314 TRACE_APPEND_STR("NULL, ");
315 break;
316 case IS_STRING: {
317 int l_added;
318 TRACE_APPEND_CHR('\'');
319 if (Z_STRLEN_PP(arg) > 15) {
320 TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
321 TRACE_APPEND_STR("...', ");
322 l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
323 } else {
324 l_added = Z_STRLEN_PP(arg);
325 TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
326 TRACE_APPEND_STR("', ");
327 l_added += 3 + 1;
328 }
329 while (--l_added) {
330 if ((*str)[*len - l_added] < 32) {
331 (*str)[*len - l_added] = '?';
332 }
333 }
334 break;
335 }
336 case IS_BOOL:
337 if (Z_LVAL_PP(arg)) {
338 TRACE_APPEND_STR("true, ");
339 } else {
340 TRACE_APPEND_STR("false, ");
341 }
342 break;
343 case IS_RESOURCE:
344 TRACE_APPEND_STR("Resource id #");
345 /* break; */
346 case IS_LONG: {
347 long lval = Z_LVAL_PP(arg);
348 char s_tmp[MAX_LENGTH_OF_LONG + 1];
349 int l_tmp = zend_sprintf(s_tmp, "%ld", lval); /* SAFE */
350 TRACE_APPEND_STRL(s_tmp, l_tmp);
351 TRACE_APPEND_STR(", ");
352 break;
353 }
354 case IS_DOUBLE: {
355 double dval = Z_DVAL_PP(arg);
356 char *s_tmp;
357 int l_tmp;
358
359 s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
360 l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval); /* SAFE */
361 TRACE_APPEND_STRL(s_tmp, l_tmp);
362 /* %G already handles removing trailing zeros from the fractional part, yay */
363 efree(s_tmp);
364 TRACE_APPEND_STR(", ");
365 break;
366 }
367 case IS_ARRAY:
368 TRACE_APPEND_STR("Array, ");
369 break;
370 case IS_OBJECT: {
371 char *class_name;
372 zend_uint class_name_len;
373 int dupl;
374
375 TRACE_APPEND_STR("Object(");
376
377 dupl = zend_get_object_classname(*arg, (const char **)&class_name, &class_name_len TSRMLS_CC);
378
379 TRACE_APPEND_STRL(class_name, class_name_len);
380 if (!dupl) {
381 efree(class_name);
382 }
383
384 TRACE_APPEND_STR("), ");
385 break;
386 }
387 default:
388 break;
389 }
390 return ZEND_HASH_APPLY_KEEP;
391 }
392 /* }}} */
393
mysqlnd_build_trace_string(zval ** frame TSRMLS_DC,int num_args,va_list args,zend_hash_key * hash_key)394 static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
395 {
396 char *s_tmp, **str;
397 int *len, *num;
398 long line;
399 HashTable *ht = Z_ARRVAL_PP(frame);
400 zval **file, **tmp;
401 uint * level;
402
403 level = va_arg(args, uint *);
404 str = va_arg(args, char**);
405 len = va_arg(args, int*);
406 num = va_arg(args, int*);
407
408 if (!*level) {
409 return ZEND_HASH_APPLY_KEEP;
410 }
411 --*level;
412
413 s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
414 sprintf(s_tmp, "#%d ", (*num)++);
415 TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
416 efree(s_tmp);
417 if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
418 if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
419 line = Z_LVAL_PP(tmp);
420 } else {
421 line = 0;
422 }
423 s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1);
424 sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line);
425 TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
426 efree(s_tmp);
427 } else {
428 TRACE_APPEND_STR("[internal function]: ");
429 }
430 TRACE_APPEND_KEY("class");
431 TRACE_APPEND_KEY("type");
432 TRACE_APPEND_KEY("function");
433 TRACE_APPEND_CHR('(');
434 if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
435 int last_len = *len;
436 zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
437 if (last_len != *len) {
438 *len -= 2; /* remove last ', ' */
439 }
440 }
441 TRACE_APPEND_STR(")\n");
442 return ZEND_HASH_APPLY_KEEP;
443 }
444 /* }}} */
445 #endif
446
447
mysqlnd_get_backtrace(uint max_levels,size_t * length TSRMLS_DC)448 PHPAPI char * mysqlnd_get_backtrace(uint max_levels, size_t * length TSRMLS_DC)
449 {
450 zval *trace;
451 char *res = estrdup(""), **str = &res, *s_tmp;
452 int res_len = 0, *len = &res_len, num = 0;
453 if (max_levels == 0) {
454 max_levels = 99999;
455 }
456
457 MAKE_STD_ZVAL(trace);
458 zend_fetch_debug_backtrace(trace, 0, 0, 0 TSRMLS_CC);
459
460 zend_hash_apply_with_arguments(Z_ARRVAL_P(trace) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_string, 4, &max_levels, str, len, &num);
461 zval_ptr_dtor(&trace);
462
463 if (max_levels) {
464 s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1);
465 sprintf(s_tmp, "#%d {main}", num);
466 TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
467 efree(s_tmp);
468 }
469
470 res[res_len] = '\0';
471 *length = res_len;
472
473 return res;
474 }
475
476
477 /*
478 * Local variables:
479 * tab-width: 4
480 * c-basic-offset: 4
481 * End:
482 * vim600: noet sw=4 ts=4 fdm=marker
483 * vim<600: noet sw=4 ts=4
484 */
485