diff options
author | Andrey Hristov <andrey@php.net> | 2007-10-05 21:23:56 +0000 |
---|---|---|
committer | Andrey Hristov <andrey@php.net> | 2007-10-05 21:23:56 +0000 |
commit | 8b9b553aa264d3c0afe611f49499e787004983c7 (patch) | |
tree | 840ea2b1127ec97a63f36ffda689a1a1fe5fb975 /ext/mysqlnd/mysqlnd_debug.c | |
parent | 9f9495a48483aac9f6bc92ceea5841a8d243e2aa (diff) | |
download | php-git-8b9b553aa264d3c0afe611f49499e787004983c7.tar.gz |
Import mysqlnd
Patch ext/mysql and ext/mysqli to support mysqlnd
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 + */ |