diff options
Diffstat (limited to 'ext/mysqlnd/mysqlnd_debug.c')
| -rw-r--r-- | ext/mysqlnd/mysqlnd_debug.c | 1345 | 
1 files changed, 1345 insertions, 0 deletions
diff --git a/ext/mysqlnd/mysqlnd_debug.c b/ext/mysqlnd/mysqlnd_debug.c new file mode 100644 index 0000000000..3dc3f592a6 --- /dev/null +++ b/ext/mysqlnd/mysqlnd_debug.c @@ -0,0 +1,1345 @@ +/* +  +----------------------------------------------------------------------+ +  | PHP Version 6                                                        | +  +----------------------------------------------------------------------+ +  | Copyright (c) 2006-2007 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$ */ + +#include "php.h" +#include "mysqlnd.h" +#include "mysqlnd_priv.h" +#include "mysqlnd_debug.h" +#include "mysqlnd_wireprotocol.h" +#include "mysqlnd_palloc.h" +#include "mysqlnd_statistics.h" +#include "zend_builtin_functions.h" + + +static const char * const mysqlnd_debug_default_trace_file = "/tmp/mysqlnd.trace"; + +#ifdef ZTS  +#define MYSQLND_ZTS(self) TSRMLS_D = (self)->TSRMLS_C +#else +#define MYSQLND_ZTS(self) +#endif + +#define MYSQLND_DEBUG_DUMP_TIME				1 +#define MYSQLND_DEBUG_DUMP_TRACE			2 +#define MYSQLND_DEBUG_DUMP_PID				4 +#define MYSQLND_DEBUG_DUMP_LINE				8 +#define MYSQLND_DEBUG_DUMP_FILE				16 +#define MYSQLND_DEBUG_DUMP_LEVEL			32 +#define MYSQLND_DEBUG_APPEND				64 +#define MYSQLND_DEBUG_FLUSH					128 +#define MYSQLND_DEBUG_TRACE_MEMORY_CALLS	256 + +static char * mysqlnd_emalloc_name	= "_mysqlnd_emalloc"; +static char * mysqlnd_pemalloc_name	= "_mysqlnd_pemalloc"; +static char * mysqlnd_ecalloc_name	= "_mysqlnd_ecalloc"; +static char * mysqlnd_pecalloc_name	= "_mysqlnd_pecalloc"; +static char * mysqlnd_erealloc_name	= "_mysqlnd_erealloc"; +static char * mysqlnd_perealloc_name= "_mysqlnd_perealloc"; +static char * mysqlnd_efree_name	= "_mysqlnd_efree"; +static char * mysqlnd_pefree_name	= "_mysqlnd_pefree"; +static char * mysqlnd_malloc_name	= "_mysqlnd_malloc"; +static char * mysqlnd_calloc_name	= "_mysqlnd_calloc"; +static char * mysqlnd_realloc_name	= "_mysqlnd_realloc"; +static char * mysqlnd_free_name		= "_mysqlnd_free"; + +/* {{{ mysqlnd_debug::open */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, open)(MYSQLND_DEBUG * self, zend_bool reopen) +{ +	MYSQLND_ZTS(self); + +	if (!self->file_name) { +		return FAIL; +	} + +	self->stream = php_stream_open_wrapper(self->file_name, +										   reopen == TRUE || self->flags & MYSQLND_DEBUG_APPEND? "ab":"wb", +										   REPORT_ERRORS, NULL); +	return self->stream? PASS:FAIL; +} +/* }}} */ + + +/* {{{ mysqlnd_debug::log */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, log)(MYSQLND_DEBUG * self, +								   unsigned int line, const char * const file, +								   unsigned int level, const char * type, const char * message) +{ +	char pipe_buffer[512]; +	enum_func_status ret; +	int i; +	char * message_line; +	uint message_line_len; +	unsigned int flags = self->flags; +	char pid_buffer[10], time_buffer[30], file_buffer[200], +		 line_buffer[6], level_buffer[7]; +	MYSQLND_ZTS(self); + +	if (!self->stream) { +		if (FAIL == self->m->open(self, FALSE)) { +			return FAIL; +		} +	} + +	if (level == -1) { +		level = zend_stack_count(&self->call_stack); +	} +	i = MIN(level, sizeof(pipe_buffer) / 2  - 1); +	pipe_buffer[i*2] = '\0'; +	for (;i > 0;i--) { +		pipe_buffer[i*2 - 1] = ' '; +		pipe_buffer[i*2 - 2] = '|'; +	} + + +	if (flags & MYSQLND_DEBUG_DUMP_PID) { +		snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid); +		pid_buffer[sizeof(pid_buffer) - 1 ] = '\0'; +	} +	if (flags & MYSQLND_DEBUG_DUMP_TIME) { +		/* The following from FF's DBUG library, which is in the public domain */ +#if defined(PHP_WIN32) +		/* FIXME This doesn't give microseconds as in Unix case, and the resolution is +		in system ticks, 10 ms intervals. See my_getsystime.c for high res */ +		SYSTEMTIME loc_t; +		GetLocalTime(&loc_t); +		snprintf(time_buffer, sizeof(time_buffer) - 1, +				 /* "%04d-%02d-%02d " */ +				 "%02d:%02d:%02d.%06d ", +				 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ +				 loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds); +		time_buffer[sizeof(time_buffer) - 1 ] = '\0'; +#else +		struct timeval tv; +		struct tm *tm_p; +		if (gettimeofday(&tv, NULL) != -1) { +			if ((tm_p= localtime((const time_t *)&tv.tv_sec))) { +				snprintf(time_buffer, sizeof(time_buffer) - 1, +						 /* "%04d-%02d-%02d " */ +						 "%02d:%02d:%02d.%06d ", +						 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ +						 tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec, +						 (int) (tv.tv_usec)); +				time_buffer[sizeof(time_buffer) - 1 ] = '\0'; +			} +		} +#endif +	} +	if (flags & MYSQLND_DEBUG_DUMP_FILE) { +		snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file); +		file_buffer[sizeof(file_buffer) - 1 ] = '\0'; +	} +	if (flags & MYSQLND_DEBUG_DUMP_LINE) { +		snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line); +		line_buffer[sizeof(line_buffer) - 1 ] = '\0'; +	} +	if (flags & MYSQLND_DEBUG_DUMP_LEVEL) { +		snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level); +		level_buffer[sizeof(level_buffer) - 1 ] = '\0'; +	} + +	message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n", +								flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"", +								pipe_buffer, type? type:"", message); + +	ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL; +	efree(message_line); +	if (flags & MYSQLND_DEBUG_FLUSH) { +		self->m->close(self); +		self->m->open(self, TRUE);	 +	} +	return ret; +} +/* }}} */ + + +/* {{{ mysqlnd_debug::log_va */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, log_va)(MYSQLND_DEBUG *self, +									  unsigned int line, const char * const file, +									  unsigned int level, const char * type, +									  const char *format, ...) +{ +	char pipe_buffer[512]; +	int i; +	enum_func_status ret; +	char * message_line, *buffer; +	uint message_line_len; +	va_list args; +	unsigned int flags = self->flags; +	char pid_buffer[10], time_buffer[30], file_buffer[200], +		 line_buffer[6], level_buffer[7]; +	MYSQLND_ZTS(self); + +	if (!self->stream) { +		if (FAIL == self->m->open(self, FALSE)) { +			return FAIL; +		} +	} + +	if (level == -1) { +		level = zend_stack_count(&self->call_stack); +	} +	i = MIN(level, sizeof(pipe_buffer) / 2  - 1); +	pipe_buffer[i*2] = '\0'; +	for (;i > 0;i--) { +		pipe_buffer[i*2 - 1] = ' '; +		pipe_buffer[i*2 - 2] = '|'; +	} + + +	if (flags & MYSQLND_DEBUG_DUMP_PID) { +		snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid); +		pid_buffer[sizeof(pid_buffer) - 1 ] = '\0'; +	} +	if (flags & MYSQLND_DEBUG_DUMP_TIME) { +		/* The following from FF's DBUG library, which is in the public domain */ +#if defined(PHP_WIN32) +		/* FIXME This doesn't give microseconds as in Unix case, and the resolution is +		in system ticks, 10 ms intervals. See my_getsystime.c for high res */ +		SYSTEMTIME loc_t; +		GetLocalTime(&loc_t); +		snprintf(time_buffer, sizeof(time_buffer) - 1, +				 /* "%04d-%02d-%02d " */ +				 "%02d:%02d:%02d.%06d ", +				 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ +				 loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds); +		time_buffer[sizeof(time_buffer) - 1 ] = '\0'; +#else +		struct timeval tv; +		struct tm *tm_p; +		if (gettimeofday(&tv, NULL) != -1) { +			if ((tm_p= localtime((const time_t *)&tv.tv_sec))) { +				snprintf(time_buffer, sizeof(time_buffer) - 1, +						 /* "%04d-%02d-%02d " */ +						 "%02d:%02d:%02d.%06d ", +						 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/ +						 tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec, +						 (int) (tv.tv_usec)); +				time_buffer[sizeof(time_buffer) - 1 ] = '\0'; +			} +		} +#endif +	} +	if (flags & MYSQLND_DEBUG_DUMP_FILE) { +		snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file); +		file_buffer[sizeof(file_buffer) - 1 ] = '\0'; +	} +	if (flags & MYSQLND_DEBUG_DUMP_LINE) { +		snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line); +		line_buffer[sizeof(line_buffer) - 1 ] = '\0'; +	} +	if (flags & MYSQLND_DEBUG_DUMP_LEVEL) { +		snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level); +		level_buffer[sizeof(level_buffer) - 1 ] = '\0'; +	} + + +	va_start(args, format); +	vspprintf(&buffer, 0, format, args); +	va_end(args); + +	message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n", +								flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"", +								flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"", +								pipe_buffer, type? type:"", buffer); +	efree(buffer); + +	ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL; +	efree(message_line); + +	if (flags & MYSQLND_DEBUG_FLUSH) { +		self->m->close(self); +		self->m->open(self, TRUE);	 +	} +	return ret; +} +/* }}} */ + + +/* FALSE - The DBG_ calls won't be traced, TRUE - will be traced */ +/* {{{ mysqlnd_res_meta::func_enter */ +static zend_bool +MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self, +										  unsigned int line, const char * const file, +										  char * func_name, uint func_name_len) +{ +	if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) { +		return FALSE; +	} +	if (zend_stack_count(&self->call_stack) >= self->nest_level_limit) { +		return FALSE; +	} + +	if ((self->flags & MYSQLND_DEBUG_TRACE_MEMORY_CALLS) == 0 &&  +		(func_name == mysqlnd_emalloc_name	|| func_name == mysqlnd_pemalloc_name	|| +		 func_name == mysqlnd_ecalloc_name	|| func_name == mysqlnd_pecalloc_name	|| +		 func_name == mysqlnd_erealloc_name || func_name == mysqlnd_perealloc_name	|| +		 func_name == mysqlnd_efree_name	|| func_name == mysqlnd_pefree_name		||  +		 func_name == mysqlnd_efree_name	|| func_name == mysqlnd_pefree_name		||  +		 func_name == mysqlnd_malloc_name	|| func_name == mysqlnd_calloc_name		||  +		 func_name == mysqlnd_realloc_name	|| func_name == mysqlnd_free_name		|| +		 func_name == mysqlnd_palloc_zval_ptr_dtor_name	|| func_name == mysqlnd_palloc_get_zval_name || +		 func_name == mysqlnd_read_header_name || func_name == mysqlnd_read_body_name)) { +		zend_stack_push(&self->call_stack, "", sizeof("")); +	   	return FALSE; +	} + +	zend_stack_push(&self->call_stack, func_name, func_name_len + 1); + +	if (zend_hash_num_elements(&self->not_filtered_functions) && +		0 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1)) +	{ +		return FALSE; +	} + +	self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, ">%s", func_name); +	return TRUE; +} +/* }}} */ + + +/* {{{ mysqlnd_res_meta::func_leave */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int line, +										  const char * const file) +{ +	char *func_name; + +	if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) { +		return PASS; +	} +	if (zend_stack_count(&self->call_stack) >= self->nest_level_limit) { +		return PASS; +	} + +	zend_stack_top(&self->call_stack, (void **)&func_name); + +	if (func_name[0] == '\0') { +		; /* don't log that function */ +	} else if (!zend_hash_num_elements(&self->not_filtered_functions) || +		1 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1)) +	{ +		self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s", func_name); +	} + +	return zend_stack_del_top(&self->call_stack) == SUCCESS? PASS:FAIL; +} +/* }}} */ + + +/* {{{ mysqlnd_res_meta::close */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, close)(MYSQLND_DEBUG * self) +{ +	MYSQLND_ZTS(self); +	if (self->stream) { +		php_stream_free(self->stream, PHP_STREAM_FREE_CLOSE); +		self->stream = NULL; +	} +	return PASS; +} +/* }}} */ + + +/* {{{ mysqlnd_res_meta::free */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_debug, free)(MYSQLND_DEBUG * self) +{ +	if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) { +		efree(self->file_name); +		self->file_name = NULL; +	} +	zend_stack_destroy(&self->call_stack); +	zend_hash_destroy(&self->not_filtered_functions); +	efree(self); +	return PASS; +} +/* }}} */ + +enum mysqlnd_debug_parser_state +{ +	PARSER_WAIT_MODIFIER, +	PARSER_WAIT_COLON, +	PARSER_WAIT_VALUE +}; + + +/* {{{ mysqlnd_res_meta::set_mode */ +static void +MYSQLND_METHOD(mysqlnd_debug, set_mode)(MYSQLND_DEBUG * self, const char * const mode) +{ +	uint mode_len = strlen(mode), i; +	enum mysqlnd_debug_parser_state state = PARSER_WAIT_MODIFIER; + +	self->flags = 0; +	self->nest_level_limit = 0; +	if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) { +		efree(self->file_name); +		self->file_name = NULL; +	} +	if (zend_hash_num_elements(&self->not_filtered_functions)) { +		zend_hash_destroy(&self->not_filtered_functions); +		zend_hash_init(&self->not_filtered_functions, 0, NULL, NULL, 0); +	} + +	for (i = 0; i < mode_len; i++) { +		switch (mode[i]) { +			case 'O': +			case 'A': +				self->flags |= MYSQLND_DEBUG_FLUSH; +			case 'a': +			case 'o': +				if (mode[i] == 'a' || mode[i] == 'A') { +					self->flags |= MYSQLND_DEBUG_APPEND; +				} +				if (i + 1 < mode_len && mode[i+1] == ',') { +					unsigned int j = i + 2; +					while (j < mode_len) { +						if (mode[j] == ':') { +							break; +						} +						j++; +					} +					if (j > i + 2) { +						self->file_name = estrndup(mode + i + 2, j - i - 2); +					} +					i = j; +				} else { +					self->file_name = (char *) mysqlnd_debug_default_trace_file; +				} +				state = PARSER_WAIT_COLON; +				break; +			case ':': +#if 0 +				if (state != PARSER_WAIT_COLON) { +					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Consecutive semicolons at position %u", i); +				} +#endif +				state = PARSER_WAIT_MODIFIER; +				break; +			case 'f': /* limit output to these functions */ +				if (i + 1 < mode_len && mode[i+1] == ',') { +					unsigned int j = i + 2; +					i++; +					while (j < mode_len) { +						if (mode[j] == ':') { +							/* function names with :: */ +							if ((j + 1 < mode_len) && mode[j+1] == ':') { +								j += 2; +								continue; +							} +						} +						if (mode[j] == ',' || mode[j] == ':') { +							if (j > i + 2) { +								char func_name[1024]; +								uint func_name_len = MIN(sizeof(func_name) - 1, j - i - 1); +								memcpy(func_name, mode + i + 1, func_name_len); +								func_name[func_name_len] = '\0';  + +								zend_hash_add_empty_element(&self->not_filtered_functions, +															func_name, func_name_len + 1); +								i = j; +							} +							if (mode[j] == ':') { +								break; +							} +						} +						j++; +					} +					i = j; +				} else { +#if 0 +					php_error_docref(NULL TSRMLS_CC, E_WARNING, +									 "Expected list of functions for '%c' found none", mode[i]);				 +#endif +				} +				state = PARSER_WAIT_COLON; +				break; +			case 'D': +			case 'd': +			case 'g': +			case 'p': +				/* unsupported */ +				if ((i + 1) < mode_len && mode[i+1] == ',') { +					i+= 2; +					while (i < mode_len) { +						if (mode[i++] == ':') { +							break; +						} +					} +				} +				state = PARSER_WAIT_COLON; +				break; +			case 'F': +				self->flags |= MYSQLND_DEBUG_DUMP_FILE; +				state = PARSER_WAIT_COLON; +				break; +			case 'i': +				self->flags |= MYSQLND_DEBUG_DUMP_PID; +				state = PARSER_WAIT_COLON; +				break; +			case 'L': +				self->flags |= MYSQLND_DEBUG_DUMP_LINE; +				state = PARSER_WAIT_COLON; +				break; +			case 'n': +				self->flags |= MYSQLND_DEBUG_DUMP_LEVEL; +				state = PARSER_WAIT_COLON; +				break; +			case 't': +				if (mode[i+1] == ',') { +					unsigned int j = i + 2; +					while (j < mode_len) { +						if (mode[j] == ':') { +							break; +						} +						j++; +					} +					if (j > i + 2) { +						char *value_str = estrndup(mode + i + 2, j - i - 2); +						self->nest_level_limit = atoi(value_str); +						efree(value_str); +					} +					i = j; +				} else { +					self->nest_level_limit = 200; /* default value for FF DBUG */ +				} +				self->flags |= MYSQLND_DEBUG_DUMP_TRACE; +				state = PARSER_WAIT_COLON; +				break; +			case 'T': +				self->flags |= MYSQLND_DEBUG_DUMP_TIME; +				state = PARSER_WAIT_COLON; +				break; +			case 'N': +			case 'P': +			case 'r': +			case 'S': +				state = PARSER_WAIT_COLON; +				break; +			case 'm': /* mysqlnd extension - trace memory functions */ +				self->flags |= MYSQLND_DEBUG_TRACE_MEMORY_CALLS; +				state = PARSER_WAIT_COLON; +				break; +			default: +				if (state == PARSER_WAIT_MODIFIER) { +#if 0 +					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unrecognized format '%c'", mode[i]); +#endif +					if (i+1 < mode_len && mode[i+1] == ',') { +						i+= 2; +						while (i < mode_len) { +							if (mode[i] == ':') { +								break; +							} +							i++; +						} +					} +					state = PARSER_WAIT_COLON; +				} else if (state == PARSER_WAIT_COLON) { +#if 0 +					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Colon expected, '%c' found", mode[i]);				 +#endif +				} +				break; +		} +	} +} +/* }}} */ + + +MYSQLND_CLASS_METHODS_START(mysqlnd_debug) +	MYSQLND_METHOD(mysqlnd_debug, open), +	MYSQLND_METHOD(mysqlnd_debug, set_mode), +	MYSQLND_METHOD(mysqlnd_debug, log), +	MYSQLND_METHOD(mysqlnd_debug, log_va), +	MYSQLND_METHOD(mysqlnd_debug, func_enter), +	MYSQLND_METHOD(mysqlnd_debug, func_leave), +	MYSQLND_METHOD(mysqlnd_debug, close), +	MYSQLND_METHOD(mysqlnd_debug, free), +MYSQLND_CLASS_METHODS_END; + + + +/* {{{ mysqlnd_debug_init */ +MYSQLND_DEBUG *mysqlnd_debug_init(TSRMLS_D) +{ +	MYSQLND_DEBUG *ret = ecalloc(1, sizeof(MYSQLND_DEBUG)); +#ifdef ZTS +	ret->TSRMLS_C = TSRMLS_C; +#endif +	ret->nest_level_limit = 0; +	ret->pid = getpid(); +	zend_stack_init(&ret->call_stack); +	zend_hash_init(&ret->not_filtered_functions, 0, NULL, NULL, 0); + +	ret->m = & mysqlnd_mysqlnd_debug_methods; +	 +	return ret; +} +/* }}} */ + + +/* {{{ _mysqlnd_debug */ +void _mysqlnd_debug(const char *mode TSRMLS_DC) +{ +#ifdef PHP_DEBUG +	MYSQLND_DEBUG *dbg = MYSQLND_G(dbg); +	if (!dbg) { +		MYSQLND_G(dbg) = dbg = mysqlnd_debug_init(TSRMLS_C); +		if (!dbg) { +			return; +		} +	} +	 +	dbg->m->close(dbg); +	dbg->m->set_mode(dbg, mode); +	while (zend_stack_count(&dbg->call_stack)) { +		zend_stack_del_top(&dbg->call_stack); +	} +#endif +} +/* }}} */ + + +#if ZEND_DEBUG +#else +#define __zend_filename "/unknown/unknown" +#define __zend_lineno   0 +#endif + + +/* {{{ _mysqlnd_emalloc */ +void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_emalloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); +	ret = emalloc(size); +	DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); +	DBG_INF_FMT("size=%lu ptr=%p", size, ret);  + +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMMOUNT, size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_pemalloc */ +void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_pemalloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	if (persistent == FALSE) { +		DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} + +	ret = pemalloc(size, persistent); +	DBG_INF_FMT("size=%lu ptr=%p persistent=%d", size, ret, persistent);  + +	if (persistent == FALSE) { +		DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} + +	if (MYSQLND_G(collect_memory_statistics)) { +		enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_MALLOC_COUNT:STAT_MEM_EMALLOC_COUNT; +		enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_MALLOC_AMMOUNT:STAT_MEM_EMALLOC_AMMOUNT; +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(s1, 1, s2, size); +	} + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_ecalloc */ +void * _mysqlnd_ecalloc(uint nmemb, size_t size MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_ecalloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); + +	ret = ecalloc(nmemb, size); + +	DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); +	DBG_INF_FMT("size=%lu ptr=%p", size, ret);  +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMMOUNT, size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_pecalloc */ +void * _mysqlnd_pecalloc(uint nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_pecalloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	if (persistent == FALSE) { +		DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} + +	ret = pecalloc(nmemb, size, persistent); +	DBG_INF_FMT("size=%lu ptr=%p", size, ret);  + +	if (persistent == FALSE) { +		DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} + +	if (MYSQLND_G(collect_memory_statistics)) { +		enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_CALLOC_COUNT:STAT_MEM_ECALLOC_COUNT; +		enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_CALLOC_AMMOUNT:STAT_MEM_ECALLOC_AMMOUNT; +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(s1, 1, s2, size); +	} + +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_erealloc */ +void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_erealloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("ptr=%p new_size=%lu", ptr, new_size);  +	DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); + +	ret = erealloc(ptr, new_size); + +	DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); +	DBG_INF_FMT("new_ptr=%p", ret);  +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMMOUNT, new_size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_perealloc */ +void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_perealloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("ptr=%p new_size=%lu persist=%d", ptr, new_size, persistent);  +	if (persistent == FALSE) { +		DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} + +	ret = perealloc(ptr, new_size, persistent); + +	DBG_INF_FMT("new_ptr=%p", ret);  + +	if (persistent == FALSE) { +		DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} +	MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_EREALLOC_COUNT:STAT_MEM_REALLOC_COUNT); +	if (MYSQLND_G(collect_memory_statistics)) { +		enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_REALLOC_COUNT:STAT_MEM_EREALLOC_COUNT; +		enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_REALLOC_AMMOUNT:STAT_MEM_EREALLOC_AMMOUNT; +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(s1, 1, s2, new_size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_efree */ +void _mysqlnd_efree(void *ptr MYSQLND_MEM_D) +{ +	DBG_ENTER(mysqlnd_efree_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("ptr=%p", ptr);  +	DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC)); +	MYSQLND_INC_GLOBAL_STATISTIC(STAT_MEM_EFREE_COUNT); + +	efree(ptr); + +	DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC)); +	DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ _mysqlnd_pefree */ +void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D) +{ +	DBG_ENTER(mysqlnd_pefree_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("ptr=%p persistent=%d", ptr, persistent);  +	if (persistent == FALSE) { +		DBG_INF_FMT("before: %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} + +	pefree(ptr, persistent); + +	if (persistent == FALSE) { +		DBG_INF_FMT("after : %lu", zend_memory_usage(persistent TSRMLS_CC)); +	} +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT); +	} +	DBG_VOID_RETURN; +} + + +/* {{{ _mysqlnd_malloc */ +void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_malloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + +	ret = malloc(size); + +	DBG_INF_FMT("size=%lu ptr=%p", size, ret);  +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_MALLOC_COUNT, 1, STAT_MEM_MALLOC_AMMOUNT, size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_calloc */ +void * _mysqlnd_calloc(uint nmemb, size_t size MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_calloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); + +	ret = calloc(nmemb, size); + +	DBG_INF_FMT("size=%lu ptr=%p", size, ret);  +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_CALLOC_COUNT, 1, STAT_MEM_CALLOC_AMMOUNT, size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_realloc */ +void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D) +{ +	void *ret; +	DBG_ENTER(mysqlnd_realloc_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("ptr=%p new_size=%lu ", new_size, ptr);  +	DBG_INF_FMT("before: %lu", zend_memory_usage(TRUE TSRMLS_CC)); + +	ret = realloc(ptr, new_size); + +	DBG_INF_FMT("new_ptr=%p", ret); + +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC2_W_VALUE(STAT_MEM_REALLOC_COUNT, 1, STAT_MEM_REALLOC_AMMOUNT, new_size); +	} +	DBG_RETURN(ret); +} +/* }}} */ + + +/* {{{ _mysqlnd_free */ +void _mysqlnd_free(void *ptr MYSQLND_MEM_D) +{ +	DBG_ENTER(mysqlnd_free_name); +	DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno); +	DBG_INF_FMT("ptr=%p", ptr);  + +	free(ptr); + +	if (MYSQLND_G(collect_memory_statistics)) { +		MYSQLND_INC_GLOBAL_STATISTIC(STAT_MEM_FREE_COUNT); +	} +	DBG_VOID_RETURN; +} +/* }}} */ + + + + +/* Follows code borrowed from zend_builtin_functions.c because the functions there are static */ + +#if PHP_MAJOR_VERSION >= 6 +/* {{{ 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, 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; +			TSRMLS_FETCH(); + +			/* +			 * 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; +			TSRMLS_FETCH(); + +			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: { +			zstr class_name; +			zend_uint class_name_len; +			int dup; +			TSRMLS_FETCH(); + +			TRACE_APPEND_STR("Object("); + +			dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC); + +			if (UG(unicode)) { +				zval tmp; + +				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); +			} else { +				TRACE_APPEND_STRL(class_name.s, class_name_len); +			} +			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, 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; + +	str = va_arg(args, char**); +	len = va_arg(args, int*); +	num = va_arg(args, int*); + +	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), (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, 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; +			TSRMLS_FETCH(); + +			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 dup; +			TSRMLS_FETCH(); + +			TRACE_APPEND_STR("Object("); + +			dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC); + +			TRACE_APPEND_STRL(class_name, class_name_len); +			if(!dup) { +				efree(class_name); +			} + +			TRACE_APPEND_STR("), "); +			break; +		} +		default: +			break; +	} +	return ZEND_HASH_APPLY_KEEP; +} +/* }}} */ + +static int mysqlnd_build_trace_string(zval **frame, 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; + +	str = va_arg(args, char**); +	len = va_arg(args, int*); +	num = va_arg(args, int*); + +	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), (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 + + +char * mysqlnd_get_backtrace(TSRMLS_D) +{ +	zval *trace; +	char *res = estrdup(""), **str = &res, *s_tmp; +	int res_len = 0, *len = &res_len, num = 0; + +	MAKE_STD_ZVAL(trace); +	zend_fetch_debug_backtrace(trace, 0, 0 TSRMLS_CC); + +	zend_hash_apply_with_arguments(Z_ARRVAL_P(trace), (apply_func_args_t)mysqlnd_build_trace_string, 3, str, len, &num); +	zval_ptr_dtor(&trace); + +	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'; + +	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 + */  | 
