mirror of
https://github.com/php/php-src.git
synced 2024-12-13 03:44:17 +08:00
92698a331f
build.
1460 lines
42 KiB
C
1460 lines
42 KiB
C
;/*
|
|
+----------------------------------------------------------------------+
|
|
| PHP Version 6 |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2006-2009 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_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 const char mysqlnd_emalloc_name[] = "_mysqlnd_emalloc";
|
|
static const char mysqlnd_pemalloc_name[] = "_mysqlnd_pemalloc";
|
|
static const char mysqlnd_ecalloc_name[] = "_mysqlnd_ecalloc";
|
|
static const char mysqlnd_pecalloc_name[] = "_mysqlnd_pecalloc";
|
|
static const char mysqlnd_erealloc_name[] = "_mysqlnd_erealloc";
|
|
static const char mysqlnd_perealloc_name[] = "_mysqlnd_perealloc";
|
|
static const char mysqlnd_efree_name[] = "_mysqlnd_efree";
|
|
static const char mysqlnd_pefree_name[] = "_mysqlnd_pefree";
|
|
static const char mysqlnd_malloc_name[] = "_mysqlnd_malloc";
|
|
static const char mysqlnd_calloc_name[] = "_mysqlnd_calloc";
|
|
static const char mysqlnd_realloc_name[] = "_mysqlnd_realloc";
|
|
static const char mysqlnd_free_name[] = "_mysqlnd_free";
|
|
static const char mysqlnd_pestrndup_name[] = "_mysqlnd_pestrndup";
|
|
static const char mysqlnd_pestrdup_name[] = "_mysqlnd_pestrdup";
|
|
|
|
const char * mysqlnd_debug_std_no_trace_funcs[] =
|
|
{
|
|
mysqlnd_emalloc_name,
|
|
mysqlnd_ecalloc_name,
|
|
mysqlnd_efree_name,
|
|
mysqlnd_erealloc_name,
|
|
mysqlnd_pemalloc_name,
|
|
mysqlnd_pecalloc_name,
|
|
mysqlnd_pefree_name,
|
|
mysqlnd_perealloc_name,
|
|
mysqlnd_malloc_name,
|
|
mysqlnd_calloc_name,
|
|
mysqlnd_realloc_name,
|
|
mysqlnd_free_name,
|
|
mysqlnd_pestrndup_name,
|
|
mysqlnd_read_header_name,
|
|
mysqlnd_read_body_name,
|
|
NULL /* must be always last */
|
|
};
|
|
|
|
/* {{{ 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;
|
|
unsigned int 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;
|
|
unsigned int 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,
|
|
const char * const func_name, unsigned int 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 && self->skip_functions) {
|
|
const char ** p = self->skip_functions;
|
|
while (*p) {
|
|
if (*p == func_name) {
|
|
zend_stack_push(&self->call_stack, "", sizeof(""));
|
|
return FALSE;
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
unsigned int 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 {
|
|
if (!self->file_name)
|
|
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];
|
|
unsigned int 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;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
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 */
|
|
PHPAPI MYSQLND_DEBUG *
|
|
mysqlnd_debug_init(const char * skip_functions[] TSRMLS_DC)
|
|
{
|
|
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;
|
|
ret->skip_functions = skip_functions;
|
|
|
|
return ret;
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ _mysqlnd_debug */
|
|
PHPAPI 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(mysqlnd_debug_std_no_trace_funcs TSRMLS_CC);
|
|
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
|
|
|
|
#define REAL_SIZE(s) (collect_memory_statistics? (s) + sizeof(size_t) : (s))
|
|
#define REAL_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) - sizeof(size_t)) : (p))
|
|
#define FAKE_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) + sizeof(size_t)) : (p))
|
|
|
|
/* {{{ _mysqlnd_emalloc */
|
|
void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
|
|
{
|
|
void *ret;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
|
|
DBG_ENTER(mysqlnd_emalloc_name);
|
|
|
|
DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
|
|
|
|
ret = emalloc(REAL_SIZE(size));
|
|
|
|
DBG_INF_FMT("size=%lu ptr=%p", size, ret);
|
|
|
|
if (collect_memory_statistics) {
|
|
*(size_t *) ret = size;
|
|
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMOUNT, size);
|
|
}
|
|
DBG_RETURN(FAKE_PTR(ret));
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ _mysqlnd_pemalloc */
|
|
void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
|
|
{
|
|
void *ret;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
DBG_ENTER(mysqlnd_pemalloc_name);
|
|
DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
|
|
|
|
ret = pemalloc(REAL_SIZE(size), persistent);
|
|
DBG_INF_FMT("size=%lu ptr=%p persistent=%d", size, ret, persistent);
|
|
|
|
if (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_AMOUNT:STAT_MEM_EMALLOC_AMOUNT;
|
|
*(size_t *) ret = size;
|
|
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
|
|
}
|
|
|
|
DBG_RETURN(FAKE_PTR(ret));
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ _mysqlnd_ecalloc */
|
|
void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
|
|
{
|
|
void *ret;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
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, REAL_SIZE(size));
|
|
|
|
DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
|
|
DBG_INF_FMT("size=%lu ptr=%p", size, ret);
|
|
if (collect_memory_statistics) {
|
|
*(size_t *) ret = size;
|
|
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMOUNT, size);
|
|
}
|
|
DBG_RETURN(FAKE_PTR(ret));
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ _mysqlnd_pecalloc */
|
|
void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
|
|
{
|
|
void *ret;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
DBG_ENTER(mysqlnd_pecalloc_name);
|
|
DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
|
|
|
|
ret = pecalloc(nmemb, REAL_SIZE(size), persistent);
|
|
DBG_INF_FMT("size=%lu ptr=%p", size, ret);
|
|
|
|
if (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_AMOUNT:STAT_MEM_ECALLOC_AMOUNT;
|
|
*(size_t *) ret = size;
|
|
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
|
|
}
|
|
|
|
DBG_RETURN(FAKE_PTR(ret));
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ _mysqlnd_erealloc */
|
|
void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
|
|
{
|
|
void *ret;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0;
|
|
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 old_size=%lu, new_size=%lu", ptr, old_size, new_size);
|
|
|
|
ret = erealloc(REAL_PTR(ptr), REAL_SIZE(new_size));
|
|
|
|
DBG_INF_FMT("new_ptr=%p", (char*)ret);
|
|
if (collect_memory_statistics) {
|
|
*(size_t *) ret = new_size;
|
|
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMOUNT, new_size);
|
|
}
|
|
DBG_RETURN(FAKE_PTR(ret));
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ _mysqlnd_perealloc */
|
|
void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
|
|
{
|
|
void *ret;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0;
|
|
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 old_size=%lu new_size=%lu persist=%d", ptr, old_size, new_size, persistent);
|
|
|
|
ret = perealloc(REAL_PTR(ptr), REAL_SIZE(new_size), persistent);
|
|
|
|
DBG_INF_FMT("new_ptr=%p", (char*)ret);
|
|
|
|
if (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_AMOUNT:STAT_MEM_EREALLOC_AMOUNT;
|
|
*(size_t *) ret = new_size;
|
|
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, new_size);
|
|
}
|
|
DBG_RETURN(FAKE_PTR(ret));
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ _mysqlnd_efree */
|
|
void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
|
|
{
|
|
size_t free_amount = 0;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
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);
|
|
|
|
if (ptr) {
|
|
if (collect_memory_statistics) {
|
|
free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
|
|
DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
|
|
}
|
|
efree(REAL_PTR(ptr));
|
|
}
|
|
|
|
if (collect_memory_statistics) {
|
|
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EFREE_COUNT, 1, STAT_MEM_EFREE_AMOUNT, free_amount);
|
|
}
|
|
DBG_VOID_RETURN;
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ _mysqlnd_pefree */
|
|
void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
|
|
{
|
|
size_t free_amount = 0;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
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 (ptr) {
|
|
if (collect_memory_statistics) {
|
|
free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
|
|
DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
|
|
}
|
|
pefree(REAL_PTR(ptr), persistent);
|
|
}
|
|
|
|
if (collect_memory_statistics) {
|
|
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT, 1,
|
|
persistent? STAT_MEM_FREE_AMOUNT:STAT_MEM_EFREE_AMOUNT, free_amount);
|
|
}
|
|
DBG_VOID_RETURN;
|
|
}
|
|
|
|
|
|
/* {{{ _mysqlnd_malloc */
|
|
void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
|
|
{
|
|
void *ret;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
DBG_ENTER(mysqlnd_malloc_name);
|
|
DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
|
|
|
|
ret = malloc(REAL_SIZE(size));
|
|
|
|
DBG_INF_FMT("size=%lu ptr=%p", size, ret);
|
|
if (collect_memory_statistics) {
|
|
*(size_t *) ret = size;
|
|
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_MALLOC_COUNT, 1, STAT_MEM_MALLOC_AMOUNT, size);
|
|
}
|
|
DBG_RETURN(FAKE_PTR(ret));
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ _mysqlnd_calloc */
|
|
void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
|
|
{
|
|
void *ret;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
DBG_ENTER(mysqlnd_calloc_name);
|
|
DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
|
|
|
|
ret = calloc(nmemb, REAL_SIZE(size));
|
|
|
|
DBG_INF_FMT("size=%lu ptr=%p", size, ret);
|
|
if (collect_memory_statistics) {
|
|
*(size_t *) ret = size;
|
|
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_CALLOC_COUNT, 1, STAT_MEM_CALLOC_AMOUNT, size);
|
|
}
|
|
DBG_RETURN(FAKE_PTR(ret));
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ _mysqlnd_realloc */
|
|
void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
|
|
{
|
|
void *ret;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
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(REAL_PTR(ptr), REAL_SIZE(new_size));
|
|
|
|
DBG_INF_FMT("new_ptr=%p", (char*)ret);
|
|
|
|
if (collect_memory_statistics) {
|
|
*(size_t *) ret = new_size;
|
|
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_REALLOC_COUNT, 1, STAT_MEM_REALLOC_AMOUNT, new_size);
|
|
}
|
|
DBG_RETURN(FAKE_PTR(ret));
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ _mysqlnd_free */
|
|
void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
|
|
{
|
|
size_t free_amount = 0;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
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);
|
|
|
|
if (ptr) {
|
|
if (collect_memory_statistics) {
|
|
free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
|
|
DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
|
|
}
|
|
free(REAL_PTR(ptr));
|
|
}
|
|
|
|
if (collect_memory_statistics) {
|
|
MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_FREE_COUNT, 1, STAT_MEM_FREE_AMOUNT, free_amount);
|
|
}
|
|
DBG_VOID_RETURN;
|
|
}
|
|
/* }}} */
|
|
|
|
#define SMART_STR_START_SIZE 2048
|
|
#define SMART_STR_PREALLOC 512
|
|
#include "ext/standard/php_smart_str.h"
|
|
|
|
|
|
/* {{{ _mysqlnd_pestrndup */
|
|
char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
|
|
{
|
|
char * ret;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
DBG_ENTER(mysqlnd_pestrndup_name);
|
|
DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
|
|
DBG_INF_FMT("ptr=%p", ptr);
|
|
|
|
ret = pemalloc(REAL_SIZE(length) + 1, persistent);
|
|
{
|
|
size_t l = length;
|
|
char * p = (char *) ptr;
|
|
char * dest = (char *) FAKE_PTR(ret);
|
|
while (*p && l--) {
|
|
*dest++ = *p++;
|
|
}
|
|
*dest = '\0';
|
|
}
|
|
|
|
if (collect_memory_statistics) {
|
|
*(size_t *) ret = length;
|
|
MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRNDUP_COUNT : STAT_MEM_ESTRNDUP_COUNT);
|
|
}
|
|
|
|
DBG_RETURN(FAKE_PTR(ret));
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ _mysqlnd_pestrdup */
|
|
char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
|
|
{
|
|
char * ret;
|
|
smart_str tmp_str = {0, 0, 0};
|
|
const char * p = ptr;
|
|
zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
|
|
DBG_ENTER(mysqlnd_pestrdup_name);
|
|
DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
|
|
DBG_INF_FMT("ptr=%p", ptr);
|
|
do {
|
|
smart_str_appendc(&tmp_str, *p);
|
|
} while (*p++);
|
|
|
|
ret = pemalloc(tmp_str.len + sizeof(size_t), persistent);
|
|
memcpy(FAKE_PTR(ret), tmp_str.c, tmp_str.len);
|
|
|
|
if (collect_memory_statistics) {
|
|
*(size_t *) ret = tmp_str.len;
|
|
MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRDUP_COUNT : STAT_MEM_ESTRDUP_COUNT);
|
|
}
|
|
smart_str_free(&tmp_str);
|
|
|
|
DBG_RETURN(FAKE_PTR(ret));
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
|
|
/* 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 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 dup;
|
|
|
|
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 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
|
|
*/
|