Annotation of embedaddon/php/ext/mysqlnd/mysqlnd_debug.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:   +----------------------------------------------------------------------+
                      3:   | PHP Version 5                                                        |
                      4:   +----------------------------------------------------------------------+
                      5:   | Copyright (c) 2006-2012 The PHP Group                                |
                      6:   +----------------------------------------------------------------------+
                      7:   | This source file is subject to version 3.01 of the PHP license,      |
                      8:   | that is bundled with this package in the file LICENSE, and is        |
                      9:   | available through the world-wide-web at the following url:           |
                     10:   | http://www.php.net/license/3_01.txt                                  |
                     11:   | If you did not receive a copy of the PHP license and are unable to   |
                     12:   | obtain it through the world-wide-web, please send a note to          |
                     13:   | license@php.net so we can mail you a copy immediately.               |
                     14:   +----------------------------------------------------------------------+
                     15:   | Authors: Georg Richter <georg@mysql.com>                             |
                     16:   |          Andrey Hristov <andrey@mysql.com>                           |
                     17:   |          Ulf Wendel <uwendel@mysql.com>                              |
                     18:   +----------------------------------------------------------------------+
                     19: */
                     20: 
                     21: /* $Id: mysqlnd_debug.c 321634 2012-01-01 13:15:04Z felipe $ */
                     22: 
                     23: #include "php.h"
                     24: #include "mysqlnd.h"
                     25: #include "mysqlnd_priv.h"
                     26: #include "mysqlnd_debug.h"
                     27: #include "mysqlnd_wireprotocol.h"
                     28: #include "mysqlnd_statistics.h"
                     29: #include "zend_builtin_functions.h"
                     30: 
                     31: static const char * const mysqlnd_debug_default_trace_file = "/tmp/mysqlnd.trace";
                     32: 
                     33: #ifdef ZTS 
                     34: #define MYSQLND_ZTS(self) TSRMLS_D = (self)->TSRMLS_C
                     35: #else
                     36: #define MYSQLND_ZTS(self)
                     37: #endif
                     38: 
                     39: static const char mysqlnd_emalloc_name[]       = "_mysqlnd_emalloc";
                     40: static const char mysqlnd_pemalloc_name[]      = "_mysqlnd_pemalloc";
                     41: static const char mysqlnd_ecalloc_name[]       = "_mysqlnd_ecalloc";
                     42: static const char mysqlnd_pecalloc_name[]      = "_mysqlnd_pecalloc";
                     43: static const char mysqlnd_erealloc_name[]      = "_mysqlnd_erealloc";
                     44: static const char mysqlnd_perealloc_name[]     = "_mysqlnd_perealloc";
                     45: static const char mysqlnd_efree_name[]         = "_mysqlnd_efree";
                     46: static const char mysqlnd_pefree_name[]                = "_mysqlnd_pefree";
                     47: static const char mysqlnd_malloc_name[]                = "_mysqlnd_malloc";
                     48: static const char mysqlnd_calloc_name[]                = "_mysqlnd_calloc";
                     49: static const char mysqlnd_realloc_name[]       = "_mysqlnd_realloc";
                     50: static const char mysqlnd_free_name[]          = "_mysqlnd_free";
                     51: static const char mysqlnd_pestrndup_name[]     = "_mysqlnd_pestrndup";
                     52: static const char mysqlnd_pestrdup_name[]      = "_mysqlnd_pestrdup";
                     53: 
                     54: const char * mysqlnd_debug_std_no_trace_funcs[] =
                     55: {
                     56:        mysqlnd_emalloc_name,
                     57:        mysqlnd_ecalloc_name,
                     58:        mysqlnd_efree_name,
                     59:        mysqlnd_erealloc_name,
                     60:        mysqlnd_pemalloc_name,
                     61:        mysqlnd_pecalloc_name,
                     62:        mysqlnd_pefree_name,
                     63:        mysqlnd_perealloc_name,
                     64:        mysqlnd_malloc_name,
                     65:        mysqlnd_calloc_name,
                     66:        mysqlnd_realloc_name,
                     67:        mysqlnd_free_name,
                     68:        mysqlnd_pestrndup_name,
                     69:        mysqlnd_read_header_name,
                     70:        mysqlnd_read_body_name,
                     71:        NULL /* must be always last */
                     72: };
                     73: 
                     74: 
                     75: /* {{{ mysqlnd_debug::open */
                     76: static enum_func_status
                     77: MYSQLND_METHOD(mysqlnd_debug, open)(MYSQLND_DEBUG * self, zend_bool reopen)
                     78: {
                     79:        MYSQLND_ZTS(self);
                     80: 
                     81:        if (!self->file_name) {
                     82:                return FAIL;
                     83:        }
                     84: 
                     85:        self->stream = php_stream_open_wrapper(self->file_name,
                     86:                                                                                   reopen == TRUE || self->flags & MYSQLND_DEBUG_APPEND? "ab":"wb",
                     87:                                                                                   REPORT_ERRORS, NULL);
                     88:        return self->stream? PASS:FAIL;
                     89: }
                     90: /* }}} */
                     91: 
                     92: 
                     93: /* {{{ mysqlnd_debug::log */
                     94: static enum_func_status
                     95: MYSQLND_METHOD(mysqlnd_debug, log)(MYSQLND_DEBUG * self,
                     96:                                                                   unsigned int line, const char * const file,
                     97:                                                                   unsigned int level, const char * type, const char * message)
                     98: {
                     99:        char pipe_buffer[512];
                    100:        enum_func_status ret;
                    101:        int i;
                    102:        char * message_line;
                    103:        unsigned int message_line_len;
                    104:        unsigned int flags = self->flags;
                    105:        char pid_buffer[10], time_buffer[30], file_buffer[200],
                    106:                 line_buffer[6], level_buffer[7];
                    107:        MYSQLND_ZTS(self);
                    108: 
                    109:        if (!self->stream && FAIL == self->m->open(self, FALSE)) {
                    110:                return FAIL;
                    111:        }
                    112: 
                    113:        if (level == -1) {
                    114:                level = zend_stack_count(&self->call_stack);
                    115:        }
                    116:        i = MIN(level, sizeof(pipe_buffer) / 2  - 1);
                    117:        pipe_buffer[i*2] = '\0';
                    118:        for (;i > 0;i--) {
                    119:                pipe_buffer[i*2 - 1] = ' ';
                    120:                pipe_buffer[i*2 - 2] = '|';
                    121:        }
                    122: 
                    123: 
                    124:        if (flags & MYSQLND_DEBUG_DUMP_PID) {
                    125:                snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid);
                    126:                pid_buffer[sizeof(pid_buffer) - 1 ] = '\0';
                    127:        }
                    128:        if (flags & MYSQLND_DEBUG_DUMP_TIME) {
                    129:                /* The following from FF's DBUG library, which is in the public domain */
                    130: #if defined(PHP_WIN32)
                    131:                /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
                    132:                in system ticks, 10 ms intervals. See my_getsystime.c for high res */
                    133:                SYSTEMTIME loc_t;
                    134:                GetLocalTime(&loc_t);
                    135:                snprintf(time_buffer, sizeof(time_buffer) - 1,
                    136:                                 /* "%04d-%02d-%02d " */
                    137:                                 "%02d:%02d:%02d.%06d ",
                    138:                                 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
                    139:                                 loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
                    140:                time_buffer[sizeof(time_buffer) - 1 ] = '\0';
                    141: #else
                    142:                struct timeval tv;
                    143:                struct tm *tm_p;
                    144:                if (gettimeofday(&tv, NULL) != -1) {
                    145:                        if ((tm_p= localtime((const time_t *)&tv.tv_sec))) {
                    146:                                snprintf(time_buffer, sizeof(time_buffer) - 1,
                    147:                                                 /* "%04d-%02d-%02d " */
                    148:                                                 "%02d:%02d:%02d.%06d ",
                    149:                                                 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
                    150:                                                 tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
                    151:                                                 (int) (tv.tv_usec));
                    152:                                time_buffer[sizeof(time_buffer) - 1 ] = '\0';
                    153:                        }
                    154:                }
                    155: #endif
                    156:        }
                    157:        if (flags & MYSQLND_DEBUG_DUMP_FILE) {
                    158:                snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file);
                    159:                file_buffer[sizeof(file_buffer) - 1 ] = '\0';
                    160:        }
                    161:        if (flags & MYSQLND_DEBUG_DUMP_LINE) {
                    162:                snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line);
                    163:                line_buffer[sizeof(line_buffer) - 1 ] = '\0';
                    164:        }
                    165:        if (flags & MYSQLND_DEBUG_DUMP_LEVEL) {
                    166:                snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level);
                    167:                level_buffer[sizeof(level_buffer) - 1 ] = '\0';
                    168:        }
                    169: 
                    170:        message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
                    171:                                                                flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"",
                    172:                                                                flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"",
                    173:                                                                flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"",
                    174:                                                                flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"",
                    175:                                                                flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"",
                    176:                                                                pipe_buffer, type? type:"", message);
                    177: 
                    178:        ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
                    179:        efree(message_line); /* allocated by spprintf */
                    180:        if (flags & MYSQLND_DEBUG_FLUSH) {
                    181:                self->m->close(self);
                    182:                self->m->open(self, TRUE);
                    183:        }
                    184:        return ret;
                    185: }
                    186: /* }}} */
                    187: 
                    188: 
                    189: /* {{{ mysqlnd_debug::log_va */
                    190: static enum_func_status
                    191: MYSQLND_METHOD(mysqlnd_debug, log_va)(MYSQLND_DEBUG *self,
                    192:                                                                          unsigned int line, const char * const file,
                    193:                                                                          unsigned int level, const char * type,
                    194:                                                                          const char *format, ...)
                    195: {
                    196:        char pipe_buffer[512];
                    197:        int i;
                    198:        enum_func_status ret;
                    199:        char * message_line, *buffer;
                    200:        unsigned int message_line_len;
                    201:        va_list args;
                    202:        unsigned int flags = self->flags;
                    203:        char pid_buffer[10], time_buffer[30], file_buffer[200],
                    204:                 line_buffer[6], level_buffer[7];
                    205:        MYSQLND_ZTS(self);
                    206: 
                    207:        if (!self->stream && FAIL == self->m->open(self, FALSE)) {
                    208:                return FAIL;
                    209:        }
                    210: 
                    211:        if (level == -1) {
                    212:                level = zend_stack_count(&self->call_stack);
                    213:        }
                    214:        i = MIN(level, sizeof(pipe_buffer) / 2  - 1);
                    215:        pipe_buffer[i*2] = '\0';
                    216:        for (;i > 0;i--) {
                    217:                pipe_buffer[i*2 - 1] = ' ';
                    218:                pipe_buffer[i*2 - 2] = '|';
                    219:        }
                    220: 
                    221: 
                    222:        if (flags & MYSQLND_DEBUG_DUMP_PID) {
                    223:                snprintf(pid_buffer, sizeof(pid_buffer) - 1, "%5u: ", self->pid);
                    224:                pid_buffer[sizeof(pid_buffer) - 1 ] = '\0';
                    225:        }
                    226:        if (flags & MYSQLND_DEBUG_DUMP_TIME) {
                    227:                /* The following from FF's DBUG library, which is in the public domain */
                    228: #if defined(PHP_WIN32)
                    229:                /* FIXME This doesn't give microseconds as in Unix case, and the resolution is
                    230:                in system ticks, 10 ms intervals. See my_getsystime.c for high res */
                    231:                SYSTEMTIME loc_t;
                    232:                GetLocalTime(&loc_t);
                    233:                snprintf(time_buffer, sizeof(time_buffer) - 1,
                    234:                                 /* "%04d-%02d-%02d " */
                    235:                                 "%02d:%02d:%02d.%06d ",
                    236:                                 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
                    237:                                 loc_t.wHour, loc_t.wMinute, loc_t.wSecond, loc_t.wMilliseconds);
                    238:                time_buffer[sizeof(time_buffer) - 1 ] = '\0';
                    239: #else
                    240:                struct timeval tv;
                    241:                struct tm *tm_p;
                    242:                if (gettimeofday(&tv, NULL) != -1) {
                    243:                        if ((tm_p= localtime((const time_t *)&tv.tv_sec))) {
                    244:                                snprintf(time_buffer, sizeof(time_buffer) - 1,
                    245:                                                 /* "%04d-%02d-%02d " */
                    246:                                                 "%02d:%02d:%02d.%06d ",
                    247:                                                 /*tm_p->tm_year + 1900, tm_p->tm_mon + 1, tm_p->tm_mday,*/
                    248:                                                 tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec,
                    249:                                                 (int) (tv.tv_usec));
                    250:                                time_buffer[sizeof(time_buffer) - 1 ] = '\0';
                    251:                        }
                    252:                }
                    253: #endif
                    254:        }
                    255:        if (flags & MYSQLND_DEBUG_DUMP_FILE) {
                    256:                snprintf(file_buffer, sizeof(file_buffer) - 1, "%14s: ", file);
                    257:                file_buffer[sizeof(file_buffer) - 1 ] = '\0';
                    258:        }
                    259:        if (flags & MYSQLND_DEBUG_DUMP_LINE) {
                    260:                snprintf(line_buffer, sizeof(line_buffer) - 1, "%5u: ", line);
                    261:                line_buffer[sizeof(line_buffer) - 1 ] = '\0';
                    262:        }
                    263:        if (flags & MYSQLND_DEBUG_DUMP_LEVEL) {
                    264:                snprintf(level_buffer, sizeof(level_buffer) - 1, "%4u: ", level);
                    265:                level_buffer[sizeof(level_buffer) - 1 ] = '\0';
                    266:        }
                    267: 
                    268: 
                    269:        va_start(args, format);
                    270:        vspprintf(&buffer, 0, format, args);
                    271:        va_end(args);
                    272: 
                    273:        message_line_len = spprintf(&message_line, 0, "%s%s%s%s%s%s%s%s\n",
                    274:                                                                flags & MYSQLND_DEBUG_DUMP_PID? pid_buffer:"",
                    275:                                                                flags & MYSQLND_DEBUG_DUMP_TIME? time_buffer:"",
                    276:                                                                flags & MYSQLND_DEBUG_DUMP_FILE? file_buffer:"",
                    277:                                                                flags & MYSQLND_DEBUG_DUMP_LINE? line_buffer:"",
                    278:                                                                flags & MYSQLND_DEBUG_DUMP_LEVEL? level_buffer:"",
                    279:                                                                pipe_buffer, type? type:"", buffer);
                    280:        efree(buffer);
                    281:        ret = php_stream_write(self->stream, message_line, message_line_len)? PASS:FAIL;
                    282:        efree(message_line); /* allocated by spprintf */
                    283: 
                    284:        if (flags & MYSQLND_DEBUG_FLUSH) {
                    285:                self->m->close(self);
                    286:                self->m->open(self, TRUE);
                    287:        }
                    288:        return ret;
                    289: }
                    290: /* }}} */
                    291: 
                    292: 
                    293: /* FALSE - The DBG_ calls won't be traced, TRUE - will be traced */
                    294: /* {{{ mysqlnd_debug::func_enter */
                    295: static zend_bool
                    296: MYSQLND_METHOD(mysqlnd_debug, func_enter)(MYSQLND_DEBUG * self,
                    297:                                                                                  unsigned int line, const char * const file,
                    298:                                                                                  const char * const func_name, unsigned int func_name_len)
                    299: {
                    300:        if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
                    301:                return FALSE;
                    302:        }
                    303:        if ((uint) zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
                    304:                return FALSE;
                    305:        }
                    306: 
                    307:        if ((self->flags & MYSQLND_DEBUG_TRACE_MEMORY_CALLS) == 0 && self->skip_functions) {
                    308:                const char ** p = self->skip_functions;
                    309:                while (*p) {
                    310:                        if (*p == func_name) {
                    311:                                zend_stack_push(&self->call_stack, "", sizeof(""));
                    312: #ifndef MYSQLND_PROFILING_DISABLED
                    313:                                if (self->flags & MYSQLND_DEBUG_PROFILE_CALLS) {
                    314:                                        uint64_t some_time = 0;
                    315:                                        zend_stack_push(&self->call_time_stack, &some_time, sizeof(some_time));
                    316:                                }
                    317: #endif
                    318:                                return FALSE;
                    319:                        }
                    320:                        p++;
                    321:                }
                    322:        }
                    323: 
                    324:        zend_stack_push(&self->call_stack, func_name, func_name_len + 1);
                    325: #ifndef MYSQLND_PROFILING_DISABLED
                    326:        if (self->flags & MYSQLND_DEBUG_PROFILE_CALLS) {
                    327:                uint64_t some_time = 0;
                    328:                zend_stack_push(&self->call_time_stack, &some_time, sizeof(some_time));
                    329:        }
                    330: #endif
                    331: 
                    332:        if (zend_hash_num_elements(&self->not_filtered_functions) &&
                    333:                0 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1))
                    334:        {
                    335:                return FALSE;
                    336:        }
                    337: 
                    338:        self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, ">%s", func_name);
                    339:        return TRUE;
                    340: }
                    341: /* }}} */
                    342: 
                    343: #ifndef MYSQLND_PROFILING_DISABLED
                    344: struct st_mysqlnd_dbg_function_profile {
                    345:        uint64_t calls;
                    346:        uint64_t min_own;
                    347:        uint64_t max_own;
                    348:        uint64_t avg_own;
                    349:        uint64_t own_underporm_calls;
                    350:        uint64_t min_in_calls;
                    351:        uint64_t max_in_calls;
                    352:        uint64_t avg_in_calls;
                    353:        uint64_t in_calls_underporm_calls;
                    354:        uint64_t min_total;
                    355:        uint64_t max_total;
                    356:        uint64_t avg_total;     
                    357:        uint64_t total_underporm_calls;
                    358: };
                    359: #define PROFILE_UNDERPERFORM_THRESHOLD 10
                    360: #endif
                    361: 
                    362: /* {{{ mysqlnd_debug::func_leave */
                    363: static enum_func_status
                    364: MYSQLND_METHOD(mysqlnd_debug, func_leave)(MYSQLND_DEBUG * self, unsigned int line, const char * const file, uint64_t call_time)
                    365: {
                    366:        char *func_name;
                    367:        uint64_t * parent_non_own_time_ptr = NULL, * mine_non_own_time_ptr = NULL;
                    368:        uint64_t mine_non_own_time = 0;
                    369:        zend_bool profile_calls = self->flags & MYSQLND_DEBUG_PROFILE_CALLS? TRUE:FALSE;
                    370: 
                    371:        if ((self->flags & MYSQLND_DEBUG_DUMP_TRACE) == 0 || self->file_name == NULL) {
                    372:                return PASS;
                    373:        }
                    374:        if ((uint) zend_stack_count(&self->call_stack) >= self->nest_level_limit) {
                    375:                return PASS;
                    376:        }
                    377: 
                    378:        zend_stack_top(&self->call_stack, (void **)&func_name);
                    379: 
                    380: #ifndef MYSQLND_PROFILING_DISABLED
                    381:        if (profile_calls) {
                    382:                zend_stack_top(&self->call_time_stack, (void **)&mine_non_own_time_ptr);
                    383:                mine_non_own_time = *mine_non_own_time_ptr;
                    384:                zend_stack_del_top(&self->call_time_stack); /* callee - removing ourselves */
                    385:        }
                    386: #endif
                    387: 
                    388:        if (func_name[0] == '\0') {
                    389:                ; /* don't log that function */
                    390:        } else if (!zend_hash_num_elements(&self->not_filtered_functions) ||
                    391:                           1 == zend_hash_exists(&self->not_filtered_functions, func_name, strlen(func_name) + 1))
                    392:        {
                    393: #ifndef MYSQLND_PROFILING_DISABLED
                    394:                if (FALSE == profile_calls) {
                    395: #endif
                    396:                        self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s", func_name);
                    397: 
                    398: #ifndef MYSQLND_PROFILING_DISABLED
                    399:                } else {
                    400:                        struct st_mysqlnd_dbg_function_profile f_profile_stack = {0};
                    401:                        struct st_mysqlnd_dbg_function_profile * f_profile = NULL;
                    402:                        uint64_t own_time = call_time - mine_non_own_time;
                    403:                        uint func_name_len = strlen(func_name);
                    404: 
                    405:                        self->m->log_va(self, line, file, zend_stack_count(&self->call_stack) - 1, NULL, "<%s (total=%u own=%u in_calls=%u)",
                    406:                                                func_name, (unsigned int) call_time, (unsigned int) own_time, (unsigned int) mine_non_own_time
                    407:                                        );
                    408: 
                    409:                        if (SUCCESS == zend_hash_find(&self->function_profiles, func_name, func_name_len + 1, (void **) &f_profile)) {
                    410:                                /* found */
                    411:                                        if (f_profile) {
                    412:                                        if (mine_non_own_time < f_profile->min_in_calls) {
                    413:                                                f_profile->min_in_calls = mine_non_own_time;
                    414:                                        } else if (mine_non_own_time > f_profile->max_in_calls) {
                    415:                                                f_profile->max_in_calls = mine_non_own_time;
                    416:                                        }
                    417:                                        f_profile->avg_in_calls = (f_profile->avg_in_calls * f_profile->calls + mine_non_own_time) / (f_profile->calls + 1);
                    418: 
                    419:                                        if (own_time < f_profile->min_own) {
                    420:                                                f_profile->min_own = own_time;
                    421:                                        } else if (own_time > f_profile->max_own) {
                    422:                                                f_profile->max_own = own_time;
                    423:                                        }
                    424:                                        f_profile->avg_own = (f_profile->avg_own * f_profile->calls + own_time) / (f_profile->calls + 1);
                    425: 
                    426:                                        if (call_time < f_profile->min_total) {
                    427:                                                f_profile->min_total = call_time;
                    428:                                        } else if (call_time > f_profile->max_total) {
                    429:                                                f_profile->max_total = call_time;
                    430:                                        }
                    431:                                        f_profile->avg_total = (f_profile->avg_total * f_profile->calls + call_time) / (f_profile->calls + 1);
                    432: 
                    433:                                        ++f_profile->calls;
                    434:                                        if (f_profile->calls > PROFILE_UNDERPERFORM_THRESHOLD) {
                    435:                                                if (f_profile->avg_in_calls < mine_non_own_time) {
                    436:                                                        f_profile->in_calls_underporm_calls++;
                    437:                                                }
                    438:                                                if (f_profile->avg_own < own_time) {
                    439:                                                        f_profile->own_underporm_calls++;
                    440:                                                }
                    441:                                                if (f_profile->avg_total < call_time) {
                    442:                                                        f_profile->total_underporm_calls++;
                    443:                                                }
                    444:                                        }
                    445:                                }
                    446:                        } else {
                    447:                                /* add */
                    448:                                f_profile = &f_profile_stack;
                    449:                                f_profile->min_in_calls = f_profile->max_in_calls = f_profile->avg_in_calls = mine_non_own_time;
                    450:                                f_profile->min_total = f_profile->max_total = f_profile->avg_total = call_time;
                    451:                                f_profile->min_own = f_profile->max_own = f_profile->avg_own = own_time;
                    452:                                f_profile->calls = 1;
                    453:                                zend_hash_add(&self->function_profiles, func_name, func_name_len+1, f_profile, sizeof(struct st_mysqlnd_dbg_function_profile), NULL);
                    454:                        }
                    455:                        if ((uint) zend_stack_count(&self->call_time_stack)) {
                    456:                                uint64_t parent_non_own_time = 0;
                    457: 
                    458:                                zend_stack_top(&self->call_time_stack, (void **)&parent_non_own_time_ptr);
                    459:                                parent_non_own_time = *parent_non_own_time_ptr;
                    460:                                parent_non_own_time += call_time;
                    461:                                zend_stack_del_top(&self->call_time_stack); /* the caller */
                    462:                                zend_stack_push(&self->call_time_stack, &parent_non_own_time, sizeof(parent_non_own_time)); /* add back the caller */
                    463:                        }
                    464:                }
                    465: #endif
                    466:        }
                    467: 
                    468:        return zend_stack_del_top(&self->call_stack) == SUCCESS? PASS:FAIL;
                    469: }
                    470: /* }}} */
                    471: 
                    472: 
                    473: /* {{{ mysqlnd_debug::close */
                    474: static enum_func_status
                    475: MYSQLND_METHOD(mysqlnd_debug, close)(MYSQLND_DEBUG * self)
                    476: {
                    477:        MYSQLND_ZTS(self);
                    478:        if (self->stream) {
                    479: #ifndef MYSQLND_PROFILING_DISABLED
                    480:                if (!(self->flags & MYSQLND_DEBUG_FLUSH) && (self->flags & MYSQLND_DEBUG_PROFILE_CALLS)) {
                    481:                        struct st_mysqlnd_dbg_function_profile * f_profile;
                    482:                        HashPosition pos_values;
                    483: 
                    484:                        self->m->log_va(self, __LINE__, __FILE__, 0, "info : ", 
                    485:                                        "number of functions: %d", zend_hash_num_elements(&self->function_profiles));
                    486:                        zend_hash_internal_pointer_reset_ex(&self->function_profiles, &pos_values);
                    487:                        while (zend_hash_get_current_data_ex(&self->function_profiles, (void **) &f_profile, &pos_values) == SUCCESS) {
                    488:                                char    *string_key = NULL;
                    489:                                uint    string_key_len;
                    490:                                ulong   num_key;
                    491: 
                    492:                                zend_hash_get_current_key_ex(&self->function_profiles, &string_key, &string_key_len, &num_key, 0, &pos_values);
                    493: 
                    494:                                self->m->log_va(self, __LINE__, __FILE__, -1, "info : ",
                    495:                                                "%-40s\tcalls=%5llu  own_slow=%5llu  in_calls_slow=%5llu  total_slow=%5llu"
                    496:                                                "   min_own=%5llu  max_own=%7llu  avg_own=%7llu   "
                    497:                                                "   min_in_calls=%5llu  max_in_calls=%7llu  avg_in_calls=%7llu"
                    498:                                                "   min_total=%5llu  max_total=%7llu  avg_total=%7llu"
                    499:                                                ,string_key
                    500:                                                ,(uint64_t) f_profile->calls
                    501:                                                ,(uint64_t) f_profile->own_underporm_calls
                    502:                                                ,(uint64_t) f_profile->in_calls_underporm_calls
                    503:                                                ,(uint64_t) f_profile->total_underporm_calls
                    504:                                                
                    505:                                                ,(uint64_t) f_profile->min_own
                    506:                                                ,(uint64_t) f_profile->max_own
                    507:                                                ,(uint64_t) f_profile->avg_own
                    508:                                                ,(uint64_t) f_profile->min_in_calls
                    509:                                                ,(uint64_t) f_profile->max_in_calls
                    510:                                                ,(uint64_t) f_profile->avg_in_calls
                    511:                                                ,(uint64_t) f_profile->min_total
                    512:                                                ,(uint64_t) f_profile->max_total
                    513:                                                ,(uint64_t) f_profile->avg_total
                    514:                                                );
                    515:                                zend_hash_move_forward_ex(&self->function_profiles, &pos_values);
                    516:                        }
                    517:                }
                    518: #endif 
                    519: 
                    520:                php_stream_free(self->stream, PHP_STREAM_FREE_CLOSE);
                    521:                self->stream = NULL;
                    522:        }
                    523:        /* no DBG_RETURN please */
                    524:        return PASS;
                    525: }
                    526: /* }}} */
                    527: 
                    528: 
                    529: /* {{{ mysqlnd_res_meta::free */
                    530: static enum_func_status
                    531: MYSQLND_METHOD(mysqlnd_debug, free)(MYSQLND_DEBUG * self)
                    532: {
                    533:        if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) {
                    534:                efree(self->file_name);
                    535:                self->file_name = NULL;
                    536:        }
                    537:        zend_stack_destroy(&self->call_stack);
                    538:        zend_stack_destroy(&self->call_time_stack);
                    539:        zend_hash_destroy(&self->not_filtered_functions);
                    540:        zend_hash_destroy(&self->function_profiles);
                    541:        efree(self);
                    542:        return PASS;
                    543: }
                    544: /* }}} */
                    545: 
                    546: enum mysqlnd_debug_parser_state
                    547: {
                    548:        PARSER_WAIT_MODIFIER,
                    549:        PARSER_WAIT_COLON,
                    550:        PARSER_WAIT_VALUE
                    551: };
                    552: 
                    553: 
                    554: /* {{{ mysqlnd_res_meta::set_mode */
                    555: static void
                    556: MYSQLND_METHOD(mysqlnd_debug, set_mode)(MYSQLND_DEBUG * self, const char * const mode)
                    557: {
                    558:        unsigned int mode_len = strlen(mode), i;
                    559:        enum mysqlnd_debug_parser_state state = PARSER_WAIT_MODIFIER;
                    560: 
                    561:        self->flags = 0;
                    562:        self->nest_level_limit = 0;
                    563:        if (self->file_name && self->file_name != mysqlnd_debug_default_trace_file) {
                    564:                efree(self->file_name);
                    565:                self->file_name = NULL;
                    566:        }
                    567:        if (zend_hash_num_elements(&self->not_filtered_functions)) {
                    568:                zend_hash_destroy(&self->not_filtered_functions);
                    569:                zend_hash_init(&self->not_filtered_functions, 0, NULL, NULL, 0);
                    570:        }
                    571: 
                    572:        for (i = 0; i < mode_len; i++) {
                    573:                switch (mode[i]) {
                    574:                        case 'O':
                    575:                        case 'A':
                    576:                                self->flags |= MYSQLND_DEBUG_FLUSH;
                    577:                        case 'a':
                    578:                        case 'o':
                    579:                                if (mode[i] == 'a' || mode[i] == 'A') {
                    580:                                        self->flags |= MYSQLND_DEBUG_APPEND;
                    581:                                }
                    582:                                if (i + 1 < mode_len && mode[i+1] == ',') {
                    583:                                        unsigned int j = i + 2;
                    584: #ifdef PHP_WIN32
                    585:                                        if (i+4 < mode_len && mode[i+3] == ':' && (mode[i+4] == '\\' || mode[i+5] == '/')) {
                    586:                                                j = i + 5;
                    587:                                        }
                    588: #endif
                    589:                                        while (j < mode_len) {
                    590:                                                if (mode[j] == ':') {
                    591:                                                        break;
                    592:                                                }
                    593:                                                j++;
                    594:                                        }
                    595:                                        if (j > i + 2) {
                    596:                                                self->file_name = estrndup(mode + i + 2, j - i - 2);
                    597:                                        }
                    598:                                        i = j;
                    599:                                } else {
                    600:                                        if (!self->file_name)
                    601:                                                self->file_name = (char *) mysqlnd_debug_default_trace_file;
                    602:                                }
                    603:                                state = PARSER_WAIT_COLON;
                    604:                                break;
                    605:                        case ':':
                    606: #if 0
                    607:                                if (state != PARSER_WAIT_COLON) {
                    608:                                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Consecutive semicolons at position %u", i);
                    609:                                }
                    610: #endif
                    611:                                state = PARSER_WAIT_MODIFIER;
                    612:                                break;
                    613:                        case 'f': /* limit output to these functions */
                    614:                                if (i + 1 < mode_len && mode[i+1] == ',') {
                    615:                                        unsigned int j = i + 2;
                    616:                                        i++;
                    617:                                        while (j < mode_len) {
                    618:                                                if (mode[j] == ':') {
                    619:                                                        /* function names with :: */
                    620:                                                        if ((j + 1 < mode_len) && mode[j+1] == ':') {
                    621:                                                                j += 2;
                    622:                                                                continue;
                    623:                                                        }
                    624:                                                }
                    625:                                                if (mode[j] == ',' || mode[j] == ':') {
                    626:                                                        if (j > i + 2) {
                    627:                                                                char func_name[1024];
                    628:                                                                unsigned int func_name_len = MIN(sizeof(func_name) - 1, j - i - 1);
                    629:                                                                memcpy(func_name, mode + i + 1, func_name_len);
                    630:                                                                func_name[func_name_len] = '\0'; 
                    631: 
                    632:                                                                zend_hash_add_empty_element(&self->not_filtered_functions,
                    633:                                                                                                                        func_name, func_name_len + 1);
                    634:                                                                i = j;
                    635:                                                        }
                    636:                                                        if (mode[j] == ':') {
                    637:                                                                break;
                    638:                                                        }
                    639:                                                }
                    640:                                                j++;
                    641:                                        }
                    642:                                        i = j;
                    643:                                } else {
                    644: #if 0
                    645:                                        php_error_docref(NULL TSRMLS_CC, E_WARNING,
                    646:                                                                         "Expected list of functions for '%c' found none", mode[i]);
                    647: #endif
                    648:                                }
                    649:                                state = PARSER_WAIT_COLON;
                    650:                                break;
                    651:                        case 'D':
                    652:                        case 'd':
                    653:                        case 'g':
                    654:                        case 'p':
                    655:                                /* unsupported */
                    656:                                if ((i + 1) < mode_len && mode[i+1] == ',') {
                    657:                                        i+= 2;
                    658:                                        while (i < mode_len) {
                    659:                                                if (mode[i] == ':') {
                    660:                                                        break;
                    661:                                                }
                    662:                                                i++;
                    663:                                        }
                    664:                                }
                    665:                                state = PARSER_WAIT_COLON;
                    666:                                break;
                    667:                        case 'F':
                    668:                                self->flags |= MYSQLND_DEBUG_DUMP_FILE;
                    669:                                state = PARSER_WAIT_COLON;
                    670:                                break;
                    671:                        case 'i':
                    672:                                self->flags |= MYSQLND_DEBUG_DUMP_PID;
                    673:                                state = PARSER_WAIT_COLON;
                    674:                                break;
                    675:                        case 'L':
                    676:                                self->flags |= MYSQLND_DEBUG_DUMP_LINE;
                    677:                                state = PARSER_WAIT_COLON;
                    678:                                break;
                    679:                        case 'n':
                    680:                                self->flags |= MYSQLND_DEBUG_DUMP_LEVEL;
                    681:                                state = PARSER_WAIT_COLON;
                    682:                                break;
                    683:                        case 't':
                    684:                                if (mode[i+1] == ',') {
                    685:                                        unsigned int j = i + 2;
                    686:                                        while (j < mode_len) {
                    687:                                                if (mode[j] == ':') {
                    688:                                                        break;
                    689:                                                }
                    690:                                                j++;
                    691:                                        }
                    692:                                        if (j > i + 2) {
                    693:                                                char *value_str = estrndup(mode + i + 2, j - i - 2);
                    694:                                                self->nest_level_limit = atoi(value_str);
                    695:                                                efree(value_str);
                    696:                                        }
                    697:                                        i = j;
                    698:                                } else {
                    699:                                        self->nest_level_limit = 200; /* default value for FF DBUG */
                    700:                                }
                    701:                                self->flags |= MYSQLND_DEBUG_DUMP_TRACE;
                    702:                                state = PARSER_WAIT_COLON;
                    703:                                break;
                    704:                        case 'T':
                    705:                                self->flags |= MYSQLND_DEBUG_DUMP_TIME;
                    706:                                state = PARSER_WAIT_COLON;
                    707:                                break;
                    708:                        case 'N':
                    709:                        case 'P':
                    710:                        case 'r':
                    711:                        case 'S':
                    712:                                state = PARSER_WAIT_COLON;
                    713:                                break;
                    714:                        case 'm': /* mysqlnd extension - trace memory functions */
                    715:                                self->flags |= MYSQLND_DEBUG_TRACE_MEMORY_CALLS;
                    716:                                state = PARSER_WAIT_COLON;
                    717:                                break;
                    718:                        case 'x': /* mysqlnd extension - profile calls */
                    719:                                self->flags |= MYSQLND_DEBUG_PROFILE_CALLS;
                    720:                                state = PARSER_WAIT_COLON;
                    721:                                break;                          
                    722:                        default:
                    723:                                if (state == PARSER_WAIT_MODIFIER) {
                    724: #if 0
                    725:                                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unrecognized format '%c'", mode[i]);
                    726: #endif
                    727:                                        if (i+1 < mode_len && mode[i+1] == ',') {
                    728:                                                i+= 2;
                    729:                                                while (i < mode_len) {
                    730:                                                        if (mode[i] == ':') {
                    731:                                                                break;
                    732:                                                        }
                    733:                                                        i++;
                    734:                                                }
                    735:                                        }
                    736:                                        state = PARSER_WAIT_COLON;
                    737:                                } else if (state == PARSER_WAIT_COLON) {
                    738: #if 0
                    739:                                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Colon expected, '%c' found", mode[i]);
                    740: #endif
                    741:                                }
                    742:                                break;
                    743:                }
                    744:        }
                    745: }
                    746: /* }}} */
                    747: 
                    748: MYSQLND_CLASS_METHODS_START(mysqlnd_debug)
                    749:        MYSQLND_METHOD(mysqlnd_debug, open),
                    750:        MYSQLND_METHOD(mysqlnd_debug, set_mode),
                    751:        MYSQLND_METHOD(mysqlnd_debug, log),
                    752:        MYSQLND_METHOD(mysqlnd_debug, log_va),
                    753:        MYSQLND_METHOD(mysqlnd_debug, func_enter),
                    754:        MYSQLND_METHOD(mysqlnd_debug, func_leave),
                    755:        MYSQLND_METHOD(mysqlnd_debug, close),
                    756:        MYSQLND_METHOD(mysqlnd_debug, free),
                    757: MYSQLND_CLASS_METHODS_END;
                    758: 
                    759: 
                    760: /* {{{ mysqlnd_debug_init */
                    761: PHPAPI MYSQLND_DEBUG *
                    762: mysqlnd_debug_init(const char * skip_functions[] TSRMLS_DC)
                    763: {
                    764:        MYSQLND_DEBUG *ret = ecalloc(1, sizeof(MYSQLND_DEBUG));
                    765: #ifdef ZTS
                    766:        ret->TSRMLS_C = TSRMLS_C;
                    767: #endif
                    768:        ret->nest_level_limit = 0;
                    769:        ret->pid = getpid();
                    770:        zend_stack_init(&ret->call_stack);
                    771:        zend_stack_init(&ret->call_time_stack);
                    772:        zend_hash_init(&ret->not_filtered_functions, 0, NULL, NULL, 0);
                    773:        zend_hash_init(&ret->function_profiles, 0, NULL, NULL, 0);
                    774: 
                    775:        ret->m = & mysqlnd_mysqlnd_debug_methods;
                    776:        ret->skip_functions = skip_functions;
                    777: 
                    778:        return ret;
                    779: }
                    780: /* }}} */
                    781: 
                    782: 
                    783: /* {{{ _mysqlnd_debug */
                    784: PHPAPI void _mysqlnd_debug(const char * mode TSRMLS_DC)
                    785: {
                    786: #ifdef PHP_DEBUG
                    787:        MYSQLND_DEBUG *dbg = MYSQLND_G(dbg);
                    788:        if (!dbg) {
                    789:                MYSQLND_G(dbg) = dbg = mysqlnd_debug_init(mysqlnd_debug_std_no_trace_funcs TSRMLS_CC);
                    790:                if (!dbg) {
                    791:                        return;
                    792:                }
                    793:        }
                    794: 
                    795:        dbg->m->close(dbg);
                    796:        dbg->m->set_mode(dbg, mode);
                    797:        while (zend_stack_count(&dbg->call_stack)) {
                    798:                zend_stack_del_top(&dbg->call_stack);
                    799:        }
                    800:        while (zend_stack_count(&dbg->call_time_stack)) {
                    801:                zend_stack_del_top(&dbg->call_time_stack);
                    802:        }
                    803: #endif
                    804: }
                    805: /* }}} */
                    806: 
                    807: 
                    808: #if ZEND_DEBUG
                    809: #else
                    810: #define __zend_filename "/unknown/unknown"
                    811: #define __zend_lineno   0
                    812: #endif
                    813: 
                    814: #define REAL_SIZE(s) (collect_memory_statistics? (s) + sizeof(size_t) : (s))
                    815: #define REAL_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) - sizeof(size_t)) : (p))
                    816: #define FAKE_PTR(p) (collect_memory_statistics && (p)? (((char *)(p)) + sizeof(size_t)) : (p))
                    817: 
                    818: /* {{{ _mysqlnd_emalloc */
                    819: void * _mysqlnd_emalloc(size_t size MYSQLND_MEM_D)
                    820: {
                    821:        void *ret;
                    822:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                    823:        long * threshold = &MYSQLND_G(debug_emalloc_fail_threshold);
                    824:        DBG_ENTER(mysqlnd_emalloc_name);
                    825: 
                    826:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                    827: 
                    828: #ifdef PHP_DEBUG
                    829:        /* -1 is also "true" */
                    830:        if (*threshold) {
                    831: #endif
                    832:                ret = emalloc(REAL_SIZE(size));
                    833: #ifdef PHP_DEBUG
                    834:                --*threshold;
                    835:        } else if (*threshold == 0) {
                    836:                ret = NULL;
                    837:        }
                    838: #endif
                    839: 
                    840:        DBG_INF_FMT("size=%lu ptr=%p", size, ret);
                    841: 
                    842:        if (ret && collect_memory_statistics) {
                    843:                *(size_t *) ret = size;
                    844:                MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EMALLOC_COUNT, 1, STAT_MEM_EMALLOC_AMOUNT, size);
                    845:        }
                    846:        DBG_RETURN(FAKE_PTR(ret));
                    847: }
                    848: /* }}} */
                    849: 
                    850: 
                    851: /* {{{ _mysqlnd_pemalloc */
                    852: void * _mysqlnd_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
                    853: {
                    854:        void *ret;
                    855:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                    856:        long * threshold = persistent? &MYSQLND_G(debug_malloc_fail_threshold):&MYSQLND_G(debug_emalloc_fail_threshold);
                    857:        DBG_ENTER(mysqlnd_pemalloc_name);
                    858:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                    859: 
                    860: #ifdef PHP_DEBUG
                    861:        /* -1 is also "true" */
                    862:        if (*threshold) {
                    863: #endif
                    864:                ret = pemalloc(REAL_SIZE(size), persistent);
                    865: #ifdef PHP_DEBUG
                    866:                --*threshold;
                    867:        } else if (*threshold == 0) {
                    868:                ret = NULL;
                    869:        }
                    870: #endif
                    871: 
                    872:        DBG_INF_FMT("size=%lu ptr=%p persistent=%u", size, ret, persistent);
                    873: 
                    874:        if (ret && collect_memory_statistics) {
                    875:                enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_MALLOC_COUNT:STAT_MEM_EMALLOC_COUNT;
                    876:                enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_MALLOC_AMOUNT:STAT_MEM_EMALLOC_AMOUNT;
                    877:                *(size_t *) ret = size;
                    878:                MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
                    879:        }
                    880: 
                    881:        DBG_RETURN(FAKE_PTR(ret));
                    882: }
                    883: /* }}} */
                    884: 
                    885: 
                    886: /* {{{ _mysqlnd_ecalloc */
                    887: void * _mysqlnd_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
                    888: {
                    889:        void *ret;
                    890:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                    891:        long * threshold = &MYSQLND_G(debug_ecalloc_fail_threshold);
                    892:        DBG_ENTER(mysqlnd_ecalloc_name);
                    893:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                    894:        DBG_INF_FMT("before: %lu", zend_memory_usage(FALSE TSRMLS_CC));
                    895: 
                    896: #ifdef PHP_DEBUG
                    897:        /* -1 is also "true" */
                    898:        if (*threshold) {
                    899: #endif
                    900:                ret = ecalloc(nmemb, REAL_SIZE(size));
                    901: #ifdef PHP_DEBUG
                    902:                --*threshold;
                    903:        } else if (*threshold == 0) {
                    904:                ret = NULL;
                    905:        }
                    906: #endif
                    907: 
                    908:        DBG_INF_FMT("after : %lu", zend_memory_usage(FALSE TSRMLS_CC));
                    909:        DBG_INF_FMT("size=%lu ptr=%p", size, ret);
                    910:        if (ret && collect_memory_statistics) {
                    911:                *(size_t *) ret = size;
                    912:                MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_ECALLOC_COUNT, 1, STAT_MEM_ECALLOC_AMOUNT, size);
                    913:        }
                    914:        DBG_RETURN(FAKE_PTR(ret));
                    915: }
                    916: /* }}} */
                    917: 
                    918: 
                    919: /* {{{ _mysqlnd_pecalloc */
                    920: void * _mysqlnd_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
                    921: {
                    922:        void *ret;
                    923:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                    924:        long * threshold = persistent? &MYSQLND_G(debug_calloc_fail_threshold):&MYSQLND_G(debug_ecalloc_fail_threshold);
                    925:        DBG_ENTER(mysqlnd_pecalloc_name);
                    926:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                    927: 
                    928: #ifdef PHP_DEBUG
                    929:        /* -1 is also "true" */
                    930:        if (*threshold) {
                    931: #endif
                    932:                ret = pecalloc(nmemb, REAL_SIZE(size), persistent);
                    933: #ifdef PHP_DEBUG
                    934:                --*threshold;
                    935:        } else if (*threshold == 0) {
                    936:                ret = NULL;
                    937:        }
                    938: #endif
                    939: 
                    940:        DBG_INF_FMT("size=%lu ptr=%p", size, ret);
                    941: 
                    942:        if (ret && collect_memory_statistics) {
                    943:                enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_CALLOC_COUNT:STAT_MEM_ECALLOC_COUNT;
                    944:                enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_CALLOC_AMOUNT:STAT_MEM_ECALLOC_AMOUNT;
                    945:                *(size_t *) ret = size;
                    946:                MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, size);
                    947:        }
                    948: 
                    949:        DBG_RETURN(FAKE_PTR(ret));
                    950: }
                    951: /* }}} */
                    952: 
                    953: 
                    954: /* {{{ _mysqlnd_erealloc */
                    955: void * _mysqlnd_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
                    956: {
                    957:        void *ret;
                    958:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                    959:        size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0;
                    960:        long * threshold = &MYSQLND_G(debug_erealloc_fail_threshold);
                    961:        DBG_ENTER(mysqlnd_erealloc_name);
                    962:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                    963:        DBG_INF_FMT("ptr=%p old_size=%lu, new_size=%lu", ptr, old_size, new_size); 
                    964: 
                    965: #ifdef PHP_DEBUG
                    966:        /* -1 is also "true" */
                    967:        if (*threshold) {
                    968: #endif
                    969:                ret = erealloc(REAL_PTR(ptr), REAL_SIZE(new_size));
                    970: #ifdef PHP_DEBUG
                    971:                --*threshold;
                    972:        } else if (*threshold == 0) {
                    973:                ret = NULL;
                    974:        }
                    975: #endif
                    976: 
                    977:        DBG_INF_FMT("new_ptr=%p", (char*)ret);
                    978:        if (ret && collect_memory_statistics) {
                    979:                *(size_t *) ret = new_size;
                    980:                MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EREALLOC_COUNT, 1, STAT_MEM_EREALLOC_AMOUNT, new_size);
                    981:        }
                    982:        DBG_RETURN(FAKE_PTR(ret));
                    983: }
                    984: /* }}} */
                    985: 
                    986: 
                    987: /* {{{ _mysqlnd_perealloc */
                    988: void * _mysqlnd_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
                    989: {
                    990:        void *ret;
                    991:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                    992:        size_t old_size = collect_memory_statistics && ptr? *(size_t *) (((char*)ptr) - sizeof(size_t)) : 0;
                    993:        long * threshold = persistent? &MYSQLND_G(debug_realloc_fail_threshold):&MYSQLND_G(debug_erealloc_fail_threshold);
                    994:        DBG_ENTER(mysqlnd_perealloc_name);
                    995:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                    996:        DBG_INF_FMT("ptr=%p old_size=%lu new_size=%lu persistent=%u", ptr, old_size, new_size, persistent); 
                    997: 
                    998: #ifdef PHP_DEBUG
                    999:        /* -1 is also "true" */
                   1000:        if (*threshold) {
                   1001: #endif
                   1002:                ret = perealloc(REAL_PTR(ptr), REAL_SIZE(new_size), persistent);
                   1003: #ifdef PHP_DEBUG
                   1004:                --*threshold;
                   1005:        } else if (*threshold == 0) {
                   1006:                ret = NULL;
                   1007:        }
                   1008: #endif
                   1009: 
                   1010:        DBG_INF_FMT("new_ptr=%p", (char*)ret);
                   1011: 
                   1012:        if (ret && collect_memory_statistics) {
                   1013:                enum mysqlnd_collected_stats s1 = persistent? STAT_MEM_REALLOC_COUNT:STAT_MEM_EREALLOC_COUNT;
                   1014:                enum mysqlnd_collected_stats s2 = persistent? STAT_MEM_REALLOC_AMOUNT:STAT_MEM_EREALLOC_AMOUNT;
                   1015:                *(size_t *) ret = new_size;
                   1016:                MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(s1, 1, s2, new_size);
                   1017:        }
                   1018:        DBG_RETURN(FAKE_PTR(ret));
                   1019: }
                   1020: /* }}} */
                   1021: 
                   1022: 
                   1023: /* {{{ _mysqlnd_efree */
                   1024: void _mysqlnd_efree(void *ptr MYSQLND_MEM_D)
                   1025: {
                   1026:        size_t free_amount = 0;
                   1027:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                   1028:        DBG_ENTER(mysqlnd_efree_name);
                   1029:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                   1030:        DBG_INF_FMT("ptr=%p", ptr); 
                   1031: 
                   1032:        if (ptr) {
                   1033:                if (collect_memory_statistics) {
                   1034:                        free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
                   1035:                        DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
                   1036:                }
                   1037:                efree(REAL_PTR(ptr));
                   1038:        }
                   1039: 
                   1040:        if (collect_memory_statistics) {
                   1041:                MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_EFREE_COUNT, 1, STAT_MEM_EFREE_AMOUNT, free_amount);
                   1042:        }
                   1043:        DBG_VOID_RETURN;
                   1044: }
                   1045: /* }}} */
                   1046: 
                   1047: 
                   1048: /* {{{ _mysqlnd_pefree */
                   1049: void _mysqlnd_pefree(void *ptr, zend_bool persistent MYSQLND_MEM_D)
                   1050: {
                   1051:        size_t free_amount = 0;
                   1052:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                   1053:        DBG_ENTER(mysqlnd_pefree_name);
                   1054:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                   1055:        DBG_INF_FMT("ptr=%p persistent=%u", ptr, persistent); 
                   1056: 
                   1057:        if (ptr) {
                   1058:                if (collect_memory_statistics) {
                   1059:                        free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
                   1060:                        DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
                   1061:                }
                   1062:                pefree(REAL_PTR(ptr), persistent);
                   1063:        }
                   1064: 
                   1065:        if (collect_memory_statistics) {
                   1066:                MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(persistent? STAT_MEM_FREE_COUNT:STAT_MEM_EFREE_COUNT, 1,
                   1067:                                                                                          persistent? STAT_MEM_FREE_AMOUNT:STAT_MEM_EFREE_AMOUNT, free_amount);
                   1068:        }
                   1069:        DBG_VOID_RETURN;
                   1070: }
                   1071: 
                   1072: 
                   1073: /* {{{ _mysqlnd_malloc */
                   1074: void * _mysqlnd_malloc(size_t size MYSQLND_MEM_D)
                   1075: {
                   1076:        void *ret;
                   1077:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                   1078:        long * threshold = &MYSQLND_G(debug_malloc_fail_threshold);
                   1079:        DBG_ENTER(mysqlnd_malloc_name);
                   1080:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                   1081: 
                   1082: #ifdef PHP_DEBUG
                   1083:        /* -1 is also "true" */
                   1084:        if (*threshold) {
                   1085: #endif
                   1086:                ret = malloc(REAL_SIZE(size));
                   1087: #ifdef PHP_DEBUG
                   1088:                --*threshold;
                   1089:        } else if (*threshold == 0) {
                   1090:                ret = NULL;
                   1091:        }
                   1092: #endif
                   1093: 
                   1094:        DBG_INF_FMT("size=%lu ptr=%p", size, ret);
                   1095:        if (ret && collect_memory_statistics) {
                   1096:                *(size_t *) ret = size;
                   1097:                MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_MALLOC_COUNT, 1, STAT_MEM_MALLOC_AMOUNT, size);
                   1098:        }
                   1099:        DBG_RETURN(FAKE_PTR(ret));
                   1100: }
                   1101: /* }}} */
                   1102: 
                   1103: 
                   1104: /* {{{ _mysqlnd_calloc */
                   1105: void * _mysqlnd_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
                   1106: {
                   1107:        void *ret;
                   1108:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                   1109:        long * threshold = &MYSQLND_G(debug_calloc_fail_threshold);
                   1110:        DBG_ENTER(mysqlnd_calloc_name);
                   1111:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                   1112: 
                   1113: #ifdef PHP_DEBUG
                   1114:        /* -1 is also "true" */
                   1115:        if (*threshold) {
                   1116: #endif
                   1117:                ret = calloc(nmemb, REAL_SIZE(size));
                   1118: #ifdef PHP_DEBUG
                   1119:                --*threshold;
                   1120:        } else if (*threshold == 0) {
                   1121:                ret = NULL;
                   1122:        }
                   1123: #endif
                   1124: 
                   1125:        DBG_INF_FMT("size=%lu ptr=%p", size, ret);
                   1126:        if (ret && collect_memory_statistics) {
                   1127:                *(size_t *) ret = size;
                   1128:                MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_CALLOC_COUNT, 1, STAT_MEM_CALLOC_AMOUNT, size);
                   1129:        }
                   1130:        DBG_RETURN(FAKE_PTR(ret));
                   1131: }
                   1132: /* }}} */
                   1133: 
                   1134: 
                   1135: /* {{{ _mysqlnd_realloc */
                   1136: void * _mysqlnd_realloc(void *ptr, size_t new_size MYSQLND_MEM_D)
                   1137: {
                   1138:        void *ret;
                   1139:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                   1140:        long * threshold = &MYSQLND_G(debug_realloc_fail_threshold);
                   1141:        DBG_ENTER(mysqlnd_realloc_name);
                   1142:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                   1143:        DBG_INF_FMT("ptr=%p new_size=%lu ", new_size, ptr); 
                   1144:        DBG_INF_FMT("before: %lu", zend_memory_usage(TRUE TSRMLS_CC));
                   1145: 
                   1146: #ifdef PHP_DEBUG
                   1147:        /* -1 is also "true" */
                   1148:        if (*threshold) {
                   1149: #endif
                   1150:                ret = realloc(REAL_PTR(ptr), REAL_SIZE(new_size));
                   1151: #ifdef PHP_DEBUG
                   1152:                --*threshold;
                   1153:        } else if (*threshold == 0) {
                   1154:                ret = NULL;
                   1155:        }
                   1156: #endif
                   1157: 
                   1158:        DBG_INF_FMT("new_ptr=%p", (char*)ret);
                   1159: 
                   1160:        if (ret && collect_memory_statistics) {
                   1161:                *(size_t *) ret = new_size;
                   1162:                MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_REALLOC_COUNT, 1, STAT_MEM_REALLOC_AMOUNT, new_size);
                   1163:        }
                   1164:        DBG_RETURN(FAKE_PTR(ret));
                   1165: }
                   1166: /* }}} */
                   1167: 
                   1168: 
                   1169: /* {{{ _mysqlnd_free */
                   1170: void _mysqlnd_free(void *ptr MYSQLND_MEM_D)
                   1171: {
                   1172:        size_t free_amount = 0;
                   1173:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                   1174:        DBG_ENTER(mysqlnd_free_name);
                   1175:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                   1176:        DBG_INF_FMT("ptr=%p", ptr); 
                   1177: 
                   1178:        if (ptr) {
                   1179:                if (collect_memory_statistics) {
                   1180:                        free_amount = *(size_t *)(((char*)ptr) - sizeof(size_t));
                   1181:                        DBG_INF_FMT("ptr=%p size=%u", ((char*)ptr) - sizeof(size_t), (unsigned int) free_amount);
                   1182:                }
                   1183:                free(REAL_PTR(ptr));
                   1184:        }
                   1185: 
                   1186:        if (collect_memory_statistics) {
                   1187:                MYSQLND_INC_GLOBAL_STATISTIC_W_VALUE2(STAT_MEM_FREE_COUNT, 1, STAT_MEM_FREE_AMOUNT, free_amount);
                   1188:        }
                   1189:        DBG_VOID_RETURN;
                   1190: }
                   1191: /* }}} */
                   1192: 
                   1193: #define SMART_STR_START_SIZE 2048
                   1194: #define SMART_STR_PREALLOC 512
                   1195: #include "ext/standard/php_smart_str.h"
                   1196: 
                   1197: 
                   1198: /* {{{ _mysqlnd_pestrndup */
                   1199: char * _mysqlnd_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
                   1200: {
                   1201:        char * ret;
                   1202:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                   1203:        DBG_ENTER(mysqlnd_pestrndup_name);
                   1204:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                   1205:        DBG_INF_FMT("ptr=%p", ptr); 
                   1206: 
                   1207:        ret = pemalloc(REAL_SIZE(length) + 1, persistent);
                   1208:        {
                   1209:                size_t l = length;
                   1210:                char * p = (char *) ptr;
                   1211:                char * dest = (char *) FAKE_PTR(ret);
                   1212:                while (*p && l--) {
                   1213:                        *dest++ = *p++;
                   1214:                }
                   1215:                *dest = '\0';
                   1216:        }
                   1217: 
                   1218:        if (collect_memory_statistics) {
                   1219:                *(size_t *) ret = length;
                   1220:                MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRNDUP_COUNT : STAT_MEM_ESTRNDUP_COUNT);
                   1221:        }
                   1222: 
                   1223:        DBG_RETURN(FAKE_PTR(ret));
                   1224: }
                   1225: /* }}} */
                   1226: 
                   1227: 
                   1228: /* {{{ _mysqlnd_pestrdup */
                   1229: char * _mysqlnd_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
                   1230: {
                   1231:        char * ret;
                   1232:        smart_str tmp_str = {0, 0, 0};
                   1233:        const char * p = ptr;
                   1234:        zend_bool collect_memory_statistics = MYSQLND_G(collect_memory_statistics);
                   1235:        DBG_ENTER(mysqlnd_pestrdup_name);
                   1236:        DBG_INF_FMT("file=%-15s line=%4d", strrchr(__zend_filename, PHP_DIR_SEPARATOR) + 1, __zend_lineno);
                   1237:        DBG_INF_FMT("ptr=%p", ptr);
                   1238:        do {
                   1239:                smart_str_appendc(&tmp_str, *p);
                   1240:        } while (*p++);
                   1241: 
                   1242:        ret = pemalloc(tmp_str.len + sizeof(size_t), persistent);
                   1243:        memcpy(FAKE_PTR(ret), tmp_str.c, tmp_str.len);
                   1244: 
                   1245:        if (ret && collect_memory_statistics) {
                   1246:                *(size_t *) ret = tmp_str.len;
                   1247:                MYSQLND_INC_GLOBAL_STATISTIC(persistent? STAT_MEM_STRDUP_COUNT : STAT_MEM_ESTRDUP_COUNT);
                   1248:        }
                   1249:        smart_str_free(&tmp_str);
                   1250: 
                   1251:        DBG_RETURN(FAKE_PTR(ret));
                   1252: }
                   1253: /* }}} */
                   1254: 
                   1255: #define MYSQLND_DEBUG_MEMORY 1
                   1256: 
                   1257: #if MYSQLND_DEBUG_MEMORY == 0
                   1258: 
                   1259: /* {{{ mysqlnd_zend_mm_emalloc */
                   1260: static void * mysqlnd_zend_mm_emalloc(size_t size MYSQLND_MEM_D)
                   1261: {
                   1262:        return emalloc(size);
                   1263: }
                   1264: /* }}} */
                   1265: 
                   1266: 
                   1267: /* {{{ mysqlnd_zend_mm_pemalloc */
                   1268: static void * mysqlnd_zend_mm_pemalloc(size_t size, zend_bool persistent MYSQLND_MEM_D)
                   1269: {
                   1270:        return pemalloc(size, persistent);
                   1271: }
                   1272: /* }}} */
                   1273: 
                   1274: 
                   1275: /* {{{ mysqlnd_zend_mm_ecalloc */
                   1276: static void * mysqlnd_zend_mm_ecalloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
                   1277: {
                   1278:        return ecalloc(nmemb, size);
                   1279: }
                   1280: /* }}} */
                   1281: 
                   1282: 
                   1283: /* {{{ mysqlnd_zend_mm_pecalloc */
                   1284: static void * mysqlnd_zend_mm_pecalloc(unsigned int nmemb, size_t size, zend_bool persistent MYSQLND_MEM_D)
                   1285: {
                   1286:        return pecalloc(nmemb, size, persistent);
                   1287: }
                   1288: /* }}} */
                   1289: 
                   1290: 
                   1291: /* {{{ mysqlnd_zend_mm_erealloc */
                   1292: static void * mysqlnd_zend_mm_erealloc(void *ptr, size_t new_size MYSQLND_MEM_D)
                   1293: {
                   1294:        return erealloc(ptr, new_size);
                   1295: }
                   1296: /* }}} */
                   1297: 
                   1298: 
                   1299: /* {{{ mysqlnd_zend_mm_perealloc */
                   1300: static void * mysqlnd_zend_mm_perealloc(void *ptr, size_t new_size, zend_bool persistent MYSQLND_MEM_D)
                   1301: {
                   1302:        return perealloc(ptr, new_size, persistent);
                   1303: }
                   1304: /* }}} */
                   1305: 
                   1306: 
                   1307: /* {{{ mysqlnd_zend_mm_efree */
                   1308: static void mysqlnd_zend_mm_efree(void * ptr MYSQLND_MEM_D)
                   1309: {
                   1310:        efree(ptr);
                   1311: }
                   1312: /* }}} */
                   1313: 
                   1314: 
                   1315: /* {{{ mysqlnd_zend_mm_pefree */
                   1316: static void mysqlnd_zend_mm_pefree(void * ptr, zend_bool persistent MYSQLND_MEM_D)
                   1317: {
                   1318:        pefree(ptr, persistent);
                   1319: }
                   1320: /* }}} */
                   1321: 
                   1322: 
                   1323: /* {{{ mysqlnd_zend_mm_malloc */
                   1324: static void * mysqlnd_zend_mm_malloc(size_t size MYSQLND_MEM_D)
                   1325: {
                   1326:        return malloc(size);
                   1327: }
                   1328: /* }}} */
                   1329: 
                   1330: 
                   1331: /* {{{ mysqlnd_zend_mm_calloc */
                   1332: static void * mysqlnd_zend_mm_calloc(unsigned int nmemb, size_t size MYSQLND_MEM_D)
                   1333: {
                   1334:        return calloc(nmemb, size);
                   1335: }
                   1336: /* }}} */
                   1337: 
                   1338: 
                   1339: /* {{{ mysqlnd_zend_mm_realloc */
                   1340: static void * mysqlnd_zend_mm_realloc(void * ptr, size_t new_size MYSQLND_MEM_D)
                   1341: {
                   1342:        return realloc(ptr, new_size);
                   1343: }
                   1344: /* }}} */
                   1345: 
                   1346: 
                   1347: /* {{{ mysqlnd_zend_mm_free */
                   1348: static void mysqlnd_zend_mm_free(void * ptr MYSQLND_MEM_D)
                   1349: {
                   1350:        free(ptr);
                   1351: }
                   1352: /* }}} */
                   1353: 
                   1354: 
                   1355: /* {{{ mysqlnd_zend_mm_pestrndup */
                   1356: static char * mysqlnd_zend_mm_pestrndup(const char * const ptr, size_t length, zend_bool persistent MYSQLND_MEM_D)
                   1357: {
                   1358:        return pestrndup(ptr, length, persistent);
                   1359: }
                   1360: /* }}} */
                   1361: 
                   1362: 
                   1363: /* {{{ mysqlnd_zend_mm_pestrdup */
                   1364: static char * mysqlnd_zend_mm_pestrdup(const char * const ptr, zend_bool persistent MYSQLND_MEM_D)
                   1365: {
                   1366:        return pestrdup(ptr, persistent);
                   1367: }
                   1368: /* }}} */
                   1369: 
                   1370: #endif
                   1371: 
                   1372: 
                   1373: PHPAPI struct st_mysqlnd_allocator_methods mysqlnd_allocator = 
                   1374: {
                   1375: #if MYSQLND_DEBUG_MEMORY
                   1376:        _mysqlnd_emalloc,
                   1377:        _mysqlnd_pemalloc,
                   1378:        _mysqlnd_ecalloc,
                   1379:        _mysqlnd_pecalloc,
                   1380:        _mysqlnd_erealloc,
                   1381:        _mysqlnd_perealloc,
                   1382:        _mysqlnd_efree,
                   1383:        _mysqlnd_pefree,
                   1384:        _mysqlnd_malloc,
                   1385:        _mysqlnd_calloc,
                   1386:        _mysqlnd_realloc,
                   1387:        _mysqlnd_free,
                   1388:        _mysqlnd_pestrndup,
                   1389:        _mysqlnd_pestrdup
                   1390: #else
                   1391:        mysqlnd_zend_mm_emalloc,
                   1392:        mysqlnd_zend_mm_pemalloc,
                   1393:        mysqlnd_zend_mm_ecalloc,
                   1394:        mysqlnd_zend_mm_pecalloc,
                   1395:        mysqlnd_zend_mm_erealloc,
                   1396:        mysqlnd_zend_mm_perealloc,
                   1397:        mysqlnd_zend_mm_efree,
                   1398:        mysqlnd_zend_mm_pefree,
                   1399:        mysqlnd_zend_mm_malloc,
                   1400:        mysqlnd_zend_mm_calloc,
                   1401:        mysqlnd_zend_mm_realloc,
                   1402:        mysqlnd_zend_mm_free,
                   1403:        mysqlnd_zend_mm_pestrndup,
                   1404:        mysqlnd_zend_mm_pestrdup
                   1405: #endif
                   1406: };
                   1407: 
                   1408: 
                   1409: 
                   1410: /* Follows code borrowed from zend_builtin_functions.c because the functions there are static */
                   1411: 
                   1412: #if MYSQLND_UNICODE
                   1413: /* {{{ gettraceasstring() macros */
                   1414: #define TRACE_APPEND_CHR(chr)                                            \
                   1415:        *str = (char*)erealloc(*str, *len + 1 + 1);                          \
                   1416:        (*str)[(*len)++] = chr
                   1417: 
                   1418: #define TRACE_APPEND_STRL(val, vallen)                                   \
                   1419:        {                                                                    \
                   1420:                int l = vallen;                                                  \
                   1421:                *str = (char*)erealloc(*str, *len + l + 1);                      \
                   1422:                memcpy((*str) + *len, val, l);                                   \
                   1423:                *len += l;                                                       \
                   1424:        }
                   1425: 
                   1426: #define TRACE_APPEND_USTRL(val, vallen) \
                   1427:        { \
                   1428:                zval tmp, copy; \
                   1429:                int use_copy; \
                   1430:                ZVAL_UNICODEL(&tmp, val, vallen, 1); \
                   1431:                zend_make_printable_zval(&tmp, &copy, &use_copy); \
                   1432:                TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
                   1433:                zval_dtor(&copy); \
                   1434:                zval_dtor(&tmp); \
                   1435:        }
                   1436: 
                   1437: #define TRACE_APPEND_ZVAL(zv) \
                   1438:        if (Z_TYPE_P((zv)) == IS_UNICODE) { \
                   1439:                zval copy; \
                   1440:                int use_copy; \
                   1441:                zend_make_printable_zval((zv), &copy, &use_copy); \
                   1442:                TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
                   1443:                zval_dtor(&copy); \
                   1444:        } else { \
                   1445:                TRACE_APPEND_STRL(Z_STRVAL_P((zv)), Z_STRLEN_P((zv))); \
                   1446:        }
                   1447: 
                   1448: #define TRACE_APPEND_STR(val)                                            \
                   1449:        TRACE_APPEND_STRL(val, sizeof(val)-1)
                   1450: 
                   1451: #define TRACE_APPEND_KEY(key)                                            \
                   1452:        if (zend_ascii_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
                   1453:                if (Z_TYPE_PP(tmp) == IS_UNICODE) { \
                   1454:                        zval copy; \
                   1455:                        int use_copy; \
                   1456:                        zend_make_printable_zval(*tmp, &copy, &use_copy); \
                   1457:                        TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
                   1458:                        zval_dtor(&copy); \
                   1459:                } else { \
                   1460:                TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));           \
                   1461:                } \
                   1462:        }
                   1463: /* }}} */
                   1464: 
                   1465: /* {{{ mysqlnd_build_trace_args */
                   1466: static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
                   1467: {
                   1468:        char **str;
                   1469:        int *len;
                   1470: 
                   1471:        str = va_arg(args, char**);
                   1472:        len = va_arg(args, int*);
                   1473: 
                   1474:        /* the trivial way would be to do:
                   1475:         * conver_to_string_ex(arg);
                   1476:         * append it and kill the now tmp arg.
                   1477:         * but that could cause some E_NOTICE and also damn long lines.
                   1478:         */
                   1479: 
                   1480:        switch (Z_TYPE_PP(arg)) {
                   1481:                case IS_NULL:
                   1482:                        TRACE_APPEND_STR("NULL, ");
                   1483:                        break;
                   1484:                case IS_STRING: {
                   1485:                        int l_added;
                   1486:                        TRACE_APPEND_CHR('\'');
                   1487:                        if (Z_STRLEN_PP(arg) > 15) {
                   1488:                                TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
                   1489:                                TRACE_APPEND_STR("...', ");
                   1490:                                l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
                   1491:                        } else {
                   1492:                                l_added = Z_STRLEN_PP(arg);
                   1493:                                TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
                   1494:                                TRACE_APPEND_STR("', ");
                   1495:                                l_added += 3 + 1;
                   1496:                        }
                   1497:                        while (--l_added) {
                   1498:                                if ((unsigned char)(*str)[*len - l_added] < 32) {
                   1499:                                        (*str)[*len - l_added] = '?';
                   1500:                                }
                   1501:                        }
                   1502:                        break;
                   1503:                }
                   1504:                case IS_UNICODE: {
                   1505:                        int l_added;
                   1506: 
                   1507:                        /*
                   1508:                         * We do not want to apply current error mode here, since
                   1509:                         * zend_make_printable_zval() uses output encoding converter.
                   1510:                         * Temporarily set output encoding converter to escape offending
                   1511:                         * chars with \uXXXX notation.
                   1512:                         */
                   1513:                        zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, ZEND_CONV_ERROR_ESCAPE_JAVA);
                   1514:                        TRACE_APPEND_CHR('\'');
                   1515:                        if (Z_USTRLEN_PP(arg) > 15) {
                   1516:                                TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), 15);
                   1517:                                TRACE_APPEND_STR("...', ");
                   1518:                                l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
                   1519:                        } else {
                   1520:                                l_added = Z_USTRLEN_PP(arg);
                   1521:                                TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), l_added);
                   1522:                                TRACE_APPEND_STR("', ");
                   1523:                                l_added += 3 + 1;
                   1524:                        }
                   1525:                        /*
                   1526:                         * Reset output encoding converter error mode.
                   1527:                         */
                   1528:                        zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, UG(from_error_mode));
                   1529:                        while (--l_added) {
                   1530:                                if ((unsigned char)(*str)[*len - l_added] < 32) {
                   1531:                                        (*str)[*len - l_added] = '?';
                   1532:                                }
                   1533:                        }
                   1534:                        break;
                   1535:                }
                   1536:                case IS_BOOL:
                   1537:                        if (Z_LVAL_PP(arg)) {
                   1538:                                TRACE_APPEND_STR("true, ");
                   1539:                        } else {
                   1540:                                TRACE_APPEND_STR("false, ");
                   1541:                        }
                   1542:                        break;
                   1543:                case IS_RESOURCE:
                   1544:                        TRACE_APPEND_STR("Resource id #");
                   1545:                        /* break; */
                   1546:                case IS_LONG: {
                   1547:                        long lval = Z_LVAL_PP(arg);
                   1548:                        char s_tmp[MAX_LENGTH_OF_LONG + 1];
                   1549:                        int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */
                   1550:                        TRACE_APPEND_STRL(s_tmp, l_tmp);
                   1551:                        TRACE_APPEND_STR(", ");
                   1552:                        break;
                   1553:                }
                   1554:                case IS_DOUBLE: {
                   1555:                        double dval = Z_DVAL_PP(arg);
                   1556:                        char *s_tmp;
                   1557:                        int l_tmp;
                   1558: 
                   1559:                        s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
                   1560:                        l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */
                   1561:                        TRACE_APPEND_STRL(s_tmp, l_tmp);
                   1562:                        /* %G already handles removing trailing zeros from the fractional part, yay */
                   1563:                        efree(s_tmp);
                   1564:                        TRACE_APPEND_STR(", ");
                   1565:                        break;
                   1566:                }
                   1567:                case IS_ARRAY:
                   1568:                        TRACE_APPEND_STR("Array, ");
                   1569:                        break;
                   1570:                case IS_OBJECT: {
                   1571:                        zval tmp;
                   1572:                        zstr class_name;
                   1573:                        zend_uint class_name_len;
                   1574:                        int dup;
                   1575: 
                   1576:                        TRACE_APPEND_STR("Object(");
                   1577: 
                   1578:                        dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
                   1579: 
                   1580:                        ZVAL_UNICODEL(&tmp, class_name.u, class_name_len, 1);
                   1581:                        convert_to_string_with_converter(&tmp, ZEND_U_CONVERTER(UG(output_encoding_conv)));
                   1582:                        TRACE_APPEND_STRL(Z_STRVAL(tmp), Z_STRLEN(tmp));
                   1583:                        zval_dtor(&tmp);
                   1584: 
                   1585:                        if(!dup) {
                   1586:                                efree(class_name.v);
                   1587:                        }
                   1588: 
                   1589:                        TRACE_APPEND_STR("), ");
                   1590:                        break;
                   1591:                }
                   1592:                default:
                   1593:                        break;
                   1594:        }
                   1595:        return ZEND_HASH_APPLY_KEEP;
                   1596: }
                   1597: /* }}} */
                   1598: 
                   1599: 
                   1600: static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
                   1601: {
                   1602:        char *s_tmp, **str;
                   1603:        int *len, *num;
                   1604:        long line;
                   1605:        HashTable *ht = Z_ARRVAL_PP(frame);
                   1606:        zval **file, **tmp;
                   1607:        uint * level;
                   1608: 
                   1609:        level = va_arg(args, uint *);
                   1610:        str = va_arg(args, char**);
                   1611:        len = va_arg(args, int*);
                   1612:        num = va_arg(args, int*);
                   1613: 
                   1614:        if (!*level) {
                   1615:                return ZEND_HASH_APPLY_KEEP;
                   1616:        }
                   1617:        --*level;
                   1618: 
                   1619:        s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
                   1620:        sprintf(s_tmp, "#%d ", (*num)++);
                   1621:        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                   1622:        efree(s_tmp);
                   1623:        if (zend_ascii_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
                   1624:                if (zend_ascii_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
                   1625:                        line = Z_LVAL_PP(tmp);
                   1626:                } else {
                   1627:                        line = 0;
                   1628:                }
                   1629:                TRACE_APPEND_ZVAL(*file);
                   1630:                s_tmp = emalloc(MAX_LENGTH_OF_LONG + 2 + 1);
                   1631:                sprintf(s_tmp, "(%ld): ", line);
                   1632:                TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                   1633:                efree(s_tmp);
                   1634:        } else {
                   1635:                TRACE_APPEND_STR("[internal function]: ");
                   1636:        }
                   1637:        TRACE_APPEND_KEY("class");
                   1638:        TRACE_APPEND_KEY("type");
                   1639:        TRACE_APPEND_KEY("function");
                   1640:        TRACE_APPEND_CHR('(');
                   1641:        if (zend_ascii_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
                   1642:                int last_len = *len;
                   1643:                zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
                   1644:                if (last_len != *len) {
                   1645:                        *len -= 2; /* remove last ', ' */
                   1646:                }
                   1647:        }
                   1648:        TRACE_APPEND_STR(")\n");
                   1649:        return ZEND_HASH_APPLY_KEEP;
                   1650: }
                   1651: /* }}} */
                   1652: 
                   1653: 
                   1654: #else /* PHP 5*/
                   1655: 
                   1656: 
                   1657: /* {{{ gettraceasstring() macros */
                   1658: #define TRACE_APPEND_CHR(chr)                                            \
                   1659:        *str = (char*)erealloc(*str, *len + 1 + 1);                          \
                   1660:        (*str)[(*len)++] = chr
                   1661: 
                   1662: #define TRACE_APPEND_STRL(val, vallen)                                   \
                   1663:        {                                                                    \
                   1664:                int l = vallen;                                                  \
                   1665:                *str = (char*)erealloc(*str, *len + l + 1);                      \
                   1666:                memcpy((*str) + *len, val, l);                                   \
                   1667:                *len += l;                                                       \
                   1668:        }
                   1669: 
                   1670: #define TRACE_APPEND_STR(val)                                            \
                   1671:        TRACE_APPEND_STRL(val, sizeof(val)-1)
                   1672: 
                   1673: #define TRACE_APPEND_KEY(key)                                            \
                   1674:        if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
                   1675:            TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));           \
                   1676:        }
                   1677: 
                   1678: /* }}} */
                   1679: 
                   1680: 
                   1681: static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
                   1682: {
                   1683:        char **str;
                   1684:        int *len;
                   1685: 
                   1686:        str = va_arg(args, char**);
                   1687:        len = va_arg(args, int*);
                   1688: 
                   1689:        /* the trivial way would be to do:
                   1690:         * conver_to_string_ex(arg);
                   1691:         * append it and kill the now tmp arg.
                   1692:         * but that could cause some E_NOTICE and also damn long lines.
                   1693:         */
                   1694: 
                   1695:        switch (Z_TYPE_PP(arg)) {
                   1696:                case IS_NULL:
                   1697:                        TRACE_APPEND_STR("NULL, ");
                   1698:                        break;
                   1699:                case IS_STRING: {
                   1700:                        int l_added;
                   1701:                        TRACE_APPEND_CHR('\'');
                   1702:                        if (Z_STRLEN_PP(arg) > 15) {
                   1703:                                TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
                   1704:                                TRACE_APPEND_STR("...', ");
                   1705:                                l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
                   1706:                        } else {
                   1707:                                l_added = Z_STRLEN_PP(arg);
                   1708:                                TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
                   1709:                                TRACE_APPEND_STR("', ");
                   1710:                                l_added += 3 + 1;
                   1711:                        }
                   1712:                        while (--l_added) {
                   1713:                                if ((*str)[*len - l_added] < 32) {
                   1714:                                        (*str)[*len - l_added] = '?';
                   1715:                                }
                   1716:                        }
                   1717:                        break;
                   1718:                }
                   1719:                case IS_BOOL:
                   1720:                        if (Z_LVAL_PP(arg)) {
                   1721:                                TRACE_APPEND_STR("true, ");
                   1722:                        } else {
                   1723:                                TRACE_APPEND_STR("false, ");
                   1724:                        }
                   1725:                        break;
                   1726:                case IS_RESOURCE:
                   1727:                        TRACE_APPEND_STR("Resource id #");
                   1728:                        /* break; */
                   1729:                case IS_LONG: {
                   1730:                        long lval = Z_LVAL_PP(arg);
                   1731:                        char s_tmp[MAX_LENGTH_OF_LONG + 1];
                   1732:                        int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */
                   1733:                        TRACE_APPEND_STRL(s_tmp, l_tmp);
                   1734:                        TRACE_APPEND_STR(", ");
                   1735:                        break;
                   1736:                }
                   1737:                case IS_DOUBLE: {
                   1738:                        double dval = Z_DVAL_PP(arg);
                   1739:                        char *s_tmp;
                   1740:                        int l_tmp;
                   1741: 
                   1742:                        s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
                   1743:                        l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */
                   1744:                        TRACE_APPEND_STRL(s_tmp, l_tmp);
                   1745:                        /* %G already handles removing trailing zeros from the fractional part, yay */
                   1746:                        efree(s_tmp);
                   1747:                        TRACE_APPEND_STR(", ");
                   1748:                        break;
                   1749:                }
                   1750:                case IS_ARRAY:
                   1751:                        TRACE_APPEND_STR("Array, ");
                   1752:                        break;
                   1753:                case IS_OBJECT: {
                   1754:                        char *class_name;
                   1755:                        zend_uint class_name_len;
                   1756:                        int dupl;
                   1757: 
                   1758:                        TRACE_APPEND_STR("Object(");
                   1759: 
                   1760:                        dupl = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);
                   1761: 
                   1762:                        TRACE_APPEND_STRL(class_name, class_name_len);
                   1763:                        if (!dupl) {
                   1764:                                efree(class_name);
                   1765:                        }
                   1766: 
                   1767:                        TRACE_APPEND_STR("), ");
                   1768:                        break;
                   1769:                }
                   1770:                default:
                   1771:                        break;
                   1772:        }
                   1773:        return ZEND_HASH_APPLY_KEEP;
                   1774: }
                   1775: /* }}} */
                   1776: 
                   1777: static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
                   1778: {
                   1779:        char *s_tmp, **str;
                   1780:        int *len, *num;
                   1781:        long line;
                   1782:        HashTable *ht = Z_ARRVAL_PP(frame);
                   1783:        zval **file, **tmp;
                   1784:        uint * level;
                   1785: 
                   1786:        level = va_arg(args, uint *);
                   1787:        str = va_arg(args, char**);
                   1788:        len = va_arg(args, int*);
                   1789:        num = va_arg(args, int*);
                   1790: 
                   1791:        if (!*level) {
                   1792:                return ZEND_HASH_APPLY_KEEP;
                   1793:        }
                   1794:        --*level;
                   1795: 
                   1796:        s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
                   1797:        sprintf(s_tmp, "#%d ", (*num)++);
                   1798:        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                   1799:        efree(s_tmp);
                   1800:        if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
                   1801:                if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
                   1802:                        line = Z_LVAL_PP(tmp);
                   1803:                } else {
                   1804:                        line = 0;
                   1805:                }
                   1806:                s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1);
                   1807:                sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line);
                   1808:                TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                   1809:                efree(s_tmp);
                   1810:        } else {
                   1811:                TRACE_APPEND_STR("[internal function]: ");
                   1812:        }
                   1813:        TRACE_APPEND_KEY("class");
                   1814:        TRACE_APPEND_KEY("type");
                   1815:        TRACE_APPEND_KEY("function");
                   1816:        TRACE_APPEND_CHR('(');
                   1817:        if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
                   1818:                int last_len = *len;
                   1819:                zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
                   1820:                if (last_len != *len) {
                   1821:                        *len -= 2; /* remove last ', ' */
                   1822:                }
                   1823:        }
                   1824:        TRACE_APPEND_STR(")\n");
                   1825:        return ZEND_HASH_APPLY_KEEP;
                   1826: }
                   1827: /* }}} */
                   1828: #endif
                   1829: 
                   1830: 
                   1831: PHPAPI char * mysqlnd_get_backtrace(uint max_levels, size_t * length TSRMLS_DC)
                   1832: {
                   1833:        zval *trace;
                   1834:        char *res = estrdup(""), **str = &res, *s_tmp;
                   1835:        int res_len = 0, *len = &res_len, num = 0;
                   1836:        if (max_levels == 0) {
                   1837:                max_levels = 99999;
                   1838:        }
                   1839: 
                   1840:        MAKE_STD_ZVAL(trace);
                   1841:        zend_fetch_debug_backtrace(trace, 0, 0 TSRMLS_CC);
                   1842: 
                   1843:        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);
                   1844:        zval_ptr_dtor(&trace);
                   1845: 
                   1846:        if (max_levels) {
                   1847:                s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1);
                   1848:                sprintf(s_tmp, "#%d {main}", num);
                   1849:                TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                   1850:                efree(s_tmp);
                   1851:        }
                   1852: 
                   1853:        res[res_len] = '\0';
                   1854:        *length = res_len;
                   1855: 
                   1856:        return res;
                   1857: }
                   1858: 
                   1859: /*
                   1860:  * Local variables:
                   1861:  * tab-width: 4
                   1862:  * c-basic-offset: 4
                   1863:  * End:
                   1864:  * vim600: noet sw=4 ts=4 fdm=marker
                   1865:  * vim<600: noet sw=4 ts=4
                   1866:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>