xref: /PHP-5.4/ext/mysqlnd/mysqlnd_bt.c (revision c0d060f5)
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, &copy, &use_copy); \
48 		TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
49 		zval_dtor(&copy); \
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), &copy, &use_copy); \
58 		TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
59 		zval_dtor(&copy); \
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, &copy, &use_copy); \
73 			TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
74 			zval_dtor(&copy); \
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