diff options
Diffstat (limited to 'ext/mysqlnd/mysqlnd_bt.c')
| -rw-r--r-- | ext/mysqlnd/mysqlnd_bt.c | 483 | 
1 files changed, 483 insertions, 0 deletions
diff --git a/ext/mysqlnd/mysqlnd_bt.c b/ext/mysqlnd/mysqlnd_bt.c new file mode 100644 index 0000000000..4863012409 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_bt.c @@ -0,0 +1,483 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 5                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2011 The PHP Group                                | +  +----------------------------------------------------------------------+ +  | This source file is subject to version 3.01 of the PHP license,      | +  | that is bundled with this package in the file LICENSE, and is        | +  | available through the world-wide-web at the following url:           | +  | http://www.php.net/license/3_01.txt                                  | +  | If you did not receive a copy of the PHP license and are unable to   | +  | obtain it through the world-wide-web, please send a note to          | +  | license@php.net so we can mail you a copy immediately.               | +  +----------------------------------------------------------------------+ +  | Authors: Georg Richter <georg@mysql.com>                             | +  |          Andrey Hristov <andrey@mysql.com>                           | +  |          Ulf Wendel <uwendel@mysql.com>                              | +  +----------------------------------------------------------------------+ +*/ + +/* $Id: mysqlnd_debug.c 309303 2011-03-16 12:42:59Z andrey $ */ + +#include "php.h" + +/* Follows code borrowed from zend_builtin_functions.c because the functions there are static */ + +#if MYSQLND_UNICODE +/* {{{ gettraceasstring() macros */ +#define TRACE_APPEND_CHR(chr)                                            \ +	*str = (char*)erealloc(*str, *len + 1 + 1);                          \ +	(*str)[(*len)++] = chr + +#define TRACE_APPEND_STRL(val, vallen)                                   \ +	{                                                                    \ +		int l = vallen;                                                  \ +		*str = (char*)erealloc(*str, *len + l + 1);                      \ +		memcpy((*str) + *len, val, l);                                   \ +		*len += l;                                                       \ +	} + +#define TRACE_APPEND_USTRL(val, vallen) \ +	{ \ +		zval tmp, copy; \ +		int use_copy; \ +		ZVAL_UNICODEL(&tmp, val, vallen, 1); \ +		zend_make_printable_zval(&tmp, ©, &use_copy); \ +		TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \ +		zval_dtor(©); \ +		zval_dtor(&tmp); \ +	} + +#define TRACE_APPEND_ZVAL(zv) \ +	if (Z_TYPE_P((zv)) == IS_UNICODE) { \ +		zval copy; \ +		int use_copy; \ +		zend_make_printable_zval((zv), ©, &use_copy); \ +		TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \ +		zval_dtor(©); \ +	} else { \ +		TRACE_APPEND_STRL(Z_STRVAL_P((zv)), Z_STRLEN_P((zv))); \ +	} + +#define TRACE_APPEND_STR(val)                                            \ +	TRACE_APPEND_STRL(val, sizeof(val)-1) + +#define TRACE_APPEND_KEY(key)                                            \ +	if (zend_ascii_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \ +		if (Z_TYPE_PP(tmp) == IS_UNICODE) { \ +			zval copy; \ +			int use_copy; \ +			zend_make_printable_zval(*tmp, ©, &use_copy); \ +			TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \ +			zval_dtor(©); \ +		} else { \ +		TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));           \ +		} \ +	} +/* }}} */ + + +/* {{{ mysqlnd_build_trace_args */ +static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) +{ +	char **str; +	int *len; + +	str = va_arg(args, char**); +	len = va_arg(args, int*); + +	/* the trivial way would be to do: +	 * conver_to_string_ex(arg); +	 * append it and kill the now tmp arg. +	 * but that could cause some E_NOTICE and also damn long lines. +	 */ + +	switch (Z_TYPE_PP(arg)) { +		case IS_NULL: +			TRACE_APPEND_STR("NULL, "); +			break; +		case IS_STRING: { +			int l_added; +			TRACE_APPEND_CHR('\''); +			if (Z_STRLEN_PP(arg) > 15) { +				TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15); +				TRACE_APPEND_STR("...', "); +				l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */ +			} else { +				l_added = Z_STRLEN_PP(arg); +				TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added); +				TRACE_APPEND_STR("', "); +				l_added += 3 + 1; +			} +			while (--l_added) { +				if ((unsigned char)(*str)[*len - l_added] < 32) { +					(*str)[*len - l_added] = '?'; +				} +			} +			break; +		} +		case IS_UNICODE: { +			int l_added; + +			/* +			 * We do not want to apply current error mode here, since +			 * zend_make_printable_zval() uses output encoding converter. +			 * Temporarily set output encoding converter to escape offending +			 * chars with \uXXXX notation. +			 */ +			zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, ZEND_CONV_ERROR_ESCAPE_JAVA); +			TRACE_APPEND_CHR('\''); +			if (Z_USTRLEN_PP(arg) > 15) { +				TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), 15); +				TRACE_APPEND_STR("...', "); +				l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */ +			} else { +				l_added = Z_USTRLEN_PP(arg); +				TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), l_added); +				TRACE_APPEND_STR("', "); +				l_added += 3 + 1; +			} +			/* +			 * Reset output encoding converter error mode. +			 */ +			zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, UG(from_error_mode)); +			while (--l_added) { +				if ((unsigned char)(*str)[*len - l_added] < 32) { +					(*str)[*len - l_added] = '?'; +				} +			} +			break; +		} +		case IS_BOOL: +			if (Z_LVAL_PP(arg)) { +				TRACE_APPEND_STR("true, "); +			} else { +				TRACE_APPEND_STR("false, "); +			} +			break; +		case IS_RESOURCE: +			TRACE_APPEND_STR("Resource id #"); +			/* break; */ +		case IS_LONG: { +			long lval = Z_LVAL_PP(arg); +			char s_tmp[MAX_LENGTH_OF_LONG + 1]; +			int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */ +			TRACE_APPEND_STRL(s_tmp, l_tmp); +			TRACE_APPEND_STR(", "); +			break; +		} +		case IS_DOUBLE: { +			double dval = Z_DVAL_PP(arg); +			char *s_tmp; +			int l_tmp; + +			s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1); +			l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */ +			TRACE_APPEND_STRL(s_tmp, l_tmp); +			/* %G already handles removing trailing zeros from the fractional part, yay */ +			efree(s_tmp); +			TRACE_APPEND_STR(", "); +			break; +		} +		case IS_ARRAY: +			TRACE_APPEND_STR("Array, "); +			break; +		case IS_OBJECT: { +			zval tmp; +			zstr class_name; +			zend_uint class_name_len; +			int dup; + +			TRACE_APPEND_STR("Object("); + +			dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC); + +			ZVAL_UNICODEL(&tmp, class_name.u, class_name_len, 1); +			convert_to_string_with_converter(&tmp, ZEND_U_CONVERTER(UG(output_encoding_conv))); +			TRACE_APPEND_STRL(Z_STRVAL(tmp), Z_STRLEN(tmp)); +			zval_dtor(&tmp); + +			if(!dup) { +				efree(class_name.v); +			} + +			TRACE_APPEND_STR("), "); +			break; +		} +		default: +			break; +	} +	return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + + +static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ +{ +	char *s_tmp, **str; +	int *len, *num; +	long line; +	HashTable *ht = Z_ARRVAL_PP(frame); +	zval **file, **tmp; +	uint * level; + +	level = va_arg(args, uint *); +	str = va_arg(args, char**); +	len = va_arg(args, int*); +	num = va_arg(args, int*); + +	if (!*level) { +		return ZEND_HASH_APPLY_KEEP; +	} +	--*level; + +	s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1); +	sprintf(s_tmp, "#%d ", (*num)++); +	TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); +	efree(s_tmp); +	if (zend_ascii_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) { +		if (zend_ascii_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) { +			line = Z_LVAL_PP(tmp); +		} else { +			line = 0; +		} +		TRACE_APPEND_ZVAL(*file); +		s_tmp = emalloc(MAX_LENGTH_OF_LONG + 2 + 1); +		sprintf(s_tmp, "(%ld): ", line); +		TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); +		efree(s_tmp); +	} else { +		TRACE_APPEND_STR("[internal function]: "); +	} +	TRACE_APPEND_KEY("class"); +	TRACE_APPEND_KEY("type"); +	TRACE_APPEND_KEY("function"); +	TRACE_APPEND_CHR('('); +	if (zend_ascii_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) { +		int last_len = *len; +		zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len); +		if (last_len != *len) { +			*len -= 2; /* remove last ', ' */ +		} +	} +	TRACE_APPEND_STR(")\n"); +	return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + + +#else /* PHP 5*/ + + +/* {{{ gettraceasstring() macros */ +#define TRACE_APPEND_CHR(chr)                                            \ +	*str = (char*)erealloc(*str, *len + 1 + 1);                          \ +	(*str)[(*len)++] = chr + +#define TRACE_APPEND_STRL(val, vallen)                                   \ +	{                                                                    \ +		int l = vallen;                                                  \ +		*str = (char*)erealloc(*str, *len + l + 1);                      \ +		memcpy((*str) + *len, val, l);                                   \ +		*len += l;                                                       \ +	} + +#define TRACE_APPEND_STR(val)                                            \ +	TRACE_APPEND_STRL(val, sizeof(val)-1) + +#define TRACE_APPEND_KEY(key)                                            \ +	if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \ +	    TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));           \ +	} + +/* }}} */ + + +static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ +{ +	char **str; +	int *len; + +	str = va_arg(args, char**); +	len = va_arg(args, int*); + +	/* the trivial way would be to do: +	 * conver_to_string_ex(arg); +	 * append it and kill the now tmp arg. +	 * but that could cause some E_NOTICE and also damn long lines. +	 */ + +	switch (Z_TYPE_PP(arg)) { +		case IS_NULL: +			TRACE_APPEND_STR("NULL, "); +			break; +		case IS_STRING: { +			int l_added; +			TRACE_APPEND_CHR('\''); +			if (Z_STRLEN_PP(arg) > 15) { +				TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15); +				TRACE_APPEND_STR("...', "); +				l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */ +			} else { +				l_added = Z_STRLEN_PP(arg); +				TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added); +				TRACE_APPEND_STR("', "); +				l_added += 3 + 1; +			} +			while (--l_added) { +				if ((*str)[*len - l_added] < 32) { +					(*str)[*len - l_added] = '?'; +				} +			} +			break; +		} +		case IS_BOOL: +			if (Z_LVAL_PP(arg)) { +				TRACE_APPEND_STR("true, "); +			} else { +				TRACE_APPEND_STR("false, "); +			} +			break; +		case IS_RESOURCE: +			TRACE_APPEND_STR("Resource id #"); +			/* break; */ +		case IS_LONG: { +			long lval = Z_LVAL_PP(arg); +			char s_tmp[MAX_LENGTH_OF_LONG + 1]; +			int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */ +			TRACE_APPEND_STRL(s_tmp, l_tmp); +			TRACE_APPEND_STR(", "); +			break; +		} +		case IS_DOUBLE: { +			double dval = Z_DVAL_PP(arg); +			char *s_tmp; +			int l_tmp; + +			s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1); +			l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */ +			TRACE_APPEND_STRL(s_tmp, l_tmp); +			/* %G already handles removing trailing zeros from the fractional part, yay */ +			efree(s_tmp); +			TRACE_APPEND_STR(", "); +			break; +		} +		case IS_ARRAY: +			TRACE_APPEND_STR("Array, "); +			break; +		case IS_OBJECT: { +			char *class_name; +			zend_uint class_name_len; +			int dupl; + +			TRACE_APPEND_STR("Object("); + +			dupl = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC); + +			TRACE_APPEND_STRL(class_name, class_name_len); +			if (!dupl) { +				efree(class_name); +			} + +			TRACE_APPEND_STR("), "); +			break; +		} +		default: +			break; +	} +	return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + +static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ +{ +	char *s_tmp, **str; +	int *len, *num; +	long line; +	HashTable *ht = Z_ARRVAL_PP(frame); +	zval **file, **tmp; +	uint * level; + +	level = va_arg(args, uint *); +	str = va_arg(args, char**); +	len = va_arg(args, int*); +	num = va_arg(args, int*); + +	if (!*level) { +		return ZEND_HASH_APPLY_KEEP; +	} +	--*level; + +	s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1); +	sprintf(s_tmp, "#%d ", (*num)++); +	TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); +	efree(s_tmp); +	if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) { +		if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) { +			line = Z_LVAL_PP(tmp); +		} else { +			line = 0; +		} +		s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1); +		sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line); +		TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); +		efree(s_tmp); +	} else { +		TRACE_APPEND_STR("[internal function]: "); +	} +	TRACE_APPEND_KEY("class"); +	TRACE_APPEND_KEY("type"); +	TRACE_APPEND_KEY("function"); +	TRACE_APPEND_CHR('('); +	if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) { +		int last_len = *len; +		zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len); +		if (last_len != *len) { +			*len -= 2; /* remove last ', ' */ +		} +	} +	TRACE_APPEND_STR(")\n"); +	return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ +#endif + + +PHPAPI char * mysqlnd_get_backtrace(uint max_levels, size_t * length TSRMLS_DC) +{ +	zval *trace; +	char *res = estrdup(""), **str = &res, *s_tmp; +	int res_len = 0, *len = &res_len, num = 0; +	if (max_levels == 0) { +		max_levels = 99999; +	} + +	MAKE_STD_ZVAL(trace); +	zend_fetch_debug_backtrace(trace, 0, 0 TSRMLS_CC); + +	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); +	zval_ptr_dtor(&trace); + +	if (max_levels) { +		s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1); +		sprintf(s_tmp, "#%d {main}", num); +		TRACE_APPEND_STRL(s_tmp, strlen(s_tmp)); +		efree(s_tmp); +	} + +	res[res_len] = '\0'; +	*length = res_len; + +	return res; +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */  | 
